<commit_before>/*
Copyright 2018 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 labelmanager includes internal functions used to add/delete labels to
// kubernetes nodes for corresponding CSI drivers
package labelmanager // import "k8s.io/kubernetes/pkg/volume/csi/labelmanager"

import (
	"encoding/json"
	"fmt"

	"github.com/golang/glog"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/util/retry"
)

const (
	// Name of node annotation that contains JSON map of driver names to node
	// names
	annotationKey = "csi.volume.kubernetes.io/nodeid"
	csiPluginName = "kubernetes.io/csi"
)

// labelManagementStruct is struct of channels used for communication between the driver registration
// code and the go routine responsible for managing the node's labels
type labelManagerStruct struct {
	nodeName types.NodeName
	k8s      kubernetes.Interface
}

// Interface implements an interface for managing labels of a node
type Interface interface {
	AddLabels(driverName string) error
}

// NewLabelManager initializes labelManagerStruct and returns available interfaces
func NewLabelManager(nodeName types.NodeName, kubeClient kubernetes.Interface) Interface {
	return labelManagerStruct{
		nodeName: nodeName,
		k8s:      kubeClient,
	}
}

// nodeLabelManager waits for labeling requests initiated by the driver's registration
// process.
func (lm labelManagerStruct) AddLabels(driverName string) error {
	err := verifyAndAddNodeId(string(lm.nodeName), lm.k8s.CoreV1().Nodes(), driverName, string(lm.nodeName))
	if err != nil {
		return fmt.Errorf("failed to update node %s's annotation with error: %+v", lm.nodeName, err)
	}
	return nil
}

// Clones the given map and returns a new map with the given key and value added.
// Returns the given map, if annotationKey is empty.
func cloneAndAddAnnotation(
	annotations map[string]string,
	annotationKey,
	annotationValue string) map[string]string {
	if annotationKey == "" {
		// Don't need to add an annotation.
		return annotations
	}
	// Clone.
	newAnnotations := map[string]string{}
	for key, value := range annotations {
		newAnnotations[key] = value
	}
	newAnnotations[annotationKey] = annotationValue
	return newAnnotations
}

func verifyAndAddNodeId(
	k8sNodeName string,
	k8sNodesClient corev1.NodeInterface,
	csiDriverName string,
	csiDriverNodeId string) error {
	// Add or update annotation on Node object
	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Retrieve the latest version of Node before attempting update, so that
		// existing changes are not overwritten. RetryOnConflict uses
		// exponential backoff to avoid exhausting the apiserver.
		result, getErr := k8sNodesClient.Get(k8sNodeName, metav1.GetOptions{})
		if getErr != nil {
			glog.Errorf("Failed to get latest version of Node: %v", getErr)
			return getErr // do not wrap error
		}

		var previousAnnotationValue string
		if result.ObjectMeta.Annotations != nil {
			previousAnnotationValue =
				result.ObjectMeta.Annotations[annotationKey]
			glog.V(3).Infof(
				"previousAnnotationValue=%q", previousAnnotationValue)
		}

		existingDriverMap := map[string]string{}
		if previousAnnotationValue != "" {
			// Parse previousAnnotationValue as JSON
			if err := json.Unmarshal([]byte(previousAnnotationValue), &existingDriverMap); err != nil {
				return fmt.Errorf(
					"failed to parse node's %q annotation value (%q) err=%v",
					annotationKey,
					previousAnnotationValue,
					err)
			}
		}

		if val, ok := existingDriverMap[csiDriverName]; ok {
			if val == csiDriverNodeId {
				// Value already exists in node annotation, nothing more to do
				glog.V(1).Infof(
					"The key value {%q: %q} alredy eixst in node %q annotation, no need to update: %v",
					csiDriverName,
					csiDriverNodeId,
					annotationKey,
					previousAnnotationValue)
				return nil
			}
		}

		// Add/update annotation value
		existingDriverMap[csiDriverName] = csiDriverNodeId
		jsonObj, err := json.Marshal(existingDriverMap)
		if err != nil {
			return fmt.Errorf(
				"failed while trying to add key value {%q: %q} to node %q annotation. Existing value: %v",
				csiDriverName,
				csiDriverNodeId,
				annotationKey,
				previousAnnotationValue)
		}

		result.ObjectMeta.Annotations = cloneAndAddAnnotation(
			result.ObjectMeta.Annotations,
			annotationKey,
			string(jsonObj))
		_, updateErr := k8sNodesClient.Update(result)
		if updateErr == nil {
			fmt.Printf(
				"Updated node %q successfully for CSI driver %q and CSI node name %q",
				k8sNodeName,
				csiDriverName,
				csiDriverNodeId)
		}
		return updateErr // do not wrap error
	})
	if retryErr != nil {
		return fmt.Errorf("node update failed: %v", retryErr)
	}
	return nil
}

// Fetches Kubernetes node API object corresponding to k8sNodeName.
// If the csiDriverName is present in the node annotation, it is removed.
func verifyAndDeleteNodeId(
	k8sNodeName string,
	k8sNodesClient corev1.NodeInterface,
	csiDriverName string) error {
	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Retrieve the latest version of Node before attempting update, so that
		// existing changes are not overwritten. RetryOnConflict uses
		// exponential backoff to avoid exhausting the apiserver.
		result, getErr := k8sNodesClient.Get(k8sNodeName, metav1.GetOptions{})
		if getErr != nil {
			glog.Errorf("failed to get latest version of Node: %v", getErr)
			return getErr // do not wrap error
		}

		var previousAnnotationValue string
		if result.ObjectMeta.Annotations != nil {
			previousAnnotationValue =
				result.ObjectMeta.Annotations[annotationKey]
			glog.V(3).Infof(
				"previousAnnotationValue=%q", previousAnnotationValue)
		}

		existingDriverMap := map[string]string{}
		if previousAnnotationValue == "" {
			// Value already exists in node annotation, nothing more to do
			glog.V(1).Infof(
				"The key %q does not exist in node %q annotation, no need to cleanup.",
				csiDriverName,
				annotationKey)
			return nil
		}

		// Parse previousAnnotationValue as JSON
		if err := json.Unmarshal([]byte(previousAnnotationValue), &existingDriverMap); err != nil {
			return fmt.Errorf(
				"failed to parse node's %q annotation value (%q) err=%v",
				annotationKey,
				previousAnnotationValue,
				err)
		}

		if _, ok := existingDriverMap[csiDriverName]; !ok {
			// Value already exists in node annotation, nothing more to do
			glog.V(1).Infof(
				"The key %q does not eixst in node %q annotation, no need to cleanup: %v",
				csiDriverName,
				annotationKey,
				previousAnnotationValue)
			return nil
		}

		// Add/update annotation value
		delete(existingDriverMap, csiDriverName)
		jsonObj, err := json.Marshal(existingDriverMap)
		if err != nil {
			return fmt.Errorf(
				"failed while trying to remove key %q from node %q annotation. Existing data: %v",
				csiDriverName,
				annotationKey,
				previousAnnotationValue)
		}

		result.ObjectMeta.Annotations = cloneAndAddAnnotation(
			result.ObjectMeta.Annotations,
			annotationKey,
			string(jsonObj))
		_, updateErr := k8sNodesClient.Update(result)
		if updateErr == nil {
			fmt.Printf(
				"Updated node %q annotation to remove CSI driver %q.",
				k8sNodeName,
				csiDriverName)
		}
		return updateErr // do not wrap error
	})
	if retryErr != nil {
		return fmt.Errorf("node update failed: %v", retryErr)
	}
	return nil
}
<commit_msg>Use glog instead of fmt<commit_after>/*
Copyright 2018 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 labelmanager includes internal functions used to add/delete labels to
// kubernetes nodes for corresponding CSI drivers
package labelmanager // import "k8s.io/kubernetes/pkg/volume/csi/labelmanager"

import (
	"encoding/json"
	"fmt"

	"github.com/golang/glog"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/util/retry"
)

const (
	// Name of node annotation that contains JSON map of driver names to node
	// names
	annotationKey = "csi.volume.kubernetes.io/nodeid"
	csiPluginName = "kubernetes.io/csi"
)

// labelManagementStruct is struct of channels used for communication between the driver registration
// code and the go routine responsible for managing the node's labels
type labelManagerStruct struct {
	nodeName types.NodeName
	k8s      kubernetes.Interface
}

// Interface implements an interface for managing labels of a node
type Interface interface {
	AddLabels(driverName string) error
}

// NewLabelManager initializes labelManagerStruct and returns available interfaces
func NewLabelManager(nodeName types.NodeName, kubeClient kubernetes.Interface) Interface {
	return labelManagerStruct{
		nodeName: nodeName,
		k8s:      kubeClient,
	}
}

// nodeLabelManager waits for labeling requests initiated by the driver's registration
// process.
func (lm labelManagerStruct) AddLabels(driverName string) error {
	err := verifyAndAddNodeId(string(lm.nodeName), lm.k8s.CoreV1().Nodes(), driverName, string(lm.nodeName))
	if err != nil {
		return fmt.Errorf("failed to update node %s's annotation with error: %+v", lm.nodeName, err)
	}
	return nil
}

// Clones the given map and returns a new map with the given key and value added.
// Returns the given map, if annotationKey is empty.
func cloneAndAddAnnotation(
	annotations map[string]string,
	annotationKey,
	annotationValue string) map[string]string {
	if annotationKey == "" {
		// Don't need to add an annotation.
		return annotations
	}
	// Clone.
	newAnnotations := map[string]string{}
	for key, value := range annotations {
		newAnnotations[key] = value
	}
	newAnnotations[annotationKey] = annotationValue
	return newAnnotations
}

func verifyAndAddNodeId(
	k8sNodeName string,
	k8sNodesClient corev1.NodeInterface,
	csiDriverName string,
	csiDriverNodeId string) error {
	// Add or update annotation on Node object
	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Retrieve the latest version of Node before attempting update, so that
		// existing changes are not overwritten. RetryOnConflict uses
		// exponential backoff to avoid exhausting the apiserver.
		result, getErr := k8sNodesClient.Get(k8sNodeName, metav1.GetOptions{})
		if getErr != nil {
			glog.Errorf("Failed to get latest version of Node: %v", getErr)
			return getErr // do not wrap error
		}

		var previousAnnotationValue string
		if result.ObjectMeta.Annotations != nil {
			previousAnnotationValue =
				result.ObjectMeta.Annotations[annotationKey]
			glog.V(3).Infof(
				"previousAnnotationValue=%q", previousAnnotationValue)
		}

		existingDriverMap := map[string]string{}
		if previousAnnotationValue != "" {
			// Parse previousAnnotationValue as JSON
			if err := json.Unmarshal([]byte(previousAnnotationValue), &existingDriverMap); err != nil {
				return fmt.Errorf(
					"failed to parse node's %q annotation value (%q) err=%v",
					annotationKey,
					previousAnnotationValue,
					err)
			}
		}

		if val, ok := existingDriverMap[csiDriverName]; ok {
			if val == csiDriverNodeId {
				// Value already exists in node annotation, nothing more to do
				glog.V(2).Infof(
					"The key value {%q: %q} alredy eixst in node %q annotation, no need to update: %v",
					csiDriverName,
					csiDriverNodeId,
					annotationKey,
					previousAnnotationValue)
				return nil
			}
		}

		// Add/update annotation value
		existingDriverMap[csiDriverName] = csiDriverNodeId
		jsonObj, err := json.Marshal(existingDriverMap)
		if err != nil {
			return fmt.Errorf(
				"failed while trying to add key value {%q: %q} to node %q annotation. Existing value: %v",
				csiDriverName,
				csiDriverNodeId,
				annotationKey,
				previousAnnotationValue)
		}

		result.ObjectMeta.Annotations = cloneAndAddAnnotation(
			result.ObjectMeta.Annotations,
			annotationKey,
			string(jsonObj))
		_, updateErr := k8sNodesClient.Update(result)
		if updateErr == nil {
			glog.V(2).Infof(
				"Updated node %q successfully for CSI driver %q and CSI node name %q",
				k8sNodeName,
				csiDriverName,
				csiDriverNodeId)
		}
		return updateErr // do not wrap error
	})
	if retryErr != nil {
		return fmt.Errorf("node update failed: %v", retryErr)
	}
	return nil
}

// Fetches Kubernetes node API object corresponding to k8sNodeName.
// If the csiDriverName is present in the node annotation, it is removed.
func verifyAndDeleteNodeId(
	k8sNodeName string,
	k8sNodesClient corev1.NodeInterface,
	csiDriverName string) error {
	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		// Retrieve the latest version of Node before attempting update, so that
		// existing changes are not overwritten. RetryOnConflict uses
		// exponential backoff to avoid exhausting the apiserver.
		result, getErr := k8sNodesClient.Get(k8sNodeName, metav1.GetOptions{})
		if getErr != nil {
			glog.Errorf("failed to get latest version of Node: %v", getErr)
			return getErr // do not wrap error
		}

		var previousAnnotationValue string
		if result.ObjectMeta.Annotations != nil {
			previousAnnotationValue =
				result.ObjectMeta.Annotations[annotationKey]
			glog.V(3).Infof(
				"previousAnnotationValue=%q", previousAnnotationValue)
		}

		existingDriverMap := map[string]string{}
		if previousAnnotationValue == "" {
			// Value already exists in node annotation, nothing more to do
			glog.V(2).Infof(
				"The key %q does not exist in node %q annotation, no need to cleanup.",
				csiDriverName,
				annotationKey)
			return nil
		}

		// Parse previousAnnotationValue as JSON
		if err := json.Unmarshal([]byte(previousAnnotationValue), &existingDriverMap); err != nil {
			return fmt.Errorf(
				"failed to parse node's %q annotation value (%q) err=%v",
				annotationKey,
				previousAnnotationValue,
				err)
		}

		if _, ok := existingDriverMap[csiDriverName]; !ok {
			// Value already exists in node annotation, nothing more to do
			glog.V(2).Infof(
				"The key %q does not eixst in node %q annotation, no need to cleanup: %v",
				csiDriverName,
				annotationKey,
				previousAnnotationValue)
			return nil
		}

		// Add/update annotation value
		delete(existingDriverMap, csiDriverName)
		jsonObj, err := json.Marshal(existingDriverMap)
		if err != nil {
			return fmt.Errorf(
				"failed while trying to remove key %q from node %q annotation. Existing data: %v",
				csiDriverName,
				annotationKey,
				previousAnnotationValue)
		}

		result.ObjectMeta.Annotations = cloneAndAddAnnotation(
			result.ObjectMeta.Annotations,
			annotationKey,
			string(jsonObj))
		_, updateErr := k8sNodesClient.Update(result)
		if updateErr == nil {
			fmt.Printf(
				"Updated node %q annotation to remove CSI driver %q.",
				k8sNodeName,
				csiDriverName)
		}
		return updateErr // do not wrap error
	})
	if retryErr != nil {
		return fmt.Errorf("node update failed: %v", retryErr)
	}
	return nil
}
<|endoftext|>


<commit_before>package babel

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"testing"

	"github.com/stvp/assert"
)

var opts = map[string]interface{}{
	"plugins": []string{
		"transform-block-scoping",
	},
}

func _TransformWithPool(t *testing.T, n int, p int) {
	input := `let foo = 1`
	expectedOutput := `var foo = 1;`
	done := make(chan struct{})
	if p > 0 {
		Init(p)
	}
	for i := 0; i < n; i++ { // make sure pool works by calling Transform multiple times
		go func(i int) {
			defer func() { done <- struct{}{} }()
			output, err := Transform(strings.NewReader(input), opts)
			assert.Nil(t, err, fmt.Sprintf("Transform(%d) failed", i))
			var outputBuf bytes.Buffer
			io.Copy(&outputBuf, output)
			assert.Equal(t, expectedOutput, outputBuf.String(), fmt.Sprintf("Transform(%d) failed", i))
		}(i)
	}
	for i := 0; i < n; i++ {
		<-done
	}
}

func TestTransform(t *testing.T) {
	_TransformWithPool(t, 2, 0)
}

func TestTransformWithPool(t *testing.T) {
	_TransformWithPool(t, 4, 2)
}

func BenchmarkTransformString(b *testing.B) {
	for i := 0; i < b.N; i++ {
		TransformString(`let foo = 1`, opts)
	}
}

func BenchmarkTransformStringWithSingletonPool(b *testing.B) {
	Init(1)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		TransformString(`let foo = 1`, opts)
	}
}

func BenchmarkTransformStringWithLargePool(b *testing.B) {
	Init(4)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		TransformString(`let foo = 1`, opts)
	}
}
<commit_msg>update test to include JSX (same as README example)<commit_after>package babel

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"testing"

	"github.com/stvp/assert"
)

var opts = map[string]interface{}{
	"plugins": []string{
		"transform-react-jsx",
		"transform-block-scoping",
	},
}

func _TransformWithPool(t *testing.T, n int, p int) {
	input := `let foo = 1;
	<div>
		Hello JSX!
		The value of foo is {foo}.
	</div>`
	expectedOutput := strings.Join([]string{
		"var foo = 1;",
		"",
		"/*#__PURE__*/",
		`React.createElement("div", null, "Hello JSX! The value of foo is ", foo, ".");`,
	}, "\n")
	done := make(chan struct{})
	if p > 0 {
		Init(p)
	}
	for i := 0; i < n; i++ { // make sure pool works by calling Transform multiple times
		go func(i int) {
			defer func() { done <- struct{}{} }()
			output, err := Transform(strings.NewReader(input), opts)
			assert.Nil(t, err, fmt.Sprintf("Transform(%d) failed", i))
			var outputBuf bytes.Buffer
			io.Copy(&outputBuf, output)
			assert.Equal(t, expectedOutput, outputBuf.String(), fmt.Sprintf("Transform(%d) failed", i))
		}(i)
	}
	for i := 0; i < n; i++ {
		<-done
	}
}

func TestTransform(t *testing.T) {
	_TransformWithPool(t, 2, 0)
}

func TestTransformWithPool(t *testing.T) {
	_TransformWithPool(t, 4, 2)
}

func BenchmarkTransformString(b *testing.B) {
	for i := 0; i < b.N; i++ {
		TransformString(`let foo = 1`, opts)
	}
}

func BenchmarkTransformStringWithSingletonPool(b *testing.B) {
	Init(1)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		TransformString(`let foo = 1`, opts)
	}
}

func BenchmarkTransformStringWithLargePool(b *testing.B) {
	Init(4)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		TransformString(`let foo = 1`, opts)
	}
}
<|endoftext|>


<commit_before>package dependency

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestNewHealthServiceQuery(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		i    string
		exp  *HealthServiceQuery
		err  bool
	}{
		{
			"empty",
			"",
			nil,
			true,
		},
		{
			"dc_only",
			"@dc1",
			nil,
			true,
		},
		{
			"near_only",
			"~near",
			nil,
			true,
		},
		{
			"tag_only",
			"tag.",
			nil,
			true,
		},
		{
			"name",
			"name",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
			},
			false,
		},
		{
			"name_dc",
			"name@dc1",
			&HealthServiceQuery{
				dc:      "dc1",
				filters: []string{"passing"},
				name:    "name",
			},
			false,
		},
		{
			"name_dc_near",
			"name@dc1~near",
			&HealthServiceQuery{
				dc:      "dc1",
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
			},
			false,
		},
		{
			"name_near",
			"name~near",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
			},
			false,
		},
		{
			"tag_name",
			"tag.name",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
				tag:     "tag",
			},
			false,
		},
		{
			"tag_name_dc",
			"tag.name@dc",
			&HealthServiceQuery{
				dc:      "dc",
				filters: []string{"passing"},
				name:    "name",
				tag:     "tag",
			},
			false,
		},
		{
			"tag_name_near",
			"tag.name~near",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
				tag:     "tag",
			},
			false,
		},
		{
			"tag_name_dc_near",
			"tag.name@dc~near",
			&HealthServiceQuery{
				dc:      "dc",
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
				tag:     "tag",
			},
			false,
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			act, err := NewHealthServiceQuery(tc.i)
			if (err != nil) != tc.err {
				t.Fatal(err)
			}

			if act != nil {
				act.stopCh = nil
			}

			assert.Equal(t, tc.exp, act)
		})
	}
	// Connect
	// all tests above also test connect, just need to check enabling it
	t.Run("connect_query", func(t *testing.T) {
		act, err := NewHealthConnectQuery("name")
		if err != nil {
			t.Fatal(err)
		}
		if act != nil {
			act.stopCh = nil
		}
		exp := &HealthServiceQuery{
			filters: []string{"passing"},
			name:    "name",
			connect: true,
		}

		assert.Equal(t, exp, act)
	})
}

func TestHealthConnectServiceQuery_Fetch(t *testing.T) {
	t.Parallel()
	cases := []struct {
		name string
		in   string
		exp  []*HealthService
	}{
		{
			"connect-service",
			"foo",
			[]*HealthService{
				&HealthService{
					Name:        "foo-sidecar-proxy",
					ID:          "foo",
					Port:        21999,
					Status:      "passing",
					Address:     "127.0.0.1",
					NodeAddress: "127.0.0.1",
					Tags:        ServiceTags([]string{}),
					NodeMeta: map[string]string{
						"consul-network-segment": ""},
				},
			},
		},
	}
	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			d, err := NewHealthConnectQuery(tc.in)
			if err != nil {
				t.Fatal(err)
			}
			defer func() {
				d.Stop()
			}()
			res, _, err := d.Fetch(testClients, nil)
			if err != nil {
				t.Fatal(err)
			}
			var act []*HealthService
			if act = res.([]*HealthService); len(act) != 1 {
				t.Fatal("Expected 1 result, got ", len(act))
			}
			// blank out fields we don't want to test
			inst := act[0]
			inst.Node, inst.NodeID = "", ""
			inst.Checks = nil
			inst.NodeTaggedAddresses = nil

			assert.Equal(t, tc.exp, act)
		})
	}
}

func TestHealthServiceQuery_Fetch(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		i    string
		exp  []*HealthService
	}{
		{
			"consul",
			"consul",
			[]*HealthService{
				&HealthService{
					Node:        testConsul.Config.NodeName,
					NodeAddress: testConsul.Config.Bind,
					NodeTaggedAddresses: map[string]string{
						"lan": "127.0.0.1",
						"wan": "127.0.0.1",
					},
					NodeMeta: map[string]string{
						"consul-network-segment": "",
					},
					ServiceMeta: map[string]string{},
					Address:     testConsul.Config.Bind,
					ID:          "consul",
					Name:        "consul",
					Tags:        []string{},
					Status:      "passing",
					Port:        testConsul.Config.Ports.Server,
				},
			},
		},
		{
			"filters",
			"consul|warning",
			[]*HealthService{},
		},
		{
			"multifilter",
			"consul|warning,passing",
			[]*HealthService{
				&HealthService{
					Node:        testConsul.Config.NodeName,
					NodeAddress: testConsul.Config.Bind,
					NodeTaggedAddresses: map[string]string{
						"lan": "127.0.0.1",
						"wan": "127.0.0.1",
					},
					NodeMeta: map[string]string{
						"consul-network-segment": "",
					},
					ServiceMeta: map[string]string{},
					Address:     testConsul.Config.Bind,
					ID:          "consul",
					Name:        "consul",
					Tags:        []string{},
					Status:      "passing",
					Port:        testConsul.Config.Ports.Server,
				},
			},
		},
		{
			"service-meta",
			"service-meta",
			[]*HealthService{
				&HealthService{
					Node:        testConsul.Config.NodeName,
					NodeAddress: testConsul.Config.Bind,
					NodeTaggedAddresses: map[string]string{
						"lan": "127.0.0.1",
						"wan": "127.0.0.1",
					},
					NodeMeta: map[string]string{
						"consul-network-segment": "",
					},
					ServiceMeta: map[string]string{
						"meta1": "value1",
					},
					Address: testConsul.Config.Bind,
					ID:      "service-meta",
					Name:    "service-meta",
					Tags:    []string{"tag1"},
					Status:  "passing",
				},
			},
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			d, err := NewHealthServiceQuery(tc.i)
			if err != nil {
				t.Fatal(err)
			}

			act, _, err := d.Fetch(testClients, nil)
			if err != nil {
				t.Fatal(err)
			}

			if act != nil {
				for _, v := range act.([]*HealthService) {
					v.NodeID = ""
					v.Checks = nil
					// delete any version data from ServiceMeta
					v.ServiceMeta = filterVersionMeta(v.ServiceMeta)
				}
			}

			assert.Equal(t, tc.exp, act)
		})
	}
}

func TestHealthServiceQuery_String(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		i    string
		exp  string
	}{
		{
			"name",
			"name",
			"health.service(name|passing)",
		},
		{
			"name_dc",
			"name@dc",
			"health.service(name@dc|passing)",
		},
		{
			"name_filter",
			"name|any",
			"health.service(name|any)",
		},
		{
			"name_multifilter",
			"name|warning,passing",
			"health.service(name|passing,warning)",
		},
		{
			"name_near",
			"name~near",
			"health.service(name~near|passing)",
		},
		{
			"name_near_filter",
			"name~near|any",
			"health.service(name~near|any)",
		},
		{
			"name_dc_near",
			"name@dc~near",
			"health.service(name@dc~near|passing)",
		},
		{
			"name_dc_near_filter",
			"name@dc~near|any",
			"health.service(name@dc~near|any)",
		},
		{
			"tag_name",
			"tag.name",
			"health.service(tag.name|passing)",
		},
		{
			"tag_name_dc",
			"tag.name@dc",
			"health.service(tag.name@dc|passing)",
		},
		{
			"tag_name_near",
			"tag.name~near",
			"health.service(tag.name~near|passing)",
		},
		{
			"tag_name_dc_near",
			"tag.name@dc~near",
			"health.service(tag.name@dc~near|passing)",
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			d, err := NewHealthServiceQuery(tc.i)
			if err != nil {
				t.Fatal(err)
			}
			assert.Equal(t, tc.exp, d.String())
		})
	}
}
<commit_msg>change testcase add default weights field<commit_after>package dependency

import (
	"fmt"
	"testing"

	"github.com/hashicorp/consul/api"
	"github.com/stretchr/testify/assert"
)

func TestNewHealthServiceQuery(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		i    string
		exp  *HealthServiceQuery
		err  bool
	}{
		{
			"empty",
			"",
			nil,
			true,
		},
		{
			"dc_only",
			"@dc1",
			nil,
			true,
		},
		{
			"near_only",
			"~near",
			nil,
			true,
		},
		{
			"tag_only",
			"tag.",
			nil,
			true,
		},
		{
			"name",
			"name",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
			},
			false,
		},
		{
			"name_dc",
			"name@dc1",
			&HealthServiceQuery{
				dc:      "dc1",
				filters: []string{"passing"},
				name:    "name",
			},
			false,
		},
		{
			"name_dc_near",
			"name@dc1~near",
			&HealthServiceQuery{
				dc:      "dc1",
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
			},
			false,
		},
		{
			"name_near",
			"name~near",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
			},
			false,
		},
		{
			"tag_name",
			"tag.name",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
				tag:     "tag",
			},
			false,
		},
		{
			"tag_name_dc",
			"tag.name@dc",
			&HealthServiceQuery{
				dc:      "dc",
				filters: []string{"passing"},
				name:    "name",
				tag:     "tag",
			},
			false,
		},
		{
			"tag_name_near",
			"tag.name~near",
			&HealthServiceQuery{
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
				tag:     "tag",
			},
			false,
		},
		{
			"tag_name_dc_near",
			"tag.name@dc~near",
			&HealthServiceQuery{
				dc:      "dc",
				filters: []string{"passing"},
				name:    "name",
				near:    "near",
				tag:     "tag",
			},
			false,
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			act, err := NewHealthServiceQuery(tc.i)
			if (err != nil) != tc.err {
				t.Fatal(err)
			}

			if act != nil {
				act.stopCh = nil
			}

			assert.Equal(t, tc.exp, act)
		})
	}
	// Connect
	// all tests above also test connect, just need to check enabling it
	t.Run("connect_query", func(t *testing.T) {
		act, err := NewHealthConnectQuery("name")
		if err != nil {
			t.Fatal(err)
		}
		if act != nil {
			act.stopCh = nil
		}
		exp := &HealthServiceQuery{
			filters: []string{"passing"},
			name:    "name",
			connect: true,
		}

		assert.Equal(t, exp, act)
	})
}

func TestHealthConnectServiceQuery_Fetch(t *testing.T) {
	t.Parallel()
	cases := []struct {
		name string
		in   string
		exp  []*HealthService
	}{
		{
			"connect-service",
			"foo",
			[]*HealthService{
				&HealthService{
					Name:        "foo-sidecar-proxy",
					ID:          "foo",
					Port:        21999,
					Status:      "passing",
					Address:     "127.0.0.1",
					NodeAddress: "127.0.0.1",
					Tags:        ServiceTags([]string{}),
					NodeMeta: map[string]string{
						"consul-network-segment": ""},
				},
			},
		},
	}
	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			d, err := NewHealthConnectQuery(tc.in)
			if err != nil {
				t.Fatal(err)
			}
			defer func() {
				d.Stop()
			}()
			res, _, err := d.Fetch(testClients, nil)
			if err != nil {
				t.Fatal(err)
			}
			var act []*HealthService
			if act = res.([]*HealthService); len(act) != 1 {
				t.Fatal("Expected 1 result, got ", len(act))
			}
			// blank out fields we don't want to test
			inst := act[0]
			inst.Node, inst.NodeID = "", ""
			inst.Checks = nil
			inst.NodeTaggedAddresses = nil

			assert.Equal(t, tc.exp, act)
		})
	}
}

func TestHealthServiceQuery_Fetch(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		i    string
		exp  []*HealthService
	}{
		{
			"consul",
			"consul",
			[]*HealthService{
				&HealthService{
					Node:        testConsul.Config.NodeName,
					NodeAddress: testConsul.Config.Bind,
					NodeTaggedAddresses: map[string]string{
						"lan": "127.0.0.1",
						"wan": "127.0.0.1",
					},
					NodeMeta: map[string]string{
						"consul-network-segment": "",
					},
					ServiceMeta: map[string]string{},
					Address:     testConsul.Config.Bind,
					ID:          "consul",
					Name:        "consul",
					Tags:        []string{},
					Status:      "passing",
					Port:        testConsul.Config.Ports.Server,
					Weights: &api.AgentWeights{
						Passing: 1,
						Warning: 1,
					},
				},
			},
		},
		{
			"filters",
			"consul|warning",
			[]*HealthService{},
		},
		{
			"multifilter",
			"consul|warning,passing",
			[]*HealthService{
				&HealthService{
					Node:        testConsul.Config.NodeName,
					NodeAddress: testConsul.Config.Bind,
					NodeTaggedAddresses: map[string]string{
						"lan": "127.0.0.1",
						"wan": "127.0.0.1",
					},
					NodeMeta: map[string]string{
						"consul-network-segment": "",
					},
					ServiceMeta: map[string]string{},
					Address:     testConsul.Config.Bind,
					ID:          "consul",
					Name:        "consul",
					Tags:        []string{},
					Status:      "passing",
					Port:        testConsul.Config.Ports.Server,
					Weights: &api.AgentWeights{
						Passing: 1,
						Warning: 1,
					},
				},
			},
		},
		{
			"service-meta",
			"service-meta",
			[]*HealthService{
				&HealthService{
					Node:        testConsul.Config.NodeName,
					NodeAddress: testConsul.Config.Bind,
					NodeTaggedAddresses: map[string]string{
						"lan": "127.0.0.1",
						"wan": "127.0.0.1",
					},
					NodeMeta: map[string]string{
						"consul-network-segment": "",
					},
					ServiceMeta: map[string]string{
						"meta1": "value1",
					},
					Address: testConsul.Config.Bind,
					ID:      "service-meta",
					Name:    "service-meta",
					Tags:    []string{"tag1"},
					Status:  "passing",
					Weights: &api.AgentWeights{
						Passing: 1,
						Warning: 1,
					},
				},
			},
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			d, err := NewHealthServiceQuery(tc.i)
			if err != nil {
				t.Fatal(err)
			}

			act, _, err := d.Fetch(testClients, nil)
			if err != nil {
				t.Fatal(err)
			}

			if act != nil {
				for _, v := range act.([]*HealthService) {
					v.NodeID = ""
					v.Checks = nil
					// delete any version data from ServiceMeta
					v.ServiceMeta = filterVersionMeta(v.ServiceMeta)
				}
			}

			assert.Equal(t, tc.exp, act)
		})
	}
}

func TestHealthServiceQuery_String(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name string
		i    string
		exp  string
	}{
		{
			"name",
			"name",
			"health.service(name|passing)",
		},
		{
			"name_dc",
			"name@dc",
			"health.service(name@dc|passing)",
		},
		{
			"name_filter",
			"name|any",
			"health.service(name|any)",
		},
		{
			"name_multifilter",
			"name|warning,passing",
			"health.service(name|passing,warning)",
		},
		{
			"name_near",
			"name~near",
			"health.service(name~near|passing)",
		},
		{
			"name_near_filter",
			"name~near|any",
			"health.service(name~near|any)",
		},
		{
			"name_dc_near",
			"name@dc~near",
			"health.service(name@dc~near|passing)",
		},
		{
			"name_dc_near_filter",
			"name@dc~near|any",
			"health.service(name@dc~near|any)",
		},
		{
			"tag_name",
			"tag.name",
			"health.service(tag.name|passing)",
		},
		{
			"tag_name_dc",
			"tag.name@dc",
			"health.service(tag.name@dc|passing)",
		},
		{
			"tag_name_near",
			"tag.name~near",
			"health.service(tag.name~near|passing)",
		},
		{
			"tag_name_dc_near",
			"tag.name@dc~near",
			"health.service(tag.name@dc~near|passing)",
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) {
			d, err := NewHealthServiceQuery(tc.i)
			if err != nil {
				t.Fatal(err)
			}
			assert.Equal(t, tc.exp, d.String())
		})
	}
}
<|endoftext|>


<commit_before>package enaml

type Deployment interface {
	GetDeployment() DeploymentManifest
}

type CloudConfig interface {
	GetManifest() CloudConfigManifest
}

type DeploymentManifest struct {
	Name           string              `yaml:"name"`
	DirectorUUID   string              `yaml:"director_uuid,omitempty"`
	Releases       []Release           `yaml:"releases,omitempty"`
	Stemcells      []Stemcell          `yaml:"stemcells,omitempty"`
	InstanceGroups []*InstanceGroup    `yaml:"instance_groups,omitempty"`
	Networks       []DeploymentNetwork `yaml:"networks,omitempty"`
	ResourcePools  []ResourcePool      `yaml:"resource_pools,omitempty"`
	DiskPools      []DiskPool          `yaml:"disk_pools,omitempty"`
	Compilation    *Compilation        `yaml:"compilation,omitempty"`
	Update         Update              `yaml:"update,omitempty"`
	Jobs           []Job               `yaml:"jobs,omitempty"`
	Properties     Properties          `yaml:"properties,omitempty"`
	CloudProvider  *CloudProvider      `yaml:"cloud_provider,omitempty"`
}

type InstanceGroup struct {
	Name               string        `yaml:"name"`
	ResourcePool       string        `yaml:"resource_pool,omitempty"`
	PersistentDisk     int           `yaml:"persistent_disk,omitempty"`
	PersistentDiskType string        `yaml:"persistent_disk_type,omitempty"`
	Instances          int           `yaml:"instances"`
	VMType             string        `yaml:"vm_type,omitempty"`
	Stemcell           string        `yaml:"stemcell,omitempty"`
	AZs                []string      `yaml:"azs,flow,omitempty"`
	Networks           []Network     `yaml:"networks,flow"`
	Jobs               []InstanceJob `yaml:"jobs"`
}

type InstanceJob struct {
	Name       string      `yaml:"name"`
	Release    string      `yaml:"release"`
	Properties interface{} `yaml:"properties"`
}

type DeploymentNetwork interface {
}

type Release struct {
	Name    string `yaml:"name"`
	Version string `yaml:"version,omitempty"`
	URL     string `yaml:"url,omitempty"`
	SHA1    string `yaml:"sha1,omitempty"`
}

func NewVIPNetwork(name string) VIPNetwork {
	return VIPNetwork{
		Name: name,
		Type: "vip",
	}
}

type VIPNetwork struct {
	Name            string          `yaml:"name"`
	Type            string          `yaml:"type"`
	CloudProperties CloudProperties `yaml:"cloud_properties,omitempty"`
}

type DynamicNetwork struct {
	Name            string          `yaml:"name"`
	Type            string          `yaml:"type"`
	DNS             []string        `yaml:"dns"`
	CloudProperties CloudProperties `yaml:"cloud_properties"`
}

func (s DynamicNetwork) GetName() (name string) {
	return s.Name
}

func NewManualNetwork(name string) ManualNetwork {
	return ManualNetwork{
		Name: name,
		Type: "manual",
	}
}

type ManualNetwork struct {
	Name    string   `yaml:"name"`
	Type    string   `yaml:"type"`
	Subnets []Subnet `yaml:"subnets"`
}

func (s *ManualNetwork) AddSubnet(subnet Subnet) (err error) {
	s.Subnets = append(s.Subnets, subnet)
	return
}

func NewSubnet(cidr, gateway, azName string) Subnet {
	return Subnet{
		Range:   cidr,
		Gateway: gateway,
		AZ:      azName,
	}
}

type Subnet struct {
	Range           string          `yaml:"range,omitempty"`
	Gateway         string          `yaml:"gateway,omitempty"`
	DNS             []string        `yaml:"dns,omitempty"`
	Reserved        []string        `yaml:"reserved,omitempty"`
	Static          []string        `yaml:"static,omitempty"`
	AZ              string          `yaml:"az,omitempty"`
	AZs             []string        `yaml:"azs,omitempty"`
	CloudProperties CloudProperties `yaml:"cloud_properties"`
}

func (s *Subnet) AddDNS(dns string) (err error) {
	s.DNS = append(s.DNS, dns)
	return
}

func (s *Subnet) AddReserved(rsv string) (err error) {
	s.Reserved = append(s.Reserved, rsv)
	return
}

type ResourcePool struct {
	Name            string                 `yaml:"name"`
	Network         string                 `yaml:"network"`
	Size            int                    `yaml:"size,omitempty"`
	Stemcell        Stemcell               `yaml:"stemcell"`
	CloudProperties CloudProperties        `yaml:"cloud_properties"`
	Env             map[string]interface{} `yaml:"env,omitempty"`
}

func (s *ResourcePool) SetStemcell(sc Stemcell) (err error) {
	s.Stemcell = sc
	return
}

type Stemcell struct {
	Alias   string `yaml:"alias,omitempty"`
	OS      string `yaml:"os,omitempty"`
	Name    string `yaml:"name,omitempty"`
	Version string `yaml:"version,omitempty"`
	URL     string `yaml:"url,omitempty"`
	SHA1    string `yaml:"sha1,omitempty"`
}

type DiskType DiskPool
type DiskPool struct {
	Name            string          `yaml:"name"`
	DiskSize        int             `yaml:"disk_size"`
	CloudProperties CloudProperties `yaml:"cloud_properties"`
}

type Compilation struct {
	Workers             int             `yaml:"workers"`
	Network             string          `yaml:"network"`
	ReuseCompilationVMs bool            `yaml:"reuse_compilation_vms"`
	CloudProperties     CloudProperties `yaml:"cloud_properties,omitempty"`
	VMType              string          `yaml:"vm_type,omitempty"`
	AZ                  string          `yaml:"az,omitempty"`
}

type Update struct {
	Canaries        int    `yaml:"canaries"`
	MaxInFlight     int    `yaml:"max_in_flight,omitempty"`
	CanaryWatchTime string `yaml:"canary_watch_time,omitempty"`
	UpdateWatchTime string `yaml:"update_watch_time,omitempty"`
	Serial          bool   `yaml:"serial,omitempty"`
}

type Job struct {
	Name               string     `yaml:"name"`
	Templates          []Template `yaml:"templates,flow"`
	Lifecycle          string     `yaml:"lifeycle,omitempty"`
	PersistentDisk     string     `yaml:"persistent_disk,omitempty"`
	PersistentDiskPool string     `yaml:"persistent_disk_pool,omitempty"`
	Properties         Properties `yaml:"properties,omitempty"`
	ResourcePool       string     `yaml:"resource_pool"`
	Update             Update     `yaml:"update,omitempty"`
	Instances          int        `yaml:"instances"`
	Networks           []Network  `yaml:"networks"`
}

func (s *Job) AddTemplate(t Template) (err error) {
	s.Templates = append(s.Templates, t)
	return
}

func (s *Job) AddNetwork(n Network) (err error) {
	s.Networks = append(s.Networks, n)
	return
}

func (s *Job) AddProperty(name string, property interface{}) (err error) {
	if s.Properties == nil {
		s.Properties = make(map[string]interface{})
	}
	s.Properties[name] = property
	return
}

type Network struct {
	Name      string        `yaml:"name"`
	StaticIPs []string      `yaml:"static_ips,omitempty"`
	Default   []interface{} `yaml:"default,omitempty"`
}

type Template struct {
	Name    string `yaml:"name"`
	Release string `yaml:"release"`
}

type CloudProvider struct {
	Template   Template                `yaml:"template,flow"`
	MBus       string                  `yaml:"mbus"`
	Properties CloudProviderProperties `yaml:"properties"`
	SSHTunnel  SSHTunnel               `yaml:"ssh_tunnel"`
}

type SSHTunnel struct {
	Host           string `yaml:"host"`
	Port           int    `yaml:"port"`
	User           string `yaml:"user"`
	PrivateKeyPath string `yaml:"private_key"`
}
type CloudProviderProperties interface{}
type Properties map[string]interface{}
type CloudProperties interface{}
<commit_msg>[#121608369] adding update field to instancegroup<commit_after>package enaml

type Deployment interface {
	GetDeployment() DeploymentManifest
}

type CloudConfig interface {
	GetManifest() CloudConfigManifest
}

type DeploymentManifest struct {
	Name           string              `yaml:"name"`
	DirectorUUID   string              `yaml:"director_uuid,omitempty"`
	Releases       []Release           `yaml:"releases,omitempty"`
	Stemcells      []Stemcell          `yaml:"stemcells,omitempty"`
	InstanceGroups []*InstanceGroup    `yaml:"instance_groups,omitempty"`
	Networks       []DeploymentNetwork `yaml:"networks,omitempty"`
	ResourcePools  []ResourcePool      `yaml:"resource_pools,omitempty"`
	DiskPools      []DiskPool          `yaml:"disk_pools,omitempty"`
	Compilation    *Compilation        `yaml:"compilation,omitempty"`
	Update         Update              `yaml:"update,omitempty"`
	Jobs           []Job               `yaml:"jobs,omitempty"`
	Properties     Properties          `yaml:"properties,omitempty"`
	CloudProvider  *CloudProvider      `yaml:"cloud_provider,omitempty"`
}

type InstanceGroup struct {
	Name               string        `yaml:"name"`
	ResourcePool       string        `yaml:"resource_pool,omitempty"`
	PersistentDisk     int           `yaml:"persistent_disk,omitempty"`
	PersistentDiskType string        `yaml:"persistent_disk_type,omitempty"`
	Instances          int           `yaml:"instances"`
	VMType             string        `yaml:"vm_type,omitempty"`
	Stemcell           string        `yaml:"stemcell,omitempty"`
	AZs                []string      `yaml:"azs,flow,omitempty"`
	Networks           []Network     `yaml:"networks,flow"`
	Jobs               []InstanceJob `yaml:"jobs"`
	Update             Update        `yaml:"update,omitempty"`
}

type InstanceJob struct {
	Name       string      `yaml:"name"`
	Release    string      `yaml:"release"`
	Properties interface{} `yaml:"properties"`
}

type DeploymentNetwork interface {
}

type Release struct {
	Name    string `yaml:"name"`
	Version string `yaml:"version,omitempty"`
	URL     string `yaml:"url,omitempty"`
	SHA1    string `yaml:"sha1,omitempty"`
}

func NewVIPNetwork(name string) VIPNetwork {
	return VIPNetwork{
		Name: name,
		Type: "vip",
	}
}

type VIPNetwork struct {
	Name            string          `yaml:"name"`
	Type            string          `yaml:"type"`
	CloudProperties CloudProperties `yaml:"cloud_properties,omitempty"`
}

type DynamicNetwork struct {
	Name            string          `yaml:"name"`
	Type            string          `yaml:"type"`
	DNS             []string        `yaml:"dns"`
	CloudProperties CloudProperties `yaml:"cloud_properties"`
}

func (s DynamicNetwork) GetName() (name string) {
	return s.Name
}

func NewManualNetwork(name string) ManualNetwork {
	return ManualNetwork{
		Name: name,
		Type: "manual",
	}
}

type ManualNetwork struct {
	Name    string   `yaml:"name"`
	Type    string   `yaml:"type"`
	Subnets []Subnet `yaml:"subnets"`
}

func (s *ManualNetwork) AddSubnet(subnet Subnet) (err error) {
	s.Subnets = append(s.Subnets, subnet)
	return
}

func NewSubnet(cidr, gateway, azName string) Subnet {
	return Subnet{
		Range:   cidr,
		Gateway: gateway,
		AZ:      azName,
	}
}

type Subnet struct {
	Range           string          `yaml:"range,omitempty"`
	Gateway         string          `yaml:"gateway,omitempty"`
	DNS             []string        `yaml:"dns,omitempty"`
	Reserved        []string        `yaml:"reserved,omitempty"`
	Static          []string        `yaml:"static,omitempty"`
	AZ              string          `yaml:"az,omitempty"`
	AZs             []string        `yaml:"azs,omitempty"`
	CloudProperties CloudProperties `yaml:"cloud_properties"`
}

func (s *Subnet) AddDNS(dns string) (err error) {
	s.DNS = append(s.DNS, dns)
	return
}

func (s *Subnet) AddReserved(rsv string) (err error) {
	s.Reserved = append(s.Reserved, rsv)
	return
}

type ResourcePool struct {
	Name            string                 `yaml:"name"`
	Network         string                 `yaml:"network"`
	Size            int                    `yaml:"size,omitempty"`
	Stemcell        Stemcell               `yaml:"stemcell"`
	CloudProperties CloudProperties        `yaml:"cloud_properties"`
	Env             map[string]interface{} `yaml:"env,omitempty"`
}

func (s *ResourcePool) SetStemcell(sc Stemcell) (err error) {
	s.Stemcell = sc
	return
}

type Stemcell struct {
	Alias   string `yaml:"alias,omitempty"`
	OS      string `yaml:"os,omitempty"`
	Name    string `yaml:"name,omitempty"`
	Version string `yaml:"version,omitempty"`
	URL     string `yaml:"url,omitempty"`
	SHA1    string `yaml:"sha1,omitempty"`
}

type DiskType DiskPool
type DiskPool struct {
	Name            string          `yaml:"name"`
	DiskSize        int             `yaml:"disk_size"`
	CloudProperties CloudProperties `yaml:"cloud_properties"`
}

type Compilation struct {
	Workers             int             `yaml:"workers"`
	Network             string          `yaml:"network"`
	ReuseCompilationVMs bool            `yaml:"reuse_compilation_vms"`
	CloudProperties     CloudProperties `yaml:"cloud_properties,omitempty"`
	VMType              string          `yaml:"vm_type,omitempty"`
	AZ                  string          `yaml:"az,omitempty"`
}

type Update struct {
	Canaries        int    `yaml:"canaries"`
	MaxInFlight     int    `yaml:"max_in_flight,omitempty"`
	CanaryWatchTime string `yaml:"canary_watch_time,omitempty"`
	UpdateWatchTime string `yaml:"update_watch_time,omitempty"`
	Serial          bool   `yaml:"serial,omitempty"`
}

type Job struct {
	Name               string     `yaml:"name"`
	Templates          []Template `yaml:"templates,flow"`
	Lifecycle          string     `yaml:"lifeycle,omitempty"`
	PersistentDisk     string     `yaml:"persistent_disk,omitempty"`
	PersistentDiskPool string     `yaml:"persistent_disk_pool,omitempty"`
	Properties         Properties `yaml:"properties,omitempty"`
	ResourcePool       string     `yaml:"resource_pool"`
	Update             Update     `yaml:"update,omitempty"`
	Instances          int        `yaml:"instances"`
	Networks           []Network  `yaml:"networks"`
}

func (s *Job) AddTemplate(t Template) (err error) {
	s.Templates = append(s.Templates, t)
	return
}

func (s *Job) AddNetwork(n Network) (err error) {
	s.Networks = append(s.Networks, n)
	return
}

func (s *Job) AddProperty(name string, property interface{}) (err error) {
	if s.Properties == nil {
		s.Properties = make(map[string]interface{})
	}
	s.Properties[name] = property
	return
}

type Network struct {
	Name      string        `yaml:"name"`
	StaticIPs []string      `yaml:"static_ips,omitempty"`
	Default   []interface{} `yaml:"default,omitempty"`
}

type Template struct {
	Name    string `yaml:"name"`
	Release string `yaml:"release"`
}

type CloudProvider struct {
	Template   Template                `yaml:"template,flow"`
	MBus       string                  `yaml:"mbus"`
	Properties CloudProviderProperties `yaml:"properties"`
	SSHTunnel  SSHTunnel               `yaml:"ssh_tunnel"`
}

type SSHTunnel struct {
	Host           string `yaml:"host"`
	Port           int    `yaml:"port"`
	User           string `yaml:"user"`
	PrivateKeyPath string `yaml:"private_key"`
}
type CloudProviderProperties interface{}
type Properties map[string]interface{}
type CloudProperties interface{}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

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

var (
	listen           = flag.String("l", ":8888", "port to accept requests")
	targetProduction = flag.String("a", "http://localhost:8080", "where production traffic goes. http://localhost:8080/production")
	altTarget        = flag.String("b", "http://localhost:8081", "where testing traffic goes. response are skipped. http://localhost:8081/test")
	debug            = flag.Bool("debug", false, "more logging, showing ignored output")
)

type Hosts struct {
	Target      url.URL
	Alternative url.URL
}

var hosts Hosts
var client *http.Client
var proxy *httputil.ReverseProxy

type TimeoutTransport struct {
	http.Transport
}

func (t *TimeoutTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	return t.Transport.RoundTrip(req)
}

func teeDirector(req *http.Request) {
	id := uuid.NewUUID().String()
	fmt.Printf("[%v][%v][<Request>][<%v>]\n", time.Now().Format(time.RFC3339Nano), id, req)
	req2 := duplicateRequest(req)

	go func() {
		defer func() {
			if r := recover(); r != nil && *debug {
				fmt.Println("Recovered in f", r)
			}
		}()

		resp, err := client.Do(req2)

		if err != nil {
			logMessage("[%v][%v][<B Error>][<%v>]\n", id, err)
		} else {
			r, e := httputil.DumpResponse(resp, true)
			if e != nil {
				logMessage("[%v][%v][<B Error Dump>][<%v>]", id, e)
			} else {
				logMessage("[%v][%v][<B Resp>][<%v>]", id, r)
			}
		}

		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
	}()

	targetQuery := hosts.Target.RawQuery
	req.URL.Scheme = hosts.Target.Scheme
	req.URL.Host = hosts.Target.Host
	req.URL.Path = singleJoiningSlash(hosts.Target.Path, req.URL.Path)
	if targetQuery == "" || req.URL.RawQuery == "" {
		req.URL.RawQuery = targetQuery + req.URL.RawQuery
	} else {
		req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
	}
}

func logMessage(message string, id string, logObj interface{}) {
	fmt.Printf(message, time.Now().Format(time.RFC3339Nano), id, logObj)
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

func duplicateRequest(request *http.Request) (request1 *http.Request) {
	b1 := new(bytes.Buffer)
	b2 := new(bytes.Buffer)
	w := io.MultiWriter(b1, b2)
	io.Copy(w, request.Body)
	request.Body = ioutil.NopCloser(bytes.NewReader(b2.Bytes()))
	request2 := &http.Request{
		Method: request.Method,
		URL: &url.URL{
			Scheme:   hosts.Alternative.Scheme,
			Host:     hosts.Alternative.Host,
			Path:     singleJoiningSlash(hosts.Alternative.Path, request.URL.Path),
			RawQuery: request.URL.RawQuery,
		},
		Proto:         "HTTP/1.1",
		ProtoMajor:    1,
		ProtoMinor:    1,
		Header:        request.Header,
		Body:          ioutil.NopCloser(bytes.NewReader(b1.Bytes())),
		ContentLength: request.ContentLength,
	}

	return request2
}

func handler(w http.ResponseWriter, r *http.Request) {
	proxy.ServeHTTP(w, r)
}

func main() {
	flag.Parse()

	target, _ := url.Parse(*targetProduction)
	alt, _ := url.Parse(*altTarget)
	client = &http.Client{
		Timeout: time.Millisecond * 2000,
	}

	hosts = Hosts{
		Target:      *target,
		Alternative: *alt,
	}

	u, _ := url.Parse(*targetProduction)
	proxy = httputil.NewSingleHostReverseProxy(u)
	proxy.Transport = &TimeoutTransport{}
	proxy.Director = teeDirector

	http.HandleFunc("/", handler)
	http.ListenAndServe(*listen, nil)
}
<commit_msg>Clean up<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

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

var (
	listen           = flag.String("l", ":8888", "port to accept requests")
	targetProduction = flag.String("a", "http://localhost:8080", "where production traffic goes. http://localhost:8080/production")
	altTarget        = flag.String("b", "http://localhost:8081", "where testing traffic goes. response are skipped. http://localhost:8081/test")
	debug            = flag.Bool("debug", false, "more logging, showing ignored output")
)

type Hosts struct {
	Target      url.URL
	Alternative url.URL
}

var hosts Hosts
var client *http.Client
var proxy *httputil.ReverseProxy

type TimeoutTransport struct {
	http.Transport
}

func (t *TimeoutTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	return t.Transport.RoundTrip(req)
}

func teeDirector(req *http.Request) {
	id := uuid.NewUUID().String()
	fmt.Printf("[%v][%v][<Request>][<%v>]\n", time.Now().Format(time.RFC3339Nano), id, req)
	req2 := duplicateRequest(req)

	go func() {
		defer func() {
			if r := recover(); r != nil && *debug {
				fmt.Println("Recovered in f", r)
			}
		}()

		resp, err := client.Do(req2)

		if err != nil {
			logMessage("[%v][%v][<B Error>][<%v>]\n", id, err)
		} else {
			r, e := httputil.DumpResponse(resp, true)
			if e != nil {
				logMessage("[%v][%v][<B Error Dump>][<%v>]\n", id, e)
			} else {
				logMessage("[%v][%v][<B Resp>][<%v>]\n", id, string(r))
			}
		}

		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
	}()

	targetQuery := hosts.Target.RawQuery
	req.URL.Scheme = hosts.Target.Scheme
	req.URL.Host = hosts.Target.Host
	req.URL.Path = singleJoiningSlash(hosts.Target.Path, req.URL.Path)
	if targetQuery == "" || req.URL.RawQuery == "" {
		req.URL.RawQuery = targetQuery + req.URL.RawQuery
	} else {
		req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
	}
}

func logMessage(message string, id string, logObj interface{}) {
	fmt.Printf(message, time.Now().Format(time.RFC3339Nano), id, logObj)
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

func duplicateRequest(request *http.Request) (request1 *http.Request) {
	b1 := new(bytes.Buffer)
	b2 := new(bytes.Buffer)
	w := io.MultiWriter(b1, b2)
	io.Copy(w, request.Body)
	request.Body = ioutil.NopCloser(bytes.NewReader(b2.Bytes()))
	request2 := &http.Request{
		Method: request.Method,
		URL: &url.URL{
			Scheme:   hosts.Alternative.Scheme,
			Host:     hosts.Alternative.Host,
			Path:     singleJoiningSlash(hosts.Alternative.Path, request.URL.Path),
			RawQuery: request.URL.RawQuery,
		},
		Proto:         request.Proto,
		ProtoMajor:    request.ProtoMajor,
		ProtoMinor:    request.ProtoMinor,
		Header:        request.Header,
		Body:          ioutil.NopCloser(bytes.NewReader(b1.Bytes())),
		ContentLength: request.ContentLength,
	}

	return request2
}

func handler(w http.ResponseWriter, r *http.Request) {
	proxy.ServeHTTP(w, r)
}

func main() {
	flag.Parse()

	target, _ := url.Parse(*targetProduction)
	alt, _ := url.Parse(*altTarget)
	client = &http.Client{
		Timeout: time.Millisecond * 2000,
	}

	hosts = Hosts{
		Target:      *target,
		Alternative: *alt,
	}

	u, _ := url.Parse(*targetProduction)
	proxy = httputil.NewSingleHostReverseProxy(u)
	proxy.Transport = &TimeoutTransport{}
	proxy.Director = teeDirector

	http.HandleFunc("/", handler)
	http.ListenAndServe(*listen, nil)
}
<|endoftext|>


<commit_before>package dashboard

import (
	"html/template"
)

type templateInfo struct {
	Projects []*Project
}

var (
	indexTmpl = template.Must(template.New("index.html").Parse(`
<!DOCTYPE html>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta content="origin-when-cross-origin" name="referrer" />
  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/frameworks-98550932b9f11a849da143d2dbc9dfaa977a17656514d323ae9ce0d6fa688b60.css" integrity="sha256-mFUJMrnxGoSdoUPS28nfqpd6F2VlFNMjrpzg1vpoi2A=" media="all" rel="stylesheet" />
  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-a17b8c9d020ded73daa7ee1a3844b4512f12076634d9249861ddf86dc33da66e.css" integrity="sha256-oXuMnQIN7XPap+4aOES0US8SB2Y02SSYYd34bcM9pm4=" media="all" rel="stylesheet" />
  <title>Dashboard</title>

Jekyll At-a-Glance Dashboard

      Repo
      Gem
      Travis
      Downloads
      Commits
      Pull Requests
      Issues
      Unreleased commits

    {{range .Projects}}

    {{end}}

	Commits are as of this week. Issues and pull requests are total open.
	Source Code.

	Look wrong? <form action="/reset.json" method="post"><input type="Submit" value="Reset the cache."></form>

`))
)
<commit_msg>slightly newer CSS :)<commit_after>package dashboard

import (
	"html/template"
)

type templateInfo struct {
	Projects []*Project
}

var (
	indexTmpl = template.Must(template.New("index.html").Parse(`
<!DOCTYPE html>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta content="origin-when-cross-origin" name="referrer" />
  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/frameworks-e04a23d39bf81b7db3c635177756ef51bc171feb440be9e174933c6eb56382da.css" integrity="sha256-4Eoj05v4G32zxjUXd1bvUbwXH+tEC+nhdJM8brVjgto=" media="all" rel="stylesheet" />
  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-24334c18831214381e60cd2cee0b86be216d64096d129d8c2b644d1fdcba1d4e.css" integrity="sha256-JDNMGIMSFDgeYM0s7guGviFtZAltEp2MK2RNH9y6HU4=" media="all" rel="stylesheet" />
  <title>Dashboard</title>

Jekyll At-a-Glance Dashboard

      Repo
      Gem
      Travis
      Downloads
      Commits
      Pull Requests
      Issues
      Unreleased commits

    {{range .Projects}}

    {{end}}

	Commits are as of this week. Issues and pull requests are total open.
	Source Code.

	Look wrong? <form action="/reset.json" method="post"><input type="Submit" value="Reset the cache."></form>

`))
)
<|endoftext|>


<commit_before>package xweb

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
)

/**
 * 默认模板函数
 * 除了这里定义的之外，还可以使用当前Action（即在方法中使用Render的Action）中定义的可导出的属性和方法（使用".属性"或".方法"来访问）
 * 另外还支持函数：
 * include      —— Include(tmplName string) interface{}
 * session      —— GetSession(key string) interface{}
 * cookie       —— Cookie(key string) string
 * XsrfFormHtml —— XsrfFormHtml() template.HTML
 * XsrfValue    —— XsrfValue() string
 * XsrfName     —— XsrfName() string
 * StaticUrl    —— StaticUrl(url string) string
 * 支持变量：
 * XwebVer      —— string
 */
var (
	DefaultFuncs template.FuncMap = template.FuncMap{
		"Now":        Now,
		"Eq":         Eq,
		"FormatDate": FormatDate,
		"Add":        Add,
		"Subtract":   Subtract,
		"IsNil":      IsNil,
		"Url":        Url,
		"UrlFor":     UrlFor,
		"Html":       Html,
		"Js":         Js,
		"Css":        Css,
		"XsrfField":  XsrfName, //alias
	}
	DefaultTemplateMgr *TemplateMgr = new(TemplateMgr)
)

func IsNil(a interface{}) bool {
	switch a.(type) {
	case nil:
		return true
	}
	return false
}

func Add(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	var intSum int64 = rleft + rright

	if isInt {
		return intSum
	} else {
		return fleft + fright + float64(intSum)
	}
}

func Subtract(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	if isInt {
		return rleft - rright
	} else {
		return fleft + float64(rleft) - (fright + float64(rright))
	}
}

func Now() time.Time {
	return time.Now()
}

func FormatDate(t time.Time, format string) string {
	return t.Format(format)
}

func Eq(left interface{}, right interface{}) bool {
	leftIsNil := (left == nil)
	rightIsNil := (right == nil)
	if leftIsNil || rightIsNil {
		if leftIsNil && rightIsNil {
			return true
		}
		return false
	}
	return fmt.Sprintf("%v", left) == fmt.Sprintf("%v", right)
}

func Html(raw string) template.HTML {
	return template.HTML(raw)
}

func Js(raw string) template.JS {
	return template.JS(raw)
}

func Css(raw string) template.CSS {
	return template.CSS(raw)
}

//Usage:UrlFor("/user/login","appName","servName") or UrlFor("/user/login","appName") or UrlFor("/user/login") or UrlFor()
func Url(args ...string) string {
	var (
		route    string
		appName  string = "root"
		servName string = "main"
	)
	size := len(args)
	switch size {
	case 1:
		route = args[0]
	case 2:
		route = args[0]
		appName = args[1]
	case 3:
		route = args[0]
		appName = args[1]
		servName = args[2]
	}
	var appUrl, url, prefix, suffix string
	if server, ok := Servers[servName]; ok {
		prefix = server.Config.UrlPrefix
		suffix = server.Config.UrlSuffix
		var appDomain string
		if domain, ok := server.App2Domain[appName]; ok {
			appUrl = "/"
			appDomain = domain
		} else if appPath, ok := server.AppsNamePath[appName]; ok {
			appUrl = appPath
		}
		if appDomain != "" {
			if strings.Contains(appDomain, "//") {
				url = appDomain
			} else {
				url = "http://" + appDomain
			}
		} else {
			url = server.Config.Url
		}
	}
	url = strings.TrimRight(url, "/") + "/"
	if size == 0 {
		return url
	}
	if appUrl != "/" {
		appUrl = strings.TrimLeft(appUrl, "/")
		if length := len(appUrl); length > 0 && appUrl[length-1] != '/' {
			appUrl = appUrl + "/"
		}
	} else {
		appUrl = ""
	}
	url += prefix + appUrl
	if route == "" {
		return url
	}
	if strings.HasSuffix(route, "/") == false {
		url += strings.TrimLeft(route, "/") + suffix
	}
	return url
}

//Usage:UrlFor("main:root:/user/login") or UrlFor("root:/user/login") or UrlFor("/user/login") or UrlFor()
//这里的main代表Server名称；root代表App名称；后面的内容为Action中方法函数所对应的网址
func UrlFor(args ...string) string {
	s := [3]string{"main", "root", ""}
	var u []string
	size := len(args)
	if size > 0 {
		u = strings.Split(args[0], ":")
	} else {
		u = []string{""}
	}
	var appUrl string = ""
	switch len(u) {
	case 1:
		s[2] = u[0]
	case 2:
		s[1] = u[0]
		s[2] = u[1]
	default:
		s[0] = u[0]
		s[1] = u[1]
		s[2] = u[2]
	}
	var url, prefix, suffix string
	if server, ok := Servers[s[0]]; ok {
		prefix = server.Config.UrlPrefix
		suffix = server.Config.UrlSuffix
		var appDomain string
		if domain, ok := server.App2Domain[s[1]]; ok {
			appUrl = "/"
			appDomain = domain
		} else if appPath, ok := server.AppsNamePath[s[1]]; ok {
			appUrl = appPath
		}
		if appDomain != "" {
			if strings.Contains(appDomain, "//") {
				url = appDomain
			} else {
				url = "http://" + appDomain
			}
		} else {
			url = server.Config.Url
		}
	}
	url = strings.TrimRight(url, "/") + "/"
	if size == 0 {
		return url
	}
	if appUrl != "/" {
		appUrl = strings.TrimLeft(appUrl, "/")
		if length := len(appUrl); length > 0 && appUrl[length-1] != '/' {
			appUrl = appUrl + "/"
		}
	} else {
		appUrl = ""
	}
	url += prefix + appUrl
	if s[2] == "" {
		return url
	}
	if strings.HasSuffix(s[2], "/") == false {
		url += strings.TrimLeft(s[2], "/") + suffix
	}
	return url
}

type TemplateMgr struct {
	Caches       map[string][]byte
	mutex        *sync.Mutex
	RootDir      string
	Ignores      map[string]bool
	IsReload     bool
	app          *App
	Preprocessor func([]byte) []byte
}

func (self *TemplateMgr) Moniter(rootDir string) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	done := make(chan bool)
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if ev == nil {
					break
				}
				if _, ok := self.Ignores[filepath.Base(ev.Name)]; ok {
					break
				}
				d, err := os.Stat(ev.Name)
				if err != nil {
					break
				}

				if ev.IsCreate() {
					if d.IsDir() {
						watcher.Watch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							self.app.Errorf("loaded template %v failed: %v", tmpl, err)
							break
						}
						self.app.Infof("loaded template file %v success", tmpl)
						self.CacheTemplate(tmpl, content)
					}
				} else if ev.IsDelete() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						self.CacheDelete(tmpl)
					}
				} else if ev.IsModify() {
					if d.IsDir() {
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							self.app.Errorf("reloaded template %v failed: %v", tmpl, err)
							break
						}

						self.CacheTemplate(tmpl, content)
						self.app.Infof("reloaded template %v success", tmpl)
					}
				} else if ev.IsRename() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						self.CacheDelete(tmpl)
					}
				}
			case err := <-watcher.Error:
				self.app.Error("error:", err)
			}
		}
	}()

	err = filepath.Walk(self.RootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return watcher.Watch(f)
		}
		return nil
	})

	if err != nil {
		self.app.Error(err.Error())
		return err
	}

	<-done

	watcher.Close()
	return nil
}

func (self *TemplateMgr) CacheAll(rootDir string) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	fmt.Print("Reading the contents of the template files, please wait... ")
	err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		tmpl := f[len(rootDir)+1:]
		tmpl = FixDirSeparator(tmpl)
		if _, ok := self.Ignores[filepath.Base(tmpl)]; !ok {
			fpath := filepath.Join(self.RootDir, tmpl)
			content, err := ioutil.ReadFile(fpath)
			if err != nil {
				self.app.Debugf("load template %s error: %v", fpath, err)
				return err
			}
			self.app.Debug("loaded template", fpath)
			self.Caches[tmpl] = content
		}
		return nil
	})
	fmt.Println("Complete.")
	return err
}

func (self *TemplateMgr) Init(app *App, rootDir string, reload bool) error {
	self.RootDir = rootDir
	self.Caches = make(map[string][]byte)
	self.Ignores = make(map[string]bool)
	self.mutex = &sync.Mutex{}
	self.app = app
	if dirExists(rootDir) {
		//self.CacheAll(rootDir)
		if reload {
			go self.Moniter(rootDir)
		}
	}

	if len(self.Ignores) == 0 {
		self.Ignores["*.tmp"] = false
	}

	return nil
}

func (self *TemplateMgr) GetTemplate(tmpl string) ([]byte, error) {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	if content, ok := self.Caches[tmpl]; ok {
		self.app.Debugf("load template %v from cache", tmpl)
		return content, nil
	}

	content, err := ioutil.ReadFile(filepath.Join(self.RootDir, tmpl))
	if err == nil {
		self.app.Debugf("load template %v from the file:", tmpl)
		self.Caches[tmpl] = content
	}
	return content, err
}

func (self *TemplateMgr) CacheTemplate(tmpl string, content []byte) {
	if self.Preprocessor != nil {
		content = self.Preprocessor(content)
	}
	self.mutex.Lock()
	defer self.mutex.Unlock()
	tmpl = FixDirSeparator(tmpl)
	self.app.Debugf("update template %v on cache", tmpl)
	self.Caches[tmpl] = content
	return
}

func (self *TemplateMgr) CacheDelete(tmpl string) {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	tmpl = FixDirSeparator(tmpl)
	if _, ok := self.Caches[tmpl]; ok {
		self.app.Debugf("delete template %v from cache", tmpl)
		delete(self.Caches, tmpl)
	}
	return
}
<commit_msg>fixed comment<commit_after>package xweb

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/howeyc/fsnotify"
)

/**
 * 默认模板函数
 * 除了这里定义的之外，还可以使用当前Action（即在方法中使用Render的Action）中定义的可导出的属性和方法（使用".属性"或".方法"来访问）
 * 另外还支持函数：
 * include      —— Include(tmplName string) interface{}
 * session      —— GetSession(key string) interface{}
 * cookie       —— Cookie(key string) string
 * XsrfFormHtml —— XsrfFormHtml() template.HTML
 * XsrfValue    —— XsrfValue() string
 * XsrfName     —— XsrfName() string
 * StaticUrl    —— StaticUrl(url string) string
 * 支持变量：
 * XwebVer      —— string
 */
var (
	DefaultFuncs template.FuncMap = template.FuncMap{
		"Now":        Now,
		"Eq":         Eq,
		"FormatDate": FormatDate,
		"Add":        Add,
		"Subtract":   Subtract,
		"IsNil":      IsNil,
		"Url":        Url,
		"UrlFor":     UrlFor,
		"Html":       Html,
		"Js":         Js,
		"Css":        Css,
		"XsrfField":  XsrfName, //alias
	}
	DefaultTemplateMgr *TemplateMgr = new(TemplateMgr)
)

func IsNil(a interface{}) bool {
	switch a.(type) {
	case nil:
		return true
	}
	return false
}

func Add(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	var intSum int64 = rleft + rright

	if isInt {
		return intSum
	} else {
		return fleft + fright + float64(intSum)
	}
}

func Subtract(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	if isInt {
		return rleft - rright
	} else {
		return fleft + float64(rleft) - (fright + float64(rright))
	}
}

func Now() time.Time {
	return time.Now()
}

func FormatDate(t time.Time, format string) string {
	return t.Format(format)
}

func Eq(left interface{}, right interface{}) bool {
	leftIsNil := (left == nil)
	rightIsNil := (right == nil)
	if leftIsNil || rightIsNil {
		if leftIsNil && rightIsNil {
			return true
		}
		return false
	}
	return fmt.Sprintf("%v", left) == fmt.Sprintf("%v", right)
}

func Html(raw string) template.HTML {
	return template.HTML(raw)
}

func Js(raw string) template.JS {
	return template.JS(raw)
}

func Css(raw string) template.CSS {
	return template.CSS(raw)
}

//Usage:Url("/user/login","appName","servName") or Url("/user/login","appName") or Url("/user/login") or UrlFor()
func Url(args ...string) string {
	var (
		route    string
		appName  string = "root"
		servName string = "main"
	)
	size := len(args)
	switch size {
	case 1:
		route = args[0]
	case 2:
		route = args[0]
		appName = args[1]
	case 3:
		route = args[0]
		appName = args[1]
		servName = args[2]
	}
	var appUrl, url, prefix, suffix string
	if server, ok := Servers[servName]; ok {
		prefix = server.Config.UrlPrefix
		suffix = server.Config.UrlSuffix
		var appDomain string
		if domain, ok := server.App2Domain[appName]; ok {
			appUrl = "/"
			appDomain = domain
		} else if appPath, ok := server.AppsNamePath[appName]; ok {
			appUrl = appPath
		}
		if appDomain != "" {
			if strings.Contains(appDomain, "//") {
				url = appDomain
			} else {
				url = "http://" + appDomain
			}
		} else {
			url = server.Config.Url
		}
	}
	url = strings.TrimRight(url, "/") + "/"
	if size == 0 {
		return url
	}
	if appUrl != "/" {
		appUrl = strings.TrimLeft(appUrl, "/")
		if length := len(appUrl); length > 0 && appUrl[length-1] != '/' {
			appUrl = appUrl + "/"
		}
	} else {
		appUrl = ""
	}
	url += prefix + appUrl
	if route == "" {
		return url
	}
	if strings.HasSuffix(route, "/") == false {
		url += strings.TrimLeft(route, "/") + suffix
	}
	return url
}

//Usage:UrlFor("main:root:/user/login") or UrlFor("root:/user/login") or UrlFor("/user/login") or UrlFor()
//这里的main代表Server名称；root代表App名称；后面的内容为Action中方法函数所对应的网址
func UrlFor(args ...string) string {
	s := [3]string{"main", "root", ""}
	var u []string
	size := len(args)
	if size > 0 {
		u = strings.Split(args[0], ":")
	} else {
		u = []string{""}
	}
	var appUrl string = ""
	switch len(u) {
	case 1:
		s[2] = u[0]
	case 2:
		s[1] = u[0]
		s[2] = u[1]
	default:
		s[0] = u[0]
		s[1] = u[1]
		s[2] = u[2]
	}
	var url, prefix, suffix string
	if server, ok := Servers[s[0]]; ok {
		prefix = server.Config.UrlPrefix
		suffix = server.Config.UrlSuffix
		var appDomain string
		if domain, ok := server.App2Domain[s[1]]; ok {
			appUrl = "/"
			appDomain = domain
		} else if appPath, ok := server.AppsNamePath[s[1]]; ok {
			appUrl = appPath
		}
		if appDomain != "" {
			if strings.Contains(appDomain, "//") {
				url = appDomain
			} else {
				url = "http://" + appDomain
			}
		} else {
			url = server.Config.Url
		}
	}
	url = strings.TrimRight(url, "/") + "/"
	if size == 0 {
		return url
	}
	if appUrl != "/" {
		appUrl = strings.TrimLeft(appUrl, "/")
		if length := len(appUrl); length > 0 && appUrl[length-1] != '/' {
			appUrl = appUrl + "/"
		}
	} else {
		appUrl = ""
	}
	url += prefix + appUrl
	if s[2] == "" {
		return url
	}
	if strings.HasSuffix(s[2], "/") == false {
		url += strings.TrimLeft(s[2], "/") + suffix
	}
	return url
}

type TemplateMgr struct {
	Caches       map[string][]byte
	mutex        *sync.Mutex
	RootDir      string
	Ignores      map[string]bool
	IsReload     bool
	app          *App
	Preprocessor func([]byte) []byte
}

func (self *TemplateMgr) Moniter(rootDir string) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	done := make(chan bool)
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if ev == nil {
					break
				}
				if _, ok := self.Ignores[filepath.Base(ev.Name)]; ok {
					break
				}
				d, err := os.Stat(ev.Name)
				if err != nil {
					break
				}

				if ev.IsCreate() {
					if d.IsDir() {
						watcher.Watch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							self.app.Errorf("loaded template %v failed: %v", tmpl, err)
							break
						}
						self.app.Infof("loaded template file %v success", tmpl)
						self.CacheTemplate(tmpl, content)
					}
				} else if ev.IsDelete() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						self.CacheDelete(tmpl)
					}
				} else if ev.IsModify() {
					if d.IsDir() {
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							self.app.Errorf("reloaded template %v failed: %v", tmpl, err)
							break
						}

						self.CacheTemplate(tmpl, content)
						self.app.Infof("reloaded template %v success", tmpl)
					}
				} else if ev.IsRename() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						self.CacheDelete(tmpl)
					}
				}
			case err := <-watcher.Error:
				self.app.Error("error:", err)
			}
		}
	}()

	err = filepath.Walk(self.RootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return watcher.Watch(f)
		}
		return nil
	})

	if err != nil {
		self.app.Error(err.Error())
		return err
	}

	<-done

	watcher.Close()
	return nil
}

func (self *TemplateMgr) CacheAll(rootDir string) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	fmt.Print("Reading the contents of the template files, please wait... ")
	err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		tmpl := f[len(rootDir)+1:]
		tmpl = FixDirSeparator(tmpl)
		if _, ok := self.Ignores[filepath.Base(tmpl)]; !ok {
			fpath := filepath.Join(self.RootDir, tmpl)
			content, err := ioutil.ReadFile(fpath)
			if err != nil {
				self.app.Debugf("load template %s error: %v", fpath, err)
				return err
			}
			self.app.Debug("loaded template", fpath)
			self.Caches[tmpl] = content
		}
		return nil
	})
	fmt.Println("Complete.")
	return err
}

func (self *TemplateMgr) Init(app *App, rootDir string, reload bool) error {
	self.RootDir = rootDir
	self.Caches = make(map[string][]byte)
	self.Ignores = make(map[string]bool)
	self.mutex = &sync.Mutex{}
	self.app = app
	if dirExists(rootDir) {
		//self.CacheAll(rootDir)
		if reload {
			go self.Moniter(rootDir)
		}
	}

	if len(self.Ignores) == 0 {
		self.Ignores["*.tmp"] = false
	}

	return nil
}

func (self *TemplateMgr) GetTemplate(tmpl string) ([]byte, error) {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	if content, ok := self.Caches[tmpl]; ok {
		self.app.Debugf("load template %v from cache", tmpl)
		return content, nil
	}

	content, err := ioutil.ReadFile(filepath.Join(self.RootDir, tmpl))
	if err == nil {
		self.app.Debugf("load template %v from the file:", tmpl)
		self.Caches[tmpl] = content
	}
	return content, err
}

func (self *TemplateMgr) CacheTemplate(tmpl string, content []byte) {
	if self.Preprocessor != nil {
		content = self.Preprocessor(content)
	}
	self.mutex.Lock()
	defer self.mutex.Unlock()
	tmpl = FixDirSeparator(tmpl)
	self.app.Debugf("update template %v on cache", tmpl)
	self.Caches[tmpl] = content
	return
}

func (self *TemplateMgr) CacheDelete(tmpl string) {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	tmpl = FixDirSeparator(tmpl)
	if _, ok := self.Caches[tmpl]; ok {
		self.app.Debugf("delete template %v from cache", tmpl)
		delete(self.Caches, tmpl)
	}
	return
}
<|endoftext|>


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

// assumptions
// we've been booted into a ramfs with all this stuff unpacked and ready.
// we don't need a loop device mount because it's all there.
// So we run /go/bin/go build installcommand
// and then exec /buildbin/sh

package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"syscall"
)

type mount struct {
	source string
	target string
	fstype string
	flags  uintptr
	opts   string
	mode   os.FileMode
}

var (
	env = map[string]string{
		"PATH":            "/go/bin:/bin:/buildbin:/usr/local/bin:",
		"LD_LIBRARY_PATH": "/usr/local/lib",
		"GOROOT":          "/go",
		"GOPATH":          "/",
		"CGO_ENABLED":     "0",
	}

	namespace = []mount{
		{source: "proc", target: "/proc", fstype: "proc", flags: syscall.MS_MGC_VAL | syscall.MS_RDONLY, opts: "", mode: os.FileMode(0555)},
	}
)

func main() {
	log.Printf("Welcome to u-root")
	envs := []string{}
	for k, v := range env {
		os.Setenv(k, v)
		envs = append(envs, k+"="+v)
	}

	for _, m := range namespace {
		if err := os.MkdirAll(m.target, m.mode); err != nil {
			log.Printf("mkdir :%s: mode %o: %v\n", m.target, m.mode)
			continue
		}
		if err := syscall.Mount(m.source, m.target, m.fstype, m.flags, m.opts); err != nil {
			log.Printf("Mount :%s: on :%s: type :%s: flags %x: %v\n", m.source, m.target, m.fstype, m.flags, m.opts, err)
		}

	}
	// populate buildbin

	if commands, err := ioutil.ReadDir("/src"); err == nil {
		for _, v := range commands {
			name := v.Name()
			if name == "installcommand" || name == "init" {
				continue
			} else {
				destPath := path.Join("/buildbin", name)
				source := "/buildbin/installcommand"
				if err := os.Symlink(source, destPath); err != nil {
					log.Printf("Symlink %v -> %v failed; %v", source, destPath, err)
				}
			}
		}
	} else {
		log.Fatalf("Can't read %v; %v", "/src", err)
	}
	log.Printf("envs %v", envs)
	os.Setenv("GOBIN", "/buildbin")
	cmd := exec.Command("go", "install", "-x", "installcommand")
	installenvs := envs
	installenvs = append(envs, "GOBIN=/buildbin")
	cmd.Env = installenvs
	cmd.Dir = "/"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Run %v", cmd)
	err := cmd.Run()
	if err != nil {
		log.Printf("%v\n", err)
	}

	os.Setenv("GOBIN", "/bin")
	cmd = exec.Command("/buildbin/sh")
	envs = append(envs, "GOBIN=/bin")
	cmd.Env = envs
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Run %v", cmd)
	err = cmd.Run()
	if err != nil {
		log.Printf("%v\n", err)
	}
	log.Printf("init: /bin/sh returned!\n")
}
<commit_msg>Init now creates more directories.<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// assumptions
// we've been booted into a ramfs with all this stuff unpacked and ready.
// we don't need a loop device mount because it's all there.
// So we run /go/bin/go build installcommand
// and then exec /buildbin/sh

package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"syscall"
)

type dir struct {
	name string
	mode   os.FileMode
}

type mount struct {
	source string
	target string
	fstype string
	flags  uintptr
	opts   string
}

var (
	env = map[string]string{
		"PATH":            "/go/bin:/bin:/buildbin:/usr/local/bin:",
		"LD_LIBRARY_PATH": "/usr/local/lib",
		"GOROOT":          "/go",
		"GOPATH":          "/",
		"CGO_ENABLED":     "0",
	}

	dirs = []dir {
		{name: "/proc", mode: os.FileMode(0555),},
		{name: "/buildbin", mode: os.FileMode(0777),},
		{name: "/bin", mode: os.FileMode(0777),},
		{name: "/tmp", mode: os.FileMode(0777),},
		{name: "/etc", mode: os.FileMode(0777),},
		{name: "/tcz", mode: os.FileMode(0777),},
		{name: "/dev", mode: os.FileMode(0777),},
		{name: "/lib", mode: os.FileMode(0777),},
		{name: "/usr/lib", mode: os.FileMode(0777),},
		{name: "/go/pkg/linux_amd64", mode: os.FileMode(0777),},
	}
	namespace = []mount{
		{source: "proc", target: "/proc", fstype: "proc", flags: syscall.MS_MGC_VAL | syscall.MS_RDONLY, opts: "",},
	}
)

func main() {
	log.Printf("Welcome to u-root")
	envs := []string{}
	for k, v := range env {
		os.Setenv(k, v)
		envs = append(envs, k+"="+v)
	}

	for _, m := range dirs {
		if err := os.MkdirAll(m.name, m.mode); err != nil {
			log.Printf("mkdir :%s: mode %o: %v\n", m.name, m.mode)
			continue
		}
	}

	for _, m := range namespace {
		if err := syscall.Mount(m.source, m.target, m.fstype, m.flags, m.opts); err != nil {
			log.Printf("Mount :%s: on :%s: type :%s: flags %x: %v\n", m.source, m.target, m.fstype, m.flags, m.opts, err)
		}

	}
	// populate buildbin

	if commands, err := ioutil.ReadDir("/src"); err == nil {
		for _, v := range commands {
			name := v.Name()
			if name == "installcommand" || name == "init" {
				continue
			} else {
				destPath := path.Join("/buildbin", name)
				source := "/buildbin/installcommand"
				if err := os.Symlink(source, destPath); err != nil {
					log.Printf("Symlink %v -> %v failed; %v", source, destPath, err)
				}
			}
		}
	} else {
		log.Fatalf("Can't read %v; %v", "/src", err)
	}
	log.Printf("envs %v", envs)
	os.Setenv("GOBIN", "/buildbin")
	cmd := exec.Command("go", "install", "-x", "installcommand")
	installenvs := envs
	installenvs = append(envs, "GOBIN=/buildbin")
	cmd.Env = installenvs
	cmd.Dir = "/"

	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	log.Printf("Run %v", cmd)
	err := cmd.Run()
	if err != nil {
		log.Printf("%v\n", err)
	}

	os.Setenv("GOBIN", "/bin")
	cmd = exec.Command("/buildbin/sh")
	envs = append(envs, "GOBIN=/bin")
	cmd.Env = envs
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	// TODO: figure out why we get EPERM when we use this.
	//cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true,}
	log.Printf("Run %v", cmd)
	err = cmd.Run()
	if err != nil {
		log.Printf("%v\n", err)
	}
	log.Printf("init: /bin/sh returned!\n")
}
<|endoftext|>


<commit_before>package ui

import (
	"bufio"
	"bytes"
	"strings"
)

// Styled is a wrapper around an existing UI that automatically
// adds formatting around the UI text.
type Styled struct {
	Ui
}

func (u *Styled) Header(msg string) {
	u.Ui.Header(u.prefix("==> ", msg))
}

func (u *Styled) Message(msg string) {
	u.Ui.Message(u.prefix("    ", msg))
}

func (u *Styled) prefix(prefix, msg string) string {
	var buf bytes.Buffer

	scan := bufio.NewScanner(strings.NewReader(msg))
	for scan.Scan() {
		buf.WriteString(prefix)
		buf.WriteString(scan.Text() + "\n")
	}

	str := buf.String()
	str = str[:len(str)-1]
	return str
}
<commit_msg>ui: fix panic case if empty string is sent<commit_after>package ui

import (
	"bufio"
	"bytes"
	"strings"
)

// Styled is a wrapper around an existing UI that automatically
// adds formatting around the UI text.
type Styled struct {
	Ui
}

func (u *Styled) Header(msg string) {
	u.Ui.Header(u.prefix("==> ", msg))
}

func (u *Styled) Message(msg string) {
	u.Ui.Message(u.prefix("    ", msg))
}

func (u *Styled) prefix(prefix, msg string) string {
	var buf bytes.Buffer

	scan := bufio.NewScanner(strings.NewReader(msg))
	for scan.Scan() {
		buf.WriteString(prefix)
		buf.WriteString(scan.Text() + "\n")
	}

	str := buf.String()
	if msg != "" {
		str = str[:len(str)-1]
	}

	return str
}
<|endoftext|>


<commit_before>package engi

import (
	"log"
)

type Camera struct {
	Point
	tracking *Entity
}

func (cam *Camera) FollowEntity(entity *Entity) {
	cam.tracking = entity
	var space *SpaceComponent
	if !cam.tracking.GetComponent(&space) {
		return
	}

	centerCam(&cam.Point, Width(), Height(), 1, space)
	// cam.Point = space.Position
}

func (cam *Camera) Update(dt float32) {
	// maxPoints := 8
	if cam.tracking != nil {
		var space *SpaceComponent
		if !cam.tracking.GetComponent(&space) {
			return
		}
		log.Println(cam.Point)
		centerCam(&cam.Point, Width(), Height(), 0.09, space)
	}
}

func centerCam(to *Point, width, height, lerp float32, space *SpaceComponent) {
	to.X += ((space.Position.X + space.Width/2) - (to.X + width/2)) * lerp
	to.Y += ((space.Position.Y + space.Height/2) - (to.Y + height/2)) * lerp
}
<commit_msg>Add a tinsy bit of docs to the camera<commit_after>// Copyright 2014 Harrison Shoebridge. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package engi

// A rather basic camera
type Camera struct {
	Point
	tracking *Entity // The entity that is currently being followed
}

func (cam *Camera) FollowEntity(entity *Entity) {
	cam.tracking = entity
	var space *SpaceComponent
	if !cam.tracking.GetComponent(&space) {
		return
	}

	centerCam(&cam.Point, Width(), Height(), 1, space)
	// cam.Point = space.Position
}

func (cam *Camera) Update(dt float32) {
	// maxPoints := 8
	if cam.tracking != nil {
		var space *SpaceComponent
		if !cam.tracking.GetComponent(&space) {
			return
		}
		centerCam(&cam.Point, Width(), Height(), 0.09, space)
	}
}

func centerCam(to *Point, width, height, lerp float32, space *SpaceComponent) {
	to.X += ((space.Position.X + space.Width/2) - (to.X + width/2)) * lerp
	to.Y += ((space.Position.Y + space.Height/2) - (to.Y + height/2)) * lerp
}
<|endoftext|>