<commit_before>// Copyright 2016 Prometheus Team
// 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 inhibit

import (
	"testing"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"

	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/store"
	"github.com/prometheus/alertmanager/types"
)

var nopLogger = log.NewNopLogger()

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

	now := time.Now()
	cases := []struct {
		initial map[model.Fingerprint]*types.Alert
		equal   model.LabelNames
		input   model.LabelSet
		result  bool
	}{
		{
			// No source alerts at all.
			initial: map[model.Fingerprint]*types.Alert{},
			input:   model.LabelSet{"a": "b"},
			result:  false,
		},
		{
			// No equal labels, any source alerts satisfies the requirement.
			initial: map[model.Fingerprint]*types.Alert{1: &types.Alert{}},
			input:   model.LabelSet{"a": "b"},
			result:  true,
		},
		{
			// Matching but already resolved.
			initial: map[model.Fingerprint]*types.Alert{
				1: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "b": "f"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
				2: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "b": "c"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
			},
			equal:  model.LabelNames{"a", "b"},
			input:  model.LabelSet{"a": "b", "b": "c"},
			result: false,
		},
		{
			// Matching and unresolved.
			initial: map[model.Fingerprint]*types.Alert{
				1: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "c": "d"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
				2: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "c": "f"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(time.Hour),
					},
				},
			},
			equal:  model.LabelNames{"a"},
			input:  model.LabelSet{"a": "b"},
			result: true,
		},
		{
			// Equal label does not match.
			initial: map[model.Fingerprint]*types.Alert{
				1: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "c", "c": "d"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
				2: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "c", "c": "f"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
			},
			equal:  model.LabelNames{"a"},
			input:  model.LabelSet{"a": "b"},
			result: false,
		},
	}

	for _, c := range cases {
		r := &InhibitRule{
			Equal:  map[model.LabelName]struct{}{},
			scache: store.NewAlerts(5 * time.Minute),
		}
		for _, ln := range c.equal {
			r.Equal[ln] = struct{}{}
		}
		for _, v := range c.initial {
			r.scache.Set(v)
		}

		if _, have := r.hasEqual(c.input, false); have != c.result {
			t.Errorf("Unexpected result %t, expected %t", have, c.result)
		}
	}
}

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

	rule1 := config.InhibitRule{
		SourceMatch: map[string]string{"s1": "1"},
		TargetMatch: map[string]string{"t1": "1"},
		Equal:       model.LabelNames{"e"},
	}
	rule2 := config.InhibitRule{
		SourceMatch: map[string]string{"s2": "1"},
		TargetMatch: map[string]string{"t2": "1"},
		Equal:       model.LabelNames{"e"},
	}
	m := types.NewMarker(prometheus.NewRegistry())
	ih := NewInhibitor(nil, []*config.InhibitRule{&rule1, &rule2}, m, nopLogger)
	now := time.Now()
	// Active alert that matches the source filter of rule1.
	sourceAlert1 := &types.Alert{
		Alert: model.Alert{
			Labels:   model.LabelSet{"s1": "1", "t1": "2", "e": "1"},
			StartsAt: now.Add(-time.Minute),
			EndsAt:   now.Add(time.Hour),
		},
	}
	// Active alert that matches the source filter _and_ the target filter of rule2.
	sourceAlert2 := &types.Alert{
		Alert: model.Alert{
			Labels:   model.LabelSet{"s2": "1", "t2": "1", "e": "1"},
			StartsAt: now.Add(-time.Minute),
			EndsAt:   now.Add(time.Hour),
		},
	}

	ih.rules[0].scache = store.NewAlerts(5 * time.Minute)
	ih.rules[0].scache.Set(sourceAlert1)
	ih.rules[1].scache = store.NewAlerts(5 * time.Minute)
	ih.rules[1].scache.Set(sourceAlert2)

	cases := []struct {
		target   model.LabelSet
		expected bool
	}{
		{
			// Matches target filter of rule1, inhibited.
			target:   model.LabelSet{"t1": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches target filter of rule2, inhibited.
			target:   model.LabelSet{"t2": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches target filter of rule1 (plus noise), inhibited.
			target:   model.LabelSet{"t1": "1", "t3": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches target filter of rule1 plus rule2, inhibited.
			target:   model.LabelSet{"t1": "1", "t2": "1", "e": "1"},
			expected: true,
		},
		{
			// Doesn't match target filter, not inhibited.
			target:   model.LabelSet{"t1": "0", "e": "1"},
			expected: false,
		},
		{
			// Matches both source and target filters of rule1,
			// inhibited because sourceAlert1 matches only the
			// source filter of rule1.
			target:   model.LabelSet{"s1": "1", "t1": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches both source and target filters of rule2,
			// not inhibited because sourceAlert2 matches also both the
			// source and target filter of rule1.
			target:   model.LabelSet{"s2": "1", "t2": "1", "e": "1"},
			expected: false,
		},
		{
			// Matches target filter, equal label doesn't match, not inhibited
			target:   model.LabelSet{"t1": "1", "e": "0"},
			expected: false,
		},
	}

	for _, c := range cases {
		if actual := ih.Mutes(c.target); actual != c.expected {
			t.Errorf("Expected (*Inhibitor).Mutes(%v) to return %t but got %t", c.target, c.expected, actual)
		}
	}
}

type fakeAlerts struct {
	alerts   []*types.Alert
	finished chan struct{}
}

func newFakeAlerts(alerts []*types.Alert) *fakeAlerts {
	return &fakeAlerts{
		alerts:   alerts,
		finished: make(chan struct{}),
	}
}

func (f *fakeAlerts) GetPending() provider.AlertIterator          { return nil }
func (f *fakeAlerts) Get(model.Fingerprint) (*types.Alert, error) { return nil, nil }
func (f *fakeAlerts) Put(...*types.Alert) error                   { return nil }
func (f *fakeAlerts) Subscribe() provider.AlertIterator {
	ch := make(chan *types.Alert)
	done := make(chan struct{})
	go func() {
		for _, a := range f.alerts {
			ch <- a
		}
		// Send another (meaningless) alert to make sure that the inhibitor has
		// processed everything.
		ch <- &types.Alert{
			Alert: model.Alert{
				Labels:   model.LabelSet{},
				StartsAt: time.Now(),
			},
		}
		close(f.finished)
		<-done
	}()
	return provider.NewAlertIterator(ch, done, nil)
}

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

	now := time.Now()
	inhibitRule := func() *config.InhibitRule {
		return &config.InhibitRule{
			SourceMatch: map[string]string{"s": "1"},
			TargetMatch: map[string]string{"t": "1"},
			Equal:       model.LabelNames{"e"},
		}
	}
	// alertOne is muted by alertTwo when it is active.
	alertOne := func() *types.Alert {
		return &types.Alert{
			Alert: model.Alert{
				Labels:   model.LabelSet{"t": "1", "e": "f"},
				StartsAt: now.Add(-time.Minute),
				EndsAt:   now.Add(time.Hour),
			},
		}
	}
	alertTwo := func(resolved bool) *types.Alert {
		var end time.Time
		if resolved {
			end = now.Add(-time.Second)
		} else {
			end = now.Add(time.Hour)
		}
		return &types.Alert{
			Alert: model.Alert{
				Labels:   model.LabelSet{"s": "1", "e": "f"},
				StartsAt: now.Add(-time.Minute),
				EndsAt:   end,
			},
		}
	}

	type exp struct {
		lbls  model.LabelSet
		muted bool
	}
	for i, tc := range []struct {
		alerts   []*types.Alert
		expected []exp
	}{
		{
			// alertOne shouldn't be muted since alertTwo hasn't fired.
			alerts: []*types.Alert{alertOne()},
			expected: []exp{
				{
					lbls:  model.LabelSet{"t": "1", "e": "f"},
					muted: false,
				},
			},
		},
		{
			// alertOne should be muted by alertTwo which is active.
			alerts: []*types.Alert{alertOne(), alertTwo(false)},
			expected: []exp{
				{
					lbls:  model.LabelSet{"t": "1", "e": "f"},
					muted: true,
				},
				{
					lbls:  model.LabelSet{"s": "1", "e": "f"},
					muted: false,
				},
			},
		},
		{
			// alertOne shouldn't be muted since alertTwo is resolved.
			alerts: []*types.Alert{alertOne(), alertTwo(false), alertTwo(true)},
			expected: []exp{
				{
					lbls:  model.LabelSet{"t": "1", "e": "f"},
					muted: false,
				},
				{
					lbls:  model.LabelSet{"s": "1", "e": "f"},
					muted: false,
				},
			},
		},
	} {
		ap := newFakeAlerts(tc.alerts)
		mk := types.NewMarker(prometheus.NewRegistry())
		inhibitor := NewInhibitor(ap, []*config.InhibitRule{inhibitRule()}, mk, nopLogger)

		go func() {
			for ap.finished != nil {
				select {
				case <-ap.finished:
					ap.finished = nil
				default:
				}
			}
			inhibitor.Stop()
		}()
		inhibitor.Run()

		for _, expected := range tc.expected {
			if inhibitor.Mutes(expected.lbls) != expected.muted {
				mute := "unmuted"
				if expected.muted {
					mute = "muted"
				}
				t.Errorf("tc: %d, expected alert with labels %q to be %s", i, expected.lbls, mute)
			}
		}
	}
}
<commit_msg>Another doc comment fix for inhibition tests<commit_after>// Copyright 2016 Prometheus Team
// 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 inhibit

import (
	"testing"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"

	"github.com/prometheus/alertmanager/config"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/store"
	"github.com/prometheus/alertmanager/types"
)

var nopLogger = log.NewNopLogger()

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

	now := time.Now()
	cases := []struct {
		initial map[model.Fingerprint]*types.Alert
		equal   model.LabelNames
		input   model.LabelSet
		result  bool
	}{
		{
			// No source alerts at all.
			initial: map[model.Fingerprint]*types.Alert{},
			input:   model.LabelSet{"a": "b"},
			result:  false,
		},
		{
			// No equal labels, any source alerts satisfies the requirement.
			initial: map[model.Fingerprint]*types.Alert{1: &types.Alert{}},
			input:   model.LabelSet{"a": "b"},
			result:  true,
		},
		{
			// Matching but already resolved.
			initial: map[model.Fingerprint]*types.Alert{
				1: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "b": "f"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
				2: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "b": "c"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
			},
			equal:  model.LabelNames{"a", "b"},
			input:  model.LabelSet{"a": "b", "b": "c"},
			result: false,
		},
		{
			// Matching and unresolved.
			initial: map[model.Fingerprint]*types.Alert{
				1: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "c": "d"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
				2: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "b", "c": "f"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(time.Hour),
					},
				},
			},
			equal:  model.LabelNames{"a"},
			input:  model.LabelSet{"a": "b"},
			result: true,
		},
		{
			// Equal label does not match.
			initial: map[model.Fingerprint]*types.Alert{
				1: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "c", "c": "d"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
				2: &types.Alert{
					Alert: model.Alert{
						Labels:   model.LabelSet{"a": "c", "c": "f"},
						StartsAt: now.Add(-time.Minute),
						EndsAt:   now.Add(-time.Second),
					},
				},
			},
			equal:  model.LabelNames{"a"},
			input:  model.LabelSet{"a": "b"},
			result: false,
		},
	}

	for _, c := range cases {
		r := &InhibitRule{
			Equal:  map[model.LabelName]struct{}{},
			scache: store.NewAlerts(5 * time.Minute),
		}
		for _, ln := range c.equal {
			r.Equal[ln] = struct{}{}
		}
		for _, v := range c.initial {
			r.scache.Set(v)
		}

		if _, have := r.hasEqual(c.input, false); have != c.result {
			t.Errorf("Unexpected result %t, expected %t", have, c.result)
		}
	}
}

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

	rule1 := config.InhibitRule{
		SourceMatch: map[string]string{"s1": "1"},
		TargetMatch: map[string]string{"t1": "1"},
		Equal:       model.LabelNames{"e"},
	}
	rule2 := config.InhibitRule{
		SourceMatch: map[string]string{"s2": "1"},
		TargetMatch: map[string]string{"t2": "1"},
		Equal:       model.LabelNames{"e"},
	}
	m := types.NewMarker(prometheus.NewRegistry())
	ih := NewInhibitor(nil, []*config.InhibitRule{&rule1, &rule2}, m, nopLogger)
	now := time.Now()
	// Active alert that matches the source filter of rule1.
	sourceAlert1 := &types.Alert{
		Alert: model.Alert{
			Labels:   model.LabelSet{"s1": "1", "t1": "2", "e": "1"},
			StartsAt: now.Add(-time.Minute),
			EndsAt:   now.Add(time.Hour),
		},
	}
	// Active alert that matches the source filter _and_ the target filter of rule2.
	sourceAlert2 := &types.Alert{
		Alert: model.Alert{
			Labels:   model.LabelSet{"s2": "1", "t2": "1", "e": "1"},
			StartsAt: now.Add(-time.Minute),
			EndsAt:   now.Add(time.Hour),
		},
	}

	ih.rules[0].scache = store.NewAlerts(5 * time.Minute)
	ih.rules[0].scache.Set(sourceAlert1)
	ih.rules[1].scache = store.NewAlerts(5 * time.Minute)
	ih.rules[1].scache.Set(sourceAlert2)

	cases := []struct {
		target   model.LabelSet
		expected bool
	}{
		{
			// Matches target filter of rule1, inhibited.
			target:   model.LabelSet{"t1": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches target filter of rule2, inhibited.
			target:   model.LabelSet{"t2": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches target filter of rule1 (plus noise), inhibited.
			target:   model.LabelSet{"t1": "1", "t3": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches target filter of rule1 plus rule2, inhibited.
			target:   model.LabelSet{"t1": "1", "t2": "1", "e": "1"},
			expected: true,
		},
		{
			// Doesn't match target filter, not inhibited.
			target:   model.LabelSet{"t1": "0", "e": "1"},
			expected: false,
		},
		{
			// Matches both source and target filters of rule1,
			// inhibited because sourceAlert1 matches only the
			// source filter of rule1.
			target:   model.LabelSet{"s1": "1", "t1": "1", "e": "1"},
			expected: true,
		},
		{
			// Matches both source and target filters of rule2,
			// not inhibited because sourceAlert2 matches also both the
			// source and target filter of rule2.
			target:   model.LabelSet{"s2": "1", "t2": "1", "e": "1"},
			expected: false,
		},
		{
			// Matches target filter, equal label doesn't match, not inhibited
			target:   model.LabelSet{"t1": "1", "e": "0"},
			expected: false,
		},
	}

	for _, c := range cases {
		if actual := ih.Mutes(c.target); actual != c.expected {
			t.Errorf("Expected (*Inhibitor).Mutes(%v) to return %t but got %t", c.target, c.expected, actual)
		}
	}
}

type fakeAlerts struct {
	alerts   []*types.Alert
	finished chan struct{}
}

func newFakeAlerts(alerts []*types.Alert) *fakeAlerts {
	return &fakeAlerts{
		alerts:   alerts,
		finished: make(chan struct{}),
	}
}

func (f *fakeAlerts) GetPending() provider.AlertIterator          { return nil }
func (f *fakeAlerts) Get(model.Fingerprint) (*types.Alert, error) { return nil, nil }
func (f *fakeAlerts) Put(...*types.Alert) error                   { return nil }
func (f *fakeAlerts) Subscribe() provider.AlertIterator {
	ch := make(chan *types.Alert)
	done := make(chan struct{})
	go func() {
		for _, a := range f.alerts {
			ch <- a
		}
		// Send another (meaningless) alert to make sure that the inhibitor has
		// processed everything.
		ch <- &types.Alert{
			Alert: model.Alert{
				Labels:   model.LabelSet{},
				StartsAt: time.Now(),
			},
		}
		close(f.finished)
		<-done
	}()
	return provider.NewAlertIterator(ch, done, nil)
}

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

	now := time.Now()
	inhibitRule := func() *config.InhibitRule {
		return &config.InhibitRule{
			SourceMatch: map[string]string{"s": "1"},
			TargetMatch: map[string]string{"t": "1"},
			Equal:       model.LabelNames{"e"},
		}
	}
	// alertOne is muted by alertTwo when it is active.
	alertOne := func() *types.Alert {
		return &types.Alert{
			Alert: model.Alert{
				Labels:   model.LabelSet{"t": "1", "e": "f"},
				StartsAt: now.Add(-time.Minute),
				EndsAt:   now.Add(time.Hour),
			},
		}
	}
	alertTwo := func(resolved bool) *types.Alert {
		var end time.Time
		if resolved {
			end = now.Add(-time.Second)
		} else {
			end = now.Add(time.Hour)
		}
		return &types.Alert{
			Alert: model.Alert{
				Labels:   model.LabelSet{"s": "1", "e": "f"},
				StartsAt: now.Add(-time.Minute),
				EndsAt:   end,
			},
		}
	}

	type exp struct {
		lbls  model.LabelSet
		muted bool
	}
	for i, tc := range []struct {
		alerts   []*types.Alert
		expected []exp
	}{
		{
			// alertOne shouldn't be muted since alertTwo hasn't fired.
			alerts: []*types.Alert{alertOne()},
			expected: []exp{
				{
					lbls:  model.LabelSet{"t": "1", "e": "f"},
					muted: false,
				},
			},
		},
		{
			// alertOne should be muted by alertTwo which is active.
			alerts: []*types.Alert{alertOne(), alertTwo(false)},
			expected: []exp{
				{
					lbls:  model.LabelSet{"t": "1", "e": "f"},
					muted: true,
				},
				{
					lbls:  model.LabelSet{"s": "1", "e": "f"},
					muted: false,
				},
			},
		},
		{
			// alertOne shouldn't be muted since alertTwo is resolved.
			alerts: []*types.Alert{alertOne(), alertTwo(false), alertTwo(true)},
			expected: []exp{
				{
					lbls:  model.LabelSet{"t": "1", "e": "f"},
					muted: false,
				},
				{
					lbls:  model.LabelSet{"s": "1", "e": "f"},
					muted: false,
				},
			},
		},
	} {
		ap := newFakeAlerts(tc.alerts)
		mk := types.NewMarker(prometheus.NewRegistry())
		inhibitor := NewInhibitor(ap, []*config.InhibitRule{inhibitRule()}, mk, nopLogger)

		go func() {
			for ap.finished != nil {
				select {
				case <-ap.finished:
					ap.finished = nil
				default:
				}
			}
			inhibitor.Stop()
		}()
		inhibitor.Run()

		for _, expected := range tc.expected {
			if inhibitor.Mutes(expected.lbls) != expected.muted {
				mute := "unmuted"
				if expected.muted {
					mute = "muted"
				}
				t.Errorf("tc: %d, expected alert with labels %q to be %s", i, expected.lbls, mute)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/showwin/speedtest-go/speedtest"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	showList   = kingpin.Flag("list", "Show available speedtest.net servers.").Short('l').Bool()
	serverIds  = kingpin.Flag("server", "Select server id to speedtest.").Short('s').Ints()
	savingMode = kingpin.Flag("saving-mode", "Using less memory (≒10MB), though low accuracy (especially > 30Mbps).").Bool()
	jsonOutput = kingpin.Flag("json", "Output results in json format").Bool()
)

type fullOutput struct {
	UserInfo *speedtest.User   `json:"user_info"`
	Servers  speedtest.Servers `json:"servers"`
}

func main() {
	kingpin.Version("1.1.2")
	kingpin.Parse()

	user, err := speedtest.FetchUserInfo()
	if err != nil {
		fmt.Println("Warning: Cannot fetch user information. http://www.speedtest.net/speedtest-config.php is temporarily unavailable.")
	}
	if !*jsonOutput {
		showUser(user)
	}

	serverList, err := speedtest.FetchServerList(user)
	checkError(err)
	if *showList {
		showServerList(serverList)
		return
	}

	targets, err := serverList.FindServer(*serverIds)
	checkError(err)

	startTest(targets, *savingMode, *jsonOutput)

	if *jsonOutput {
		jsonBytes, err := json.Marshal(
			fullOutput{
				UserInfo: user,
				Servers:  targets,
			},
		)
		checkError(err)

		fmt.Println(string(jsonBytes))
	}
}

func startTest(servers speedtest.Servers, savingMode bool, jsonOuput bool) {
	for _, s := range servers {
		if !jsonOuput {
			showServer(s)
		}

		err := s.PingTest()
		checkError(err)

		if jsonOuput {
			err := s.DownloadTest(savingMode)
			checkError(err)

			err = s.UploadTest(savingMode)
			checkError(err)

			continue
		}

		showLatencyResult(s)

		err = testDownload(s, savingMode)
		checkError(err)
		err = testUpload(s, savingMode)
		checkError(err)

		showServerResult(s)
	}

	if !jsonOuput && len(servers) > 1 {
		showAverageServerResult(servers)
	}
}

func testDownload(server *speedtest.Server, savingMode bool) error {
	quit := make(chan bool)
	fmt.Printf("Download Test: ")
	go dots(quit)
	err := server.DownloadTest(savingMode)
	quit <- true
	if err != nil {
		return err
	}
	fmt.Println()
	return err
}

func testUpload(server *speedtest.Server, savingMode bool) error {
	quit := make(chan bool)
	fmt.Printf("Upload Test: ")
	go dots(quit)
	err := server.UploadTest(savingMode)
	quit <- true
	if err != nil {
		return err
	}
	fmt.Println()
	return nil
}

func dots(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		default:
			time.Sleep(time.Second)
			fmt.Print(".")
		}
	}
}

func showUser(user *speedtest.User) {
	if user.IP != "" {
		fmt.Printf("Testing From IP: %s\n", user.String())
	}
}

func showServerList(serverList speedtest.ServerList) {
	for _, s := range serverList.Servers {
		fmt.Printf("[%4s] %8.2fkm ", s.ID, s.Distance)
		fmt.Printf(s.Name + " (" + s.Country + ") by " + s.Sponsor + "\n")
	}
}

func showServer(s *speedtest.Server) {
	fmt.Printf(" \n")
	fmt.Printf("Target Server: [%4s] %8.2fkm ", s.ID, s.Distance)
	fmt.Printf(s.Name + " (" + s.Country + ") by " + s.Sponsor + "\n")
}

func showLatencyResult(server *speedtest.Server) {
	fmt.Println("Latency:", server.Latency)
}

// ShowResult : show testing result
func showServerResult(server *speedtest.Server) {
	fmt.Printf(" \n")

	fmt.Printf("Download: %5.2f Mbit/s\n", server.DLSpeed)
	fmt.Printf("Upload: %5.2f Mbit/s\n\n", server.ULSpeed)
	valid := server.CheckResultValid()
	if !valid {
		fmt.Println("Warning: Result seems to be wrong. Please speedtest again.")
	}
}

func showAverageServerResult(servers speedtest.Servers) {
	avgDL := 0.0
	avgUL := 0.0
	for _, s := range servers {
		avgDL = avgDL + s.DLSpeed
		avgUL = avgUL + s.ULSpeed
	}
	fmt.Printf("Download Avg: %5.2f Mbit/s\n", avgDL/float64(len(servers)))
	fmt.Printf("Upload Avg: %5.2f Mbit/s\n", avgUL/float64(len(servers)))
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
}
<commit_msg>Add timestamp to the output<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/showwin/speedtest-go/speedtest"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	showList   = kingpin.Flag("list", "Show available speedtest.net servers.").Short('l').Bool()
	serverIds  = kingpin.Flag("server", "Select server id to speedtest.").Short('s').Ints()
	savingMode = kingpin.Flag("saving-mode", "Using less memory (≒10MB), though low accuracy (especially > 30Mbps).").Bool()
	jsonOutput = kingpin.Flag("json", "Output results in json format").Bool()
)

type fullOutput struct {
	Timestamp time.Time         `json:"timestamp"`
	UserInfo  *speedtest.User   `json:"user_info"`
	Servers   speedtest.Servers `json:"servers"`
}

func main() {
	kingpin.Version("1.1.2")
	kingpin.Parse()

	user, err := speedtest.FetchUserInfo()
	if err != nil {
		fmt.Println("Warning: Cannot fetch user information. http://www.speedtest.net/speedtest-config.php is temporarily unavailable.")
	}
	if !*jsonOutput {
		showUser(user)
	}

	serverList, err := speedtest.FetchServerList(user)
	checkError(err)
	if *showList {
		showServerList(serverList)
		return
	}

	targets, err := serverList.FindServer(*serverIds)
	checkError(err)

	startTest(targets, *savingMode, *jsonOutput)

	if *jsonOutput {
		jsonBytes, err := json.Marshal(
			fullOutput{
				Timestamp: time.Now(),
				UserInfo:  user,
				Servers:   targets,
			},
		)
		checkError(err)

		fmt.Println(string(jsonBytes))
	}
}

func startTest(servers speedtest.Servers, savingMode bool, jsonOuput bool) {
	for _, s := range servers {
		if !jsonOuput {
			showServer(s)
		}

		err := s.PingTest()
		checkError(err)

		if jsonOuput {
			err := s.DownloadTest(savingMode)
			checkError(err)

			err = s.UploadTest(savingMode)
			checkError(err)

			continue
		}

		showLatencyResult(s)

		err = testDownload(s, savingMode)
		checkError(err)
		err = testUpload(s, savingMode)
		checkError(err)

		showServerResult(s)
	}

	if !jsonOuput && len(servers) > 1 {
		showAverageServerResult(servers)
	}
}

func testDownload(server *speedtest.Server, savingMode bool) error {
	quit := make(chan bool)
	fmt.Printf("Download Test: ")
	go dots(quit)
	err := server.DownloadTest(savingMode)
	quit <- true
	if err != nil {
		return err
	}
	fmt.Println()
	return err
}

func testUpload(server *speedtest.Server, savingMode bool) error {
	quit := make(chan bool)
	fmt.Printf("Upload Test: ")
	go dots(quit)
	err := server.UploadTest(savingMode)
	quit <- true
	if err != nil {
		return err
	}
	fmt.Println()
	return nil
}

func dots(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		default:
			time.Sleep(time.Second)
			fmt.Print(".")
		}
	}
}

func showUser(user *speedtest.User) {
	if user.IP != "" {
		fmt.Printf("Testing From IP: %s\n", user.String())
	}
}

func showServerList(serverList speedtest.ServerList) {
	for _, s := range serverList.Servers {
		fmt.Printf("[%4s] %8.2fkm ", s.ID, s.Distance)
		fmt.Printf(s.Name + " (" + s.Country + ") by " + s.Sponsor + "\n")
	}
}

func showServer(s *speedtest.Server) {
	fmt.Printf(" \n")
	fmt.Printf("Target Server: [%4s] %8.2fkm ", s.ID, s.Distance)
	fmt.Printf(s.Name + " (" + s.Country + ") by " + s.Sponsor + "\n")
}

func showLatencyResult(server *speedtest.Server) {
	fmt.Println("Latency:", server.Latency)
}

// ShowResult : show testing result
func showServerResult(server *speedtest.Server) {
	fmt.Printf(" \n")

	fmt.Printf("Download: %5.2f Mbit/s\n", server.DLSpeed)
	fmt.Printf("Upload: %5.2f Mbit/s\n\n", server.ULSpeed)
	valid := server.CheckResultValid()
	if !valid {
		fmt.Println("Warning: Result seems to be wrong. Please speedtest again.")
	}
}

func showAverageServerResult(servers speedtest.Servers) {
	avgDL := 0.0
	avgUL := 0.0
	for _, s := range servers {
		avgDL = avgDL + s.DLSpeed
		avgUL = avgUL + s.ULSpeed
	}
	fmt.Printf("Download Avg: %5.2f Mbit/s\n", avgDL/float64(len(servers)))
	fmt.Printf("Upload Avg: %5.2f Mbit/s\n", avgUL/float64(len(servers)))
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
}
<|endoftext|>


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

// +build ignore

package main

import (
	"image"
	"image/color"
	"image/png"
	"os"
	"path"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/io/h5"
	"github.com/cpmech/gosl/la"
	"github.com/cpmech/gosl/rnd"
	"github.com/cpmech/gosl/utl"
)

// Board holds figure/board data
type Board struct {
	nrow   int         // number of rows in board
	ncol   int         // number of cols in board
	width  int         // spot width
	height int         // spot height
	pad    int         // padding
	img    *image.Gray // image interface
}

// NewBoard returns a new board to display samples
func NewBoard(numSamples, maxSampleWidth, maxSampleHeight, padding int) (o *Board) {
	o = new(Board)
	o.pad = padding
	o.nrow, o.ncol = utl.BestSquareApprox(numSamples)
	o.width, o.height = maxSampleWidth, maxSampleHeight
	totalWidth := o.ncol*(o.width+o.pad) + o.pad
	totalHeight := o.nrow*(o.height+o.pad) + o.pad
	o.img = image.NewGray(image.Rect(0, 0, totalWidth, totalHeight))
	return
}

// NumSpots returns the computed total number of spots in board; i.e. nrow * ncol
func (o *Board) NumSpots() int {
	return o.nrow * o.ncol
}

// DrawSpot draws rectangle indicating sample spot
func (o *Board) DrawSpot(x, y int) {
	clr := color.Gray{255}
	for j := 0; j < o.width; j++ {
		o.img.Set(x+j, y, clr)
		o.img.Set(x+j, y+o.height, clr)
	}
	for i := 0; i < o.height; i++ {
		o.img.Set(x, y+i, clr)
		o.img.Set(x+o.width, y+i, clr)
	}
}

// Paint paints board
func (o *Board) Paint(samples Samples, smin, smax float64) {
	k := 0
	row := o.pad
	for i := 0; i < o.nrow; i++ {
		col := o.pad
		for j := 0; j < o.ncol; j++ {
			if k >= len(samples) {
				return
			}
			samples[k].Paint(o.img, row, col, smin, smax)
			//o.DrawSpot(row, col)
			col += o.width + o.pad
			k++
		}
		row += o.height + o.pad
	}
}

// SavePng saves png figure
func (o *Board) SavePng(outdir, fnameKey string) {

	// create file
	furl := path.Join(outdir, fnameKey+".png")
	file, err := os.Create(furl)
	if err != nil {
		chk.Panic("cannot create file at")
	}
	defer file.Close()

	// encode png
	err = png.Encode(file, o.img)
	if err != nil {
		chk.Panic("cannot encode image")
	}
	io.Pf("file <%s> written\n", furl)
}

func main() {

	// NOTE: this example expects an environment variable called
	//       $GOSLDATA containing all Gosl data files

	// read data file
	f := h5.Open("$GOSLDATA", "angEx4data1", false)
	defer f.Close()
	Xraw := f.GetArray("/Xcolmaj/value")
	nSamples := f.GetInt("/m/value")
	sampleSize := f.GetInt("/n/value")
	X := la.NewMatrixRaw(nSamples, sampleSize, Xraw)

	// constants
	nSelected := 100 // number of samples to display
	padding := 1     // padding

	// select samples
	rnd.Init(0)
	samples := NewSamples(X, nSelected)
	smin, smax, wmax, hmax := samples.Stat()

	// board
	board := NewBoard(nSelected, wmax, hmax, padding)
	board.Paint(samples, smin, smax)
	board.SavePng("/tmp/gosl", "angEx4data1fig")
}
<commit_msg>Use imgd package in h5 example<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"github.com/cpmech/gosl/io/h5"
	"github.com/cpmech/gosl/la"
	"github.com/cpmech/gosl/ml/imgd"
	"github.com/cpmech/gosl/rnd"
)

func main() {

	// NOTE: this example expects an environment variable called
	//       $GOSLDATA containing all Gosl data files

	// read data file
	f := h5.Open("$GOSLDATA", "angEx4data1", false)
	defer f.Close()
	Xraw := f.GetArray("/Xcolmaj/value")
	nSamples := f.GetInt("/m/value")
	sampleSize := f.GetInt("/n/value")
	X := la.NewMatrixRaw(nSamples, sampleSize, Xraw)

	// constants
	nSelected := 100 // number of samples to display
	padding := 1     // padding
	rowMaj := false  // row major
	random := true   // random selection

	// select samples
	rnd.Init(0)
	samples := imgd.NewGraySamples(X, nSelected, rowMaj, random)
	smin, smax, wmax, hmax := samples.Stat()

	// board
	board := imgd.NewGrayBoard(nSelected, wmax, hmax, padding)
	board.Paint(samples, smin, smax, false)
	board.SavePng("/tmp/gosl", "angEx4data1fig")
}
<|endoftext|>


<commit_before>// Copyright 2016 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use 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 tcell

import (
	"sync"
	"unicode/utf8"

	"golang.org/x/text/transform"
)

// NewSimulationScreen returns a SimulationScreen.  Note that
// SimulationScreen is also a Screen.
func NewSimulationScreen(charset string) SimulationScreen {
	if charset == "" {
		charset = "UTF-8"
	}
	s := &simscreen{charset: charset}
	return s
}

// SimulationScreen represents a screen simulation.  This is intended to
// be a superset of normal Screens, but also adds some important interfaces
// for testing.
type SimulationScreen interface {
	// InjectKeyBytes injects a stream of bytes corresponding to
	// the native encoding (see charset).  It turns true if the entire
	// set of bytes were processed and delivered as KeyEvents, false
	// if any bytes were not fully understood.  Any bytes that are not
	// fully converted are discarded.
	InjectKeyBytes(buf []byte) bool

	// InjectKey injects a key event.  The rune is a UTF-8 rune, post
	// any translation.
	InjectKey(key Key, r rune, mod ModMask)

	// InjectMouse injects a mouse event.
	InjectMouse(x, y int, buttons ButtonMask, mod ModMask)

	// SetSize resizes the underlying physical screen.  It also causes
	// a resize event to be injected during the next Show() or Sync().
	// A new physical contents array will be allocated (with data from
	// the old copied), so any prior value obtained with GetContents
	// won't be used anymore
	SetSize(width, height int)

	// GetContents returns screen contents as an array of
	// cells, along with the physical width & height.   Note that the
	// physical contents will be used until the next time SetSize()
	// is called.
	GetContents() (cells []SimCell, width int, height int)

	// GetCursor returns the cursor details.
	GetCursor() (x int, y int, visible bool)

	Screen
}

// SimCell represents a simulated screen cell.  The purpose of this
// is to track on screen content.
type SimCell struct {
	// Bytes is the actual character bytes.  Normally this is
	// rune data, but it could be be data in another encoding system.
	Bytes []byte

	// Style is the style used to display the data.
	Style Style

	// Runes is the list of runes, unadulterated, in UTF-8.
	Runes []rune
}

type simscreen struct {
	physw int
	physh int
	fini  bool
	style Style
	evch  chan Event
	quit  chan struct{}

	front     []SimCell
	back      CellBuffer
	clear     bool
	cursorx   int
	cursory   int
	cursorvis bool
	mouse     bool
	charset   string
	encoder   transform.Transformer
	decoder   transform.Transformer
	fillchar  rune
	fillstyle Style
	fallback  map[rune]string

	sync.Mutex
}

func (s *simscreen) Init() error {
	s.evch = make(chan Event, 10)
	s.fillchar = 'X'
	s.fillstyle = StyleDefault
	s.mouse = false
	s.physw = 80
	s.physh = 25
	s.cursorx = -1
	s.cursory = -1
	s.style = StyleDefault

	if enc := GetEncoding(s.charset); enc != nil {
		s.encoder = enc.NewEncoder()
		s.decoder = enc.NewDecoder()
	} else {
		return ErrNoCharset
	}

	s.front = make([]SimCell, s.physw*s.physh)
	s.back.Resize(80, 25)

	// default fallbacks
	s.fallback = make(map[rune]string)
	for k, v := range RuneFallbacks {
		s.fallback[k] = v
	}
	return nil
}

func (s *simscreen) Fini() {
	s.Lock()
	s.fini = true
	s.back.Resize(0, 0)
	s.Unlock()
	if s.quit != nil {
		close(s.quit)
	}
	s.physw = 0
	s.physh = 0
	s.front = nil
}

func (s *simscreen) SetStyle(style Style) {
	s.Lock()
	s.style = style
	s.Unlock()
}

func (s *simscreen) Clear() {
	s.Fill(' ', s.style)
}

func (s *simscreen) Fill(r rune, style Style) {
	s.Lock()
	s.back.Fill(r, style)
	s.Unlock()
}

func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) {

	if len(ch) > 0 {
		s.SetContent(x, y, ch[0], ch[1:], style)
	} else {
		s.SetContent(x, y, ' ', nil, style)
	}
}

func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) {

	s.Lock()
	s.back.SetContent(x, y, mainc, combc, st)
	s.Unlock()
}

func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) {
	var mainc rune
	var combc []rune
	var style Style
	var width int
	s.Lock()
	mainc, combc, style, width = s.back.GetContent(x, y)
	s.Unlock()
	return mainc, combc, style, width
}

func (s *simscreen) drawCell(x, y int) int {

	mainc, combc, style, width := s.back.GetContent(x, y)
	if !s.back.Dirty(x, y) {
		return width
	}
	if x >= s.physw || y >= s.physh || x < 0 || y < 0 {
		return width
	}
	simc := &s.front[(y*s.physw)+x]

	if style == StyleDefault {
		style = s.style
	}
	simc.Style = style
	simc.Runes = append([]rune{mainc}, combc...)

	// now emit runes - taking care to not overrun width with a
	// wide character, and to ensure that we emit exactly one regular
	// character followed up by any residual combing characters

	simc.Bytes = nil

	if x > s.physw-width {
		simc.Runes = []rune{' '}
		simc.Bytes = []byte{' '}
		return width
	}

	lbuf := make([]byte, 12)
	ubuf := make([]byte, 12)
	nout := 0

	for _, r := range simc.Runes {

		l := utf8.EncodeRune(ubuf, r)

		nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true)

		if nout == 0 || lbuf[0] == '\x1a' {

			// skip combining

			if subst, ok := s.fallback[r]; ok {
				simc.Bytes = append(simc.Bytes,
					[]byte(subst)...)

			} else if r >= ' ' && r <= '~' {
				simc.Bytes = append(simc.Bytes, byte(r))

			} else if simc.Bytes == nil {
				simc.Bytes = append(simc.Bytes, '?')
			}
		} else {
			simc.Bytes = append(simc.Bytes, lbuf[:nout]...)
		}
	}
	s.back.SetDirty(x, y, false)
	return width
}

func (s *simscreen) ShowCursor(x, y int) {
	s.Lock()
	s.cursorx, s.cursory = x, y
	s.showCursor()
	s.Unlock()
}

func (s *simscreen) HideCursor() {
	s.ShowCursor(-1, -1)
}

func (s *simscreen) showCursor() {

	x, y := s.cursorx, s.cursory
	if x < 0 || y < 0 || x >= s.physw || y >= s.physh {
		s.cursorvis = false
	} else {
		s.cursorvis = true
	}
}

func (s *simscreen) hideCursor() {
	// does not update cursor position
	s.cursorvis = false
}

func (s *simscreen) Show() {
	s.Lock()
	s.resize()
	s.draw()
	s.Unlock()
}

func (s *simscreen) clearScreen() {
	// We emulate a hardware clear by filling with a specific pattern
	for i := range s.front {
		s.front[i].Style = s.fillstyle
		s.front[i].Runes = []rune{s.fillchar}
		s.front[i].Bytes = []byte{byte(s.fillchar)}
	}
	s.clear = false
}

func (s *simscreen) draw() {
	s.hideCursor()
	if s.clear {
		s.clearScreen()
	}

	w, h := s.back.Size()
	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			width := s.drawCell(x, y)
			x += width - 1
		}
	}
	s.showCursor()
}

func (s *simscreen) EnableMouse() {
	s.mouse = true
}

func (s *simscreen) DisableMouse() {
	s.mouse = false
}

func (s *simscreen) Size() (int, int) {
	s.Lock()
	w, h := s.back.Size()
	s.Unlock()
	return w, h
}

func (s *simscreen) resize() {
	w, h := s.physw, s.physh
	ow, oh := s.back.Size()
	if w != ow || h != oh {
		s.back.Resize(w, h)
		ev := NewEventResize(w, h)
		s.PostEvent(ev)
	}
}

func (s *simscreen) Colors() int {
	return 256
}

func (s *simscreen) PollEvent() Event {
	select {
	case <-s.quit:
		return nil
	case ev := <-s.evch:
		return ev
	}
}

func (s *simscreen) PostEventWait(ev Event) {
	s.evch <- ev
}

func (s *simscreen) PostEvent(ev Event) error {
	select {
	case s.evch <- ev:
		return nil
	default:
		return ErrEventQFull
	}
}

func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) {
	ev := NewEventMouse(x, y, buttons, mod)
	s.PostEvent(ev)
}

func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) {
	ev := NewEventKey(KeyRune, r, ModNone)
	s.PostEvent(ev)
}

func (s *simscreen) InjectKeyBytes(b []byte) bool {
	failed := false

outer:
	for len(b) > 0 {
		if b[0] >= ' ' && b[0] <= 0x7F {
			// printable ASCII easy to deal with -- no encodings
			ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
			s.PostEvent(ev)
			b = b[1:]
			continue
		}

		if b[0] < 0x80 {
			mod := ModNone
			// No encodings start with low numbered values
			if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ {
				mod = ModCtrl
			}
			ev := NewEventKey(Key(b[0]), 0, mod)
			s.PostEvent(ev)
			continue
		}

		utfb := make([]byte, len(b)*4) // worst case
		for l := 1; l < len(b); l++ {
			s.decoder.Reset()
			nout, nin, _ := s.decoder.Transform(utfb, b[:l], true)

			if nout != 0 {
				r, _ := utf8.DecodeRune(utfb[:nout])
				if r != utf8.RuneError {
					ev := NewEventKey(KeyRune, r, ModNone)
					s.PostEvent(ev)
				}
				b = b[nin:]
				continue outer
			}
		}
		failed = true
		b = b[1:]
		continue
	}

	return !failed
}

func (s *simscreen) Sync() {
	s.Lock()
	s.clear = true
	s.resize()
	s.back.Invalidate()
	s.draw()
	s.Unlock()
}

func (s *simscreen) CharacterSet() string {
	return s.charset
}

func (s *simscreen) SetSize(w, h int) {
	s.Lock()
	newc := make([]SimCell, w*h)
	for row := 0; row < h && row < s.physh; row++ {
		for col := 0; col < w && col < s.physw; col++ {
			newc[(row*w)+col] = s.front[(row*s.physw)+col]
		}
	}
	s.physw = w
	s.physh = h
	s.Unlock()
}

func (s *simscreen) GetContents() ([]SimCell, int, int) {
	s.Lock()
	cells, w, h := s.front, s.physw, s.physh
	s.Unlock()
	return cells, w, h
}

func (s *simscreen) GetCursor() (int, int, bool) {
	s.Lock()
	x, y, vis := s.cursorx, s.cursory, s.cursorvis
	s.Unlock()
	return x, y, vis
}

func (s *simscreen) RegisterRuneFallback(r rune, subst string) {
	s.Lock()
	s.fallback[r] = subst
	s.Unlock()
}

func (s *simscreen) UnregisterRuneFallback(r rune) {
	s.Lock()
	delete(s.fallback, r)
	s.Unlock()
}

func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {

	if enc := s.encoder; enc != nil {
		nb := make([]byte, 6)
		ob := make([]byte, 6)
		num := utf8.EncodeRune(ob, r)

		enc.Reset()
		dst, _, err := enc.Transform(nb, ob[:num], true)
		if dst != 0 && err == nil && nb[0] != '\x1A' {
			return true
		}
	}
	if !checkFallbacks {
		return false
	}
	if _, ok := s.fallback[r]; ok {
		return true
	}
	return false
}

func (s *simscreen) HasMouse() bool {
	return false
}

func (s *simscreen) Resize(int, int, int, int) {}

func (s *simscreen) HasKey(Key) bool {
	return true
}
<commit_msg>Fix simscreen.SetSize<commit_after>// Copyright 2016 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use 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 tcell

import (
	"sync"
	"unicode/utf8"

	"golang.org/x/text/transform"
)

// NewSimulationScreen returns a SimulationScreen.  Note that
// SimulationScreen is also a Screen.
func NewSimulationScreen(charset string) SimulationScreen {
	if charset == "" {
		charset = "UTF-8"
	}
	s := &simscreen{charset: charset}
	return s
}

// SimulationScreen represents a screen simulation.  This is intended to
// be a superset of normal Screens, but also adds some important interfaces
// for testing.
type SimulationScreen interface {
	// InjectKeyBytes injects a stream of bytes corresponding to
	// the native encoding (see charset).  It turns true if the entire
	// set of bytes were processed and delivered as KeyEvents, false
	// if any bytes were not fully understood.  Any bytes that are not
	// fully converted are discarded.
	InjectKeyBytes(buf []byte) bool

	// InjectKey injects a key event.  The rune is a UTF-8 rune, post
	// any translation.
	InjectKey(key Key, r rune, mod ModMask)

	// InjectMouse injects a mouse event.
	InjectMouse(x, y int, buttons ButtonMask, mod ModMask)

	// SetSize resizes the underlying physical screen.  It also causes
	// a resize event to be injected during the next Show() or Sync().
	// A new physical contents array will be allocated (with data from
	// the old copied), so any prior value obtained with GetContents
	// won't be used anymore
	SetSize(width, height int)

	// GetContents returns screen contents as an array of
	// cells, along with the physical width & height.   Note that the
	// physical contents will be used until the next time SetSize()
	// is called.
	GetContents() (cells []SimCell, width int, height int)

	// GetCursor returns the cursor details.
	GetCursor() (x int, y int, visible bool)

	Screen
}

// SimCell represents a simulated screen cell.  The purpose of this
// is to track on screen content.
type SimCell struct {
	// Bytes is the actual character bytes.  Normally this is
	// rune data, but it could be be data in another encoding system.
	Bytes []byte

	// Style is the style used to display the data.
	Style Style

	// Runes is the list of runes, unadulterated, in UTF-8.
	Runes []rune
}

type simscreen struct {
	physw int
	physh int
	fini  bool
	style Style
	evch  chan Event
	quit  chan struct{}

	front     []SimCell
	back      CellBuffer
	clear     bool
	cursorx   int
	cursory   int
	cursorvis bool
	mouse     bool
	charset   string
	encoder   transform.Transformer
	decoder   transform.Transformer
	fillchar  rune
	fillstyle Style
	fallback  map[rune]string

	sync.Mutex
}

func (s *simscreen) Init() error {
	s.evch = make(chan Event, 10)
	s.fillchar = 'X'
	s.fillstyle = StyleDefault
	s.mouse = false
	s.physw = 80
	s.physh = 25
	s.cursorx = -1
	s.cursory = -1
	s.style = StyleDefault

	if enc := GetEncoding(s.charset); enc != nil {
		s.encoder = enc.NewEncoder()
		s.decoder = enc.NewDecoder()
	} else {
		return ErrNoCharset
	}

	s.front = make([]SimCell, s.physw*s.physh)
	s.back.Resize(80, 25)

	// default fallbacks
	s.fallback = make(map[rune]string)
	for k, v := range RuneFallbacks {
		s.fallback[k] = v
	}
	return nil
}

func (s *simscreen) Fini() {
	s.Lock()
	s.fini = true
	s.back.Resize(0, 0)
	s.Unlock()
	if s.quit != nil {
		close(s.quit)
	}
	s.physw = 0
	s.physh = 0
	s.front = nil
}

func (s *simscreen) SetStyle(style Style) {
	s.Lock()
	s.style = style
	s.Unlock()
}

func (s *simscreen) Clear() {
	s.Fill(' ', s.style)
}

func (s *simscreen) Fill(r rune, style Style) {
	s.Lock()
	s.back.Fill(r, style)
	s.Unlock()
}

func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) {

	if len(ch) > 0 {
		s.SetContent(x, y, ch[0], ch[1:], style)
	} else {
		s.SetContent(x, y, ' ', nil, style)
	}
}

func (s *simscreen) SetContent(x, y int, mainc rune, combc []rune, st Style) {

	s.Lock()
	s.back.SetContent(x, y, mainc, combc, st)
	s.Unlock()
}

func (s *simscreen) GetContent(x, y int) (rune, []rune, Style, int) {
	var mainc rune
	var combc []rune
	var style Style
	var width int
	s.Lock()
	mainc, combc, style, width = s.back.GetContent(x, y)
	s.Unlock()
	return mainc, combc, style, width
}

func (s *simscreen) drawCell(x, y int) int {

	mainc, combc, style, width := s.back.GetContent(x, y)
	if !s.back.Dirty(x, y) {
		return width
	}
	if x >= s.physw || y >= s.physh || x < 0 || y < 0 {
		return width
	}
	simc := &s.front[(y*s.physw)+x]

	if style == StyleDefault {
		style = s.style
	}
	simc.Style = style
	simc.Runes = append([]rune{mainc}, combc...)

	// now emit runes - taking care to not overrun width with a
	// wide character, and to ensure that we emit exactly one regular
	// character followed up by any residual combing characters

	simc.Bytes = nil

	if x > s.physw-width {
		simc.Runes = []rune{' '}
		simc.Bytes = []byte{' '}
		return width
	}

	lbuf := make([]byte, 12)
	ubuf := make([]byte, 12)
	nout := 0

	for _, r := range simc.Runes {

		l := utf8.EncodeRune(ubuf, r)

		nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true)

		if nout == 0 || lbuf[0] == '\x1a' {

			// skip combining

			if subst, ok := s.fallback[r]; ok {
				simc.Bytes = append(simc.Bytes,
					[]byte(subst)...)

			} else if r >= ' ' && r <= '~' {
				simc.Bytes = append(simc.Bytes, byte(r))

			} else if simc.Bytes == nil {
				simc.Bytes = append(simc.Bytes, '?')
			}
		} else {
			simc.Bytes = append(simc.Bytes, lbuf[:nout]...)
		}
	}
	s.back.SetDirty(x, y, false)
	return width
}

func (s *simscreen) ShowCursor(x, y int) {
	s.Lock()
	s.cursorx, s.cursory = x, y
	s.showCursor()
	s.Unlock()
}

func (s *simscreen) HideCursor() {
	s.ShowCursor(-1, -1)
}

func (s *simscreen) showCursor() {

	x, y := s.cursorx, s.cursory
	if x < 0 || y < 0 || x >= s.physw || y >= s.physh {
		s.cursorvis = false
	} else {
		s.cursorvis = true
	}
}

func (s *simscreen) hideCursor() {
	// does not update cursor position
	s.cursorvis = false
}

func (s *simscreen) Show() {
	s.Lock()
	s.resize()
	s.draw()
	s.Unlock()
}

func (s *simscreen) clearScreen() {
	// We emulate a hardware clear by filling with a specific pattern
	for i := range s.front {
		s.front[i].Style = s.fillstyle
		s.front[i].Runes = []rune{s.fillchar}
		s.front[i].Bytes = []byte{byte(s.fillchar)}
	}
	s.clear = false
}

func (s *simscreen) draw() {
	s.hideCursor()
	if s.clear {
		s.clearScreen()
	}

	w, h := s.back.Size()
	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			width := s.drawCell(x, y)
			x += width - 1
		}
	}
	s.showCursor()
}

func (s *simscreen) EnableMouse() {
	s.mouse = true
}

func (s *simscreen) DisableMouse() {
	s.mouse = false
}

func (s *simscreen) Size() (int, int) {
	s.Lock()
	w, h := s.back.Size()
	s.Unlock()
	return w, h
}

func (s *simscreen) resize() {
	w, h := s.physw, s.physh
	ow, oh := s.back.Size()
	if w != ow || h != oh {
		s.back.Resize(w, h)
		ev := NewEventResize(w, h)
		s.PostEvent(ev)
	}
}

func (s *simscreen) Colors() int {
	return 256
}

func (s *simscreen) PollEvent() Event {
	select {
	case <-s.quit:
		return nil
	case ev := <-s.evch:
		return ev
	}
}

func (s *simscreen) PostEventWait(ev Event) {
	s.evch <- ev
}

func (s *simscreen) PostEvent(ev Event) error {
	select {
	case s.evch <- ev:
		return nil
	default:
		return ErrEventQFull
	}
}

func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) {
	ev := NewEventMouse(x, y, buttons, mod)
	s.PostEvent(ev)
}

func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) {
	ev := NewEventKey(KeyRune, r, ModNone)
	s.PostEvent(ev)
}

func (s *simscreen) InjectKeyBytes(b []byte) bool {
	failed := false

outer:
	for len(b) > 0 {
		if b[0] >= ' ' && b[0] <= 0x7F {
			// printable ASCII easy to deal with -- no encodings
			ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
			s.PostEvent(ev)
			b = b[1:]
			continue
		}

		if b[0] < 0x80 {
			mod := ModNone
			// No encodings start with low numbered values
			if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ {
				mod = ModCtrl
			}
			ev := NewEventKey(Key(b[0]), 0, mod)
			s.PostEvent(ev)
			continue
		}

		utfb := make([]byte, len(b)*4) // worst case
		for l := 1; l < len(b); l++ {
			s.decoder.Reset()
			nout, nin, _ := s.decoder.Transform(utfb, b[:l], true)

			if nout != 0 {
				r, _ := utf8.DecodeRune(utfb[:nout])
				if r != utf8.RuneError {
					ev := NewEventKey(KeyRune, r, ModNone)
					s.PostEvent(ev)
				}
				b = b[nin:]
				continue outer
			}
		}
		failed = true
		b = b[1:]
		continue
	}

	return !failed
}

func (s *simscreen) Sync() {
	s.Lock()
	s.clear = true
	s.resize()
	s.back.Invalidate()
	s.draw()
	s.Unlock()
}

func (s *simscreen) CharacterSet() string {
	return s.charset
}

func (s *simscreen) SetSize(w, h int) {
	s.Lock()
	newc := make([]SimCell, w*h)
	for row := 0; row < h && row < s.physh; row++ {
		for col := 0; col < w && col < s.physw; col++ {
			newc[(row*w)+col] = s.front[(row*s.physw)+col]
		}
	}
	s.cursorx, s.cursory = -1, -1
	s.physw, s.physh = w, h
	s.front = newc
	s.back.Resize(w, h)
	s.Unlock()
}

func (s *simscreen) GetContents() ([]SimCell, int, int) {
	s.Lock()
	cells, w, h := s.front, s.physw, s.physh
	s.Unlock()
	return cells, w, h
}

func (s *simscreen) GetCursor() (int, int, bool) {
	s.Lock()
	x, y, vis := s.cursorx, s.cursory, s.cursorvis
	s.Unlock()
	return x, y, vis
}

func (s *simscreen) RegisterRuneFallback(r rune, subst string) {
	s.Lock()
	s.fallback[r] = subst
	s.Unlock()
}

func (s *simscreen) UnregisterRuneFallback(r rune) {
	s.Lock()
	delete(s.fallback, r)
	s.Unlock()
}

func (s *simscreen) CanDisplay(r rune, checkFallbacks bool) bool {

	if enc := s.encoder; enc != nil {
		nb := make([]byte, 6)
		ob := make([]byte, 6)
		num := utf8.EncodeRune(ob, r)

		enc.Reset()
		dst, _, err := enc.Transform(nb, ob[:num], true)
		if dst != 0 && err == nil && nb[0] != '\x1A' {
			return true
		}
	}
	if !checkFallbacks {
		return false
	}
	if _, ok := s.fallback[r]; ok {
		return true
	}
	return false
}

func (s *simscreen) HasMouse() bool {
	return false
}

func (s *simscreen) Resize(int, int, int, int) {}

func (s *simscreen) HasKey(Key) bool {
	return true
}
<|endoftext|>


<commit_before>package command

import (
	"strings"
)

type ACLListCommand struct {
	Meta
}

func (c *ACLListCommand) Help() string {
	helpText := `
Usage: consul-cli list [options]

  List all active ACL tokens.

Options:

` + c.ConsulHelp()

	return strings.TrimSpace(helpText)
}

func (c *ACLListCommand) Run(args []string) int {
	flags := c.Meta.FlagSet()
	flags.Usage = func() { c.UI.Output(c.Help()) }

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

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

	queryOpts := c.QueryOptions()
	acls, _, err := client.List(queryOpts)
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	c.OutputJSON(acls, true)

	return 0
}

func (c *ACLListCommand) Synopsis() string {
	return "List a value"
}
<commit_msg>Update Synopsis for acl-list<commit_after>package command

import (
	"strings"
)

type ACLListCommand struct {
	Meta
}

func (c *ACLListCommand) Help() string {
	helpText := `
Usage: consul-cli list [options]

  List all active ACL tokens.

Options:

` + c.ConsulHelp()

	return strings.TrimSpace(helpText)
}

func (c *ACLListCommand) Run(args []string) int {
	flags := c.Meta.FlagSet()
	flags.Usage = func() { c.UI.Output(c.Help()) }

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

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

	queryOpts := c.QueryOptions()
	acls, _, err := client.List(queryOpts)
	if err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	c.OutputJSON(acls, true)

	return 0
}

func (c *ACLListCommand) Synopsis() string {
	return "List all active ACL tokens"
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced 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 cmd

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/control-center/serviced/cli/api"
)

// initTemplate is the initializer for serviced template
func (c *ServicedCli) initTemplate() {
	c.app.Commands = append(c.app.Commands, cli.Command{
		Name:        "template",
		Usage:       "Administers templates",
		Description: "",
		Subcommands: []cli.Command{
			{
				Name:         "list",
				Usage:        "Lists all templates",
				Description:  "serviced template list [TEMPLATEID]",
				BashComplete: c.printTemplatesFirst,
				Action:       c.cmdTemplateList,
				Flags: []cli.Flag{
					cli.BoolFlag{"verbose, v", "Show JSON format"},
				},
			}, {
				Name:        "add",
				Usage:       "Add a new template",
				Description: "serviced template add FILE",
				Action:      c.cmdTemplateAdd,
			}, {
				Name:         "remove",
				ShortName:    "rm",
				Usage:        "Remove an existing template",
				Description:  "serviced template remove TEMPLATEID ...",
				BashComplete: c.printTemplatesAll,
				Action:       c.cmdTemplateRemove,
			}, {
				Name:         "deploy",
				Usage:        "Deploys a template's services to a pool",
				Description:  "serviced template deploy TEMPLATEID POOLID DEPLOYMENTID",
				BashComplete: c.printTemplateDeploy,
				Action:       c.cmdTemplateDeploy,
				Flags: []cli.Flag{
					cli.BoolFlag{"manual-assign-ips", "Manually assign IP addresses"},
				},
			}, {
				Name:        "compile",
				Usage:       "Convert a directory of service definitions into a template",
				Description: "serviced template compile PATH",
				Action:      c.cmdTemplateCompile,
				Flags: []cli.Flag{
					cli.GenericFlag{
						"map", &api.ImageMap{}, "Map a given image name to another (e.g. -map zenoss/zenoss5x:latest,quay.io/zenoss-core:alpha2)"},
				},
			},
		},
	})
}

// Returns a list of all available template IDs
func (c *ServicedCli) templates() (data []string) {
	templates, err := c.driver.GetServiceTemplates()
	if err != nil || templates == nil || len(templates) == 0 {
		return
	}

	data = make([]string, len(templates))
	for i, t := range templates {
		data[i] = t.ID
	}

	return
}

// Bash-completion command that prints the list of templates as the first
// argument
func (c *ServicedCli) printTemplatesFirst(ctx *cli.Context) {
	if len(ctx.Args()) > 0 {
		return
	}

	for _, t := range c.templates() {
		fmt.Println(t)
	}
}

// Bash-completion command that prints the command options for
// serviced template deploy
func (c *ServicedCli) printTemplateDeploy(ctx *cli.Context) {
	var output []string

	switch len(ctx.Args()) {
	case 0:
		output = c.templates()
	case 1:
		output = c.pools()
	}

	for _, o := range output {
		fmt.Println(o)
	}
}

// Bash-completion command that prints the list of templates as all arguments
func (c *ServicedCli) printTemplatesAll(ctx *cli.Context) {
	args := ctx.Args()

	for _, t := range c.templates() {
		for _, a := range args {
			if t == a {
				goto next
			}
		}
		fmt.Println(t)
	next:
	}
}

// serviced template list [--verbose, -v] [TEMPLATEID]
func (c *ServicedCli) cmdTemplateList(ctx *cli.Context) {
	if len(ctx.Args()) > 0 {
		templateID := ctx.Args()[0]
		if template, err := c.driver.GetServiceTemplate(templateID); err != nil {
			fmt.Fprintln(os.Stderr, err)
		} else if template == nil {
			fmt.Fprintln(os.Stderr, "template not found")
		} else if jsonTemplate, err := json.MarshalIndent(template, " ", "  "); err != nil {
			fmt.Fprintf(os.Stderr, "failed to marshal template: %s\n", err)
		} else {
			fmt.Println(string(jsonTemplate))
		}
		return
	}

	templates, err := c.driver.GetServiceTemplates()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	} else if templates == nil || len(templates) == 0 {
		fmt.Fprintln(os.Stderr, "no templates found")
		return
	}

	if ctx.Bool("verbose") {
		if jsonTemplate, err := json.MarshalIndent(templates, " ", "  "); err != nil {
			fmt.Fprintf(os.Stderr, "failed to marshal template list: %s\n", err)
		} else {
			fmt.Println(string(jsonTemplate))
		}
	} else {
		tableTemplate := newtable(0, 8, 2)
		tableTemplate.printrow("TEMPLATEID", "NAME", "DESCRIPTION")
		for _, t := range templates {
			tableTemplate.printrow(t.ID, t.Name, t.Description)
		}
		tableTemplate.flush()
	}
}

// serviced template add TEMPLATE
func (c *ServicedCli) cmdTemplateAdd(ctx *cli.Context) {
	var input *os.File

	if filepath := ctx.Args().First(); filepath != "" {
		var err error
		if input, err = os.Open(filepath); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
		defer input.Close()
	} else {
		input = os.Stdin
	}

	if template, err := c.driver.AddServiceTemplate(input); err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if template == nil {
		fmt.Fprintln(os.Stderr, "received nil template")
	} else {
		fmt.Println(template.ID)
	}
}

// serviced template remove TEMPLATEID ...
func (c *ServicedCli) cmdTemplateRemove(ctx *cli.Context) {
	args := ctx.Args()
	if len(args) < 1 {
		fmt.Printf("Incorrect Usage.\n\n")
		cli.ShowCommandHelp(ctx, "remove")
		return
	}

	for _, id := range args {
		if err := c.driver.RemoveServiceTemplate(id); err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", id, err)
		} else {
			fmt.Println(id)
		}
	}
}

// serviced template deploy TEMPLATEID POOLID DEPLOYMENTID [--manual-assign-ips]
func (c *ServicedCli) cmdTemplateDeploy(ctx *cli.Context) {
	args := ctx.Args()
	if len(args) < 3 {
		fmt.Printf("Incorrect Usage.\n\n")
		cli.ShowCommandHelp(ctx, "deploy")
		return
	}

	cfg := api.DeployTemplateConfig{
		ID:              args[0],
		PoolID:          args[1],
		DeploymentID:    args[2],
		ManualAssignIPs: ctx.Bool("manual-assign-ips"),
	}

	fmt.Fprintln(os.Stderr, "Deploying template - please wait...")
	if service, err := c.driver.DeployServiceTemplate(cfg); err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if service == nil {
		fmt.Fprintln(os.Stderr, "received nil service definition")
	} else {
		fmt.Println(service.ID)
	}
}

// serviced template compile DIR [[--map IMAGE,IMAGE] ...]
func (c *ServicedCli) cmdTemplateCompile(ctx *cli.Context) {
	args := ctx.Args()
	if len(args) < 1 {
		fmt.Printf("Incorrect Usage.\n\n")
		cli.ShowCommandHelp(ctx, "compile")
		return
	}

	cfg := api.CompileTemplateConfig{
		Dir: args[0],
		Map: *ctx.Generic("map").(*api.ImageMap),
	}

	if template, err := c.driver.CompileServiceTemplate(cfg); err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if template == nil {
		fmt.Fprintln(os.Stderr, "received nil template")
	} else if jsonTemplate, err := json.MarshalIndent(template, " ", "  "); err != nil {
		fmt.Fprintln(os.Stderr, "failed to marshal template: %s", err)
	} else {
		fmt.Println(string(jsonTemplate))
	}
}
<commit_msg>add serviced and template version information to compiled template data<commit_after>// Copyright 2014 The Serviced 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 cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/control-center/serviced/cli/api"
	template "github.com/control-center/serviced/domain/servicetemplate"
	"github.com/control-center/serviced/servicedversion"
)

// initTemplate is the initializer for serviced template
func (c *ServicedCli) initTemplate() {
	c.app.Commands = append(c.app.Commands, cli.Command{
		Name:        "template",
		Usage:       "Administers templates",
		Description: "",
		Subcommands: []cli.Command{
			{
				Name:         "list",
				Usage:        "Lists all templates",
				Description:  "serviced template list [TEMPLATEID]",
				BashComplete: c.printTemplatesFirst,
				Action:       c.cmdTemplateList,
				Flags: []cli.Flag{
					cli.BoolFlag{"verbose, v", "Show JSON format"},
				},
			}, {
				Name:        "add",
				Usage:       "Add a new template",
				Description: "serviced template add FILE",
				Action:      c.cmdTemplateAdd,
			}, {
				Name:         "remove",
				ShortName:    "rm",
				Usage:        "Remove an existing template",
				Description:  "serviced template remove TEMPLATEID ...",
				BashComplete: c.printTemplatesAll,
				Action:       c.cmdTemplateRemove,
			}, {
				Name:         "deploy",
				Usage:        "Deploys a template's services to a pool",
				Description:  "serviced template deploy TEMPLATEID POOLID DEPLOYMENTID",
				BashComplete: c.printTemplateDeploy,
				Action:       c.cmdTemplateDeploy,
				Flags: []cli.Flag{
					cli.BoolFlag{"manual-assign-ips", "Manually assign IP addresses"},
				},
			}, {
				Name:        "compile",
				Usage:       "Convert a directory of service definitions into a template",
				Description: "serviced template compile PATH",
				Action:      c.cmdTemplateCompile,
				Flags: []cli.Flag{
					cli.GenericFlag{
						"map", &api.ImageMap{}, "Map a given image name to another (e.g. -map zenoss/zenoss5x:latest,quay.io/zenoss-core:alpha2)"},
				},
			},
		},
	})
}

// Returns a list of all available template IDs
func (c *ServicedCli) templates() (data []string) {
	templates, err := c.driver.GetServiceTemplates()
	if err != nil || templates == nil || len(templates) == 0 {
		return
	}

	data = make([]string, len(templates))
	for i, t := range templates {
		data[i] = t.ID
	}

	return
}

// Bash-completion command that prints the list of templates as the first
// argument
func (c *ServicedCli) printTemplatesFirst(ctx *cli.Context) {
	if len(ctx.Args()) > 0 {
		return
	}

	for _, t := range c.templates() {
		fmt.Println(t)
	}
}

// Bash-completion command that prints the command options for
// serviced template deploy
func (c *ServicedCli) printTemplateDeploy(ctx *cli.Context) {
	var output []string

	switch len(ctx.Args()) {
	case 0:
		output = c.templates()
	case 1:
		output = c.pools()
	}

	for _, o := range output {
		fmt.Println(o)
	}
}

// Bash-completion command that prints the list of templates as all arguments
func (c *ServicedCli) printTemplatesAll(ctx *cli.Context) {
	args := ctx.Args()

	for _, t := range c.templates() {
		for _, a := range args {
			if t == a {
				goto next
			}
		}
		fmt.Println(t)
	next:
	}
}

// serviced template list [--verbose, -v] [TEMPLATEID]
func (c *ServicedCli) cmdTemplateList(ctx *cli.Context) {
	if len(ctx.Args()) > 0 {
		templateID := ctx.Args()[0]
		if template, err := c.driver.GetServiceTemplate(templateID); err != nil {
			fmt.Fprintln(os.Stderr, err)
		} else if template == nil {
			fmt.Fprintln(os.Stderr, "template not found")
		} else if jsonTemplate, err := json.MarshalIndent(template, " ", "  "); err != nil {
			fmt.Fprintf(os.Stderr, "failed to marshal template: %s\n", err)
		} else {
			fmt.Println(string(jsonTemplate))
		}
		return
	}

	templates, err := c.driver.GetServiceTemplates()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	} else if templates == nil || len(templates) == 0 {
		fmt.Fprintln(os.Stderr, "no templates found")
		return
	}

	if ctx.Bool("verbose") {
		if jsonTemplate, err := json.MarshalIndent(templates, " ", "  "); err != nil {
			fmt.Fprintf(os.Stderr, "failed to marshal template list: %s\n", err)
		} else {
			fmt.Println(string(jsonTemplate))
		}
	} else {
		tableTemplate := newtable(0, 8, 2)
		tableTemplate.printrow("TEMPLATEID", "NAME", "DESCRIPTION")
		for _, t := range templates {
			tableTemplate.printrow(t.ID, t.Name, t.Description)
		}
		tableTemplate.flush()
	}
}

// serviced template add TEMPLATE
func (c *ServicedCli) cmdTemplateAdd(ctx *cli.Context) {
	var input *os.File

	if filepath := ctx.Args().First(); filepath != "" {
		var err error
		if input, err = os.Open(filepath); err != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
		defer input.Close()
	} else {
		input = os.Stdin
	}

	if template, err := c.driver.AddServiceTemplate(input); err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if template == nil {
		fmt.Fprintln(os.Stderr, "received nil template")
	} else {
		fmt.Println(template.ID)
	}
}

// serviced template remove TEMPLATEID ...
func (c *ServicedCli) cmdTemplateRemove(ctx *cli.Context) {
	args := ctx.Args()
	if len(args) < 1 {
		fmt.Printf("Incorrect Usage.\n\n")
		cli.ShowCommandHelp(ctx, "remove")
		return
	}

	for _, id := range args {
		if err := c.driver.RemoveServiceTemplate(id); err != nil {
			fmt.Fprintf(os.Stderr, "%s: %s\n", id, err)
		} else {
			fmt.Println(id)
		}
	}
}

// serviced template deploy TEMPLATEID POOLID DEPLOYMENTID [--manual-assign-ips]
func (c *ServicedCli) cmdTemplateDeploy(ctx *cli.Context) {
	args := ctx.Args()
	if len(args) < 3 {
		fmt.Printf("Incorrect Usage.\n\n")
		cli.ShowCommandHelp(ctx, "deploy")
		return
	}

	cfg := api.DeployTemplateConfig{
		ID:              args[0],
		PoolID:          args[1],
		DeploymentID:    args[2],
		ManualAssignIPs: ctx.Bool("manual-assign-ips"),
	}

	fmt.Fprintln(os.Stderr, "Deploying template - please wait...")
	if service, err := c.driver.DeployServiceTemplate(cfg); err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if service == nil {
		fmt.Fprintln(os.Stderr, "received nil service definition")
	} else {
		fmt.Println(service.ID)
	}
}

type metaTemplate struct {
	template.ServiceTemplate
	ServicedVersion servicedversion.ServicedVersion
	TemplateVersion map[string]string
}

// serviced template compile DIR [[--map IMAGE,IMAGE] ...]
func (c *ServicedCli) cmdTemplateCompile(ctx *cli.Context) {
	args := ctx.Args()
	if len(args) < 1 {
		fmt.Printf("Incorrect Usage.\n\n")
		cli.ShowCommandHelp(ctx, "compile")
		return
	}

	cfg := api.CompileTemplateConfig{
		Dir: args[0],
		Map: *ctx.Generic("map").(*api.ImageMap),
	}

	if template, err := c.driver.CompileServiceTemplate(cfg); err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if template == nil {
		fmt.Fprintln(os.Stderr, "received nil template")
	} else {
		cmd := fmt.Sprintf("cd %s && git rev-parse HEAD", args[0])
		commit, err := exec.Command("sh", "-c", cmd).Output()
		if err != nil {
			commit = []byte("unknown")
		}
		cmd = fmt.Sprintf("cd %s && git config --get remote.origin.url", args[0])
		repo, err := exec.Command("sh", "-c", cmd).Output()
		if err != nil {
			repo = []byte("unknown")
		}
		cmd = fmt.Sprintf("cd %s && git rev-parse --abbrev-ref HEAD", args[0])
		branch, err := exec.Command("sh", "-c", cmd).Output()
		if err != nil {
			branch = []byte("unknown")
		}
		cmd = fmt.Sprintf("cd %s && git describe --always", args[0])
		tag, err := exec.Command("sh", "-c", cmd).Output()
		if err != nil {
			tag = []byte("unknown")
		}
		templateVersion := map[string]string{
			"repo":   strings.Trim(string(repo), "\n"),
			"branch": strings.Trim(string(branch), "\n"),
			"tag":    strings.Trim(string(tag), "\n"),
			"commit": strings.Trim(string(commit), "\n"),
		}
		mTemplate := metaTemplate{*template, servicedversion.GetVersion(), templateVersion}
		jsonTemplate, err := json.MarshalIndent(mTemplate, " ", "  ")
		if err != nil {
			fmt.Fprintln(os.Stderr, "failed to marshal template: %s", err)
		} else {
			fmt.Println(string(jsonTemplate))
		}
	}
}
<|endoftext|>


<commit_before>package convert

func Add(s []string, a string) []string {
	for _, existing := range s {
		if a == existing {
			return s
		}
	}
	return append(s, a)

}

func Union(s []string, a []string) []string {
	for _, entry := range a {
		found := false
		for _, existing := range s {
			if entry == existing {
				found = true
				break
			}
		}
		if !found {
			s = append(s, entry)
		}
	}
	return s
}

func Uniq(s []string) (r []string) {
	for _, entry := range s {
		found := false
		for _, existing := range r {
			if existing == entry {
				found = true
				break
			}
		}
		if !found {
			r = append(r, entry)
		}
	}
	return
}
<commit_msg>Change the file format<commit_after>package convert

func Add(s []string, a string) []string {
	for _, existing := range s {
		if a == existing {
			return s
		}
	}
	return append(s, a)
}

func Union(s []string, a []string) []string {
	for _, entry := range a {
		found := false
		for _, existing := range s {
			if entry == existing {
				found = true
				break
			}
		}
		if !found {
			s = append(s, entry)
		}
	}
	return s
}

func Uniq(s []string) (r []string) {
	for _, entry := range s {
		found := false
		for _, existing := range r {
			if existing == entry {
				found = true
				break
			}
		}
		if !found {
			r = append(r, entry)
		}
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/openshift/origin/pkg/generate/app"
	"github.com/rtnpro/henge/pkg/generate/dockercompose"
	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apimachinery/registered"

	// Install OpenShift APIs
	_ "github.com/openshift/origin/pkg/build/api/install"
	_ "github.com/openshift/origin/pkg/deploy/api/install"
	_ "github.com/openshift/origin/pkg/image/api/install"
	_ "github.com/openshift/origin/pkg/route/api/install"
	_ "github.com/openshift/origin/pkg/template/api/install"
)

func main() {

	flag.Parse()

	template, err := dockercompose.Generate(flag.Args()[0:]...)
	if err != nil {
		return
	}

	if errs := app.AsVersionedObjects(template.Objects, kapi.Scheme, kapi.Scheme, registered.EnabledVersions()...); len(errs) > 0 {
		for _, err := range errs {
			fmt.Printf("error: %v\n", err)
		}
	}

	// make it List instead of Template
	list := &kapi.List{Items: template.Objects}
	for _, obj := range list.Items {
		fmt.Printf("%#v\n", obj)
	}

}
<commit_msg>Remove redundant imports of openshift install pkgs.<commit_after>package main

import (
	"flag"
	"fmt"

	"github.com/openshift/origin/pkg/generate/app"
	"github.com/rtnpro/henge/pkg/generate/dockercompose"
	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
)

func main() {

	flag.Parse()

	template, err := dockercompose.Generate(flag.Args()[0:]...)
	if err != nil {
		return
	}

	if errs := app.AsVersionedObjects(template.Objects, kapi.Scheme, kapi.Scheme, registered.EnabledVersions()...); len(errs) > 0 {
		for _, err := range errs {
			fmt.Printf("error: %v\n", err)
		}
	}

	// make it List instead of Template
	list := &kapi.List{Items: template.Objects}
	for _, obj := range list.Items {
		fmt.Printf("%#v\n", obj)
	}

}
<|endoftext|>


<commit_before>package stack

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/swarm"
	"github.com/docker/docker/cli"
	"github.com/docker/docker/cli/command"
	"github.com/docker/docker/cli/command/idresolver"
	"github.com/docker/docker/cli/command/task"
	"github.com/docker/docker/opts"
	"github.com/spf13/cobra"
)

type psOptions struct {
	all       bool
	filter    opts.FilterOpt
	noTrunc   bool
	namespace string
	noResolve bool
}

func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
	opts := psOptions{filter: opts.NewFilterOpt()}

	cmd := &cobra.Command{
		Use:   "ps [OPTIONS] STACK",
		Short: "List the tasks in the stack",
		Args:  cli.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			opts.namespace = args[0]
			return runPS(dockerCli, opts)
		},
	}
	flags := cmd.Flags()
	flags.BoolVarP(&opts.all, "all", "a", false, "Display all tasks")
	flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
	flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
	flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")

	return cmd
}

func runPS(dockerCli *command.DockerCli, opts psOptions) error {
	namespace := opts.namespace
	client := dockerCli.Client()
	ctx := context.Background()

	filter := getStackFilterFromOpt(opts.namespace, opts.filter)
	if !opts.all && !filter.Include("desired-state") {
		filter.Add("desired-state", string(swarm.TaskStateRunning))
		filter.Add("desired-state", string(swarm.TaskStateAccepted))
	}

	tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: filter})
	if err != nil {
		return err
	}

	if len(tasks) == 0 {
		fmt.Fprintf(dockerCli.Out(), "Nothing found in stack: %s\n", namespace)
		return nil
	}

	return task.Print(dockerCli, ctx, tasks, idresolver.New(client, opts.noResolve), opts.noTrunc)
}
<commit_msg>Remove `docker stack ps -a` to match removal of `docker service/node ps -a`<commit_after>package stack

import (
	"fmt"

	"golang.org/x/net/context"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/cli"
	"github.com/docker/docker/cli/command"
	"github.com/docker/docker/cli/command/idresolver"
	"github.com/docker/docker/cli/command/task"
	"github.com/docker/docker/opts"
	"github.com/spf13/cobra"
)

type psOptions struct {
	filter    opts.FilterOpt
	noTrunc   bool
	namespace string
	noResolve bool
}

func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
	opts := psOptions{filter: opts.NewFilterOpt()}

	cmd := &cobra.Command{
		Use:   "ps [OPTIONS] STACK",
		Short: "List the tasks in the stack",
		Args:  cli.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			opts.namespace = args[0]
			return runPS(dockerCli, opts)
		},
	}
	flags := cmd.Flags()
	flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
	flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
	flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")

	return cmd
}

func runPS(dockerCli *command.DockerCli, opts psOptions) error {
	namespace := opts.namespace
	client := dockerCli.Client()
	ctx := context.Background()

	filter := getStackFilterFromOpt(opts.namespace, opts.filter)

	tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: filter})
	if err != nil {
		return err
	}

	if len(tasks) == 0 {
		fmt.Fprintf(dockerCli.Out(), "Nothing found in stack: %s\n", namespace)
		return nil
	}

	return task.Print(dockerCli, ctx, tasks, idresolver.New(client, opts.noResolve), opts.noTrunc)
}
<|endoftext|>


<commit_before>package datastore

import (
	"errors"
	"io"
	"time"

	query "github.com/ipfs/go-datastore/query"
)

/*
Datastore represents storage for any key-value pair.

Datastores are general enough to be backed by all kinds of different storage:
in-memory caches, databases, a remote datastore, flat files on disk, etc.

The general idea is to wrap a more complicated storage facility in a simple,
uniform interface, keeping the freedom of using the right tools for the job.
In particular, a Datastore can aggregate other datastores in interesting ways,
like sharded (to distribute load) or tiered access (caches before databases).

While Datastores should be written general enough to accept all sorts of
values, some implementations will undoubtedly have to be specific (e.g. SQL
databases where fields should be decomposed into columns), particularly to
support queries efficiently. Moreover, certain datastores may enforce certain
types of values (e.g. requiring an io.Reader, a specific struct, etc) or
serialization formats (JSON, Protobufs, etc).

IMPORTANT: No Datastore should ever Panic! This is a cross-module interface,
and thus it should behave predictably and handle exceptional conditions with
proper error reporting. Thus, all Datastore calls may return errors, which
should be checked by callers.
*/
type Datastore interface {
	Read
	Write
	io.Closer
}

// Write is the write-side of the Datastore interface.
type Write interface {
	// Put stores the object `value` named by `key`.
	//
	// The generalized Datastore interface does not impose a value type,
	// allowing various datastore middleware implementations (which do not
	// handle the values directly) to be composed together.
	//
	// Ultimately, the lowest-level datastore will need to do some value checking
	// or risk getting incorrect values. It may also be useful to expose a more
	// type-safe interface to your application, and do the checking up-front.
	Put(key Key, value []byte) error

	// Delete removes the value for given `key`.
	Delete(key Key) error
}

// Read is the read-side of the Datastore interface.
type Read interface {
	// Get retrieves the object `value` named by `key`.
	// Get will return ErrNotFound if the key is not mapped to a value.
	Get(key Key) (value []byte, err error)

	// Has returns whether the `key` is mapped to a `value`.
	// In some contexts, it may be much cheaper only to check for existence of
	// a value, rather than retrieving the value itself. (e.g. HTTP HEAD).
	// The default implementation is found in `GetBackedHas`.
	Has(key Key) (exists bool, err error)

	// GetSize returns the size of the `value` named by `key`.
	// In some contexts, it may be much cheaper to only get the size of the
	// value rather than retrieving the value itself.
	GetSize(key Key) (size int, err error)

	// Query searches the datastore and returns a query result. This function
	// may return before the query actually runs. To wait for the query:
	//
	//   result, _ := ds.Query(q)
	//
	//   // use the channel interface; result may come in at different times
	//   for entry := range result.Next() { ... }
	//
	//   // or wait for the query to be completely done
	//   entries, _ := result.Rest()
	//   for entry := range entries { ... }
	//
	Query(q query.Query) (query.Results, error)
}

// Batching datastores support deferred, grouped updates to the database.
// `Batch`es do NOT have transactional semantics: updates to the underlying
// datastore are not guaranteed to occur in the same iota of time. Similarly,
// batched updates will not be flushed to the underlying datastore until
// `Commit` has been called. `Txn`s from a `TxnDatastore` have all the
// capabilities of a `Batch`, but the reverse is NOT true.
type Batching interface {
	Datastore

	Batch() (Batch, error)
}

// ErrBatchUnsupported is returned if the by Batch if the Datastore doesn't
// actually support batching.
var ErrBatchUnsupported = errors.New("this datastore does not support batching")

// ThreadSafeDatastore is an interface that all threadsafe datastore should
// implement to leverage type safety checks.
type ThreadSafeDatastore interface {
	Datastore

	IsThreadSafe()
}

// CheckedDatastore is an interface that should be implemented by datastores
// which may need checking on-disk data integrity.
type CheckedDatastore interface {
	Datastore

	Check() error
}

// CheckedDatastore is an interface that should be implemented by datastores
// which want to provide a mechanism to check data integrity and/or
// error correction.
type ScrubbedDatastore interface {
	Datastore

	Scrub() error
}

// GCDatastore is an interface that should be implemented by datastores which
// don't free disk space by just removing data from them.
type GCDatastore interface {
	Datastore

	CollectGarbage() error
}

// PersistentDatastore is an interface that should be implemented by datastores
// which can report disk usage.
type PersistentDatastore interface {
	Datastore

	// DiskUsage returns the space used by a datastore, in bytes.
	DiskUsage() (uint64, error)
}

// DiskUsage checks if a Datastore is a
// PersistentDatastore and returns its DiskUsage(),
// otherwise returns 0.
func DiskUsage(d Datastore) (uint64, error) {
	persDs, ok := d.(PersistentDatastore)
	if !ok {
		return 0, nil
	}
	return persDs.DiskUsage()
}

// TTLDatastore is an interface that should be implemented by datastores that
// support expiring entries.
type TTLDatastore interface {
	Datastore

	PutWithTTL(key Key, value []byte, ttl time.Duration) error
	SetTTL(key Key, ttl time.Duration) error
	GetExpiration(key Key) (time.Time, error)
}

// Txn extends the Datastore type. Txns allow users to batch queries and
// mutations to the Datastore into atomic groups, or transactions. Actions
// performed on a transaction will not take hold until a successful call to
// Commit has been made. Likewise, transactions can be aborted by calling
// Discard before a successful Commit has been made.
type Txn interface {
	Datastore

	// Commit finalizes a transaction, attempting to commit it to the Datastore.
	// May return an error if the transaction has gone stale. The presence of an
	// error is an indication that the data was not committed to the Datastore.
	Commit() error
	// Discard throws away changes recorded in a transaction without committing
	// them to the underlying Datastore. Any calls made to Discard after Commit
	// has been successfully called will have no effect on the transaction and
	// state of the Datastore, making it safe to defer.
	Discard()
}

// TxnDatastore is an interface that should be implemented by datastores that
// support transactions.
type TxnDatastore interface {
	Datastore

	NewTransaction(readOnly bool) (Txn, error)
}

// Errors

// ErrNotFound is returned by Get, Has, and Delete when a datastore does not
// map the given key to a value.
var ErrNotFound = errors.New("datastore: key not found")

// ErrInvalidType is returned by Put when a given value is incopatible with
// the type the datastore supports. This means a conversion (or serialization)
// is needed beforehand.
var ErrInvalidType = errors.New("datastore: invalid type error")

// GetBackedHas provides a default Datastore.Has implementation.
// It exists so Datastore.Has implementations can use it, like so:
//
// func (*d SomeDatastore) Has(key Key) (exists bool, err error) {
//   return GetBackedHas(d, key)
// }
func GetBackedHas(ds Datastore, key Key) (bool, error) {
	_, err := ds.Get(key)
	switch err {
	case nil:
		return true, nil
	case ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

// GetBackedSize provides a default Datastore.GetSize implementation.
// It exists so Datastore.GetSize implementations can use it, like so:
//
// func (*d SomeDatastore) GetSize(key Key) (size int, err error) {
//   return GetBackedSize(d, key)
// }
func GetBackedSize(ds Datastore, key Key) (int, error) {
	value, err := ds.Get(key)
	if err == nil {
		return len(value), nil
	}
	return -1, err
}

type Batch interface {
	Write

	Commit() error
}
<commit_msg>remove io.Closer from the transaction interface<commit_after>package datastore

import (
	"errors"
	"io"
	"time"

	query "github.com/ipfs/go-datastore/query"
)

/*
Datastore represents storage for any key-value pair.

Datastores are general enough to be backed by all kinds of different storage:
in-memory caches, databases, a remote datastore, flat files on disk, etc.

The general idea is to wrap a more complicated storage facility in a simple,
uniform interface, keeping the freedom of using the right tools for the job.
In particular, a Datastore can aggregate other datastores in interesting ways,
like sharded (to distribute load) or tiered access (caches before databases).

While Datastores should be written general enough to accept all sorts of
values, some implementations will undoubtedly have to be specific (e.g. SQL
databases where fields should be decomposed into columns), particularly to
support queries efficiently. Moreover, certain datastores may enforce certain
types of values (e.g. requiring an io.Reader, a specific struct, etc) or
serialization formats (JSON, Protobufs, etc).

IMPORTANT: No Datastore should ever Panic! This is a cross-module interface,
and thus it should behave predictably and handle exceptional conditions with
proper error reporting. Thus, all Datastore calls may return errors, which
should be checked by callers.
*/
type Datastore interface {
	Read
	Write
	io.Closer
}

// Write is the write-side of the Datastore interface.
type Write interface {
	// Put stores the object `value` named by `key`.
	//
	// The generalized Datastore interface does not impose a value type,
	// allowing various datastore middleware implementations (which do not
	// handle the values directly) to be composed together.
	//
	// Ultimately, the lowest-level datastore will need to do some value checking
	// or risk getting incorrect values. It may also be useful to expose a more
	// type-safe interface to your application, and do the checking up-front.
	Put(key Key, value []byte) error

	// Delete removes the value for given `key`.
	Delete(key Key) error
}

// Read is the read-side of the Datastore interface.
type Read interface {
	// Get retrieves the object `value` named by `key`.
	// Get will return ErrNotFound if the key is not mapped to a value.
	Get(key Key) (value []byte, err error)

	// Has returns whether the `key` is mapped to a `value`.
	// In some contexts, it may be much cheaper only to check for existence of
	// a value, rather than retrieving the value itself. (e.g. HTTP HEAD).
	// The default implementation is found in `GetBackedHas`.
	Has(key Key) (exists bool, err error)

	// GetSize returns the size of the `value` named by `key`.
	// In some contexts, it may be much cheaper to only get the size of the
	// value rather than retrieving the value itself.
	GetSize(key Key) (size int, err error)

	// Query searches the datastore and returns a query result. This function
	// may return before the query actually runs. To wait for the query:
	//
	//   result, _ := ds.Query(q)
	//
	//   // use the channel interface; result may come in at different times
	//   for entry := range result.Next() { ... }
	//
	//   // or wait for the query to be completely done
	//   entries, _ := result.Rest()
	//   for entry := range entries { ... }
	//
	Query(q query.Query) (query.Results, error)
}

// Batching datastores support deferred, grouped updates to the database.
// `Batch`es do NOT have transactional semantics: updates to the underlying
// datastore are not guaranteed to occur in the same iota of time. Similarly,
// batched updates will not be flushed to the underlying datastore until
// `Commit` has been called. `Txn`s from a `TxnDatastore` have all the
// capabilities of a `Batch`, but the reverse is NOT true.
type Batching interface {
	Datastore

	Batch() (Batch, error)
}

// ErrBatchUnsupported is returned if the by Batch if the Datastore doesn't
// actually support batching.
var ErrBatchUnsupported = errors.New("this datastore does not support batching")

// ThreadSafeDatastore is an interface that all threadsafe datastore should
// implement to leverage type safety checks.
type ThreadSafeDatastore interface {
	Datastore

	IsThreadSafe()
}

// CheckedDatastore is an interface that should be implemented by datastores
// which may need checking on-disk data integrity.
type CheckedDatastore interface {
	Datastore

	Check() error
}

// CheckedDatastore is an interface that should be implemented by datastores
// which want to provide a mechanism to check data integrity and/or
// error correction.
type ScrubbedDatastore interface {
	Datastore

	Scrub() error
}

// GCDatastore is an interface that should be implemented by datastores which
// don't free disk space by just removing data from them.
type GCDatastore interface {
	Datastore

	CollectGarbage() error
}

// PersistentDatastore is an interface that should be implemented by datastores
// which can report disk usage.
type PersistentDatastore interface {
	Datastore

	// DiskUsage returns the space used by a datastore, in bytes.
	DiskUsage() (uint64, error)
}

// DiskUsage checks if a Datastore is a
// PersistentDatastore and returns its DiskUsage(),
// otherwise returns 0.
func DiskUsage(d Datastore) (uint64, error) {
	persDs, ok := d.(PersistentDatastore)
	if !ok {
		return 0, nil
	}
	return persDs.DiskUsage()
}

// TTLDatastore is an interface that should be implemented by datastores that
// support expiring entries.
type TTLDatastore interface {
	Datastore

	PutWithTTL(key Key, value []byte, ttl time.Duration) error
	SetTTL(key Key, ttl time.Duration) error
	GetExpiration(key Key) (time.Time, error)
}

// Txn extends the Datastore type. Txns allow users to batch queries and
// mutations to the Datastore into atomic groups, or transactions. Actions
// performed on a transaction will not take hold until a successful call to
// Commit has been made. Likewise, transactions can be aborted by calling
// Discard before a successful Commit has been made.
type Txn interface {
	Read
	Write

	// Commit finalizes a transaction, attempting to commit it to the Datastore.
	// May return an error if the transaction has gone stale. The presence of an
	// error is an indication that the data was not committed to the Datastore.
	Commit() error
	// Discard throws away changes recorded in a transaction without committing
	// them to the underlying Datastore. Any calls made to Discard after Commit
	// has been successfully called will have no effect on the transaction and
	// state of the Datastore, making it safe to defer.
	Discard()
}

// TxnDatastore is an interface that should be implemented by datastores that
// support transactions.
type TxnDatastore interface {
	Datastore

	NewTransaction(readOnly bool) (Txn, error)
}

// Errors

// ErrNotFound is returned by Get, Has, and Delete when a datastore does not
// map the given key to a value.
var ErrNotFound = errors.New("datastore: key not found")

// ErrInvalidType is returned by Put when a given value is incopatible with
// the type the datastore supports. This means a conversion (or serialization)
// is needed beforehand.
var ErrInvalidType = errors.New("datastore: invalid type error")

// GetBackedHas provides a default Datastore.Has implementation.
// It exists so Datastore.Has implementations can use it, like so:
//
// func (*d SomeDatastore) Has(key Key) (exists bool, err error) {
//   return GetBackedHas(d, key)
// }
func GetBackedHas(ds Datastore, key Key) (bool, error) {
	_, err := ds.Get(key)
	switch err {
	case nil:
		return true, nil
	case ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

// GetBackedSize provides a default Datastore.GetSize implementation.
// It exists so Datastore.GetSize implementations can use it, like so:
//
// func (*d SomeDatastore) GetSize(key Key) (size int, err error) {
//   return GetBackedSize(d, key)
// }
func GetBackedSize(ds Datastore, key Key) (int, error) {
	value, err := ds.Get(key)
	if err == nil {
		return len(value), nil
	}
	return -1, err
}

type Batch interface {
	Write

	Commit() error
}
<|endoftext|>


<commit_before>package lessgo

import (
	"encoding/json"
	"fmt"
	"mime"
	"os"
	"path/filepath"

	"github.com/lessgo/lessgo/config"
	"github.com/lessgo/lessgo/session"
)

func printInfo() {
	fmt.Printf(">%s %s (%s)\n", NAME, VERSION, ADDRESS)
}

func registerMime() error {
	for k, v := range mimemaps {
		mime.AddExtensionType(k, v)
	}
	return nil
}

func registerConfig() (err error) {
	fname := APP_CONFIG
	appconf, err := config.NewConfig("ini", fname)
	if err == nil {
		trySet(appconf)
		return appconf.SaveConfigFile(fname)
	}

	os.MkdirAll(filepath.Dir(fname), 0777)
	f, err := os.Create(fname)
	if err != nil {
		panic(err)
	}
	f.Close()
	appconf, err = config.NewConfig("ini", fname)
	defaultConfig(appconf)
	return appconf.SaveConfigFile(fname)
}

func registerRouter() error {
	// 从数据读取动态配置

	// 与源码配置进行同步

	// 创建真实路由
	ResetRealRoute()

	return nil
}

func registerSession() (err error) {
	if !AppConfig.Session.Enable {
		return
	}
	conf := map[string]interface{}{
		"cookieName":      AppConfig.Session.CookieName,
		"gclifetime":      AppConfig.Session.GcMaxlifetime,
		"providerConfig":  filepath.ToSlash(AppConfig.Session.ProviderConfig),
		"secure":          AppConfig.Listen.EnableHTTPS,
		"enableSetCookie": AppConfig.Session.EnableSetCookie,
		"domain":          AppConfig.Session.Domain,
		"cookieLifeTime":  AppConfig.Session.CookieLifeTime,
	}
	confBytes, err := json.Marshal(conf)
	if err != nil {
		return err
	}
	sessionConfig := string(confBytes)
	GlobalSessions, err = session.NewManager(AppConfig.Session.Provider, sessionConfig)
	if err != nil {
		return
	}
	go GlobalSessions.GC()
	return
}

func registerRootMiddlewares() {
	defer DefLessgo.Echo.PreUse(Logger(), Recover())
	DefLessgo.Echo.Get("/test2", test2)
	DefLessgo.Echo.SufUse(WrapMiddleware(test3))
	// DefLessgo.Echo.AfterUse(WrapMiddleware(test4))
	DefLessgo.Echo.PreUse(WrapMiddleware(test1))
	DefLessgo.Echo.BeforeUse(WrapMiddleware(test4))
}

func checkHooks(err error) {
	if err == nil {
		return
	}
	DefLessgo.Echo.Logger().Fatal("%v", err)
}
<commit_msg>移除测试中间件<commit_after>package lessgo

import (
	"encoding/json"
	"fmt"
	"mime"
	"os"
	"path/filepath"

	"github.com/lessgo/lessgo/config"
	"github.com/lessgo/lessgo/session"
)

func printInfo() {
	fmt.Printf(">%s %s (%s)\n", NAME, VERSION, ADDRESS)
}

func registerMime() error {
	for k, v := range mimemaps {
		mime.AddExtensionType(k, v)
	}
	return nil
}

func registerConfig() (err error) {
	fname := APP_CONFIG
	appconf, err := config.NewConfig("ini", fname)
	if err == nil {
		trySet(appconf)
		return appconf.SaveConfigFile(fname)
	}

	os.MkdirAll(filepath.Dir(fname), 0777)
	f, err := os.Create(fname)
	if err != nil {
		panic(err)
	}
	f.Close()
	appconf, err = config.NewConfig("ini", fname)
	defaultConfig(appconf)
	return appconf.SaveConfigFile(fname)
}

func registerRouter() error {
	// 从数据读取动态配置

	// 与源码配置进行同步

	// 创建真实路由
	ResetRealRoute()

	return nil
}

func registerSession() (err error) {
	if !AppConfig.Session.Enable {
		return
	}
	conf := map[string]interface{}{
		"cookieName":      AppConfig.Session.CookieName,
		"gclifetime":      AppConfig.Session.GcMaxlifetime,
		"providerConfig":  filepath.ToSlash(AppConfig.Session.ProviderConfig),
		"secure":          AppConfig.Listen.EnableHTTPS,
		"enableSetCookie": AppConfig.Session.EnableSetCookie,
		"domain":          AppConfig.Session.Domain,
		"cookieLifeTime":  AppConfig.Session.CookieLifeTime,
	}
	confBytes, err := json.Marshal(conf)
	if err != nil {
		return err
	}
	sessionConfig := string(confBytes)
	GlobalSessions, err = session.NewManager(AppConfig.Session.Provider, sessionConfig)
	if err != nil {
		return
	}
	go GlobalSessions.GC()
	return
}

func registerRootMiddlewares() {
	defer DefLessgo.Echo.PreUse(Logger(), Recover())
	// DefLessgo.Echo.Get("/test2", test2)
	// DefLessgo.Echo.SufUse(WrapMiddleware(test3))
	// DefLessgo.Echo.AfterUse(WrapMiddleware(test4))
	// DefLessgo.Echo.PreUse(WrapMiddleware(test1))
	// DefLessgo.Echo.BeforeUse(WrapMiddleware(test4))
}

func checkHooks(err error) {
	if err == nil {
		return
	}
	DefLessgo.Echo.Logger().Fatal("%v", err)
}
<|endoftext|>