<commit_before>/*
Copyright 2019 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 fi

import (
	"bytes"
	"fmt"
	"io"
	"reflect"
	"sort"
	"strings"
	"sync"

	"k8s.io/klog/v2"
	"k8s.io/kops/pkg/assets"
	"k8s.io/kops/pkg/diff"
	"k8s.io/kops/util/pkg/reflectutils"
)

// DryRunTarget is a special Target that does not execute anything, but instead tracks all changes.
// By running against a DryRunTarget, a list of changes that would be made can be easily collected,
// without any special support from the Tasks.
type DryRunTarget struct {
	mutex sync.Mutex

	changes   []*render
	deletions []Deletion

	// The destination to which the final report will be printed on Finish()
	out io.Writer

	// assetBuilder records all assets used
	assetBuilder *assets.AssetBuilder
}

type render struct {
	a       Task
	aIsNil  bool
	e       Task
	changes Task
}

// ByTaskKey sorts []*render by TaskKey (type/name)
type ByTaskKey []*render

func (a ByTaskKey) Len() int      { return len(a) }
func (a ByTaskKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByTaskKey) Less(i, j int) bool {
	return buildTaskKey(a[i].e) < buildTaskKey(a[j].e)
}

// DeletionByTaskName sorts []Deletion by TaskName
type DeletionByTaskName []Deletion

func (a DeletionByTaskName) Len() int      { return len(a) }
func (a DeletionByTaskName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a DeletionByTaskName) Less(i, j int) bool {
	return a[i].TaskName() < a[j].TaskName()
}

var _ Target = &DryRunTarget{}

func NewDryRunTarget(assetBuilder *assets.AssetBuilder, out io.Writer) *DryRunTarget {
	t := &DryRunTarget{}
	t.out = out
	t.assetBuilder = assetBuilder
	return t
}

func (t *DryRunTarget) ProcessDeletions() bool {
	// We display deletions
	return true
}

func (t *DryRunTarget) Render(a, e, changes Task) error {
	valA := reflect.ValueOf(a)
	aIsNil := valA.IsNil()

	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.changes = append(t.changes, &render{
		a:       a,
		aIsNil:  aIsNil,
		e:       e,
		changes: changes,
	})
	return nil
}

func (t *DryRunTarget) Delete(deletion Deletion) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.deletions = append(t.deletions, deletion)

	return nil
}

func idForTask(taskMap map[string]Task, t Task) string {
	for k, v := range taskMap {
		if v == t {
			// Skip task type, if present (taskType/taskName)
			firstSlash := strings.Index(k, "/")
			if firstSlash != -1 {
				k = k[firstSlash+1:]
			}
			return k
		}
	}
	klog.Fatalf("unknown task: %v", t)
	return "?"
}

func (t *DryRunTarget) PrintReport(taskMap map[string]Task, out io.Writer) error {
	b := &bytes.Buffer{}

	if len(t.changes) != 0 {
		var creates []*render
		var updates []*render

		for _, r := range t.changes {
			if r.aIsNil {
				creates = append(creates, r)
			} else {
				updates = append(updates, r)
			}
		}

		// Give everything a consistent ordering
		sort.Sort(ByTaskKey(creates))
		sort.Sort(ByTaskKey(updates))

		if len(creates) != 0 {
			fmt.Fprintf(b, "Will create resources:\n")
			for _, r := range creates {
				taskName := getTaskName(r.changes)
				fmt.Fprintf(b, "  %s/%s\n", taskName, idForTask(taskMap, r.e))

				changes := reflect.ValueOf(r.changes)
				if changes.Kind() == reflect.Ptr && !changes.IsNil() {
					changes = changes.Elem()
				}

				if changes.Kind() == reflect.Struct {
					for i := 0; i < changes.NumField(); i++ {

						field := changes.Field(i)

						fieldName := changes.Type().Field(i).Name
						if changes.Type().Field(i).PkgPath != "" {
							// Not exported
							continue
						}

						fieldValue := reflectutils.ValueAsString(field)

						shouldPrint := true
						if fieldName == "Name" {
							// The field name is already printed above, no need to repeat it.
							shouldPrint = false
						}
						if fieldName == "Lifecycle" {
							// Lifecycle is a "system" field; no need to show it
							shouldPrint = false
						}
						if fieldValue == "<nil>" || fieldValue == "<resource>" {
							// Uninformative
							shouldPrint = false
						}
						if fieldValue == "id:<nil>" {
							// Uninformative, but we can often print the name instead
							name := ""
							if field.CanInterface() {
								hasName, ok := field.Interface().(HasName)
								if ok {
									name = StringValue(hasName.GetName())
								}
							}
							if name != "" {
								fieldValue = "name:" + name
							} else {
								shouldPrint = false
							}
						}
						if shouldPrint {
							fmt.Fprintf(b, "  \t%-20s\t%s\n", fieldName, fieldValue)
						}
					}
				}

				fmt.Fprintf(b, "\n")
			}
		}

		if len(updates) != 0 {
			fmt.Fprintf(b, "Will modify resources:\n")
			// We can't use our reflection helpers here - we want corresponding values from a,e,c
			for _, r := range updates {
				changeList, err := buildChangeList(r.a, r.e, r.changes)
				if err != nil {
					return err
				}
				taskName := getTaskName(r.changes)
				fmt.Fprintf(b, "  %s/%s\n", taskName, idForTask(taskMap, r.e))

				if len(changeList) == 0 {
					fmt.Fprintf(b, "   internal consistency error!\n")
					fmt.Fprintf(b, "    actual: %+v\n", r.a)
					fmt.Fprintf(b, "    expect: %+v\n", r.e)
					continue
				}

				for _, change := range changeList {
					lines := strings.Split(change.Description, "\n")
					if len(lines) == 1 {
						fmt.Fprintf(b, "  \t%-20s\t%s\n", change.FieldName, change.Description)
					} else {
						fmt.Fprintf(b, "  \t%-20s\n", change.FieldName)
						for _, line := range lines {
							fmt.Fprintf(b, "  \t%-20s\t%s\n", "", line)
						}
					}
				}
				fmt.Fprintf(b, "\n")
			}
		}
	}

	if len(t.deletions) != 0 {
		// Give everything a consistent ordering
		sort.Sort(DeletionByTaskName(t.deletions))

		fmt.Fprintf(b, "Will delete items:\n")
		for _, d := range t.deletions {
			fmt.Fprintf(b, "  %-20s %s\n", d.TaskName(), d.Item())
		}
	}

	if len(t.assetBuilder.ContainerAssets) != 0 {
		klog.V(4).Infof("ContainerAssets:")
		for _, a := range t.assetBuilder.ContainerAssets {
			klog.V(4).Infof("  %s %s", a.DockerImage, a.CanonicalLocation)
		}
	}

	if len(t.assetBuilder.FileAssets) != 0 {
		klog.V(4).Infof("FileAssets:")
		for _, a := range t.assetBuilder.FileAssets {
			if a.DownloadURL != nil && a.CanonicalURL != nil {
				klog.V(4).Infof("  %s %s", a.DownloadURL.String(), a.CanonicalURL.String())
			} else if a.DownloadURL != nil {
				klog.V(4).Infof("  %s", a.DownloadURL.String())
			}
		}
	}

	_, err := out.Write(b.Bytes())
	return err
}

type change struct {
	FieldName   string
	Description string
}

func buildChangeList(a, e, changes Task) ([]change, error) {
	var changeList []change

	valC := reflect.ValueOf(changes)
	valA := reflect.ValueOf(a)
	valE := reflect.ValueOf(e)
	if valC.Kind() == reflect.Ptr && !valC.IsNil() {
		valC = valC.Elem()
	}
	if valA.Kind() == reflect.Ptr && !valA.IsNil() {
		valA = valA.Elem()
	}
	if valE.Kind() == reflect.Ptr && !valE.IsNil() {
		valE = valE.Elem()
	}
	if valC.Kind() == reflect.Struct {
		for i := 0; i < valC.NumField(); i++ {
			if valC.Type().Field(i).PkgPath != "" {
				// Not exported
				continue
			}

			fieldValC := valC.Field(i)

			changed := true
			switch fieldValC.Kind() {
			case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map:
				changed = !fieldValC.IsNil()

			case reflect.String:
				changed = fieldValC.Interface().(string) != ""
			}
			if !changed {
				continue
			}

			if fieldValC.Kind() == reflect.String && fieldValC.Interface().(string) == "" {
				// No change
				continue
			}

			fieldValE := valE.Field(i)

			description := ""
			ignored := false
			if fieldValE.CanInterface() {
				fieldValA := valA.Field(i)

				switch fieldValE.Interface().(type) {
				//case SimpleUnit:
				//	ignored = true
				case Resource, ResourceHolder:
					resA, okA := tryResourceAsString(fieldValA)
					resE, okE := tryResourceAsString(fieldValE)
					if okA && okE {
						description = diff.FormatDiff(resA, resE)
					}
				}

				if !ignored && description == "" {
					description = fmt.Sprintf(" %v -> %v", reflectutils.ValueAsString(fieldValA), reflectutils.ValueAsString(fieldValE))
				}
			}
			if ignored {
				continue
			}
			changeList = append(changeList, change{FieldName: valC.Type().Field(i).Name, Description: description})
		}
	} else {
		return nil, fmt.Errorf("unhandled change type: %v", valC.Type())
	}

	return changeList, nil
}

func tryResourceAsString(v reflect.Value) (string, bool) {
	if !v.IsValid() {
		return "", false
	}
	if !v.CanInterface() {
		return "", false
	}

	// Guard against nil interface go-tcha
	if v.Kind() == reflect.Interface && v.IsNil() {
		return "", false
	}
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return "", false
	}

	intf := v.Interface()
	if res, ok := intf.(Resource); ok {
		s, err := ResourceAsString(res)
		if err != nil {
			klog.Warningf("error converting to resource: %v", err)
			return "", false
		}
		return s, true
	}
	if res, ok := intf.(*ResourceHolder); ok {
		s, err := res.AsString()
		if err != nil {
			klog.Warningf("error converting to resource: %v", err)
			return "", false
		}
		return s, true
	}
	return "", false
}

func getTaskName(t Task) string {
	s := fmt.Sprintf("%T", t)
	lastDot := strings.LastIndexByte(s, '.')
	if lastDot != -1 {
		s = s[lastDot+1:]
	}
	return s
}

// Finish is called at the end of a run, and prints a list of changes to the configured Writer
func (t *DryRunTarget) Finish(taskMap map[string]Task) error {
	return t.PrintReport(taskMap, t.out)
}

// Deletions returns all task names which is going to be deleted
func (t *DryRunTarget) Deletions() []string {
	var deletions []string
	for _, d := range t.deletions {
		deletions = append(deletions, d.TaskName())
	}
	return deletions
}

// Changes returns tasks which is going to be created or updated
func (t *DryRunTarget) Changes() (map[string]Task, map[string]Task) {
	creates := make(map[string]Task)
	updates := make(map[string]Task)
	for _, r := range t.changes {
		if r.aIsNil {
			creates[getTaskName(r.changes)] = r.changes
		} else {
			updates[getTaskName(r.changes)] = r.changes
		}
	}
	return creates, updates
}

// HasChanges returns true iff any changes would have been made
func (t *DryRunTarget) HasChanges() bool {
	return len(t.changes)+len(t.deletions) != 0
}
<commit_msg>Print changes also for consistency errors<commit_after>/*
Copyright 2019 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 fi

import (
	"bytes"
	"fmt"
	"io"
	"reflect"
	"sort"
	"strings"
	"sync"

	"k8s.io/klog/v2"
	"k8s.io/kops/pkg/assets"
	"k8s.io/kops/pkg/diff"
	"k8s.io/kops/util/pkg/reflectutils"
)

// DryRunTarget is a special Target that does not execute anything, but instead tracks all changes.
// By running against a DryRunTarget, a list of changes that would be made can be easily collected,
// without any special support from the Tasks.
type DryRunTarget struct {
	mutex sync.Mutex

	changes   []*render
	deletions []Deletion

	// The destination to which the final report will be printed on Finish()
	out io.Writer

	// assetBuilder records all assets used
	assetBuilder *assets.AssetBuilder
}

type render struct {
	a       Task
	aIsNil  bool
	e       Task
	changes Task
}

// ByTaskKey sorts []*render by TaskKey (type/name)
type ByTaskKey []*render

func (a ByTaskKey) Len() int      { return len(a) }
func (a ByTaskKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByTaskKey) Less(i, j int) bool {
	return buildTaskKey(a[i].e) < buildTaskKey(a[j].e)
}

// DeletionByTaskName sorts []Deletion by TaskName
type DeletionByTaskName []Deletion

func (a DeletionByTaskName) Len() int      { return len(a) }
func (a DeletionByTaskName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a DeletionByTaskName) Less(i, j int) bool {
	return a[i].TaskName() < a[j].TaskName()
}

var _ Target = &DryRunTarget{}

func NewDryRunTarget(assetBuilder *assets.AssetBuilder, out io.Writer) *DryRunTarget {
	t := &DryRunTarget{}
	t.out = out
	t.assetBuilder = assetBuilder
	return t
}

func (t *DryRunTarget) ProcessDeletions() bool {
	// We display deletions
	return true
}

func (t *DryRunTarget) Render(a, e, changes Task) error {
	valA := reflect.ValueOf(a)
	aIsNil := valA.IsNil()

	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.changes = append(t.changes, &render{
		a:       a,
		aIsNil:  aIsNil,
		e:       e,
		changes: changes,
	})
	return nil
}

func (t *DryRunTarget) Delete(deletion Deletion) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	t.deletions = append(t.deletions, deletion)

	return nil
}

func idForTask(taskMap map[string]Task, t Task) string {
	for k, v := range taskMap {
		if v == t {
			// Skip task type, if present (taskType/taskName)
			firstSlash := strings.Index(k, "/")
			if firstSlash != -1 {
				k = k[firstSlash+1:]
			}
			return k
		}
	}
	klog.Fatalf("unknown task: %v", t)
	return "?"
}

func (t *DryRunTarget) PrintReport(taskMap map[string]Task, out io.Writer) error {
	b := &bytes.Buffer{}

	if len(t.changes) != 0 {
		var creates []*render
		var updates []*render

		for _, r := range t.changes {
			if r.aIsNil {
				creates = append(creates, r)
			} else {
				updates = append(updates, r)
			}
		}

		// Give everything a consistent ordering
		sort.Sort(ByTaskKey(creates))
		sort.Sort(ByTaskKey(updates))

		if len(creates) != 0 {
			fmt.Fprintf(b, "Will create resources:\n")
			for _, r := range creates {
				taskName := getTaskName(r.changes)
				fmt.Fprintf(b, "  %s/%s\n", taskName, idForTask(taskMap, r.e))

				changes := reflect.ValueOf(r.changes)
				if changes.Kind() == reflect.Ptr && !changes.IsNil() {
					changes = changes.Elem()
				}

				if changes.Kind() == reflect.Struct {
					for i := 0; i < changes.NumField(); i++ {

						field := changes.Field(i)

						fieldName := changes.Type().Field(i).Name
						if changes.Type().Field(i).PkgPath != "" {
							// Not exported
							continue
						}

						fieldValue := reflectutils.ValueAsString(field)

						shouldPrint := true
						if fieldName == "Name" {
							// The field name is already printed above, no need to repeat it.
							shouldPrint = false
						}
						if fieldName == "Lifecycle" {
							// Lifecycle is a "system" field; no need to show it
							shouldPrint = false
						}
						if fieldValue == "<nil>" || fieldValue == "<resource>" {
							// Uninformative
							shouldPrint = false
						}
						if fieldValue == "id:<nil>" {
							// Uninformative, but we can often print the name instead
							name := ""
							if field.CanInterface() {
								hasName, ok := field.Interface().(HasName)
								if ok {
									name = StringValue(hasName.GetName())
								}
							}
							if name != "" {
								fieldValue = "name:" + name
							} else {
								shouldPrint = false
							}
						}
						if shouldPrint {
							fmt.Fprintf(b, "  \t%-20s\t%s\n", fieldName, fieldValue)
						}
					}
				}

				fmt.Fprintf(b, "\n")
			}
		}

		if len(updates) != 0 {
			fmt.Fprintf(b, "Will modify resources:\n")
			// We can't use our reflection helpers here - we want corresponding values from a,e,c
			for _, r := range updates {
				changeList, err := buildChangeList(r.a, r.e, r.changes)
				if err != nil {
					return err
				}
				taskName := getTaskName(r.changes)
				fmt.Fprintf(b, "  %s/%s\n", taskName, idForTask(taskMap, r.e))

				if len(changeList) == 0 {
					fmt.Fprintf(b, "   internal consistency error!\n")
					fmt.Fprintf(b, "    actual: %+v\n", r.a)
					fmt.Fprintf(b, "    expect: %+v\n", r.e)
					fmt.Fprintf(b, "    change: %+v\n", r.changes)
					continue
				}

				for _, change := range changeList {
					lines := strings.Split(change.Description, "\n")
					if len(lines) == 1 {
						fmt.Fprintf(b, "  \t%-20s\t%s\n", change.FieldName, change.Description)
					} else {
						fmt.Fprintf(b, "  \t%-20s\n", change.FieldName)
						for _, line := range lines {
							fmt.Fprintf(b, "  \t%-20s\t%s\n", "", line)
						}
					}
				}
				fmt.Fprintf(b, "\n")
			}
		}
	}

	if len(t.deletions) != 0 {
		// Give everything a consistent ordering
		sort.Sort(DeletionByTaskName(t.deletions))

		fmt.Fprintf(b, "Will delete items:\n")
		for _, d := range t.deletions {
			fmt.Fprintf(b, "  %-20s %s\n", d.TaskName(), d.Item())
		}
	}

	if len(t.assetBuilder.ContainerAssets) != 0 {
		klog.V(4).Infof("ContainerAssets:")
		for _, a := range t.assetBuilder.ContainerAssets {
			klog.V(4).Infof("  %s %s", a.DockerImage, a.CanonicalLocation)
		}
	}

	if len(t.assetBuilder.FileAssets) != 0 {
		klog.V(4).Infof("FileAssets:")
		for _, a := range t.assetBuilder.FileAssets {
			if a.DownloadURL != nil && a.CanonicalURL != nil {
				klog.V(4).Infof("  %s %s", a.DownloadURL.String(), a.CanonicalURL.String())
			} else if a.DownloadURL != nil {
				klog.V(4).Infof("  %s", a.DownloadURL.String())
			}
		}
	}

	_, err := out.Write(b.Bytes())
	return err
}

type change struct {
	FieldName   string
	Description string
}

func buildChangeList(a, e, changes Task) ([]change, error) {
	var changeList []change

	valC := reflect.ValueOf(changes)
	valA := reflect.ValueOf(a)
	valE := reflect.ValueOf(e)
	if valC.Kind() == reflect.Ptr && !valC.IsNil() {
		valC = valC.Elem()
	}
	if valA.Kind() == reflect.Ptr && !valA.IsNil() {
		valA = valA.Elem()
	}
	if valE.Kind() == reflect.Ptr && !valE.IsNil() {
		valE = valE.Elem()
	}
	if valC.Kind() == reflect.Struct {
		for i := 0; i < valC.NumField(); i++ {
			if valC.Type().Field(i).PkgPath != "" {
				// Not exported
				continue
			}

			fieldValC := valC.Field(i)

			changed := true
			switch fieldValC.Kind() {
			case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map:
				changed = !fieldValC.IsNil()

			case reflect.String:
				changed = fieldValC.Interface().(string) != ""
			}
			if !changed {
				continue
			}

			if fieldValC.Kind() == reflect.String && fieldValC.Interface().(string) == "" {
				// No change
				continue
			}

			fieldValE := valE.Field(i)

			description := ""
			ignored := false
			if fieldValE.CanInterface() {
				fieldValA := valA.Field(i)

				switch fieldValE.Interface().(type) {
				//case SimpleUnit:
				//	ignored = true
				case Resource, ResourceHolder:
					resA, okA := tryResourceAsString(fieldValA)
					resE, okE := tryResourceAsString(fieldValE)
					if okA && okE {
						description = diff.FormatDiff(resA, resE)
					}
				}

				if !ignored && description == "" {
					description = fmt.Sprintf(" %v -> %v", reflectutils.ValueAsString(fieldValA), reflectutils.ValueAsString(fieldValE))
				}
			}
			if ignored {
				continue
			}
			changeList = append(changeList, change{FieldName: valC.Type().Field(i).Name, Description: description})
		}
	} else {
		return nil, fmt.Errorf("unhandled change type: %v", valC.Type())
	}

	return changeList, nil
}

func tryResourceAsString(v reflect.Value) (string, bool) {
	if !v.IsValid() {
		return "", false
	}
	if !v.CanInterface() {
		return "", false
	}

	// Guard against nil interface go-tcha
	if v.Kind() == reflect.Interface && v.IsNil() {
		return "", false
	}
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return "", false
	}

	intf := v.Interface()
	if res, ok := intf.(Resource); ok {
		s, err := ResourceAsString(res)
		if err != nil {
			klog.Warningf("error converting to resource: %v", err)
			return "", false
		}
		return s, true
	}
	if res, ok := intf.(*ResourceHolder); ok {
		s, err := res.AsString()
		if err != nil {
			klog.Warningf("error converting to resource: %v", err)
			return "", false
		}
		return s, true
	}
	return "", false
}

func getTaskName(t Task) string {
	s := fmt.Sprintf("%T", t)
	lastDot := strings.LastIndexByte(s, '.')
	if lastDot != -1 {
		s = s[lastDot+1:]
	}
	return s
}

// Finish is called at the end of a run, and prints a list of changes to the configured Writer
func (t *DryRunTarget) Finish(taskMap map[string]Task) error {
	return t.PrintReport(taskMap, t.out)
}

// Deletions returns all task names which is going to be deleted
func (t *DryRunTarget) Deletions() []string {
	var deletions []string
	for _, d := range t.deletions {
		deletions = append(deletions, d.TaskName())
	}
	return deletions
}

// Changes returns tasks which is going to be created or updated
func (t *DryRunTarget) Changes() (map[string]Task, map[string]Task) {
	creates := make(map[string]Task)
	updates := make(map[string]Task)
	for _, r := range t.changes {
		if r.aIsNil {
			creates[getTaskName(r.changes)] = r.changes
		} else {
			updates[getTaskName(r.changes)] = r.changes
		}
	}
	return creates, updates
}

// HasChanges returns true iff any changes would have been made
func (t *DryRunTarget) HasChanges() bool {
	return len(t.changes)+len(t.deletions) != 0
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

package caller

import (
	"path/filepath"
	"regexp"
	"testing"
)

func TestCallResolver(t *testing.T) {
	cr := NewCallResolver(0, regexp.MustCompile(`resolver_test\.go.*$`))
	// Also test that caching doesn't have obvious hiccups.
	for i := 0; i < 2; i++ {
		if l := len(cr.cache); l != i {
			t.Fatalf("cache has %d entries, expected %d", l, i)
		}
		file, _, fun := func() (string, int, string) {
			return cr.Lookup(1)
		}()
		if file != "resolver_test.go" {
			t.Fatalf("wrong file '%s'", file)
		}
		if fun != "TestCallResolver" {
			t.Fatalf("unexpected caller reported: %s", fun)
		}
	}
}

func TestDefaultCallResolver(t *testing.T) {
	defer func() { defaultCallResolver.cache = map[uintptr]*cachedLookup{} }()

	for i := 0; i < 2; i++ {
		if l := len(defaultCallResolver.cache); l != i {
			t.Fatalf("cache has %d entries, expected %d", l, i)
		}
		file, _, fun := Lookup(0)
		if fun != "TestDefaultCallResolver" {
			t.Fatalf("unexpected caller reported: %s", fun)
		}

		if file != filepath.Join("util", "caller", "resolver_test.go") {
			t.Fatalf("wrong file '%s'", file)
		}
	}
}
<commit_msg>add benchmark of caller lookup<commit_after>// Copyright 2015 The Cockroach 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.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

package caller

import (
	"fmt"
	"path/filepath"
	"regexp"
	"testing"
)

func TestCallResolver(t *testing.T) {
	cr := NewCallResolver(0, regexp.MustCompile(`resolver_test\.go.*$`))
	// Also test that caching doesn't have obvious hiccups.
	for i := 0; i < 2; i++ {
		if l := len(cr.cache); l != i {
			t.Fatalf("cache has %d entries, expected %d", l, i)
		}
		file, _, fun := func() (string, int, string) {
			return cr.Lookup(1)
		}()
		if file != "resolver_test.go" {
			t.Fatalf("wrong file '%s'", file)
		}
		if fun != "TestCallResolver" {
			t.Fatalf("unexpected caller reported: %s", fun)
		}
	}
}

func TestDefaultCallResolver(t *testing.T) {
	defer func() { defaultCallResolver.cache = map[uintptr]*cachedLookup{} }()

	for i := 0; i < 2; i++ {
		if l := len(defaultCallResolver.cache); l != i {
			t.Fatalf("cache has %d entries, expected %d", l, i)
		}
		file, _, fun := Lookup(0)
		if fun != "TestDefaultCallResolver" {
			t.Fatalf("unexpected caller reported: %s", fun)
		}

		if file != filepath.Join("util", "caller", "resolver_test.go") {
			t.Fatalf("wrong file '%s'", file)
		}
	}
}

func BenchmarkFormatedCaller(b *testing.B) {
	for i := 0; i < b.N; i++ {
		file, line, _ := Lookup(1)
		s := fmt.Sprintf("%s:%d", file, line)
		if testing.Verbose() {
			b.Log(s)
		}
	}
}

func BenchmarkSimpleCaller(b *testing.B) {
	for i := 0; i < b.N; i++ {
		file, line, _ := Lookup(1)
		if testing.Verbose() {
			s := fmt.Sprintf("%s:%d", file, line)
			b.Log(s)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Rob Thornton
// All rights reserved.
// This source code is governed by a Simplied BSD-License. Please see the
// LICENSE included in this distribution for a copy of the full license
// or, if one is not included, you may also find a copy at
// http://opensource.org/licenses/BSD-2-Clause

package comp

import (
	"fmt"
	"os"
	"reflect"
	"strconv"

	"github.com/rthornton128/calc/ast"
	"github.com/rthornton128/calc/parse"
	"github.com/rthornton128/calc/token"
)

type compiler struct {
	fp *os.File
}

func CompileFile(fname, src string) {

	var c compiler
	fp, err := os.Create(fname + ".c")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer fp.Close()

	f := parse.ParseFile(fname, src)
	if f == nil {
		os.Exit(1)
	}
	c.fp = fp
	c.compFile(f)
}

/* Utility */

func roundUp16(n int) int {
	if r := n % 16; r != 0 {
		return n + (16 - r)
	}
	return n
}

/* Main Compiler */

func (c *compiler) compNode(node ast.Node) int {
	switch n := node.(type) {
	case *ast.BasicLit: /* nop */
	case *ast.BinaryExpr:
		return c.compBinaryExpr(n)
	case *ast.DeclExpr:
		return c.compDeclExpr(n)
	}
	return 0
}

func (c *compiler) compBinaryExpr(b *ast.BinaryExpr) int {
	vars := map[string]int{}
	noffset := 0

	switch n := b.List[0].(type) {
	case *ast.BasicLit:
		c.compInt(n, "eax")
	case *ast.BinaryExpr:
		c.compBinaryExpr(n)
	case *ast.CallExpr:
		c.compCallExpr(n)
	case *ast.Ident:
		noffset = c.compIdent(n, noffset, vars, "movl(ebp+%d, eax);\n")
	}

	for _, node := range b.List[1:] {
		switch n := node.(type) {
		case *ast.BasicLit:
			c.compInt(n, "edx")
		case *ast.BinaryExpr:
			fmt.Fprintln(c.fp, "pushl(eax);")
			c.compBinaryExpr(n)
			fmt.Fprintln(c.fp, "movl(eax, edx);")
			fmt.Fprintln(c.fp, "popl(eax);")
		case *ast.CallExpr:
			fmt.Fprintln(c.fp, "pushl(eax);")
			c.compCallExpr(n)
			fmt.Fprintln(c.fp, "movl(eax, edx);")
			fmt.Fprintln(c.fp, "popl(eax);")
		case *ast.Ident:
			noffset = c.compIdent(n, noffset, vars, "movl(ebp+%d, edx);\n")
		}
		switch b.Op {
		case token.ADD:
			fmt.Fprintln(c.fp, "addl(edx, eax);")
		case token.SUB:
			fmt.Fprintln(c.fp, "subl(edx, eax);")
		case token.MUL:
			fmt.Fprintln(c.fp, "mull(edx, eax);")
		case token.QUO:
			fmt.Fprintln(c.fp, "divl(edx, eax);")
		case token.REM:
			fmt.Fprintln(c.fp, "reml(edx, eax);")
		}
	}

	return 0
}

func (c *compiler) compCallExpr(e *ast.CallExpr) int {
	//vars := map[string]int{}
	offset := 4

	for _, v := range e.Args {
		switch n := v.(type) {
		case *ast.BasicLit:
			c.compInt(n, fmt.Sprintf("esp+%d", offset))
			offset += 4
		}
	}
	fmt.Fprintf(c.fp, "%s();\n", e.Name.Name)
	return 0
}

func (c *compiler) compDeclExpr(d *ast.DeclExpr) int {
	if d.Name.Name == "main" {
		fmt.Fprintf(c.fp, "int %s(void) {\n", d.Name.Name)
		fmt.Fprintln(c.fp, "stack_init();")
	} else {
		fmt.Fprintf(c.fp, "void %s(void) {\n", d.Name.Name)
	}
	x := c.countVars(d)
	if x > 0 {
		fmt.Fprintf(c.fp, "enter(%d);\n", roundUp16(x))
		c.compNode(d.Body)
		fmt.Fprintln(c.fp, "leave();")
	} else {
		c.compNode(d.Body)
	}
	if d.Name.Name == "main" {
		fmt.Fprintln(c.fp, "printf(\"%d\\n\", *(int32_t *)eax);")
		fmt.Fprintln(c.fp, "stack_end();")
		fmt.Fprintf(c.fp, "return *(int32_t *)eax;\n")
	} else {
		fmt.Fprintf(c.fp, "return;\n")
	}
	fmt.Fprintln(c.fp, "}")
	return 0
}

func (c *compiler) compIdent(
	n *ast.Ident,
	noffset int,
	vars map[string]int,
	format string,
) int {
	offset, ok := vars[n.Name]
	if !ok {
		offset = noffset
		vars[n.Name] = offset
		noffset += 4
	}
	fmt.Fprintf(c.fp, format, offset)
	return noffset
}

func (c *compiler) compInt(n *ast.BasicLit, reg string) {
	i, err := strconv.Atoi(n.Lit)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Fprintf(c.fp, "setl(%d, %s);\n", i, reg)
}

func (c *compiler) compScopeDecls(scope *ast.Scope) {
	for k, v := range scope.Table {
		if v.Kind == ast.Decl {
			if v.Name != "main" {
				fmt.Fprintf(c.fp, "void %s(void);\n", k)
			}
			defer c.compNode(v.Value)
		}
	}
}

func (c *compiler) compFile(f *ast.File) {
	fmt.Fprintln(c.fp, "#include <stdio.h>")
	fmt.Fprintln(c.fp, "#include <runtime.h>")
	c.compScopeDecls(f.Scope)
}

func (c *compiler) countVars(n ast.Node) (x int) {
	if n != nil && !reflect.ValueOf(n).IsNil() {
		switch e := n.(type) {
		case *ast.DeclExpr:
			x = len(e.Params)
			x += c.countVars(e.Body)
		case *ast.IfExpr:
			x = c.countVars(e.Then)
			x = c.countVars(e.Else)
		case *ast.ExprList:
			for _, v := range e.List {
				x += c.countVars(v)
			}
		case *ast.VarExpr:
			x = 1
		}
	}
	return
}
<commit_msg>implement ifexpr, comparisons, initial ident symbol offsets, varexpr stub and typeexpr stub<commit_after>// Copyright (c) 2014, Rob Thornton
// All rights reserved.
// This source code is governed by a Simplied BSD-License. Please see the
// LICENSE included in this distribution for a copy of the full license
// or, if one is not included, you may also find a copy at
// http://opensource.org/licenses/BSD-2-Clause

package comp

import (
	"fmt"
	"os"
	"reflect"
	"strconv"

	"github.com/rthornton128/calc/ast"
	"github.com/rthornton128/calc/parse"
	"github.com/rthornton128/calc/token"
)

type compiler struct {
	fp       *os.File
	curDecl  string
	curScope *ast.Scope
	symbols  map[string]int
}

func CompileFile(fname, src string) {

	var c compiler
	fp, err := os.Create(fname + ".c")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer fp.Close()

	f := parse.ParseFile(fname, src)
	if f == nil {
		os.Exit(1)
	}
	c.fp = fp
	c.symbols = make(map[string]int)
	c.compFile(f)
}

/* Utility */

func roundUp16(n int) int {
	if r := n % 16; r != 0 {
		return n + (16 - r)
	}
	return n
}

/* Main Compiler */

func (c *compiler) compNode(node ast.Node) int {
	fmt.Println("compNode")
	switch n := node.(type) {
	case *ast.BasicLit:
		c.compInt(n, "eax")
	case *ast.BinaryExpr:
		return c.compBinaryExpr(n)
	case *ast.CallExpr:
		return c.compCallExpr(n)
	case *ast.DeclExpr:
		return c.compDeclExpr(n)
	case *ast.ExprList:
		for i := range n.List {
			c.compNode(n.List[i])
		}
	case *ast.Ident:
		c.compIdent(n, "movl(ebp+%d, eax);\n")
	case *ast.IfExpr:
		fmt.Println("if")
		c.compIfExpr(n)
	}
	return 0
}

func (c *compiler) compBinaryExpr(b *ast.BinaryExpr) int {
	switch n := b.List[0].(type) {
	case *ast.BasicLit:
		c.compInt(n, "eax")
	case *ast.BinaryExpr:
		c.compBinaryExpr(n)
	case *ast.CallExpr:
		c.compCallExpr(n)
	case *ast.Ident:
		c.compIdent(n, "movl(ebp+%d, eax);\n")
	}

	for _, node := range b.List[1:] {
		switch n := node.(type) {
		case *ast.BasicLit:
			c.compInt(n, "edx")
		case *ast.BinaryExpr:
			fmt.Fprintln(c.fp, "pushl(eax);")
			c.compBinaryExpr(n)
			fmt.Fprintln(c.fp, "movl(eax, edx);")
			fmt.Fprintln(c.fp, "popl(eax);")
		case *ast.CallExpr:
			fmt.Fprintln(c.fp, "pushl(eax);")
			c.compCallExpr(n)
			fmt.Fprintln(c.fp, "movl(eax, edx);")
			fmt.Fprintln(c.fp, "popl(eax);")
		case *ast.Ident:
			c.compIdent(n, "movl(ebp+%d, edx);\n")
		}
		switch b.Op {
		case token.ADD:
			fmt.Fprintln(c.fp, "addl(edx, eax);")
		case token.SUB:
			fmt.Fprintln(c.fp, "subl(edx, eax);")
		case token.MUL:
			fmt.Fprintln(c.fp, "mull(edx, eax);")
		case token.QUO:
			fmt.Fprintln(c.fp, "divl(edx, eax);")
		case token.REM:
			fmt.Fprintln(c.fp, "reml(edx, eax);")
		case token.AND:
			fmt.Fprintln(c.fp, "andl(eax, edx);")
		case token.EQL:
			fmt.Fprintln(c.fp, "eql(eax, edx);")
		case token.GTE:
			fmt.Fprintln(c.fp, "gel(eax, edx);")
		case token.GTT:
			fmt.Fprintln(c.fp, "gtl(eax, edx);")
		case token.LST:
			fmt.Fprintln(c.fp, "ltl(eax, edx);")
		case token.LTE:
			fmt.Fprintln(c.fp, "lel(eax, edx);")
		case token.NEQ:
			fmt.Fprintln(c.fp, "nel(eax, edx);")
		case token.OR:
			fmt.Fprintln(c.fp, "orl(eax, edx);")
		}
	}

	return 0
}

func (c *compiler) compCallExpr(e *ast.CallExpr) int {
	offset := 4

	for _, v := range e.Args {
		switch n := v.(type) {
		case *ast.BasicLit:
			c.compInt(n, fmt.Sprintf("esp+%d", offset))
			offset += 4
		default:
			c.compNode(n)
		}
	}
	fmt.Fprintf(c.fp, "%s();\n", e.Name.Name)
	return 0
}

func (c *compiler) compDeclExpr(d *ast.DeclExpr) int {
	offset := 0
	c.curDecl = d.Name.Name
	for _, p := range d.Params {
		c.symbols[c.curDecl+p.Name] = offset
		offset += 4
	}
	if d.Name.Name == "main" {
		fmt.Fprintf(c.fp, "int %s(void) {\n", d.Name.Name)
		fmt.Fprintln(c.fp, "stack_init();")
	} else {
		fmt.Fprintf(c.fp, "void %s(void) {\n", d.Name.Name)
	}
	x := c.countVars(d)
	if x > 0 {
		fmt.Fprintf(c.fp, "enter(%d);\n", roundUp16(x))
		fmt.Println("comp body")
		c.compNode(d.Body)
		fmt.Fprintln(c.fp, "leave();")
	} else {
		c.compNode(d.Body)
	}
	if d.Name.Name == "main" {
		fmt.Fprintln(c.fp, "printf(\"%d\\n\", *(int32_t *)eax);")
		fmt.Fprintln(c.fp, "stack_end();")
		fmt.Fprintf(c.fp, "return *(int32_t *)eax;\n")
	} else {
		fmt.Fprintf(c.fp, "return;\n")
	}
	fmt.Fprintln(c.fp, "}")
	//c.curScope = c.curScope.Parent
	return 0
}

func (c *compiler) compFile(f *ast.File) {
	fmt.Fprintln(c.fp, "#include <stdio.h>")
	fmt.Fprintln(c.fp, "#include <runtime.h>")
	c.curScope = f.Scope
	c.compScopeDecls()
}

func (c *compiler) compIdent(n *ast.Ident, format string) {
	offset, ok := c.symbols[c.curDecl+n.Name]
	if !ok {
		fmt.Println("error: no offset")
	}
	fmt.Fprintf(c.fp, format, offset)
}

func (c *compiler) compIfExpr(n *ast.IfExpr) {
	switch e := n.Cond.(type) {
	case *ast.BasicLit:
		c.compInt(e, "eax")
	case *ast.BinaryExpr:
		c.compBinaryExpr(e)
	}
	fmt.Fprintln(c.fp, "if (*(int32_t *)eax != 0) {")
	c.compNode(n.Then)
	if n.Else != nil && !reflect.ValueOf(n.Else).IsNil() {
		fmt.Fprintln(c.fp, "} else {")
		c.compNode(n.Else)
	}
	fmt.Fprintln(c.fp, "}")
}

func (c *compiler) compInt(n *ast.BasicLit, reg string) {
	i, err := strconv.Atoi(n.Lit)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Fprintf(c.fp, "setl(%d, %s);\n", i, reg)
}

func (c *compiler) compScopeDecls() {
	for k, v := range c.curScope.Table {
		if v.Kind == ast.Decl {
			if v.Name != "main" {
				fmt.Fprintf(c.fp, "void %s(void);\n", k)
			}
			defer c.compNode(v.Value)
		}
	}
}

func (c *compiler) compVar(n *ast.VarExpr) {
	ob := n.Object
	if ob.Value != nil && !reflect.ValueOf(ob.Value).IsNil() {
		if ob.Type.Name != c.typeExpr(ob.Value) {
			fmt.Println("danger will robinson!")
		}
	}
}

func (c *compiler) countVars(n ast.Node) (x int) {
	/* TODO: assign var offset here? */
	if n != nil && !reflect.ValueOf(n).IsNil() {
		switch e := n.(type) {
		case *ast.DeclExpr:
			x = len(e.Params)
			x += c.countVars(e.Body)
		case *ast.IfExpr:
			x = c.countVars(e.Then)
			x = c.countVars(e.Else)
		case *ast.ExprList:
			for _, v := range e.List {
				x += c.countVars(v)
			}
		case *ast.VarExpr:
			x = 1
		}
	}
	return
}

func (c *compiler) typeExpr(e ast.Expr) string {
	switch n := e.(type) {
	case *ast.BasicLit:
		switch n.Kind {
		case token.INTEGER:
			return "int"
		}
	case *ast.BinaryExpr:
		return "int"
	case *ast.CallExpr:
		ob := c.curScope.Lookup(n.Name.Name)
		return c.typeExpr(ob.Value)
	case *ast.DeclExpr:
		return n.Type.Name
	case *ast.ExprList:
		return c.typeExpr(n.List[len(n.List)-1])
	case *ast.IfExpr:
		return n.Type.Name
	case *ast.VarExpr:
		return c.typeExpr(n.Object.Value)
	}
	return ""
}
<|endoftext|>


<commit_before>package conductor

import (
	"encoding/json"
	"net/http"
)

// ReadJSON reads data from request body to the interface provided.
func ReadJSON(r *http.Request, data interface{}) error {
	body := make([]byte, r.ContentLength)

	if _, err := r.Body.Read(body); err != nil {
		return err
	}

	if err := json.Unmarshal(body, data); err != nil {
		return err
	}

	return nil
}

// WriteJSON writes data as JSON to the output writer.
// Data expected to be able to be marshaled to JSON.
func WriteJSON(w http.ResponseWriter, data interface{}) error {
	output, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(output)
	return nil
}
<commit_msg>Use ioutil.ReadAll for ReadJSON<commit_after>package conductor

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
)

// ReadJSON reads data from request body to the interface provided.
func ReadJSON(r *http.Request, data interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(body, data); err != nil {
		return err
	}

	return nil
}

// WriteJSON writes data as JSON to the output writer.
// Data expected to be able to be marshaled to JSON.
func WriteJSON(w http.ResponseWriter, data interface{}) error {
	output, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(output)
	return nil
}
<|endoftext|>


<commit_before>/*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 conf

import (
	"encoding/json"
	"github.com/op/go-logging"
	"github.com/skyrings/skyring-common/tools/logger"
	"io/ioutil"
	"path"
)

const (
	ProviderConfDir   = "providers.d"
	ProviderBinaryDir = "providers"
	aboutConfigFile   = "about.conf"
)

type SkyringConfig struct {
	Host              string `json:"host"`
	HttpPort          int    `json:"httpPort"`
	SSLEnabled        bool   `json:"sslEnabled"`
	SslPort           int    `json:"sslPort"`
	SslCert           string `json:"sslCert"`
	SslKey            string `json:"sslKey"`
	SupportedVersions []int  `json:"supportedversions"`
}

type SkyringLogging struct {
	LogToStderr bool          `json:"logToStderr"`
	Filename    string        `json:"filename"`
	Level       logging.Level `json:"level"`
}

type AuthConfig struct {
	ProviderName string
	ConfigFile   string
}

type ProvisionerConfig struct {
	ProvisionerName string `json:"provisionername"`
	ConfigFilePath  string `json:"configfilepath"`
	RedhatStorage   bool   `json:"redhatstorage"`
	RedhatUseCdn    bool   `json:"redhatusecdn"`
}

type SkyringCollection struct {
	Config               SkyringConfig                `json:"config"`
	Logging              SkyringLogging               `json:"logging"`
	NodeManagementConfig NodeManagerConfig            `json:"nodemanagementconfig"`
	DBConfig             AppDBConfig                  `json:"dbconfig"`
	TimeSeriesDBConfig   MonitoringDBconfig           `json:"timeseriesdbconfig"`
	Authentication       AuthConfig                   `json:"authentication"`
	SummaryConfig        SystemSummaryConfig          `json:"summary"`
	Provisioners         map[string]ProvisionerConfig `json:"provisioners"`
	SysCapabilities      SystemCapabilities           `json:"systemcapabilities"`
	ScheduleConfig       ScheduleConfig               `json:"schedule"`
}

type SystemSummaryConfig struct {
	NetSummaryInterval int `json:"netSummaryInterval"`
}

type ScheduleConfig struct {
	ClustersSyncInterval int `json:"clustersSyncInterval"`
}

type AppDBConfig struct {
	Hostname string `json:"hostname"`
	Port     int    `json:"port"`
	Database string `json:"database"`
	User     string `json:"user"`
	Password string `json:"password"`
}

type MonitoringDBconfig struct {
	Hostname       string `json:"hostname"`
	Port           int    `json:"port"`
	DataPushPort   int    `json:"dataPushPort"`
	CollectionName string `json:"collection_name"`
	User           string `json:"user"`
	Password       string `json:"password"`
	ManagerName    string `json:"managername"`
	ConfigFilePath string `json:"configfilepath"`
}

type NodeManagerConfig struct {
	ManagerName    string `json:"managername"`
	ConfigFilePath string `json:"configfilepath"`
}

type SystemCapabilities struct {
	ProductName            string            `bson:"productname",json:"productname"`
	ProductVersion         string            `bson:"productversion",json:"productversion"`
	StorageProviderDetails map[string]string `bson:"providerversion",json:"providerversion"`
	DbSoftware             string            `bson:"dbsoftware",json:"dbsoftware"`
	DbSoftwareVersion      string            `bson:"dbsoftwareversion",json:"dbsoftwareversion"`
}

var (
	SystemConfig SkyringCollection
)

func LoadAppConfiguration(configFilePath string, configFile string) {
	file, err := ioutil.ReadFile(path.Join(configFilePath, configFile))
	if err != nil {
		logger.Get().Critical("Error reading skyring config. error: %v", err)
		return
	}
	err = json.Unmarshal(file, &SystemConfig)
	if err != nil {
		logger.Get().Critical("Error unmarshalling skyring config. error: %v", err)
		return
	}
	//Initialize the Provisioner Map
	SystemConfig.Provisioners = make(map[string]ProvisionerConfig)
	//Initialize System_capabilities
	file, err = ioutil.ReadFile(path.Join(configFilePath, aboutConfigFile))
	if err != nil {
		logger.Get().Critical("Error reading about config. error: %v", err)
		return
	}
	err = json.Unmarshal(file, &SystemConfig.SysCapabilities)
	if err != nil {
		logger.Get().Critical("Error unmarshalling about config. error: %v", err)
		return
	}
	SystemConfig.SysCapabilities.StorageProviderDetails = make(map[string]string)
}
<commit_msg>models: Added monitoring software details to SysCapabilities<commit_after>/*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 conf

import (
	"encoding/json"
	"github.com/op/go-logging"
	"github.com/skyrings/skyring-common/tools/logger"
	"io/ioutil"
	"path"
)

const (
	ProviderConfDir   = "providers.d"
	ProviderBinaryDir = "providers"
	aboutConfigFile   = "about.conf"
)

type SkyringConfig struct {
	Host              string `json:"host"`
	HttpPort          int    `json:"httpPort"`
	SSLEnabled        bool   `json:"sslEnabled"`
	SslPort           int    `json:"sslPort"`
	SslCert           string `json:"sslCert"`
	SslKey            string `json:"sslKey"`
	SupportedVersions []int  `json:"supportedversions"`
}

type SkyringLogging struct {
	LogToStderr bool          `json:"logToStderr"`
	Filename    string        `json:"filename"`
	Level       logging.Level `json:"level"`
}

type AuthConfig struct {
	ProviderName string
	ConfigFile   string
}

type ProvisionerConfig struct {
	ProvisionerName string `json:"provisionername"`
	ConfigFilePath  string `json:"configfilepath"`
	RedhatStorage   bool   `json:"redhatstorage"`
	RedhatUseCdn    bool   `json:"redhatusecdn"`
}

type SkyringCollection struct {
	Config               SkyringConfig                `json:"config"`
	Logging              SkyringLogging               `json:"logging"`
	NodeManagementConfig NodeManagerConfig            `json:"nodemanagementconfig"`
	DBConfig             AppDBConfig                  `json:"dbconfig"`
	TimeSeriesDBConfig   MonitoringDBconfig           `json:"timeseriesdbconfig"`
	Authentication       AuthConfig                   `json:"authentication"`
	SummaryConfig        SystemSummaryConfig          `json:"summary"`
	Provisioners         map[string]ProvisionerConfig `json:"provisioners"`
	SysCapabilities      SystemCapabilities           `json:"systemcapabilities"`
	ScheduleConfig       ScheduleConfig               `json:"schedule"`
}

type SystemSummaryConfig struct {
	NetSummaryInterval int `json:"netSummaryInterval"`
}

type ScheduleConfig struct {
	ClustersSyncInterval int `json:"clustersSyncInterval"`
}

type AppDBConfig struct {
	Hostname string `json:"hostname"`
	Port     int    `json:"port"`
	Database string `json:"database"`
	User     string `json:"user"`
	Password string `json:"password"`
}

type MonitoringDBconfig struct {
	Hostname       string `json:"hostname"`
	Port           int    `json:"port"`
	DataPushPort   int    `json:"dataPushPort"`
	CollectionName string `json:"collection_name"`
	User           string `json:"user"`
	Password       string `json:"password"`
	ManagerName    string `json:"managername"`
	ConfigFilePath string `json:"configfilepath"`
}

type NodeManagerConfig struct {
	ManagerName    string `json:"managername"`
	ConfigFilePath string `json:"configfilepath"`
}

type SystemCapabilities struct {
	ProductName            string            `bson:"productname",json:"productname"`
	ProductVersion         string            `bson:"productversion",json:"productversion"`
	StorageProviderDetails map[string]string `bson:"providerversion",json:"providerversion"`
	DbSoftware             string            `bson:"dbsoftware",json:"dbsoftware"`
	DbSoftwareVersion      string            `bson:"dbsoftwareversion",json:"dbsoftwareversion"`
	MonitoringSoftware     string            `bson:"monitoringsoftware",json:"monitoringsoftware"`
	MonitoringSoftwareVer  string            `bson:"monitoringsoftwarever",json:"monitoringsoftwarever"`
}

var (
	SystemConfig SkyringCollection
)

func LoadAppConfiguration(configFilePath string, configFile string) {
	file, err := ioutil.ReadFile(path.Join(configFilePath, configFile))
	if err != nil {
		logger.Get().Critical("Error reading skyring config. error: %v", err)
		return
	}
	err = json.Unmarshal(file, &SystemConfig)
	if err != nil {
		logger.Get().Critical("Error unmarshalling skyring config. error: %v", err)
		return
	}
	//Initialize the Provisioner Map
	SystemConfig.Provisioners = make(map[string]ProvisionerConfig)
	//Initialize System_capabilities
	file, err = ioutil.ReadFile(path.Join(configFilePath, aboutConfigFile))
	if err != nil {
		logger.Get().Critical("Error reading about config. error: %v", err)
		return
	}
	err = json.Unmarshal(file, &SystemConfig.SysCapabilities)
	if err != nil {
		logger.Get().Critical("Error unmarshalling about config. error: %v", err)
		return
	}
	SystemConfig.SysCapabilities.StorageProviderDetails = make(map[string]string)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2015, b3log.org
//
// 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 conf includes configurations related manipulations.
package conf

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/b3log/wide/event"
	"github.com/b3log/wide/log"
	"github.com/b3log/wide/util"
)

const (
	// PathSeparator holds the OS-specific path separator.
	PathSeparator = string(os.PathSeparator)
	// PathListSeparator holds the OS-specific path list separator.
	PathListSeparator = string(os.PathListSeparator)

	// WideVersion holds the current wide version.
	WideVersion = "1.1.0"
	// CodeMirrorVer holds the current editor version.
	CodeMirrorVer = "4.10"

	HelloWorld = `package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}
`
)

// Configuration.
type conf struct {
	IP                    string // server ip, ${ip}
	Port                  string // server port
	Context               string // server context
	Server                string // server host and port ({IP}:{Port})
	StaticServer          string // static resources server scheme, host and port (http://{IP}:{Port})
	LogLevel              string // logging level: trace/debug/info/warn/error
	Channel               string // channel (ws://{IP}:{Port})
	HTTPSessionMaxAge     int    // HTTP session max age (in seciond)
	StaticResourceVersion string // version of static resources
	MaxProcs              int    // Go max procs
	RuntimeMode           string // runtime mode (dev/prod)
	WD                    string // current working direcitory, ${pwd}
	Locale                string // default locale
	Playground            string // playground directory
}

// Logger.
var logger = log.NewLogger(os.Stdout)

// Wide configurations.
var Wide *conf

// configurations of users.
var Users []*User

// Indicates whether runs via Docker.
var Docker bool

// Load loads the Wide configurations from wide.json and users' configurations from users/{username}.json.
func Load(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
	confPlayground string, confDocker bool) {
	// XXX: ugly args list....

	initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
		confPlayground, confDocker)
	initUsers()
}

func initUsers() {
	f, err := os.Open("conf/users")
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	names, err := f.Readdirnames(-1)
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}
	f.Close()

	for _, name := range names {
		user := &User{}

		bytes, _ := ioutil.ReadFile("conf/users/" + name)

		err := json.Unmarshal(bytes, user)
		if err != nil {
			logger.Errorf("Parses [%s] error: %v", name, err)

			os.Exit(-1)
		}

		Users = append(Users, user)
	}

	initWorkspaceDirs()
	initCustomizedConfs()
}

func initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
	confPlayground string, confDocker bool) {
	bytes, err := ioutil.ReadFile(confPath)
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	Wide = &conf{}

	err = json.Unmarshal(bytes, Wide)
	if err != nil {
		logger.Error("Parses [wide.json] error: ", err)

		os.Exit(-1)
	}

	// Logging Level
	log.SetLevel(Wide.LogLevel)
	if "" != confLogLevel {
		Wide.LogLevel = confLogLevel
		log.SetLevel(confLogLevel)
	}

	logger.Debug("Conf: \n" + string(bytes))

	// Working Driectory
	Wide.WD = util.OS.Pwd()
	logger.Debugf("${pwd} [%s]", Wide.WD)

	// User Home
	home, err := util.OS.Home()
	if nil != err {
		logger.Error("Can't get user's home, please report this issue to developer", err)

		os.Exit(-1)
	}

	logger.Debugf("${user.home} [%s]", home)

	// Playground Directory
	Wide.Playground = strings.Replace(Wide.Playground, "${home}", home, 1)
	if "" != confPlayground {
		Wide.Playground = confPlayground
	}

	if !util.File.IsExist(Wide.Playground) {
		if err := os.Mkdir(Wide.Playground, 0775); nil != err {
			logger.Errorf("Create Playground [%s] error", err)

			os.Exit(-1)
		}
	}

	// IP
	ip, err := util.Net.LocalIP()
	if err != nil {
		logger.Error(err)

		os.Exit(-1)
	}

	logger.Debugf("${ip} [%s]", ip)

	Docker = confDocker

	if "" != confIP {
		ip = confIP
	}

	Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)

	if "" != confPort {
		Wide.Port = confPort
	}

	// Server
	Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
	Wide.Server = strings.Replace(Wide.Server, "{Port}", Wide.Port, 1)
	if "" != confServer {
		Wide.Server = confServer
	}

	// Static Server
	Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
	Wide.StaticServer = strings.Replace(Wide.StaticServer, "{Port}", Wide.Port, 1)
	if "" != confStaticServer {
		Wide.StaticServer = confStaticServer
	}

	// Context
	if "" != confContext {
		Wide.Context = confContext
	}

	Wide.StaticResourceVersion = strings.Replace(Wide.StaticResourceVersion, "${time}", strconv.FormatInt(time.Now().UnixNano(), 10), 1)

	// Channel
	Wide.Channel = strings.Replace(Wide.Channel, "{IP}", Wide.IP, 1)
	Wide.Channel = strings.Replace(Wide.Channel, "{Port}", Wide.Port, 1)
	if "" != confChannel {
		Wide.Channel = confChannel
	}
}

// FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes).
//
// Exits process if found fatal issues (such as not found $GOPATH),
// Notifies user by notification queue if found warning issues (such as not found gocode).
func FixedTimeCheckEnv() {
	checkEnv() // check immediately

	go func() {
		for _ = range time.Tick(time.Minute * 7) {
			checkEnv()
		}
	}()
}

func checkEnv() {
	cmd := exec.Command("go", "version")
	buf, err := cmd.CombinedOutput()
	if nil != err {
		logger.Error("Not found 'go' command, please make sure Go has been installed correctly")

		os.Exit(-1)
	}
	logger.Trace(string(buf))

	if "" == os.Getenv("GOPATH") {
		logger.Error("Not found $GOPATH, please configure it before running Wide")

		os.Exit(-1)
	}

	gocode := util.Go.GetExecutableInGOBIN("gocode")
	cmd = exec.Command(gocode)
	_, err = cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}

		logger.Warnf("Not found gocode [%s]", gocode)
	}

	ideStub := util.Go.GetExecutableInGOBIN("ide_stub")
	cmd = exec.Command(ideStub, "version")
	_, err = cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeIDEStubNotFound}

		logger.Warnf("Not found ide_stub [%s]", ideStub)
	}
}

// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
func GetUserWorkspace(username string) string {
	for _, user := range Users {
		if user.Name == username {
			return user.GetWorkspace()
		}
	}

	return ""
}

// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
func GetGoFmt(username string) string {
	for _, user := range Users {
		if user.Name == username {
			switch user.GoFormat {
			case "gofmt":
				return "gofmt"
			case "goimports":
				return util.Go.GetExecutableInGOBIN("goimports")
			default:
				logger.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
				return "gofmt"
			}
		}
	}

	return "gofmt"
}

// GetUser gets configuration of the user specified by the given username, returns nil if not found.
func GetUser(username string) *User {
	if "playground" == username { // reserved user for Playground
		// mock it
		return NewUser("playground", "", "", "")
	}

	for _, user := range Users {
		if user.Name == username {
			return user
		}
	}

	return nil
}

// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
	for _, user := range Users {
		UpdateCustomizedConf(user.Name)
	}
}

// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
//  1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
	var u *User
	for _, user := range Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
		if user.Name == username {
			u = user
		}
	}

	if nil == u {
		return
	}

	model := map[string]interface{}{"user": u}

	t, err := template.ParseFiles("static/user/style.css.tmpl")
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	wd := util.OS.Pwd()
	dir := filepath.Clean(wd + "/static/user/" + u.Name)
	if err := os.MkdirAll(dir, 0755); nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	fout, err := os.Create(dir + PathSeparator + "style.css")
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	defer fout.Close()

	if err := t.Execute(fout, model); nil != err {
		logger.Error(err)

		os.Exit(-1)
	}
}

// initWorkspaceDirs initializes the directories of users' workspaces.
//
// Creates directories if not found on path of workspace.
func initWorkspaceDirs() {
	paths := []string{}

	for _, user := range Users {
		paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
	}

	for _, path := range paths {
		CreateWorkspaceDir(path)
	}
}

// CreateWorkspaceDir creates (if not exists) directories on the path.
//
//  1. root directory:{path}
//  2. src directory: {path}/src
//  3. package directory: {path}/pkg
//  4. binary directory: {path}/bin
func CreateWorkspaceDir(path string) {
	createDir(path)
	createDir(path + PathSeparator + "src")
	createDir(path + PathSeparator + "pkg")
	createDir(path + PathSeparator + "bin")
}

// createDir creates a directory on the path if it not exists.
func createDir(path string) {
	if !util.File.IsExist(path) {
		if err := os.MkdirAll(path, 0775); nil != err {
			logger.Error(err)

			os.Exit(-1)
		}

		logger.Infof("Created a workspace [%s]", path)
	}
}

// GetEditorThemes gets the names of editor themes.
func GetEditorThemes() []string {
	ret := []string{}

	f, _ := os.Open("static/js/overwrite/codemirror" + "/theme")
	names, _ := f.Readdirnames(-1)
	f.Close()

	for _, name := range names {
		ret = append(ret, name[:strings.LastIndex(name, ".")])
	}

	sort.Strings(ret)

	return ret
}

// GetThemes gets the names of themes.
func GetThemes() []string {
	ret := []string{}

	f, _ := os.Open("static/css/themes")
	names, _ := f.Readdirnames(-1)
	f.Close()

	for _, name := range names {
		ret = append(ret, name[:strings.LastIndex(name, ".")])
	}

	sort.Strings(ret)

	return ret
}
<commit_msg>skip hiden files in user conf dir<commit_after>// Copyright (c) 2014-2015, b3log.org
//
// 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 conf includes configurations related manipulations.
package conf

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/b3log/wide/event"
	"github.com/b3log/wide/log"
	"github.com/b3log/wide/util"
)

const (
	// PathSeparator holds the OS-specific path separator.
	PathSeparator = string(os.PathSeparator)
	// PathListSeparator holds the OS-specific path list separator.
	PathListSeparator = string(os.PathListSeparator)

	// WideVersion holds the current wide version.
	WideVersion = "1.1.0"
	// CodeMirrorVer holds the current editor version.
	CodeMirrorVer = "4.10"

	HelloWorld = `package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}
`
)

// Configuration.
type conf struct {
	IP                    string // server ip, ${ip}
	Port                  string // server port
	Context               string // server context
	Server                string // server host and port ({IP}:{Port})
	StaticServer          string // static resources server scheme, host and port (http://{IP}:{Port})
	LogLevel              string // logging level: trace/debug/info/warn/error
	Channel               string // channel (ws://{IP}:{Port})
	HTTPSessionMaxAge     int    // HTTP session max age (in seciond)
	StaticResourceVersion string // version of static resources
	MaxProcs              int    // Go max procs
	RuntimeMode           string // runtime mode (dev/prod)
	WD                    string // current working direcitory, ${pwd}
	Locale                string // default locale
	Playground            string // playground directory
}

// Logger.
var logger = log.NewLogger(os.Stdout)

// Wide configurations.
var Wide *conf

// configurations of users.
var Users []*User

// Indicates whether runs via Docker.
var Docker bool

// Load loads the Wide configurations from wide.json and users' configurations from users/{username}.json.
func Load(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
	confPlayground string, confDocker bool) {
	// XXX: ugly args list....

	initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
		confPlayground, confDocker)
	initUsers()
}

func initUsers() {
	f, err := os.Open("conf/users")
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	names, err := f.Readdirnames(-1)
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}
	f.Close()

	for _, name := range names {
		if strings.HasPrefix(name, ".") { // hiden files that not be created by Wide
			continue
		}

		user := &User{}

		bytes, _ := ioutil.ReadFile("conf/users/" + name)

		err := json.Unmarshal(bytes, user)
		if err != nil {
			logger.Errorf("Parses [%s] error: %v", name, err)

			os.Exit(-1)
		}

		Users = append(Users, user)
	}

	initWorkspaceDirs()
	initCustomizedConfs()
}

func initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
	confPlayground string, confDocker bool) {
	bytes, err := ioutil.ReadFile(confPath)
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	Wide = &conf{}

	err = json.Unmarshal(bytes, Wide)
	if err != nil {
		logger.Error("Parses [wide.json] error: ", err)

		os.Exit(-1)
	}

	// Logging Level
	log.SetLevel(Wide.LogLevel)
	if "" != confLogLevel {
		Wide.LogLevel = confLogLevel
		log.SetLevel(confLogLevel)
	}

	logger.Debug("Conf: \n" + string(bytes))

	// Working Driectory
	Wide.WD = util.OS.Pwd()
	logger.Debugf("${pwd} [%s]", Wide.WD)

	// User Home
	home, err := util.OS.Home()
	if nil != err {
		logger.Error("Can't get user's home, please report this issue to developer", err)

		os.Exit(-1)
	}

	logger.Debugf("${user.home} [%s]", home)

	// Playground Directory
	Wide.Playground = strings.Replace(Wide.Playground, "${home}", home, 1)
	if "" != confPlayground {
		Wide.Playground = confPlayground
	}

	if !util.File.IsExist(Wide.Playground) {
		if err := os.Mkdir(Wide.Playground, 0775); nil != err {
			logger.Errorf("Create Playground [%s] error", err)

			os.Exit(-1)
		}
	}

	// IP
	ip, err := util.Net.LocalIP()
	if err != nil {
		logger.Error(err)

		os.Exit(-1)
	}

	logger.Debugf("${ip} [%s]", ip)

	Docker = confDocker

	if "" != confIP {
		ip = confIP
	}

	Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)

	if "" != confPort {
		Wide.Port = confPort
	}

	// Server
	Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
	Wide.Server = strings.Replace(Wide.Server, "{Port}", Wide.Port, 1)
	if "" != confServer {
		Wide.Server = confServer
	}

	// Static Server
	Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
	Wide.StaticServer = strings.Replace(Wide.StaticServer, "{Port}", Wide.Port, 1)
	if "" != confStaticServer {
		Wide.StaticServer = confStaticServer
	}

	// Context
	if "" != confContext {
		Wide.Context = confContext
	}

	Wide.StaticResourceVersion = strings.Replace(Wide.StaticResourceVersion, "${time}", strconv.FormatInt(time.Now().UnixNano(), 10), 1)

	// Channel
	Wide.Channel = strings.Replace(Wide.Channel, "{IP}", Wide.IP, 1)
	Wide.Channel = strings.Replace(Wide.Channel, "{Port}", Wide.Port, 1)
	if "" != confChannel {
		Wide.Channel = confChannel
	}
}

// FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes).
//
// Exits process if found fatal issues (such as not found $GOPATH),
// Notifies user by notification queue if found warning issues (such as not found gocode).
func FixedTimeCheckEnv() {
	checkEnv() // check immediately

	go func() {
		for _ = range time.Tick(time.Minute * 7) {
			checkEnv()
		}
	}()
}

func checkEnv() {
	cmd := exec.Command("go", "version")
	buf, err := cmd.CombinedOutput()
	if nil != err {
		logger.Error("Not found 'go' command, please make sure Go has been installed correctly")

		os.Exit(-1)
	}
	logger.Trace(string(buf))

	if "" == os.Getenv("GOPATH") {
		logger.Error("Not found $GOPATH, please configure it before running Wide")

		os.Exit(-1)
	}

	gocode := util.Go.GetExecutableInGOBIN("gocode")
	cmd = exec.Command(gocode)
	_, err = cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}

		logger.Warnf("Not found gocode [%s]", gocode)
	}

	ideStub := util.Go.GetExecutableInGOBIN("ide_stub")
	cmd = exec.Command(ideStub, "version")
	_, err = cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeIDEStubNotFound}

		logger.Warnf("Not found ide_stub [%s]", ideStub)
	}
}

// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
func GetUserWorkspace(username string) string {
	for _, user := range Users {
		if user.Name == username {
			return user.GetWorkspace()
		}
	}

	return ""
}

// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
func GetGoFmt(username string) string {
	for _, user := range Users {
		if user.Name == username {
			switch user.GoFormat {
			case "gofmt":
				return "gofmt"
			case "goimports":
				return util.Go.GetExecutableInGOBIN("goimports")
			default:
				logger.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
				return "gofmt"
			}
		}
	}

	return "gofmt"
}

// GetUser gets configuration of the user specified by the given username, returns nil if not found.
func GetUser(username string) *User {
	if "playground" == username { // reserved user for Playground
		// mock it
		return NewUser("playground", "", "", "")
	}

	for _, user := range Users {
		if user.Name == username {
			return user
		}
	}

	return nil
}

// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
	for _, user := range Users {
		UpdateCustomizedConf(user.Name)
	}
}

// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
//  1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
	var u *User
	for _, user := range Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
		if user.Name == username {
			u = user
		}
	}

	if nil == u {
		return
	}

	model := map[string]interface{}{"user": u}

	t, err := template.ParseFiles("static/user/style.css.tmpl")
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	wd := util.OS.Pwd()
	dir := filepath.Clean(wd + "/static/user/" + u.Name)
	if err := os.MkdirAll(dir, 0755); nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	fout, err := os.Create(dir + PathSeparator + "style.css")
	if nil != err {
		logger.Error(err)

		os.Exit(-1)
	}

	defer fout.Close()

	if err := t.Execute(fout, model); nil != err {
		logger.Error(err)

		os.Exit(-1)
	}
}

// initWorkspaceDirs initializes the directories of users' workspaces.
//
// Creates directories if not found on path of workspace.
func initWorkspaceDirs() {
	paths := []string{}

	for _, user := range Users {
		paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
	}

	for _, path := range paths {
		CreateWorkspaceDir(path)
	}
}

// CreateWorkspaceDir creates (if not exists) directories on the path.
//
//  1. root directory:{path}
//  2. src directory: {path}/src
//  3. package directory: {path}/pkg
//  4. binary directory: {path}/bin
func CreateWorkspaceDir(path string) {
	createDir(path)
	createDir(path + PathSeparator + "src")
	createDir(path + PathSeparator + "pkg")
	createDir(path + PathSeparator + "bin")
}

// createDir creates a directory on the path if it not exists.
func createDir(path string) {
	if !util.File.IsExist(path) {
		if err := os.MkdirAll(path, 0775); nil != err {
			logger.Error(err)

			os.Exit(-1)
		}

		logger.Infof("Created a workspace [%s]", path)
	}
}

// GetEditorThemes gets the names of editor themes.
func GetEditorThemes() []string {
	ret := []string{}

	f, _ := os.Open("static/js/overwrite/codemirror" + "/theme")
	names, _ := f.Readdirnames(-1)
	f.Close()

	for _, name := range names {
		ret = append(ret, name[:strings.LastIndex(name, ".")])
	}

	sort.Strings(ret)

	return ret
}

// GetThemes gets the names of themes.
func GetThemes() []string {
	ret := []string{}

	f, _ := os.Open("static/css/themes")
	names, _ := f.Readdirnames(-1)
	f.Close()

	for _, name := range names {
		ret = append(ret, name[:strings.LastIndex(name, ".")])
	}

	sort.Strings(ret)

	return ret
}
<|endoftext|>


<commit_before>// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package configure provides a very simple gnu configure/make style configure
// script generating a simple Makefile and go file containing all the configured
// variables.
package configure

import (
	"bytes"
	"fmt"
	"github.com/jessevdk/go-flags"
	"io"
	"os"
	"path"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"runtime"
)

// Options contains all the standard configure options to specify various
// directories. Use NewOptions to create an instance of this type with the
// common default values for each variable.
type Options struct {
	Prefix        string `long:"prefix" description:"install architecture-independent files in PREFIX"`
	ExecPrefix    string `long:"execprefix" description:"install architecture-dependent files in EPREFIX"`
	BinDir        string `long:"bindir" description:"user executables"`
	LibExecDir    string `long:"libexecdir" description:"program executables"`
	SysConfDir    string `long:"sysconfdir" description:"read-only single-machine data"`
	LibDir        string `long:"libdir" description:"program executables"`
	DataRootDir   string `long:"datarootdir" description:"read-only arch.-independent data root"`
	DataDir       string `long:"datadir" description:"read-only arc.-independent data"`
	ManDir        string `long:"mandir" description:"man documentation"`
}

// NewOptions creates a new Options with common default values.
func NewOptions() *Options {
	return &Options{
		Prefix:        "/usr/local",
		ExecPrefix:    "${prefix}",
		BinDir:        "${execprefix}/bin",
		LibExecDir:    "${execprefix}/libexec",
		LibDir:        "${execprefix}/lib",
		SysConfDir:    "${prefix}/etc",
		DataRootDir:   "${prefix}/share",
		DataDir:       "${datarootdir}",
		ManDir:        "${datarootdir}/man",
	}
}

// Package is the package name in which the GoConfig file will be written
var Package = "main"

// Makefile is the filename of the makefile that will be generated
var Makefile = "go.make"

// GoConfig is the filename of the go file that will be generated containing
// all the variable values.
var GoConfig = "appconfig"

// GoConfigVariable is the name of the variable inside the GoConfig file
// containing all the variable values.
var GoConfigVariable = "AppConfig"

// Target is the executable name to build. If left empty, the name is deduced
// from the directory (similar to what go does)
var Target = ""

// Version is the application version
var Version []int = []int{0, 1}

type expandStringPart struct {
	Value      string
	IsVariable bool
}

func (x *expandStringPart) expand(m map[string]*expandString) (string, []string) {
	if x.IsVariable {
		s, ok := m[x.Value]

		if !ok {
			return "", nil
		} else {
			ret := s.expand(m)
			rets := make([]string, len(s.dependencies), len(s.dependencies)+1)

			copy(rets, s.dependencies)

			return ret, append(rets, x.Value)
		}
	}

	return x.Value, nil
}

type expandString struct {
	Name  string
	Parts []expandStringPart

	dependencies []string
	value        string
	hasExpanded  bool
}

func (x *expandString) dependsOn(name string) bool {
	i := sort.SearchStrings(x.dependencies, name)

	return i < len(x.dependencies) && x.dependencies[i] == name
}

func (x *expandString) expand(m map[string]*expandString) string {
	if !x.hasExpanded {
		// Prevent infinite loop by circular dependencies
		x.hasExpanded = true
		buf := bytes.Buffer{}

		for _, v := range x.Parts {
			s, deps := v.expand(m)
			buf.WriteString(s)

			x.dependencies = append(x.dependencies, deps...)
		}

		sort.Strings(x.dependencies)
		x.value = buf.String()
	}

	return x.value
}

// Config represents the current configuration. See Configure for more
// information.
type Config struct {
	*flags.Parser

	values   map[string]interface{}
	expanded map[string]*expandString
}

func (x *Config) extract() map[string]interface{} {
	ret := make(map[string]interface{})

	for _, grp := range x.Parser.Groups {
		for longname, option := range grp.LongNames {
			ret[longname] = option.Value.Interface()
		}
	}

	return ret
}

func (x *Config) expand() map[string]*expandString {
	ret := make(map[string]*expandString)

	r, _ := regexp.Compile(`\$\{[^}]*\}`)

	for name, val := range x.values {
		es := expandString{
			Name: name,
		}

		// Find all variable references
		s := fmt.Sprintf("%v", val)

		matches := r.FindAllStringIndex(s, -1)

		for i, match := range matches {
			var prefix string

			if i == 0 {
				prefix = s[0:match[0]]
			} else {
				prefix = s[matches[i-1][1]:match[0]]
			}

			if len(prefix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: prefix, IsVariable: false})
			}

			varname := s[match[0]+2 : match[1]-1]
			es.Parts = append(es.Parts, expandStringPart{Value: varname, IsVariable: true})
		}

		if len(matches) == 0 {
			es.Parts = append(es.Parts, expandStringPart{Value: s, IsVariable: false})
		} else {
			last := matches[len(matches)-1]
			suffix := s[last[1]:]

			if len(suffix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: suffix, IsVariable: false})
			}
		}

		ret[name] = &es
	}

	for _, val := range ret {
		val.expand(ret)
	}

	return ret
}

// Configure runs the configure process with options as provided by the given
// data variable. If data is nil, the default options will be used
// (see NewOptions). Note that the data provided is simply passed to go-flags.
// For more information on flags parsing, see the documentation of go-flags.
// If GoConfig is not empty, then the go configuration will be written to the
// GoConfig file. Similarly, if Makefile is not empty, the Makefile will be
// written.
func Configure(data interface{}) (*Config, error) {
	if data == nil {
		data = NewOptions()
	}

	parser := flags.NewParser(data, flags.PrintErrors)

	if _, err := parser.Parse(); err != nil {
		return nil, err
	}

	ret := &Config{
		Parser: parser,
	}

	ret.values = ret.extract()
	ret.expanded = ret.expand()

	if len(GoConfig) != 0 {
		filename := GoConfig

		if !strings.HasSuffix(filename, ".go") {
			filename += ".go"
		}

		f, err := os.Create(filename)

		if err != nil {
			return nil, err
		}

		ret.WriteGoConfig(f)
		f.Close()
	}

	if len(Makefile) != 0 {
		f, err := os.Create(Makefile)

		if err != nil {
			return nil, err
		}

		ret.WriteMakefile(f)
		f.Close()

		os.Chmod(Makefile, 0755)

		f, err = os.OpenFile(path.Join(path.Dir(Makefile), "Makefile"),
		                     os.O_CREATE | os.O_EXCL | os.O_WRONLY,
		                     0644)

		if err == nil {
			fmt.Fprintf(f, "include %s\n", path.Base(Makefile))
			f.Close()
		}
	}

	return ret, nil
}

// Expand expands the variable value indicated by name
func (x *Config) Expand(name string) string {
	return x.expanded[name].expand(x.expanded)
}

// WriteGoConfig writes the go configuration file containing all the variable
// values to the given writer. Note that it will write a package line if
// the Package variable is not empty. The GoConfigVariable name will
// be used as the variable name for the configuration.
func (x *Config) WriteGoConfig(writer io.Writer) {
	if len(Package) > 0 {
		fmt.Fprintf(writer, "package %v\n\n", Package)
	}

	fmt.Fprintf(writer, "var %s = struct {\n", GoConfigVariable)
	values := make([]string, 0)

	variables := make([]string, 0, len(x.values))
	optionmap := make(map[string]*flags.Option)

	// Write all options
	for _, grp := range x.Parser.Groups {
		for _, option := range grp.LongNames {
			name := option.Field.Name

			variables = append(variables, name)
			optionmap[name] = option
		}
	}

	sort.Strings(variables)

	for i, name := range variables {
		if i != 0 {
			io.WriteString(writer, "\n")
		}

		option := optionmap[name]
		val := option.Value.Interface()

		fmt.Fprintf(writer, "\t// %s\n", option.Description)
		fmt.Fprintf(writer, "\t%v %T\n", name, val)

		var value string

		if option.Value.Type().Kind() == reflect.String {
			value = fmt.Sprintf("%#v", x.Expand(option.LongName))
		} else {
			value = fmt.Sprintf("%#v", val)
		}

		values = append(values, value)
	}

	if len(variables) > 0 {
		io.WriteString(writer, "\n")
	}

	io.WriteString(writer, "\t// Application version\n")
	io.WriteString(writer, "\tVersion []int\n")
	fmt.Fprintln(writer, "}{")

	for _, v := range values {
		fmt.Fprintf(writer, "\t%v,\n", v)
	}

	for i, v := range Version {
		if i != 0 {
			io.WriteString(writer, ", ")
		} else {
			io.WriteString(writer, "\t[]int{")
		}

		fmt.Fprintf(writer, "%v", v)
	}

	fmt.Fprintln(writer, "},")
	fmt.Fprintln(writer, "}")
}

// WriteMakefile writes a Makefile for the given parser to the given writer.
// The Makefile contains the common build, clean, distclean, install and
// uninstall rules.
func (x *Config) WriteMakefile(writer io.Writer) {
	// Write a very basic makefile
	io.WriteString(writer, "#!/usr/bin/make -f\n\n")

	vars := make([]*expandString, 0, len(x.expanded))

	for name, v := range x.expanded {
		inserted := false

		// Insert into vars based on dependencies
		for i, vv := range vars {
			if vv.dependsOn(name) {
				tail := vars[i:]

				if i == 0 {
					vars = append([]*expandString{v}, vars...)
				} else {
					vars = append(append(vars[0:i-1], v), tail...)
				}

				inserted = true
				break
			}
		}

		if !inserted {
			vars = append(vars, v)
		}
	}

	if len(vars) > 0 {
		io.WriteString(writer, "# Variables\n")

		for _, v := range vars {
			fmt.Fprintf(writer, "%s = ", v.Name)

			for _, part := range v.Parts {
				if part.IsVariable {
					fmt.Fprintf(writer, "$(%s)", part.Value)
				} else {
					fmt.Fprintf(writer, "%s", part.Value)
				}
			}

			io.WriteString(writer, "\n")
		}

		io.WriteString(writer, "\n")
	}

	target := Target

	if len(target) == 0 {
		pc := make([]uintptr, 3)
		n := runtime.Callers(1, pc)

		me, _ := runtime.FuncForPC(pc[0]).FileLine(pc[0])

		for i := 1; i < n; i++ {
			f := runtime.FuncForPC(pc[i])
			fname, _ := f.FileLine(pc[i])

			if fname != me {
				target = path.Base(path.Dir(fname))
				break
			}
		}
	}

	fmt.Fprintf(writer, "TARGET = %s\n", target)
	fmt.Fprintf(writer, "\nSOURCES = $(wildcard *.go)\n\n")

	io.WriteString(writer, "# Rules\n")
	io.WriteString(writer, "$(TARGET): $(SOURCES)\n")
	io.WriteString(writer, "\tgo build -o $(TARGET) $(SOURCES)\n\n")

	io.WriteString(writer, "clean:\n")
	io.WriteString(writer, "\trm -f $(TARGET)\n\n")

	io.WriteString(writer, "distclean: clean\n\n")

	io.WriteString(writer, "install: $(TARGET)\n")
	io.WriteString(writer, "\tmkdir -p $(DESTDIR)$(bindir) && cp $(TARGET) $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, "uninstall:\n")
	io.WriteString(writer, "\trm -f $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, ".PHONY: install uninstall distclean clean")
}
<commit_msg>Remove configure.go from sources<commit_after>// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package configure provides a very simple gnu configure/make style configure
// script generating a simple Makefile and go file containing all the configured
// variables.
package configure

import (
	"bytes"
	"fmt"
	"github.com/jessevdk/go-flags"
	"io"
	"os"
	"path"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"runtime"
)

// Options contains all the standard configure options to specify various
// directories. Use NewOptions to create an instance of this type with the
// common default values for each variable.
type Options struct {
	Prefix        string `long:"prefix" description:"install architecture-independent files in PREFIX"`
	ExecPrefix    string `long:"execprefix" description:"install architecture-dependent files in EPREFIX"`
	BinDir        string `long:"bindir" description:"user executables"`
	LibExecDir    string `long:"libexecdir" description:"program executables"`
	SysConfDir    string `long:"sysconfdir" description:"read-only single-machine data"`
	LibDir        string `long:"libdir" description:"program executables"`
	DataRootDir   string `long:"datarootdir" description:"read-only arch.-independent data root"`
	DataDir       string `long:"datadir" description:"read-only arc.-independent data"`
	ManDir        string `long:"mandir" description:"man documentation"`
}

// NewOptions creates a new Options with common default values.
func NewOptions() *Options {
	return &Options{
		Prefix:        "/usr/local",
		ExecPrefix:    "${prefix}",
		BinDir:        "${execprefix}/bin",
		LibExecDir:    "${execprefix}/libexec",
		LibDir:        "${execprefix}/lib",
		SysConfDir:    "${prefix}/etc",
		DataRootDir:   "${prefix}/share",
		DataDir:       "${datarootdir}",
		ManDir:        "${datarootdir}/man",
	}
}

// Package is the package name in which the GoConfig file will be written
var Package = "main"

// Makefile is the filename of the makefile that will be generated
var Makefile = "go.make"

// GoConfig is the filename of the go file that will be generated containing
// all the variable values.
var GoConfig = "appconfig"

// GoConfigVariable is the name of the variable inside the GoConfig file
// containing all the variable values.
var GoConfigVariable = "AppConfig"

// Target is the executable name to build. If left empty, the name is deduced
// from the directory (similar to what go does)
var Target = ""

// Version is the application version
var Version []int = []int{0, 1}

type expandStringPart struct {
	Value      string
	IsVariable bool
}

func (x *expandStringPart) expand(m map[string]*expandString) (string, []string) {
	if x.IsVariable {
		s, ok := m[x.Value]

		if !ok {
			return "", nil
		} else {
			ret := s.expand(m)
			rets := make([]string, len(s.dependencies), len(s.dependencies)+1)

			copy(rets, s.dependencies)

			return ret, append(rets, x.Value)
		}
	}

	return x.Value, nil
}

type expandString struct {
	Name  string
	Parts []expandStringPart

	dependencies []string
	value        string
	hasExpanded  bool
}

func (x *expandString) dependsOn(name string) bool {
	i := sort.SearchStrings(x.dependencies, name)

	return i < len(x.dependencies) && x.dependencies[i] == name
}

func (x *expandString) expand(m map[string]*expandString) string {
	if !x.hasExpanded {
		// Prevent infinite loop by circular dependencies
		x.hasExpanded = true
		buf := bytes.Buffer{}

		for _, v := range x.Parts {
			s, deps := v.expand(m)
			buf.WriteString(s)

			x.dependencies = append(x.dependencies, deps...)
		}

		sort.Strings(x.dependencies)
		x.value = buf.String()
	}

	return x.value
}

// Config represents the current configuration. See Configure for more
// information.
type Config struct {
	*flags.Parser

	values   map[string]interface{}
	expanded map[string]*expandString
}

func (x *Config) extract() map[string]interface{} {
	ret := make(map[string]interface{})

	for _, grp := range x.Parser.Groups {
		for longname, option := range grp.LongNames {
			ret[longname] = option.Value.Interface()
		}
	}

	return ret
}

func (x *Config) expand() map[string]*expandString {
	ret := make(map[string]*expandString)

	r, _ := regexp.Compile(`\$\{[^}]*\}`)

	for name, val := range x.values {
		es := expandString{
			Name: name,
		}

		// Find all variable references
		s := fmt.Sprintf("%v", val)

		matches := r.FindAllStringIndex(s, -1)

		for i, match := range matches {
			var prefix string

			if i == 0 {
				prefix = s[0:match[0]]
			} else {
				prefix = s[matches[i-1][1]:match[0]]
			}

			if len(prefix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: prefix, IsVariable: false})
			}

			varname := s[match[0]+2 : match[1]-1]
			es.Parts = append(es.Parts, expandStringPart{Value: varname, IsVariable: true})
		}

		if len(matches) == 0 {
			es.Parts = append(es.Parts, expandStringPart{Value: s, IsVariable: false})
		} else {
			last := matches[len(matches)-1]
			suffix := s[last[1]:]

			if len(suffix) != 0 {
				es.Parts = append(es.Parts, expandStringPart{Value: suffix, IsVariable: false})
			}
		}

		ret[name] = &es
	}

	for _, val := range ret {
		val.expand(ret)
	}

	return ret
}

// Configure runs the configure process with options as provided by the given
// data variable. If data is nil, the default options will be used
// (see NewOptions). Note that the data provided is simply passed to go-flags.
// For more information on flags parsing, see the documentation of go-flags.
// If GoConfig is not empty, then the go configuration will be written to the
// GoConfig file. Similarly, if Makefile is not empty, the Makefile will be
// written.
func Configure(data interface{}) (*Config, error) {
	if data == nil {
		data = NewOptions()
	}

	parser := flags.NewParser(data, flags.PrintErrors)

	if _, err := parser.Parse(); err != nil {
		return nil, err
	}

	ret := &Config{
		Parser: parser,
	}

	ret.values = ret.extract()
	ret.expanded = ret.expand()

	if len(GoConfig) != 0 {
		filename := GoConfig

		if !strings.HasSuffix(filename, ".go") {
			filename += ".go"
		}

		f, err := os.Create(filename)

		if err != nil {
			return nil, err
		}

		ret.WriteGoConfig(f)
		f.Close()
	}

	if len(Makefile) != 0 {
		f, err := os.Create(Makefile)

		if err != nil {
			return nil, err
		}

		ret.WriteMakefile(f)
		f.Close()

		os.Chmod(Makefile, 0755)

		f, err = os.OpenFile(path.Join(path.Dir(Makefile), "Makefile"),
		                     os.O_CREATE | os.O_EXCL | os.O_WRONLY,
		                     0644)

		if err == nil {
			fmt.Fprintf(f, "include %s\n", path.Base(Makefile))
			f.Close()
		}
	}

	return ret, nil
}

// Expand expands the variable value indicated by name
func (x *Config) Expand(name string) string {
	return x.expanded[name].expand(x.expanded)
}

// WriteGoConfig writes the go configuration file containing all the variable
// values to the given writer. Note that it will write a package line if
// the Package variable is not empty. The GoConfigVariable name will
// be used as the variable name for the configuration.
func (x *Config) WriteGoConfig(writer io.Writer) {
	if len(Package) > 0 {
		fmt.Fprintf(writer, "package %v\n\n", Package)
	}

	fmt.Fprintf(writer, "var %s = struct {\n", GoConfigVariable)
	values := make([]string, 0)

	variables := make([]string, 0, len(x.values))
	optionmap := make(map[string]*flags.Option)

	// Write all options
	for _, grp := range x.Parser.Groups {
		for _, option := range grp.LongNames {
			name := option.Field.Name

			variables = append(variables, name)
			optionmap[name] = option
		}
	}

	sort.Strings(variables)

	for i, name := range variables {
		if i != 0 {
			io.WriteString(writer, "\n")
		}

		option := optionmap[name]
		val := option.Value.Interface()

		fmt.Fprintf(writer, "\t// %s\n", option.Description)
		fmt.Fprintf(writer, "\t%v %T\n", name, val)

		var value string

		if option.Value.Type().Kind() == reflect.String {
			value = fmt.Sprintf("%#v", x.Expand(option.LongName))
		} else {
			value = fmt.Sprintf("%#v", val)
		}

		values = append(values, value)
	}

	if len(variables) > 0 {
		io.WriteString(writer, "\n")
	}

	io.WriteString(writer, "\t// Application version\n")
	io.WriteString(writer, "\tVersion []int\n")
	fmt.Fprintln(writer, "}{")

	for _, v := range values {
		fmt.Fprintf(writer, "\t%v,\n", v)
	}

	for i, v := range Version {
		if i != 0 {
			io.WriteString(writer, ", ")
		} else {
			io.WriteString(writer, "\t[]int{")
		}

		fmt.Fprintf(writer, "%v", v)
	}

	fmt.Fprintln(writer, "},")
	fmt.Fprintln(writer, "}")
}

// WriteMakefile writes a Makefile for the given parser to the given writer.
// The Makefile contains the common build, clean, distclean, install and
// uninstall rules.
func (x *Config) WriteMakefile(writer io.Writer) {
	// Write a very basic makefile
	io.WriteString(writer, "#!/usr/bin/make -f\n\n")

	vars := make([]*expandString, 0, len(x.expanded))

	for name, v := range x.expanded {
		inserted := false

		// Insert into vars based on dependencies
		for i, vv := range vars {
			if vv.dependsOn(name) {
				tail := vars[i:]

				if i == 0 {
					vars = append([]*expandString{v}, vars...)
				} else {
					vars = append(append(vars[0:i-1], v), tail...)
				}

				inserted = true
				break
			}
		}

		if !inserted {
			vars = append(vars, v)
		}
	}

	if len(vars) > 0 {
		io.WriteString(writer, "# Variables\n")

		for _, v := range vars {
			fmt.Fprintf(writer, "%s = ", v.Name)

			for _, part := range v.Parts {
				if part.IsVariable {
					fmt.Fprintf(writer, "$(%s)", part.Value)
				} else {
					fmt.Fprintf(writer, "%s", part.Value)
				}
			}

			io.WriteString(writer, "\n")
		}

		io.WriteString(writer, "\n")
	}

	target := Target

	if len(target) == 0 {
		pc := make([]uintptr, 3)
		n := runtime.Callers(1, pc)

		me, _ := runtime.FuncForPC(pc[0]).FileLine(pc[0])

		for i := 1; i < n; i++ {
			f := runtime.FuncForPC(pc[i])
			fname, _ := f.FileLine(pc[i])

			if fname != me {
				target = path.Base(path.Dir(fname))
				break
			}
		}
	}

	fmt.Fprintf(writer, "TARGET = %s\n", target)
	io.WriteString(writer, "\nSOURCES = $(subst configure.go,,$(wildcard *.go))")

	io.WriteString(writer, "\n\n")

	io.WriteString(writer, "# Rules\n")
	io.WriteString(writer, "$(TARGET): $(SOURCES)\n")
	io.WriteString(writer, "\tgo build -o $(TARGET) $(SOURCES)\n\n")

	io.WriteString(writer, "clean:\n")
	io.WriteString(writer, "\trm -f $(TARGET)\n\n")

	io.WriteString(writer, "distclean: clean\n\n")

	io.WriteString(writer, "install: $(TARGET)\n")
	io.WriteString(writer, "\tmkdir -p $(DESTDIR)$(bindir) && cp $(TARGET) $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, "uninstall:\n")
	io.WriteString(writer, "\trm -f $(DESTDIR)$(bindir)/$(TARGET)\n\n")

	io.WriteString(writer, ".PHONY: install uninstall distclean clean")
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestIsMultiples(t *testing.T) {

	h := p001Helper{}

	var tests = []struct {
		target   int
		base     int
		expected bool
	}{
		{6, 3, true},
		{3, 3, true},
		{0, 3, false},
		{7, 3, false},
	}

	for _, tt := range tests {
		actual := h.isMultiples(tt.target, tt.base)
		if actual != tt.expected {
			t.Errorf("%d is not multiples of %d", tt.target, tt.base)
		}
	}
}

func TestRun(t *testing.T) {
	var actual, expected, target int

	expected = 23
	target = 10
	actual = run(target, 3, 5)
	if actual != expected {
		t.Errorf("p001 takes %d as input and should return %d, it actually returns %d", target, expected, actual)
	}
}
<commit_msg>add more test cases<commit_after>package main

import "testing"

func TestIsMultiples(t *testing.T) {

	h := p001Helper{}

	var tests = []struct {
		target   int
		base     int
		expected bool
	}{
		{6, 3, true},
		{3, 3, true},
		{0, 3, false},
		{7, 3, false},
		{7, 5, false},
		{10, 5, true},
	}

	for _, tt := range tests {
		actual := h.isMultiples(tt.target, tt.base)
		if actual != tt.expected {
			t.Errorf("%d is not multiples of %d", tt.target, tt.base)
		}
	}
}

func TestRun(t *testing.T) {
	var actual, expected, target int

	expected = 23
	target = 10
	actual = run(target, 3, 5)
	if actual != expected {
		t.Errorf("p001 takes %d as input and should return %d, it actually returns %d", target, expected, actual)
	}
}
<|endoftext|>


<commit_before>// Shared server connections between different clients.

package main

import (
	"sync"
	"time"
)

// Maximum number of connections to a server.
const maxServerConnCnt = 20

// Store each server's connections in separate channels, getting
// connections for different servers can be done in parallel.
type ConnPool struct {
	idleConn map[string]chan *serverConn
	sync.RWMutex
}

var connPool *ConnPool

func initConnPool() {
	connPool = new(ConnPool)
	connPool.idleConn = make(map[string]chan *serverConn)
}

func (cp *ConnPool) Get(hostPort string) *serverConn {
	cp.RLock()
	ch, ok := cp.idleConn[hostPort]
	cp.RUnlock()

	if !ok {
		return nil
	}

	for {
		select {
		case sv := <-ch:
			if sv.mayBeClosed() {
				sv.Close()
				continue
			}
			return sv
		default:
			return nil
		}
	}
}

func (cp *ConnPool) Put(sv *serverConn) {
	var ch chan *serverConn

	cp.RLock()
	ch, ok := cp.idleConn[sv.hostPort]
	cp.RUnlock()

	if !ok {
		debug.Printf("connPool %s: new channel", sv.hostPort)
		ch = make(chan *serverConn, maxServerConnCnt)
		ch <- sv
		cp.Lock()
		cp.idleConn[sv.hostPort] = ch
		cp.Unlock()
		// start a new goroutine to close stale server connections
		go closeStaleServerConn(ch, sv.hostPort)
		return
	}

	select {
	case ch <- sv:
		return
	default:
		// Simply close the connection if can't put into channel immediately.
		// A better solution would remove old connections from the channel and
		// add the new one. But's it's more complicated and this should happen
		// rarely.
		debug.Printf("connPool %s: channel full", sv.hostPort)
		sv.Close()
	}
}

func closeStaleServerConn(ch chan *serverConn, hostPort string) {
	// Tricky here. When removing a channel from the map, there maybe
	// goroutines doing Put and Get using that channel.

	// For Get, there's no problem because it will return immediately.
	// For Put, it's possible that a new connection is added to the
	// channel, but the channel is no longer in the map.
	// So after removed the channel from the map, we wait for several seconds
	// and then close all connections left in it.

	// It's possible that Put add the connection after the final wait, but
	// that should not happen in practice, and the worst result is just lost
	// some memory and open fd.
done:
	for {
		time.Sleep(defaultServerConnTimeout)
	cleanup:
		for {
			select {
			case sv := <-ch:
				if sv.mayBeClosed() {
					debug.Printf("connPool channel %s: close one conn\n", hostPort)
					sv.Close()
				} else {
					// Put it back and wait.
					debug.Printf("connPool channel %s: put back conn\n", hostPort)
					ch <- sv
					break cleanup
				}
			default:
				// no more connection in this channel
				// remove the channel from the map
				connPool.Lock()
				delete(connPool.idleConn, hostPort)
				connPool.Unlock()
				debug.Printf("connPool channel %s: removed\n", hostPort)
				break done
			}
		}
	}
	// Final wait and then close all left connections. In practice, there
	// should be no other goroutines holding reference to the channel.
	time.Sleep(2 * time.Second)
	for len(ch) > 0 {
		sv := <-ch
		sv.Close()
	}
	debug.Printf("connPool channel %s: cleanup done\n", hostPort)
}
<commit_msg>Revert "Simplify close server conn after remove channel from map."<commit_after>// Shared server connections between different clients.

package main

import (
	"sync"
	"time"
)

// Maximum number of connections to a server.
const maxServerConnCnt = 20

// Store each server's connections in separate channels, getting
// connections for different servers can be done in parallel.
type ConnPool struct {
	idleConn map[string]chan *serverConn
	sync.RWMutex
}

var connPool *ConnPool

func initConnPool() {
	connPool = new(ConnPool)
	connPool.idleConn = make(map[string]chan *serverConn)
}

func (cp *ConnPool) Get(hostPort string) *serverConn {
	cp.RLock()
	ch, ok := cp.idleConn[hostPort]
	cp.RUnlock()

	if !ok {
		return nil
	}

	for {
		select {
		case sv := <-ch:
			if sv.mayBeClosed() {
				sv.Close()
				continue
			}
			return sv
		default:
			return nil
		}
	}
}

func (cp *ConnPool) Put(sv *serverConn) {
	var ch chan *serverConn

	cp.RLock()
	ch, ok := cp.idleConn[sv.hostPort]
	cp.RUnlock()

	if !ok {
		debug.Printf("connPool %s: new channel", sv.hostPort)
		ch = make(chan *serverConn, maxServerConnCnt)
		ch <- sv
		cp.Lock()
		cp.idleConn[sv.hostPort] = ch
		cp.Unlock()
		// start a new goroutine to close stale server connections
		go closeStaleServerConn(ch, sv.hostPort)
		return
	}

	select {
	case ch <- sv:
		return
	default:
		// Simply close the connection if can't put into channel immediately.
		// A better solution would remove old connections from the channel and
		// add the new one. But's it's more complicated and this should happen
		// rarely.
		debug.Printf("connPool %s: channel full", sv.hostPort)
		sv.Close()
	}
}

func closeStaleServerConn(ch chan *serverConn, hostPort string) {
	// Tricky here. When removing a channel from the map, there maybe
	// goroutines doing Put and Get using that channel.

	// For Get, there's no problem because it will return immediately.
	// For Put, it's possible that a new connection is added to the
	// channel, but the channel is no longer in the map.
	// So after removed the channel from the map, we wait for several seconds
	// and then close all connections left in it.

	// It's possible that Put add the connection after the final wait, but
	// that should not happen in practice, and the worst result is just lost
	// some memory and open fd.
done:
	for {
		time.Sleep(defaultServerConnTimeout)
	cleanup:
		for {
			select {
			case sv := <-ch:
				if sv.mayBeClosed() {
					debug.Printf("connPool channel %s: close one conn\n", hostPort)
					sv.Close()
				} else {
					// Put it back and wait.
					debug.Printf("connPool channel %s: put back conn\n", hostPort)
					ch <- sv
					break cleanup
				}
			default:
				// no more connection in this channel
				// remove the channel from the map
				connPool.Lock()
				delete(connPool.idleConn, hostPort)
				connPool.Unlock()
				debug.Printf("connPool channel %s: removed\n", hostPort)
				break done
			}
		}
	}
	// Final wait and then close all left connections. In practice, there
	// should be no other goroutines holding reference to the channel.
	time.Sleep(2 * time.Second)
	for {
		select {
		case sv := <-ch:
			debug.Printf("connPool channel %s: close conn after removed\n", hostPort)
			sv.Close()
		default:
			debug.Printf("connPool channel %s: cleanup done\n", hostPort)
			return
		}
	}
}
<|endoftext|>