<commit_before>// Copyright 2018 The gVisor 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 watchdog is responsible for monitoring the sentry for tasks that may
// potentially be stuck or looping inderterminally causing hard to debug hungs in
// the untrusted app.
//
// It works by periodically querying all tasks to check whether they are in user
// mode (RunUser), kernel mode (RunSys), or blocked in the kernel (OffCPU). Tasks
// that have been running in kernel mode for a long time in the same syscall
// without blocking are considered stuck and are reported.
//
// When a stuck task is detected, the watchdog can take one of the following actions:
//		1. LogWarning: Logs a warning message followed by a stack dump of all goroutines.
//			 If a tasks continues to be stuck, the message will repeat every minute, unless
//			 a new stuck task is detected
//		2. Panic: same as above, followed by panic()
//
package watchdog

import (
	"bytes"
	"fmt"
	"time"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/log"
	"gvisor.dev/gvisor/pkg/metric"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
	"gvisor.dev/gvisor/pkg/sync"
)

// Opts configures the watchdog.
type Opts struct {
	// TaskTimeout is the amount of time to allow a task to execute the
	// same syscall without blocking before it's declared stuck.
	TaskTimeout time.Duration

	// TaskTimeoutAction indicates what action to take when a stuck tasks
	// is detected.
	TaskTimeoutAction Action

	// StartupTimeout is the amount of time to allow between watchdog
	// creation and calling watchdog.Start.
	StartupTimeout time.Duration

	// StartupTimeoutAction indicates what action to take when
	// watchdog.Start is not called within the timeout.
	StartupTimeoutAction Action
}

// DefaultOpts is a default set of options for the watchdog.
var DefaultOpts = Opts{
	// Task timeout.
	TaskTimeout:       3 * time.Minute,
	TaskTimeoutAction: LogWarning,

	// Startup timeout.
	StartupTimeout:       30 * time.Second,
	StartupTimeoutAction: LogWarning,
}

// descheduleThreshold is the amount of time scheduling needs to be off before the entire wait period
// is discounted from task's last update time. It's set high enough that small scheduling delays won't
// trigger it.
const descheduleThreshold = 1 * time.Second

var (
	stuckStartup = metric.MustCreateNewUint64Metric("/watchdog/stuck_startup_detected", true /* sync */, "Incremented once on startup watchdog timeout")
	stuckTasks   = metric.MustCreateNewUint64Metric("/watchdog/stuck_tasks_detected", true /* sync */, "Cumulative count of stuck tasks detected")
)

// Amount of time to wait before dumping the stack to the log again when the same task(s) remains stuck.
var stackDumpSameTaskPeriod = time.Minute

// Action defines what action to take when a stuck task is detected.
type Action int

const (
	// LogWarning logs warning message followed by stack trace.
	LogWarning Action = iota

	// Panic will do the same logging as LogWarning and panic().
	Panic
)

// String returns Action's string representation.
func (a Action) String() string {
	switch a {
	case LogWarning:
		return "LogWarning"
	case Panic:
		return "Panic"
	default:
		panic(fmt.Sprintf("Invalid action: %d", a))
	}
}

// Watchdog is the main watchdog class. It controls a goroutine that periodically
// analyses all tasks and reports if any of them appear to be stuck.
type Watchdog struct {
	// Configuration options are embedded.
	Opts

	// period indicates how often to check all tasks. It's calculated based on
	// opts.TaskTimeout.
	period time.Duration

	// k is where the tasks come from.
	k *kernel.Kernel

	// stop is used to notify to watchdog should stop.
	stop chan struct{}

	// done is used to notify when the watchdog has stopped.
	done chan struct{}

	// offenders map contains all tasks that are currently stuck.
	offenders map[*kernel.Task]*offender

	// lastStackDump tracks the last time a stack dump was generated to prevent
	// spamming the log.
	lastStackDump time.Time

	// lastRun is set to the last time the watchdog executed a monitoring loop.
	lastRun ktime.Time

	// mu protects the fields below.
	mu sync.Mutex

	// running is true if the watchdog is running.
	running bool

	// startCalled is true if Start has ever been called. It remains true
	// even if Stop is called.
	startCalled bool
}

type offender struct {
	lastUpdateTime ktime.Time
}

// New creates a new watchdog.
func New(k *kernel.Kernel, opts Opts) *Watchdog {
	// 4 is arbitrary, just don't want to prolong 'TaskTimeout' too much.
	period := opts.TaskTimeout / 4
	w := &Watchdog{
		Opts:      opts,
		k:         k,
		period:    period,
		offenders: make(map[*kernel.Task]*offender),
		stop:      make(chan struct{}),
		done:      make(chan struct{}),
	}

	// Handle StartupTimeout if it exists.
	if w.StartupTimeout > 0 {
		log.Infof("Watchdog waiting %v for startup", w.StartupTimeout)
		go w.waitForStart() // S/R-SAFE: watchdog is stopped buring save and restarted after restore.
	}

	return w
}

// Start starts the watchdog.
func (w *Watchdog) Start() {
	w.mu.Lock()
	defer w.mu.Unlock()
	w.startCalled = true

	if w.running {
		return
	}

	if w.TaskTimeout == 0 {
		log.Infof("Watchdog task timeout disabled")
		return
	}
	w.lastRun = w.k.MonotonicClock().Now()

	log.Infof("Starting watchdog, period: %v, timeout: %v, action: %v", w.period, w.TaskTimeout, w.TaskTimeoutAction)
	go w.loop() // S/R-SAFE: watchdog is stopped during save and restarted after restore.
	w.running = true
}

// Stop requests the watchdog to stop and wait for it.
func (w *Watchdog) Stop() {
	if w.TaskTimeout == 0 {
		return
	}

	w.mu.Lock()
	defer w.mu.Unlock()
	if !w.running {
		return
	}
	log.Infof("Stopping watchdog")
	w.stop <- struct{}{}
	<-w.done
	w.running = false
	log.Infof("Watchdog stopped")
}

// waitForStart waits for Start to be called and takes action if it does not
// happen within the startup timeout.
func (w *Watchdog) waitForStart() {
	<-time.After(w.StartupTimeout)
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.startCalled {
		// We are fine.
		return
	}

	stuckStartup.Increment()

	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("Watchdog.Start() not called within %s", w.StartupTimeout))
	w.doAction(w.StartupTimeoutAction, false, &buf)
}

// loop is the main watchdog routine. It only returns when 'Stop()' is called.
func (w *Watchdog) loop() {
	// Loop until someone stops it.
	for {
		select {
		case <-w.stop:
			w.done <- struct{}{}
			return
		case <-time.After(w.period):
			w.runTurn()
		}
	}
}

// runTurn runs a single pass over all tasks and reports anything it finds.
func (w *Watchdog) runTurn() {
	// Someone needs to watch the watchdog. The call below can get stuck if there
	// is a deadlock affecting root's PID namespace mutex. Run it in a goroutine
	// and report if it takes too long to return.
	var tasks []*kernel.Task
	done := make(chan struct{})
	go func() { // S/R-SAFE: watchdog is stopped and restarted during S/R.
		tasks = w.k.TaskSet().Root.Tasks()
		close(done)
	}()

	select {
	case <-done:
	case <-time.After(w.TaskTimeout):
		// Report if the watchdog is not making progress.
		// No one is watching the watchdog watcher though.
		w.reportStuckWatchdog()
		<-done
	}

	newOffenders := make(map[*kernel.Task]*offender)
	newTaskFound := false
	now := ktime.FromNanoseconds(int64(w.k.CPUClockNow() * uint64(linux.ClockTick)))

	// The process may be running with low CPU limit making tasks appear stuck because
	// are starved of CPU cycles. An estimate is that Tasks could have been starved
	// since the last time the watchdog run. If the watchdog detects that scheduling
	// is off, it will discount the entire duration since last run from 'lastUpdateTime'.
	discount := time.Duration(0)
	if now.Sub(w.lastRun.Add(w.period)) > descheduleThreshold {
		discount = now.Sub(w.lastRun)
	}
	w.lastRun = now

	log.Infof("Watchdog starting loop, tasks: %d, discount: %v", len(tasks), discount)
	for _, t := range tasks {
		tsched := t.TaskGoroutineSchedInfo()

		// An offender is a task running inside the kernel for longer than the specified timeout.
		if tsched.State == kernel.TaskGoroutineRunningSys {
			lastUpdateTime := ktime.FromNanoseconds(int64(tsched.Timestamp * uint64(linux.ClockTick)))
			elapsed := now.Sub(lastUpdateTime) - discount
			if elapsed > w.TaskTimeout {
				tc, ok := w.offenders[t]
				if !ok {
					// New stuck task detected.
					//
					// Note that tasks blocked doing IO may be considered stuck in kernel,
					// unless they are surrounded b
					// Task.UninterruptibleSleepStart/Finish.
					tc = &offender{lastUpdateTime: lastUpdateTime}
					stuckTasks.Increment()
					newTaskFound = true
				}
				newOffenders[t] = tc
			}
		}
	}
	if len(newOffenders) > 0 {
		w.report(newOffenders, newTaskFound, now)
	}

	// Remember which tasks have been reported.
	w.offenders = newOffenders
}

// report takes appropriate action when a stuck task is detected.
func (w *Watchdog) report(offenders map[*kernel.Task]*offender, newTaskFound bool, now ktime.Time) {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("Sentry detected %d stuck task(s):\n", len(offenders)))
	for t, o := range offenders {
		tid := w.k.TaskSet().Root.IDOfTask(t)
		buf.WriteString(fmt.Sprintf("\tTask tid: %v (%#x), entered RunSys state %v ago.\n", tid, uint64(tid), now.Sub(o.lastUpdateTime)))
	}

	buf.WriteString("Search for '(*Task).run(0x..., 0x<tid>)' in the stack dump to find the offending goroutine")

	// Force stack dump only if a new task is detected.
	w.doAction(w.TaskTimeoutAction, newTaskFound, &buf)
}

func (w *Watchdog) reportStuckWatchdog() {
	var buf bytes.Buffer
	buf.WriteString("Watchdog goroutine is stuck:")
	w.doAction(w.TaskTimeoutAction, false, &buf)
}

// doAction will take the given action. If the action is LogWarning, the stack
// is not always dumped to the log to prevent log flooding. "forceStack"
// guarantees that the stack will be dumped regardless.
func (w *Watchdog) doAction(action Action, forceStack bool, msg *bytes.Buffer) {
	switch action {
	case LogWarning:
		// Dump stack only if forced or sometime has passed since the last time a
		// stack dump was generated.
		if !forceStack && time.Since(w.lastStackDump) < stackDumpSameTaskPeriod {
			msg.WriteString("\n...[stack dump skipped]...")
			log.Warningf(msg.String())
			return
		}
		log.TracebackAll(msg.String())
		w.lastStackDump = time.Now()

	case Panic:
		// Panic will skip over running tasks, which is likely the culprit here. So manually
		// dump all stacks before panic'ing.
		log.TracebackAll(msg.String())

		// Attempt to flush metrics, timeout and move on in case metrics are stuck as well.
		metricsEmitted := make(chan struct{}, 1)
		go func() { // S/R-SAFE: watchdog is stopped during save and restarted after restore.
			// Flush metrics before killing process.
			metric.EmitMetricUpdate()
			metricsEmitted <- struct{}{}
		}()
		select {
		case <-metricsEmitted:
		case <-time.After(1 * time.Second):
		}
		panic(fmt.Sprintf("%s\nStack for running G's are skipped while panicking.", msg.String()))

	default:
		panic(fmt.Sprintf("Unknown watchdog action %v", action))

	}
}
<commit_msg>Remove duplicate colon from warning log.<commit_after>// Copyright 2018 The gVisor 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 watchdog is responsible for monitoring the sentry for tasks that may
// potentially be stuck or looping inderterminally causing hard to debug hungs in
// the untrusted app.
//
// It works by periodically querying all tasks to check whether they are in user
// mode (RunUser), kernel mode (RunSys), or blocked in the kernel (OffCPU). Tasks
// that have been running in kernel mode for a long time in the same syscall
// without blocking are considered stuck and are reported.
//
// When a stuck task is detected, the watchdog can take one of the following actions:
//		1. LogWarning: Logs a warning message followed by a stack dump of all goroutines.
//			 If a tasks continues to be stuck, the message will repeat every minute, unless
//			 a new stuck task is detected
//		2. Panic: same as above, followed by panic()
//
package watchdog

import (
	"bytes"
	"fmt"
	"time"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/log"
	"gvisor.dev/gvisor/pkg/metric"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
	"gvisor.dev/gvisor/pkg/sync"
)

// Opts configures the watchdog.
type Opts struct {
	// TaskTimeout is the amount of time to allow a task to execute the
	// same syscall without blocking before it's declared stuck.
	TaskTimeout time.Duration

	// TaskTimeoutAction indicates what action to take when a stuck tasks
	// is detected.
	TaskTimeoutAction Action

	// StartupTimeout is the amount of time to allow between watchdog
	// creation and calling watchdog.Start.
	StartupTimeout time.Duration

	// StartupTimeoutAction indicates what action to take when
	// watchdog.Start is not called within the timeout.
	StartupTimeoutAction Action
}

// DefaultOpts is a default set of options for the watchdog.
var DefaultOpts = Opts{
	// Task timeout.
	TaskTimeout:       3 * time.Minute,
	TaskTimeoutAction: LogWarning,

	// Startup timeout.
	StartupTimeout:       30 * time.Second,
	StartupTimeoutAction: LogWarning,
}

// descheduleThreshold is the amount of time scheduling needs to be off before the entire wait period
// is discounted from task's last update time. It's set high enough that small scheduling delays won't
// trigger it.
const descheduleThreshold = 1 * time.Second

var (
	stuckStartup = metric.MustCreateNewUint64Metric("/watchdog/stuck_startup_detected", true /* sync */, "Incremented once on startup watchdog timeout")
	stuckTasks   = metric.MustCreateNewUint64Metric("/watchdog/stuck_tasks_detected", true /* sync */, "Cumulative count of stuck tasks detected")
)

// Amount of time to wait before dumping the stack to the log again when the same task(s) remains stuck.
var stackDumpSameTaskPeriod = time.Minute

// Action defines what action to take when a stuck task is detected.
type Action int

const (
	// LogWarning logs warning message followed by stack trace.
	LogWarning Action = iota

	// Panic will do the same logging as LogWarning and panic().
	Panic
)

// String returns Action's string representation.
func (a Action) String() string {
	switch a {
	case LogWarning:
		return "LogWarning"
	case Panic:
		return "Panic"
	default:
		panic(fmt.Sprintf("Invalid action: %d", a))
	}
}

// Watchdog is the main watchdog class. It controls a goroutine that periodically
// analyses all tasks and reports if any of them appear to be stuck.
type Watchdog struct {
	// Configuration options are embedded.
	Opts

	// period indicates how often to check all tasks. It's calculated based on
	// opts.TaskTimeout.
	period time.Duration

	// k is where the tasks come from.
	k *kernel.Kernel

	// stop is used to notify to watchdog should stop.
	stop chan struct{}

	// done is used to notify when the watchdog has stopped.
	done chan struct{}

	// offenders map contains all tasks that are currently stuck.
	offenders map[*kernel.Task]*offender

	// lastStackDump tracks the last time a stack dump was generated to prevent
	// spamming the log.
	lastStackDump time.Time

	// lastRun is set to the last time the watchdog executed a monitoring loop.
	lastRun ktime.Time

	// mu protects the fields below.
	mu sync.Mutex

	// running is true if the watchdog is running.
	running bool

	// startCalled is true if Start has ever been called. It remains true
	// even if Stop is called.
	startCalled bool
}

type offender struct {
	lastUpdateTime ktime.Time
}

// New creates a new watchdog.
func New(k *kernel.Kernel, opts Opts) *Watchdog {
	// 4 is arbitrary, just don't want to prolong 'TaskTimeout' too much.
	period := opts.TaskTimeout / 4
	w := &Watchdog{
		Opts:      opts,
		k:         k,
		period:    period,
		offenders: make(map[*kernel.Task]*offender),
		stop:      make(chan struct{}),
		done:      make(chan struct{}),
	}

	// Handle StartupTimeout if it exists.
	if w.StartupTimeout > 0 {
		log.Infof("Watchdog waiting %v for startup", w.StartupTimeout)
		go w.waitForStart() // S/R-SAFE: watchdog is stopped buring save and restarted after restore.
	}

	return w
}

// Start starts the watchdog.
func (w *Watchdog) Start() {
	w.mu.Lock()
	defer w.mu.Unlock()
	w.startCalled = true

	if w.running {
		return
	}

	if w.TaskTimeout == 0 {
		log.Infof("Watchdog task timeout disabled")
		return
	}
	w.lastRun = w.k.MonotonicClock().Now()

	log.Infof("Starting watchdog, period: %v, timeout: %v, action: %v", w.period, w.TaskTimeout, w.TaskTimeoutAction)
	go w.loop() // S/R-SAFE: watchdog is stopped during save and restarted after restore.
	w.running = true
}

// Stop requests the watchdog to stop and wait for it.
func (w *Watchdog) Stop() {
	if w.TaskTimeout == 0 {
		return
	}

	w.mu.Lock()
	defer w.mu.Unlock()
	if !w.running {
		return
	}
	log.Infof("Stopping watchdog")
	w.stop <- struct{}{}
	<-w.done
	w.running = false
	log.Infof("Watchdog stopped")
}

// waitForStart waits for Start to be called and takes action if it does not
// happen within the startup timeout.
func (w *Watchdog) waitForStart() {
	<-time.After(w.StartupTimeout)
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.startCalled {
		// We are fine.
		return
	}

	stuckStartup.Increment()

	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("Watchdog.Start() not called within %s", w.StartupTimeout))
	w.doAction(w.StartupTimeoutAction, false, &buf)
}

// loop is the main watchdog routine. It only returns when 'Stop()' is called.
func (w *Watchdog) loop() {
	// Loop until someone stops it.
	for {
		select {
		case <-w.stop:
			w.done <- struct{}{}
			return
		case <-time.After(w.period):
			w.runTurn()
		}
	}
}

// runTurn runs a single pass over all tasks and reports anything it finds.
func (w *Watchdog) runTurn() {
	// Someone needs to watch the watchdog. The call below can get stuck if there
	// is a deadlock affecting root's PID namespace mutex. Run it in a goroutine
	// and report if it takes too long to return.
	var tasks []*kernel.Task
	done := make(chan struct{})
	go func() { // S/R-SAFE: watchdog is stopped and restarted during S/R.
		tasks = w.k.TaskSet().Root.Tasks()
		close(done)
	}()

	select {
	case <-done:
	case <-time.After(w.TaskTimeout):
		// Report if the watchdog is not making progress.
		// No one is watching the watchdog watcher though.
		w.reportStuckWatchdog()
		<-done
	}

	newOffenders := make(map[*kernel.Task]*offender)
	newTaskFound := false
	now := ktime.FromNanoseconds(int64(w.k.CPUClockNow() * uint64(linux.ClockTick)))

	// The process may be running with low CPU limit making tasks appear stuck because
	// are starved of CPU cycles. An estimate is that Tasks could have been starved
	// since the last time the watchdog run. If the watchdog detects that scheduling
	// is off, it will discount the entire duration since last run from 'lastUpdateTime'.
	discount := time.Duration(0)
	if now.Sub(w.lastRun.Add(w.period)) > descheduleThreshold {
		discount = now.Sub(w.lastRun)
	}
	w.lastRun = now

	log.Infof("Watchdog starting loop, tasks: %d, discount: %v", len(tasks), discount)
	for _, t := range tasks {
		tsched := t.TaskGoroutineSchedInfo()

		// An offender is a task running inside the kernel for longer than the specified timeout.
		if tsched.State == kernel.TaskGoroutineRunningSys {
			lastUpdateTime := ktime.FromNanoseconds(int64(tsched.Timestamp * uint64(linux.ClockTick)))
			elapsed := now.Sub(lastUpdateTime) - discount
			if elapsed > w.TaskTimeout {
				tc, ok := w.offenders[t]
				if !ok {
					// New stuck task detected.
					//
					// Note that tasks blocked doing IO may be considered stuck in kernel,
					// unless they are surrounded b
					// Task.UninterruptibleSleepStart/Finish.
					tc = &offender{lastUpdateTime: lastUpdateTime}
					stuckTasks.Increment()
					newTaskFound = true
				}
				newOffenders[t] = tc
			}
		}
	}
	if len(newOffenders) > 0 {
		w.report(newOffenders, newTaskFound, now)
	}

	// Remember which tasks have been reported.
	w.offenders = newOffenders
}

// report takes appropriate action when a stuck task is detected.
func (w *Watchdog) report(offenders map[*kernel.Task]*offender, newTaskFound bool, now ktime.Time) {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("Sentry detected %d stuck task(s):\n", len(offenders)))
	for t, o := range offenders {
		tid := w.k.TaskSet().Root.IDOfTask(t)
		buf.WriteString(fmt.Sprintf("\tTask tid: %v (%#x), entered RunSys state %v ago.\n", tid, uint64(tid), now.Sub(o.lastUpdateTime)))
	}

	buf.WriteString("Search for '(*Task).run(0x..., 0x<tid>)' in the stack dump to find the offending goroutine")

	// Force stack dump only if a new task is detected.
	w.doAction(w.TaskTimeoutAction, newTaskFound, &buf)
}

func (w *Watchdog) reportStuckWatchdog() {
	var buf bytes.Buffer
	buf.WriteString("Watchdog goroutine is stuck")
	w.doAction(w.TaskTimeoutAction, false, &buf)
}

// doAction will take the given action. If the action is LogWarning, the stack
// is not always dumped to the log to prevent log flooding. "forceStack"
// guarantees that the stack will be dumped regardless.
func (w *Watchdog) doAction(action Action, forceStack bool, msg *bytes.Buffer) {
	switch action {
	case LogWarning:
		// Dump stack only if forced or sometime has passed since the last time a
		// stack dump was generated.
		if !forceStack && time.Since(w.lastStackDump) < stackDumpSameTaskPeriod {
			msg.WriteString("\n...[stack dump skipped]...")
			log.Warningf(msg.String())
			return
		}
		log.TracebackAll(msg.String())
		w.lastStackDump = time.Now()

	case Panic:
		// Panic will skip over running tasks, which is likely the culprit here. So manually
		// dump all stacks before panic'ing.
		log.TracebackAll(msg.String())

		// Attempt to flush metrics, timeout and move on in case metrics are stuck as well.
		metricsEmitted := make(chan struct{}, 1)
		go func() { // S/R-SAFE: watchdog is stopped during save and restarted after restore.
			// Flush metrics before killing process.
			metric.EmitMetricUpdate()
			metricsEmitted <- struct{}{}
		}()
		select {
		case <-metricsEmitted:
		case <-time.After(1 * time.Second):
		}
		panic(fmt.Sprintf("%s\nStack for running G's are skipped while panicking.", msg.String()))

	default:
		panic(fmt.Sprintf("Unknown watchdog action %v", action))

	}
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/json"
	"fmt"
	"github.com/fatih/color"
	"os"
	"strings"
)

// Configuration is a stuct used to read in repos and software projects from an
// external configuration file.
type Configuration struct {
	Repos []string

	// Github entries
	Github struct {
		Token string
		User  string
	}

	// Email nested entries
	Email struct {
		Server   string
		Address  string
		Password string
	}
}

// ReadConfig is a helper function for reading in a configuration file.
func ReadConfig() *Configuration {
	homedir := os.Getenv("HOME")
	configpath := (homedir + "/.stalker.json")
	file, _ := os.Open(configpath)

	// Stop execution if config isn't found
	if _, err := os.Stat(configpath); os.IsNotExist(err) {
		fmt.Println("Config " + configpath + " not found!")
		os.Exit(0)
	}

	// Decode json config
	decoder := json.NewDecoder(file)
	configuration := Configuration{}
	err := decoder.Decode(&configuration)
	if err != nil {
		fmt.Println("error:", err)
	}
	return &configuration
}

// GetToken is a helper function for determining if a Github auth token has been
// set.
func GetToken() string {

	configuration := ReadConfig()
	token := configuration.Github.Token

	if token == "" {
		return "empty"
	}
	return token
}

// IsTokenSet is a helper function that tells you if you have a github auth token set.
func IsTokenSet() {

	configuration := ReadConfig()
	token := configuration.Github.Token

	warn := color.New(color.FgYellow).PrintFunc()
	tokenSet := color.New(color.FgGreen).PrintFunc()

	if token == "" {
		warn("GITHUB AUTH TOKEN NOT SET\n\n")
		fmt.Println("Skipping authenticaiton may create rate limiting issues")
	} else {
		tokenSet("GitHub auth token has been set\n\n")
	}
}

// PrintStarredRepos tries to print tags of repos that have been starred
// according to the "user" configuration setting that is read from the config
// file.
func PrintStarredRepos() {

	IsTokenSet()
	configuration := ReadConfig()
	username := configuration.Github.User
	userRepos := GetStarredRepos(username)

	for _, repo := range userRepos {
		repo := strings.Split(repo, "/")
		user := repo[len(repo)-2]
		project := repo[len(repo)-1]
		tag, _ := LatestTag(user, project)
		fmt.Println("User: " + user + " Project: " + project + " Tag: " + tag)
	}
}

// PrintFromConfig tries to print versions based on repos that have been read
// in from a config file.
func PrintFromConfig() {

	IsTokenSet()
	configuration := ReadConfig()

	// Split user and project in order to parse them separately
	for _, repo := range configuration.Repos {
		repo := strings.Split(repo, "/")
		if repo[0] == "github.com" {
			user := repo[len(repo)-2]
			project := repo[len(repo)-1]
			tag, _ := LatestTag(user, project)
			fmt.Println("User: " + user + " Project: " + project + " Tag: " + tag)

			// TODO
			// Notify if there is a new version and email the repo, version,
			// link to the changelog and the changelog notes
		}
	}
}
<commit_msg>Better message to explain what's happening<commit_after>package util

import (
	"encoding/json"
	"fmt"
	"github.com/fatih/color"
	"os"
	"strings"
)

// Configuration is a stuct used to read in repos and software projects from an
// external configuration file.
type Configuration struct {
	Repos []string

	// Github entries
	Github struct {
		Token string
		User  string
	}

	// Email nested entries
	Email struct {
		Server   string
		Address  string
		Password string
	}
}

// ReadConfig is a helper function for reading in a configuration file.
func ReadConfig() *Configuration {
	homedir := os.Getenv("HOME")
	configpath := (homedir + "/.stalker.json")
	file, _ := os.Open(configpath)

	// Stop execution if config isn't found
	if _, err := os.Stat(configpath); os.IsNotExist(err) {
		fmt.Println("Config " + configpath + " not found!")
		os.Exit(0)
	}

	// Decode json config
	decoder := json.NewDecoder(file)
	configuration := Configuration{}
	err := decoder.Decode(&configuration)
	if err != nil {
		fmt.Println("error:", err)
	}
	return &configuration
}

// GetToken is a helper function for determining if a Github auth token has been
// set.
func GetToken() string {

	configuration := ReadConfig()
	token := configuration.Github.Token

	if token == "" {
		return "empty"
	}
	return token
}

// IsTokenSet is a helper function that tells you if you have a github auth token set.
func IsTokenSet() {

	configuration := ReadConfig()
	token := configuration.Github.Token

	warn := color.New(color.FgYellow).PrintFunc()
	tokenSet := color.New(color.FgGreen).PrintFunc()

	if token == "" {
		warn("GITHUB AUTH TOKEN NOT SET\n\n")
		fmt.Println("Skipping authenticaiton may create rate limiting issues")
	} else {
		tokenSet("GitHub auth token has been set\n\n")
	}
	fmt.Println("Printing repos with recently updated tags\n")
}

// PrintStarredRepos tries to print tags of repos that have been starred
// according to the "user" configuration setting that is read from the config
// file.
func PrintStarredRepos() {

	IsTokenSet()
	configuration := ReadConfig()
	username := configuration.Github.User
	userRepos := GetStarredRepos(username)

	for _, repo := range userRepos {
		repo := strings.Split(repo, "/")
		user := repo[len(repo)-2]
		project := repo[len(repo)-1]
		tag, _ := LatestTag(user, project)
		fmt.Println("User: " + user + " Project: " + project + " Tag: " + tag)
	}
}

// PrintFromConfig tries to print versions based on repos that have been read
// in from a config file.
func PrintFromConfig() {

	IsTokenSet()
	configuration := ReadConfig()

	// Split user and project in order to parse them separately
	for _, repo := range configuration.Repos {
		repo := strings.Split(repo, "/")
		if repo[0] == "github.com" {
			user := repo[len(repo)-2]
			project := repo[len(repo)-1]
			tag, _ := LatestTag(user, project)
			fmt.Println("User: " + user + " Project: " + project + " Tag: " + tag)

			// TODO
			// Notify if there is a new version and email the repo, version,
			// link to the changelog and the changelog notes
		}
	}
}
<|endoftext|>


<commit_before>package detour

import (
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"testing"
	"time"

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

var (
	directMsg string = "hello direct"
	detourMsg string = "hello detour"
	iranResp  string = `HTTP/1.1 403 Forbidden
Connection:close

<meta http-equiv="Content-Type" content="text/html; charset=windows-1256"><title>M1-6
</title>Connection closed by foreign host.`
)

func proxyTo(proxiedURL string) func(network, addr string) (net.Conn, error) {
	return func(network, addr string) (net.Conn, error) {
		u, _ := url.Parse(proxiedURL)
		return net.Dial("tcp", u.Host)
	}
}

func TestBlockedImmediately(t *testing.T) {
	defer stopMockServers()
	proxiedURL, _ := newMockServer(detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, mock := newMockServer(directMsg)

	client := &http.Client{Timeout: 50 * time.Millisecond}
	mock.Timeout(200*time.Millisecond, directMsg)
	resp, err := client.Get(mockURL)
	assert.Error(t, err, "direct access to a timeout url should fail")

	client = newClient(proxiedURL, 100*time.Millisecond)
	resp, err = client.Get("http://255.0.0.1") // it's reserved for future use so will always time out
	if assert.NoError(t, err, "should have no error if dialing times out") {
		assert.True(t, wlTemporarily("255.0.0.1:80"), "should be added to whitelist if dialing times out")
		assertContent(t, resp, detourMsg, "should detour if dialing times out")
	}

	client = newClient(proxiedURL, 100*time.Millisecond)
	resp, err = client.Get("http://127.0.0.1:4325") // hopefully this port didn't open, so connection will be refused
	if assert.NoError(t, err, "should have no error if connection is refused") {
		assert.True(t, wlTemporarily("127.0.0.1:4325"), "should be added to whitelist if connection is refused")
		assertContent(t, resp, detourMsg, "should detour if connection is refused")
	}

	u, _ := url.Parse(mockURL)
	resp, err = client.Get(mockURL)
	if assert.NoError(t, err, "should have no error if reading times out") {
		assert.True(t, wlTemporarily(u.Host), "should be added to whitelist if reading times out")
		assertContent(t, resp, detourMsg, "should detour if reading times out")
	}

	client = newClient(proxiedURL, 100*time.Millisecond)
	RemoveFromWl(u.Host)
	resp, err = client.PostForm(mockURL, url.Values{"key": []string{"value"}})
	if assert.Error(t, err, "Non-idempotent method should not be detoured in same connection") {
		assert.True(t, wlTemporarily(u.Host), "but should be added to whitelist so will detour next time")
	}
}

func TestBlockedAfterwards(t *testing.T) {
	defer stopMockServers()
	proxiedURL, _ := newMockServer(detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, mock := newMockServer(directMsg)
	client := newClient(proxiedURL, 100*time.Millisecond)

	mock.Msg(directMsg)
	resp, err := client.Get(mockURL)
	if assert.NoError(t, err, "should have no error for normal response") {
		assertContent(t, resp, directMsg, "should access directly for normal response")
	}
	mock.Timeout(200*time.Millisecond, directMsg)
	_, err = client.Get(mockURL)
	assert.Error(t, err, "should have error if reading times out for a previously worked url")
	resp, err = client.Get(mockURL)
	if assert.NoError(t, err, "but should have no error for the second time") {
		u, _ := url.Parse(mockURL)
		assert.True(t, wlTemporarily(u.Host), "should be added to whitelist if reading times out")
		assertContent(t, resp, detourMsg, "should detour if reading times out")
	}
}

func TestRemoveFromWhitelist(t *testing.T) {
	defer stopMockServers()
	proxiedURL, proxy := newMockServer(detourMsg)
	proxy.Timeout(200*time.Millisecond, detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, _ := newMockServer(directMsg)
	client := newClient(proxiedURL, 100*time.Millisecond)

	u, _ := url.Parse(mockURL)
	AddToWl(u.Host, false)
	_, err := client.Get(mockURL)
	if assert.Error(t, err, "should have error if reading times out through detour") {
		time.Sleep(50 * time.Millisecond)
		assert.False(t, whitelisted(u.Host), "should be removed from whitelist if reading times out through detour")
	}

}

func TestClosing(t *testing.T) {
	defer stopMockServers()
	proxiedURL, proxy := newMockServer(detourMsg)
	proxy.Timeout(200*time.Millisecond, detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, mock := newMockServer(directMsg)
	mock.Msg(directMsg)
	DirectAddrCh = make(chan string)
	{
		if _, err := newClient(proxiedURL, 100*time.Millisecond).Get(mockURL); err != nil {
			log.Debugf("Unable to send GET request to mock URL: %v", err)
		}
	}
	u, _ := url.Parse(mockURL)
	addr := <-DirectAddrCh
	assert.Equal(t, u.Host, addr, "should get notified when a direct connetion has no error while closing")
}

func TestIranRules(t *testing.T) {
	defer stopMockServers()
	proxiedURL, _ := newMockServer(detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	SetCountry("IR")
	u, mock := newMockServer(directMsg)
	client := newClient(proxiedURL, 100*time.Millisecond)

	mock.Raw(iranResp)
	resp, err := client.Get(u)
	if assert.NoError(t, err, "should not error if content hijacked in Iran") {
		assertContent(t, resp, detourMsg, "should detour if content hijacked in Iran")
	}

	// this test can verifies dns hijack detection if runs inside Iran,
	// but only will time out and detour if runs outside Iran
	resp, err = client.Get("http://" + iranRedirectAddr)
	if assert.NoError(t, err, "should not error if dns hijacked in Iran") {
		assertContent(t, resp, detourMsg, "should detour if dns hijacked in Iran")
	}
}

func newClient(proxyURL string, timeout time.Duration) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Dial: Dialer(proxyTo(proxyURL))},
		Timeout: timeout,
	}
}
func assertContent(t *testing.T, resp *http.Response, msg string, reason string) {
	b, err := ioutil.ReadAll(resp.Body)
	assert.NoError(t, err, reason)
	assert.Equal(t, msg, string(b), reason)
}
<commit_msg>Leaving more time for site to leave whitelist<commit_after>package detour

import (
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"testing"
	"time"

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

var (
	directMsg string = "hello direct"
	detourMsg string = "hello detour"
	iranResp  string = `HTTP/1.1 403 Forbidden
Connection:close

<meta http-equiv="Content-Type" content="text/html; charset=windows-1256"><title>M1-6
</title>Connection closed by foreign host.`
)

func proxyTo(proxiedURL string) func(network, addr string) (net.Conn, error) {
	return func(network, addr string) (net.Conn, error) {
		u, _ := url.Parse(proxiedURL)
		return net.Dial("tcp", u.Host)
	}
}

func TestBlockedImmediately(t *testing.T) {
	defer stopMockServers()
	proxiedURL, _ := newMockServer(detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, mock := newMockServer(directMsg)

	client := &http.Client{Timeout: 50 * time.Millisecond}
	mock.Timeout(200*time.Millisecond, directMsg)
	resp, err := client.Get(mockURL)
	assert.Error(t, err, "direct access to a timeout url should fail")

	client = newClient(proxiedURL, 100*time.Millisecond)
	resp, err = client.Get("http://255.0.0.1") // it's reserved for future use so will always time out
	if assert.NoError(t, err, "should have no error if dialing times out") {
		assert.True(t, wlTemporarily("255.0.0.1:80"), "should be added to whitelist if dialing times out")
		assertContent(t, resp, detourMsg, "should detour if dialing times out")
	}

	client = newClient(proxiedURL, 100*time.Millisecond)
	resp, err = client.Get("http://127.0.0.1:4325") // hopefully this port didn't open, so connection will be refused
	if assert.NoError(t, err, "should have no error if connection is refused") {
		assert.True(t, wlTemporarily("127.0.0.1:4325"), "should be added to whitelist if connection is refused")
		assertContent(t, resp, detourMsg, "should detour if connection is refused")
	}

	u, _ := url.Parse(mockURL)
	resp, err = client.Get(mockURL)
	if assert.NoError(t, err, "should have no error if reading times out") {
		assert.True(t, wlTemporarily(u.Host), "should be added to whitelist if reading times out")
		assertContent(t, resp, detourMsg, "should detour if reading times out")
	}

	client = newClient(proxiedURL, 100*time.Millisecond)
	RemoveFromWl(u.Host)
	resp, err = client.PostForm(mockURL, url.Values{"key": []string{"value"}})
	if assert.Error(t, err, "Non-idempotent method should not be detoured in same connection") {
		assert.True(t, wlTemporarily(u.Host), "but should be added to whitelist so will detour next time")
	}
}

func TestBlockedAfterwards(t *testing.T) {
	defer stopMockServers()
	proxiedURL, _ := newMockServer(detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, mock := newMockServer(directMsg)
	client := newClient(proxiedURL, 100*time.Millisecond)

	mock.Msg(directMsg)
	resp, err := client.Get(mockURL)
	if assert.NoError(t, err, "should have no error for normal response") {
		assertContent(t, resp, directMsg, "should access directly for normal response")
	}
	mock.Timeout(200*time.Millisecond, directMsg)
	_, err = client.Get(mockURL)
	assert.Error(t, err, "should have error if reading times out for a previously worked url")
	resp, err = client.Get(mockURL)
	if assert.NoError(t, err, "but should have no error for the second time") {
		u, _ := url.Parse(mockURL)
		assert.True(t, wlTemporarily(u.Host), "should be added to whitelist if reading times out")
		assertContent(t, resp, detourMsg, "should detour if reading times out")
	}
}

func TestRemoveFromWhitelist(t *testing.T) {
	defer stopMockServers()
	proxiedURL, proxy := newMockServer(detourMsg)
	proxy.Timeout(200*time.Millisecond, detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, _ := newMockServer(directMsg)
	client := newClient(proxiedURL, 100*time.Millisecond)

	u, _ := url.Parse(mockURL)
	AddToWl(u.Host, false)
	_, err := client.Get(mockURL)
	if assert.Error(t, err, "should have error if reading times out through detour") {
		time.Sleep(250 * time.Millisecond)
		assert.False(t, whitelisted(u.Host), "should be removed from whitelist if reading times out through detour")
	}

}

func TestClosing(t *testing.T) {
	defer stopMockServers()
	proxiedURL, proxy := newMockServer(detourMsg)
	proxy.Timeout(200*time.Millisecond, detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	mockURL, mock := newMockServer(directMsg)
	mock.Msg(directMsg)
	DirectAddrCh = make(chan string)
	{
		if _, err := newClient(proxiedURL, 100*time.Millisecond).Get(mockURL); err != nil {
			log.Debugf("Unable to send GET request to mock URL: %v", err)
		}
	}
	u, _ := url.Parse(mockURL)
	addr := <-DirectAddrCh
	assert.Equal(t, u.Host, addr, "should get notified when a direct connetion has no error while closing")
}

func TestIranRules(t *testing.T) {
	defer stopMockServers()
	proxiedURL, _ := newMockServer(detourMsg)
	TimeoutToDetour = 50 * time.Millisecond
	SetCountry("IR")
	u, mock := newMockServer(directMsg)
	client := newClient(proxiedURL, 100*time.Millisecond)

	mock.Raw(iranResp)
	resp, err := client.Get(u)
	if assert.NoError(t, err, "should not error if content hijacked in Iran") {
		assertContent(t, resp, detourMsg, "should detour if content hijacked in Iran")
	}

	// this test can verifies dns hijack detection if runs inside Iran,
	// but only will time out and detour if runs outside Iran
	resp, err = client.Get("http://" + iranRedirectAddr)
	if assert.NoError(t, err, "should not error if dns hijacked in Iran") {
		assertContent(t, resp, detourMsg, "should detour if dns hijacked in Iran")
	}
}

func newClient(proxyURL string, timeout time.Duration) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Dial: Dialer(proxyTo(proxyURL))},
		Timeout: timeout,
	}
}
func assertContent(t *testing.T, resp *http.Response, msg string, reason string) {
	b, err := ioutil.ReadAll(resp.Body)
	assert.NoError(t, err, reason)
	assert.Equal(t, msg, string(b), reason)
}
<|endoftext|>


<commit_before>package sdm630_test

import (
	"github.com/gonium/gosdm630"
	"testing"
	"time"
)

func TestQuerySnipMerge(t *testing.T) {
	r := sdm630.Readings{
		Timestamp:      time.Now(),
		Unix:           time.Now().Unix(),
		ModbusDeviceId: 1,
		UniqueId:       "Instrument1",
		Power: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(1.0), L2: sdm630.F2fp(2.0), L3: sdm630.F2fp(3.0),
		},
		Voltage: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(1.0), L2: sdm630.F2fp(2.0), L3: sdm630.F2fp(3.0),
		},
		Current: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(4.0), L2: sdm630.F2fp(5.0), L3: sdm630.F2fp(6.0),
		},
		Cosphi: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(7.0), L2: sdm630.F2fp(8.0), L3: sdm630.F2fp(9.0),
		},
		Import: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(10.0), L2: sdm630.F2fp(11.0), L3: sdm630.F2fp(12.0),
		},
		Export: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(13.0), L2: sdm630.F2fp(14.0), L3: sdm630.F2fp(15.0),
		},
	}

	setvalue := float64(230.0)
	var sniptests = []struct {
		snip  sdm630.QuerySnip
		param func(sdm630.Readings) float64
	}{
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Voltage,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Voltage.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Voltage,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Voltage.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Voltage,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Voltage.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Current,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Current.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Current,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Current.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Current,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Current.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Power,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Power.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Power,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Power.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Power,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Power.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Cosphi,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Cosphi.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Cosphi,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Cosphi.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Cosphi,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Cosphi.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Import,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Import.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Import,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Import.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Import,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Import.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Export,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Export.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Export,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Export.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Export,
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Export.L3) },
		},
	}

	for _, test := range sniptests {
		r.MergeSnip(test.snip)
		if test.param(r) != setvalue {
			t.Errorf("Merge of querysnip failed: Expected %.2f, got %.2f",
				setvalue, test.param(r))
		}
	}
}
<commit_msg>fixed IEC61850 designation in testsuite<commit_after>package sdm630_test

import (
	"github.com/gonium/gosdm630"
	"testing"
	"time"
)

func TestQuerySnipMerge(t *testing.T) {
	r := sdm630.Readings{
		Timestamp:      time.Now(),
		Unix:           time.Now().Unix(),
		ModbusDeviceId: 1,
		UniqueId:       "Instrument1",
		Power: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(1.0), L2: sdm630.F2fp(2.0), L3: sdm630.F2fp(3.0),
		},
		Voltage: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(1.0), L2: sdm630.F2fp(2.0), L3: sdm630.F2fp(3.0),
		},
		Current: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(4.0), L2: sdm630.F2fp(5.0), L3: sdm630.F2fp(6.0),
		},
		Cosphi: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(7.0), L2: sdm630.F2fp(8.0), L3: sdm630.F2fp(9.0),
		},
		Import: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(10.0), L2: sdm630.F2fp(11.0), L3: sdm630.F2fp(12.0),
		},
		Export: sdm630.ThreePhaseReadings{
			L1: sdm630.F2fp(13.0), L2: sdm630.F2fp(14.0), L3: sdm630.F2fp(15.0),
		},
	}

	setvalue := float64(230.0)
	var sniptests = []struct {
		snip  sdm630.QuerySnip
		param func(sdm630.Readings) float64
	}{
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Voltage,
			IEC61850: "VolLocPhsA", Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Voltage.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Voltage, IEC61850: "VolLocPhsB",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Voltage.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Voltage, IEC61850: "VolLocPhsC",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Voltage.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Current, IEC61850: "AmpLocPhsA",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Current.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Current, IEC61850: "AmpLocPhsB",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Current.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Current, IEC61850: "AmpLocPhsC",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Current.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Power, IEC61850: "WLocPhsA",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Power.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Power, IEC61850: "WLocPhsB",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Power.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Power, IEC61850: "WLocPhsC",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Power.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Cosphi, IEC61850: "AngLocPhsA",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Cosphi.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Cosphi, IEC61850: "AngLocPhsB",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Cosphi.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Cosphi, IEC61850: "AngLocPhsC",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Cosphi.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Import, IEC61850: "TotkWhImportPhsA",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Import.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Import, IEC61850: "TotkWhImportPhsB",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Import.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Import, IEC61850: "TotkWhImportPhsC",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Import.L3) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML1Export, IEC61850: "TotkWhExportPhsA",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Export.L1) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML2Export, IEC61850: "TotkWhExportPhsB",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Export.L2) },
		},
		{sdm630.QuerySnip{DeviceId: 1, OpCode: sdm630.OpCodeSDML3Export, IEC61850: "TotkWhExportPhsC",
			Value: setvalue},
			func(r sdm630.Readings) float64 { return sdm630.Fp2f(r.Export.L3) },
		},
	}

	for _, test := range sniptests {
		r.MergeSnip(test.snip)
		if test.param(r) != setvalue {
			t.Errorf("Merge of querysnip failed: Expected %.2f, got %.2f",
				setvalue, test.param(r))
		}
	}
}
<|endoftext|>


<commit_before>package app

import (
	"html/template"
	"net/http"
	"net/url"

	"appengine"
	"appengine/datastore"
)

const feedPageRaw = `

{{.Title | html}}

{{range .Items}}

{{.Title | html}}

{{end}}

`

func showFeed(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	url, err := url.QueryUnescape(r.URL.RawQuery)
	if err != nil {
		handleError(w, err)
		return
	}
	feedRoot := datastore.NewKey(c, "feedRoot", "feedRoot", 0, nil)
	fk := datastore.NewKey(c, "feed", url, 0, feedRoot)
	f := new(RSS)
	err = datastore.Get(c, fk, f)
	if err != nil {
		handleError(w, err)
		return
	}
	_, err = datastore.NewQuery("item").Ancestor(fk).Order("-PubDate").GetAll(c, &f.Items)
	if err != nil {
		handleError(w, err)
		return
	}
	templ, err := template.New("showFeed").Parse(feedPageRaw)
	if err != nil {
		handleError(w, err)
		return
	}
	err = templ.Execute(w, f)
	if err != nil {
		handleError(w, err)
		return
	}
}
<commit_msg>Home link from feed display page.<commit_after>package app

import (
	"html/template"
	"net/http"
	"net/url"

	"appengine"
	"appengine/datastore"
)

const feedPageRaw = `

Home

{{.Title | html}}

{{range .Items}}

{{.Title | html}}

{{end}}

`

func showFeed(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	url, err := url.QueryUnescape(r.URL.RawQuery)
	if err != nil {
		handleError(w, err)
		return
	}
	feedRoot := datastore.NewKey(c, "feedRoot", "feedRoot", 0, nil)
	fk := datastore.NewKey(c, "feed", url, 0, feedRoot)
	f := new(RSS)
	err = datastore.Get(c, fk, f)
	if err != nil {
		handleError(w, err)
		return
	}
	_, err = datastore.NewQuery("item").Ancestor(fk).Order("-PubDate").GetAll(c, &f.Items)
	if err != nil {
		handleError(w, err)
		return
	}
	templ, err := template.New("showFeed").Parse(feedPageRaw)
	if err != nil {
		handleError(w, err)
		return
	}
	err = templ.Execute(w, f)
	if err != nil {
		handleError(w, err)
		return
	}
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"strings"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/jobspec"
	"github.com/hashicorp/nomad/scheduler"
	"github.com/mitchellh/colorstring"
)

const (
	casHelp = `To submit the job with version verification run:

nomad run -verify %d %s

When running the job with the verify flag, the job will only be run if the server side
version matches the the verify index returned. If the index has changed, another user has
modified the job and the plan's results are potentially invalid.`
)

type PlanCommand struct {
	Meta
	color *colorstring.Colorize
}

func (c *PlanCommand) Help() string {
	helpText := `
Usage: nomad plan [options] <file>

General Options:

  ` + generalOptionsUsage() + `

Run Options:

  -diff
    Defaults to true, but can be toggled off to omit diff output.

  -no-color
    Disable colored output.

  -verbose
    Increased diff verbosity

`
	return strings.TrimSpace(helpText)
}

func (c *PlanCommand) Synopsis() string {
	return "Dry-run a job update to determine its effects"
}

func (c *PlanCommand) Run(args []string) int {
	var diff, verbose bool

	flags := c.Meta.FlagSet("plan", FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&diff, "diff", true, "")
	flags.BoolVar(&verbose, "verbose", false, "")

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

	// Check that we got exactly one job
	args = flags.Args()
	if len(args) != 1 {
		c.Ui.Error(c.Help())
		return 1
	}
	file := args[0]

	// Parse the job file
	job, err := jobspec.ParseFile(file)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing job file %s: %s", file, err))
		return 1
	}

	// Initialize any fields that need to be.
	job.InitFields()

	// Check that the job is valid
	if err := job.Validate(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error validating job: %s", err))
		return 1
	}

	// Convert it to something we can use
	apiJob, err := convertStructJob(job)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Submit the job
	resp, _, err := client.Jobs().Plan(apiJob, diff, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error during plan: %s", err))
		return 1
	}

	if diff {
		c.Ui.Output(fmt.Sprintf("%s\n",
			c.Colorize().Color(strings.TrimSpace(formatJobDiff(resp.Diff, verbose)))))
	}

	c.Ui.Output(c.Colorize().Color("[bold]Scheduler dry-run:[reset]"))
	c.Ui.Output(c.Colorize().Color(formatDryRun(resp.CreatedEvals)))

	c.Ui.Output(c.Colorize().Color(formatCas(resp.Cas, file)))
	return 0
}

func formatCas(cas uint64, jobName string) string {
	help := fmt.Sprintf(casHelp, cas, jobName)
	out := fmt.Sprintf("[reset][bold]Job Verify Index: %d[reset]\n%s", cas, help)
	return out
}

func formatDryRun(evals []*api.Evaluation) string {
	// "- All tasks successfully allocated." bold and green

	var rolling *api.Evaluation
	var blocked *api.Evaluation
	for _, eval := range evals {
		if eval.TriggeredBy == "rolling-update" {
			rolling = eval
		} else if eval.Status == "blocked" {
			blocked = eval
		}
	}

	var out string
	if blocked == nil {
		out = "[bold][green]  - All tasks successfully allocated.[reset]\n"
	} else {
		out = "[bold][yellow]  - WARNING: Failed to place all allocations.[reset]\n"
	}

	if rolling != nil {
		out += fmt.Sprintf("[green]  - Rolling update, next evaluation will be in %s.\n", rolling.Wait)
	}

	return out
}

func formatJobDiff(job *api.JobDiff, verbose bool) string {
	out := fmt.Sprintf("%s[bold]Job: %q\n", getDiffString(job.Type), job.ID)

	if job.Type == "Edited" || verbose {
		for _, field := range job.Fields {
			out += fmt.Sprintf("%s\n", formatFieldDiff(field, "", verbose))
		}

		for _, object := range job.Objects {
			out += fmt.Sprintf("%s\n", formatObjectDiff(object, "", verbose))
		}
	}

	for _, tg := range job.TaskGroups {
		out += fmt.Sprintf("%s\n", formatTaskGroupDiff(tg, verbose))
	}

	return out
}

func formatTaskGroupDiff(tg *api.TaskGroupDiff, verbose bool) string {
	out := fmt.Sprintf("%s[bold]Task Group: %q", getDiffString(tg.Type), tg.Name)

	// Append the updates
	if l := len(tg.Updates); l > 0 {
		updates := make([]string, 0, l)
		for updateType, count := range tg.Updates {
			var color string
			switch updateType {
			case scheduler.UpdateTypeIgnore:
			case scheduler.UpdateTypeCreate:
				color = "[green]"
			case scheduler.UpdateTypeDestroy:
				color = "[red]"
			case scheduler.UpdateTypeMigrate:
				color = "[blue]"
			case scheduler.UpdateTypeInplaceUpdate:
				color = "[cyan]"
			case scheduler.UpdateTypeDestructiveUpdate:
				color = "[yellow]"
			}
			updates = append(updates, fmt.Sprintf("[reset]%s%d %s", color, count, updateType))
		}
		out += fmt.Sprintf(" (%s[reset])\n", strings.Join(updates, ", "))
	} else {
		out += "[reset]\n"
	}

	if tg.Type == "Edited" || verbose {
		for _, field := range tg.Fields {
			out += fmt.Sprintf("%s\n", formatFieldDiff(field, "  ", verbose))
		}

		for _, object := range tg.Objects {
			out += fmt.Sprintf("%s\n", formatObjectDiff(object, "  ", verbose))
		}
	}

	for _, task := range tg.Tasks {
		out += fmt.Sprintf("%s\n", formatTaskDiff(task, verbose))
	}

	return out
}

func formatTaskDiff(task *api.TaskDiff, verbose bool) string {
	out := fmt.Sprintf("  %s[bold]Task: %q", getDiffString(task.Type), task.Name)
	if len(task.Annotations) != 0 {
		out += fmt.Sprintf(" [reset](%s)", colorAnnotations(task.Annotations))
	}

	if task.Type == "None" {
		return out
	} else if (task.Type == "Deleted" || task.Type == "Added") && !verbose {
		return out
	} else {
		out += "\n"
	}

	for _, field := range task.Fields {
		out += fmt.Sprintf("%s\n", formatFieldDiff(field, "    ", verbose))
	}

	for _, object := range task.Objects {
		out += fmt.Sprintf("%s\n", formatObjectDiff(object, "    ", verbose))
	}

	return out
}

func formatFieldDiff(diff *api.FieldDiff, prefix string, verbose bool) string {
	out := prefix
	switch diff.Type {
	case "Added":
		out += fmt.Sprintf("%s%s: %q", getDiffString(diff.Type), diff.Name, diff.New)
	case "Deleted":
		out += fmt.Sprintf("%s%s: %q", getDiffString(diff.Type), diff.Name, diff.Old)
	case "Edited":
		out += fmt.Sprintf("%s%s: %q => %q", getDiffString(diff.Type), diff.Name, diff.Old, diff.New)
	default:
		out += fmt.Sprintf("%s: %q", diff.Name, diff.New)
	}

	// Color the annotations where possible
	if l := len(diff.Annotations); l != 0 {
		out += fmt.Sprintf(" (%s)", colorAnnotations(diff.Annotations))
	}

	return out
}

func colorAnnotations(annotations []string) string {
	l := len(annotations)
	if l == 0 {
		return ""
	}

	colored := make([]string, l)
	for i, annotation := range annotations {
		switch annotation {
		case "forces create":
			colored[i] = fmt.Sprintf("[green]%s[reset]", annotation)
		case "forces destroy":
			colored[i] = fmt.Sprintf("[red]%s[reset]", annotation)
		case "forces in-place update":
			colored[i] = fmt.Sprintf("[cyan]%s[reset]", annotation)
		case "forces create/destroy update":
			colored[i] = fmt.Sprintf("[yellow]%s[reset]", annotation)
		default:
			colored[i] = annotation
		}
	}

	return strings.Join(colored, ", ")
}

func formatObjectDiff(diff *api.ObjectDiff, prefix string, verbose bool) string {
	out := fmt.Sprintf("%s%s%s {\n", prefix, getDiffString(diff.Type), diff.Name)

	newPrefix := prefix + "  "
	numFields := len(diff.Fields)
	numObjects := len(diff.Objects)
	haveObjects := numObjects != 0
	for i, field := range diff.Fields {
		out += formatFieldDiff(field, newPrefix, verbose)
		if i+1 != numFields || haveObjects {
			out += "\n"
		}
	}

	for i, object := range diff.Objects {
		out += formatObjectDiff(object, newPrefix, verbose)
		if i+1 != numObjects {
			out += "\n"
		}
	}

	return fmt.Sprintf("%s\n%s}", out, prefix)
}

func getDiffString(diffType string) string {
	switch diffType {
	case "Added":
		return "[green]+[reset] "
	case "Deleted":
		return "[red]-[reset] "
	case "Edited":
		return "[light_yellow]+/-[reset] "
	default:
		return ""
	}
}
<commit_msg>Alligned properly<commit_after>package command

import (
	"fmt"
	"strings"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/jobspec"
	"github.com/hashicorp/nomad/scheduler"
	"github.com/mitchellh/colorstring"
)

const (
	jobModifyIndexHelp = `To submit the job with version verification run:

nomad run -verify %d %s

When running the job with the verify flag, the job will only be run if the
server side version matches the the job modify index returned. If the index has
changed, another user has modified the job and the plan's results are
potentially invalid.`
)

type PlanCommand struct {
	Meta
	color *colorstring.Colorize
}

func (c *PlanCommand) Help() string {
	helpText := `
Usage: nomad plan [options] <file>

General Options:

  ` + generalOptionsUsage() + `

Run Options:

  -diff
    Defaults to true, but can be toggled off to omit diff output.

  -no-color
    Disable colored output.

  -verbose
    Increased diff verbosity

`
	return strings.TrimSpace(helpText)
}

func (c *PlanCommand) Synopsis() string {
	return "Dry-run a job update to determine its effects"
}

func (c *PlanCommand) Run(args []string) int {
	var diff, verbose bool

	flags := c.Meta.FlagSet("plan", FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.BoolVar(&diff, "diff", true, "")
	flags.BoolVar(&verbose, "verbose", false, "")

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

	// Check that we got exactly one job
	args = flags.Args()
	if len(args) != 1 {
		c.Ui.Error(c.Help())
		return 1
	}
	file := args[0]

	// Parse the job file
	job, err := jobspec.ParseFile(file)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error parsing job file %s: %s", file, err))
		return 1
	}

	// Initialize any fields that need to be.
	job.InitFields()

	// Check that the job is valid
	if err := job.Validate(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error validating job: %s", err))
		return 1
	}

	// Convert it to something we can use
	apiJob, err := convertStructJob(job)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error converting job: %s", err))
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Submit the job
	resp, _, err := client.Jobs().Plan(apiJob, diff, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error during plan: %s", err))
		return 1
	}

	if diff {
		c.Ui.Output(fmt.Sprintf("%s\n",
			c.Colorize().Color(strings.TrimSpace(formatJobDiff(resp.Diff, verbose)))))
	}

	c.Ui.Output(c.Colorize().Color("[bold]Scheduler dry-run:[reset]"))
	c.Ui.Output(c.Colorize().Color(formatDryRun(resp.CreatedEvals)))

	c.Ui.Output(c.Colorize().Color(formatJobModifyIndex(resp.JobModifyIndex, file)))
	return 0
}

func formatJobModifyIndex(jobModifyIndex uint64, jobName string) string {
	help := fmt.Sprintf(jobModifyIndexHelp, jobModifyIndex, jobName)
	out := fmt.Sprintf("[reset][bold]Job Modify Index: %d[reset]\n%s", jobModifyIndex, help)
	return out
}

func formatDryRun(evals []*api.Evaluation) string {
	// "- All tasks successfully allocated." bold and green

	var rolling *api.Evaluation
	var blocked *api.Evaluation
	for _, eval := range evals {
		if eval.TriggeredBy == "rolling-update" {
			rolling = eval
		} else if eval.Status == "blocked" {
			blocked = eval
		}
	}

	var out string
	if blocked == nil {
		out = "[bold][green]  - All tasks successfully allocated.[reset]\n"
	} else {
		out = "[bold][yellow]  - WARNING: Failed to place all allocations.[reset]\n"
	}

	if rolling != nil {
		out += fmt.Sprintf("[green]  - Rolling update, next evaluation will be in %s.\n", rolling.Wait)
	}

	return out
}

func formatJobDiff(job *api.JobDiff, verbose bool) string {
	marker, _ := getDiffString(job.Type)
	out := fmt.Sprintf("%s[bold]Job: %q\n", marker, job.ID)

	longestField, longestMarker := getLongestPrefixes(job.Fields, job.Objects)
	for _, tg := range job.TaskGroups {
		if _, l := getDiffString(tg.Type); l > longestMarker {
			longestMarker = l
		}
	}

	subStartPrefix := ""
	if job.Type == "Edited" || verbose {
		for _, field := range job.Fields {
			_, mLength := getDiffString(field.Type)
			kPrefix := longestMarker - mLength
			vPrefix := longestField - len(field.Name)
			out += fmt.Sprintf("%s\n", formatFieldDiff(
				field,
				subStartPrefix,
				strings.Repeat(" ", kPrefix),
				strings.Repeat(" ", vPrefix)))
		}

		for _, object := range job.Objects {
			_, mLength := getDiffString(object.Type)
			kPrefix := longestMarker - mLength
			out += fmt.Sprintf("%s\n", formatObjectDiff(
				object,
				subStartPrefix,
				strings.Repeat(" ", kPrefix)))
		}
	}

	for _, tg := range job.TaskGroups {
		_, mLength := getDiffString(tg.Type)
		kPrefix := longestMarker - mLength
		out += fmt.Sprintf("%s\n", formatTaskGroupDiff(tg, strings.Repeat(" ", kPrefix), verbose))
	}

	return out
}

func formatTaskGroupDiff(tg *api.TaskGroupDiff, tgPrefix string, verbose bool) string {
	marker, _ := getDiffString(tg.Type)
	out := fmt.Sprintf("%s%s[bold]Task Group: %q[reset]", marker, tgPrefix, tg.Name)

	// Append the updates
	if l := len(tg.Updates); l > 0 {
		updates := make([]string, 0, l)
		for updateType, count := range tg.Updates {
			var color string
			switch updateType {
			case scheduler.UpdateTypeIgnore:
			case scheduler.UpdateTypeCreate:
				color = "[green]"
			case scheduler.UpdateTypeDestroy:
				color = "[red]"
			case scheduler.UpdateTypeMigrate:
				color = "[blue]"
			case scheduler.UpdateTypeInplaceUpdate:
				color = "[cyan]"
			case scheduler.UpdateTypeDestructiveUpdate:
				color = "[yellow]"
			}
			updates = append(updates, fmt.Sprintf("[reset]%s%d %s", color, count, updateType))
		}
		out += fmt.Sprintf(" (%s[reset])\n", strings.Join(updates, ", "))
	} else {
		out += "[reset]\n"
	}

	longestField, longestMarker := getLongestPrefixes(tg.Fields, tg.Objects)
	for _, task := range tg.Tasks {
		if _, l := getDiffString(task.Type); l > longestMarker {
			longestMarker = l
		}
	}

	subStartPrefix := strings.Repeat(" ", len(tgPrefix)+2)
	if tg.Type == "Edited" || verbose {
		for _, field := range tg.Fields {
			_, mLength := getDiffString(field.Type)
			kPrefix := longestMarker - mLength
			vPrefix := longestField - len(field.Name)
			out += fmt.Sprintf("%s\n", formatFieldDiff(
				field,
				subStartPrefix,
				strings.Repeat(" ", kPrefix),
				strings.Repeat(" ", vPrefix)))
		}

		for _, object := range tg.Objects {
			_, mLength := getDiffString(object.Type)
			kPrefix := longestMarker - mLength
			out += fmt.Sprintf("%s\n", formatObjectDiff(
				object,
				subStartPrefix,
				strings.Repeat(" ", kPrefix)))
		}
	}

	for _, task := range tg.Tasks {
		_, mLength := getDiffString(task.Type)
		prefix := strings.Repeat(" ", (longestMarker - mLength))
		out += fmt.Sprintf("%s\n", formatTaskDiff(task, subStartPrefix, prefix, verbose))
	}

	return out
}

func formatTaskDiff(task *api.TaskDiff, startPrefix, taskPrefix string, verbose bool) string {
	marker, _ := getDiffString(task.Type)
	out := fmt.Sprintf("%s%s%s[bold]Task: %q", startPrefix, marker, taskPrefix, task.Name)
	if len(task.Annotations) != 0 {
		out += fmt.Sprintf(" [reset](%s)", colorAnnotations(task.Annotations))
	}

	if task.Type == "None" {
		return out
	} else if (task.Type == "Deleted" || task.Type == "Added") && !verbose {
		return out
	} else {
		out += "\n"
	}

	subStartPrefix := strings.Repeat(" ", len(startPrefix)+2)
	longestField, longestMarker := getLongestPrefixes(task.Fields, task.Objects)
	for _, field := range task.Fields {
		_, mLength := getDiffString(field.Type)
		kPrefix := longestMarker - mLength
		vPrefix := longestField - len(field.Name)
		out += fmt.Sprintf("%s\n", formatFieldDiff(
			field,
			subStartPrefix,
			strings.Repeat(" ", kPrefix),
			strings.Repeat(" ", vPrefix)))
	}

	for _, object := range task.Objects {
		_, mLength := getDiffString(object.Type)
		kPrefix := longestMarker - mLength
		out += fmt.Sprintf("%s\n", formatObjectDiff(
			object,
			subStartPrefix,
			strings.Repeat(" ", kPrefix)))
	}

	return out
}

func formatFieldDiff(diff *api.FieldDiff, startPrefix, keyPrefix, valuePrefix string) string {
	marker, _ := getDiffString(diff.Type)
	out := fmt.Sprintf("%s%s%s%s: %s", startPrefix, marker, keyPrefix, diff.Name, valuePrefix)
	switch diff.Type {
	case "Added":
		out += fmt.Sprintf("%q", diff.New)
	case "Deleted":
		out += fmt.Sprintf("%q", diff.Old)
	case "Edited":
		out += fmt.Sprintf("%q => %q", diff.Old, diff.New)
	default:
		out += fmt.Sprintf("%q", diff.New)
	}

	// Color the annotations where possible
	if l := len(diff.Annotations); l != 0 {
		out += fmt.Sprintf(" (%s)", colorAnnotations(diff.Annotations))
	}

	return out
}

func getLongestPrefixes(fields []*api.FieldDiff, objects []*api.ObjectDiff) (longestField, longestMarker int) {
	for _, field := range fields {
		if l := len(field.Name); l > longestField {
			longestField = l
		}
		if _, l := getDiffString(field.Type); l > longestMarker {
			longestMarker = l
		}
	}
	for _, obj := range objects {
		if _, l := getDiffString(obj.Type); l > longestMarker {
			longestMarker = l
		}
	}
	return longestField, longestMarker
}

func formatObjectDiff(diff *api.ObjectDiff, startPrefix, keyPrefix string) string {
	marker, _ := getDiffString(diff.Type)
	out := fmt.Sprintf("%s%s%s%s {\n", startPrefix, marker, keyPrefix, diff.Name)

	// Determine the length of the longest name and longest diff marker to
	// properly align names and values
	longestField, longestMarker := getLongestPrefixes(diff.Fields, diff.Objects)
	subStartPrefix := strings.Repeat(" ", len(startPrefix)+2)
	numFields := len(diff.Fields)
	numObjects := len(diff.Objects)
	haveObjects := numObjects != 0
	for i, field := range diff.Fields {
		_, mLength := getDiffString(field.Type)
		kPrefix := longestMarker - mLength
		vPrefix := longestField - len(field.Name)
		out += formatFieldDiff(
			field,
			subStartPrefix,
			strings.Repeat(" ", kPrefix),
			strings.Repeat(" ", vPrefix))

		// Avoid a dangling new line
		if i+1 != numFields || haveObjects {
			out += "\n"
		}
	}

	for i, object := range diff.Objects {
		_, mLength := getDiffString(object.Type)
		kPrefix := longestMarker - mLength
		out += formatObjectDiff(object, subStartPrefix, strings.Repeat(" ", kPrefix))

		// Avoid a dangling new line
		if i+1 != numObjects {
			out += "\n"
		}
	}

	return fmt.Sprintf("%s\n%s}", out, startPrefix)
}

func getDiffString(diffType string) (string, int) {
	switch diffType {
	case "Added":
		return "[green]+[reset] ", 2
	case "Deleted":
		return "[red]-[reset] ", 2
	case "Edited":
		return "[light_yellow]+/-[reset] ", 4
	default:
		return "", 0
	}
}

func colorAnnotations(annotations []string) string {
	l := len(annotations)
	if l == 0 {
		return ""
	}

	colored := make([]string, l)
	for i, annotation := range annotations {
		switch annotation {
		case "forces create":
			colored[i] = fmt.Sprintf("[green]%s[reset]", annotation)
		case "forces destroy":
			colored[i] = fmt.Sprintf("[red]%s[reset]", annotation)
		case "forces in-place update":
			colored[i] = fmt.Sprintf("[cyan]%s[reset]", annotation)
		case "forces create/destroy update":
			colored[i] = fmt.Sprintf("[yellow]%s[reset]", annotation)
		default:
			colored[i] = annotation
		}
	}

	return strings.Join(colored, ", ")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"os"
	"os/exec"
	"testing"
)

func TestRunCommand(t *testing.T) {
	w := new(bytes.Buffer)
	err := runCommand(`echo -n {{env "USER"}}`, prepareFunc(func(cmd *exec.Cmd) {
		cmd.Stdout = w
	}))
	assert.Nil(t, err)
	assert.Equal(t, os.Getenv("USER"), w.String())
}
<commit_msg>add more test<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/stretchr/testify/assert"
	"os"
	"os/exec"
	"testing"
)

func TestRunCommand(t *testing.T) {
	w := new(bytes.Buffer)
	err := runCommand(`echo -n {{env "USER"}}`, prepareFunc(func(cmd *exec.Cmd) {
		cmd.Stdout = w
	}))
	assert.Nil(t, err)
	assert.Equal(t, os.Getenv("USER"), w.String())
}

func TestRunComplexCommand(t *testing.T) {
	w := new(bytes.Buffer)
	cmd := `echo -n {{if env "USER"}}--user={{env "USER"}}{{end}}
                {{if env "DO_NOT_MATCH"}}--group={{env "USER"}}{{end}}
                --listen={{ipv4 "127.0"}}:{{env "PORT" "80"}}`
	expected := fmt.Sprintf("--user=%s --listen=127.0.0.1:80", os.Getenv("USER"))

	err := runCommand(cmd, prepareFunc(func(cmd *exec.Cmd) {
		cmd.Stdout = w
	}))
	assert.Nil(t, err)
	assert.Equal(t, expected, w.String())
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/spf13/cobra"
	"github.com/blablacar/cnt/builder"
	"github.com/blablacar/cnt/config"
	"github.com/blablacar/cnt/log"
	"github.com/blablacar/cnt/logger"
)

var buildArgs = builder.BuildArgs{}

func Execute() {
	log.Set(logger.NewLogger())

	var rootCmd = &cobra.Command{Use: "cnt"}
	buildCmd.Flags().BoolVarP(&buildArgs.Zip, "nozip", "z", false, "Zip final image or not")
	rootCmd.Flags().BoolVarP(&buildArgs.Clean, "clean", "c", false, "Clean before doing anything")

	rootCmd.AddCommand(buildCmd, cleanCmd, pushCmd, installCmd, testCmd, versionCmd)

	config.GetConfig().Load()
	rootCmd.Execute()

	log.Get().Info("Victory !")
}

func discoverAndRunBuildType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Build()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Build()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunPushType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Push()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Push()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunInstallType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Install()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Install()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunCleanType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Clean()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Clean()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunTestType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Test()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Test()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func runCleanIfRequested(path string, args builder.BuildArgs) {
	if args.Clean {
		discoverAndRunCleanType(path, args)
	}
}

<commit_msg>fix clean flag<commit_after>package commands

import (
	"github.com/spf13/cobra"
	"github.com/blablacar/cnt/builder"
	"github.com/blablacar/cnt/config"
	"github.com/blablacar/cnt/log"
	"github.com/blablacar/cnt/logger"
)

var buildArgs = builder.BuildArgs{}

func Execute() {
	log.Set(logger.NewLogger())

	var rootCmd = &cobra.Command{Use: "cnt"}
	buildCmd.Flags().BoolVarP(&buildArgs.Zip, "nozip", "z", false, "Zip final image or not")
	rootCmd.PersistentFlags().BoolVarP(&buildArgs.Clean, "clean", "c", false, "Clean before doing anything")

	rootCmd.AddCommand(buildCmd, cleanCmd, pushCmd, installCmd, testCmd, versionCmd)

	config.GetConfig().Load()
	rootCmd.Execute()

	log.Get().Info("Victory !")
}

func discoverAndRunBuildType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Build()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Build()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunPushType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Push()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Push()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunInstallType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Install()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Install()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunCleanType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Clean()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Clean()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func discoverAndRunTestType(path string, args builder.BuildArgs) {
	if cnt, err := builder.OpenCnt(path, args); err == nil {
		cnt.Test()
	} else if pod, err := builder.OpenPod(path, args); err == nil {
		pod.Test()
	} else {
		log.Get().Panic("Cannot found cnt-manifest.yml")
	}
}

func runCleanIfRequested(path string, args builder.BuildArgs) {
	if args.Clean {
		discoverAndRunCleanType(path, args)
	}
}

<|endoftext|>


<commit_before>package commands

import (
	"errors"
	"fmt"
	"os"
	"strings"
	"text/template"

	"github.com/docker/machine/cli"
	"github.com/docker/machine/libmachine/log"
)

const (
	envTmpl = `kubectl config set-cluster kmachine --server={{ .K8sHost }} --insecure-skip-tls-verify=true{{ .Suffix2 }}kubectl config set-credentials kuser --token={{ .K8sToken }}{{ .Suffix2 }}kubectl config set-context kmachine --user=kuser --cluster=kmachine{{ .Suffix2 }}kubectl config use-context kmachine{{ .Suffix2 }}{{ .Prefix }}DOCKER_TLS_VERIFY{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}DOCKER_HOST{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}DOCKER_CERT_PATH{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ .Prefix }}DOCKER_MACHINE_NAME{{ .Delimiter }}{{ .MachineName }}{{ .Suffix }}{{ if .NoProxyVar }}{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}{{end}}{{ .UsageHint }}`
)

var (
	improperEnvArgsError = errors.New("Error: Expected either one machine name, or -u flag to unset the variables in the arguments.")
)

type ShellConfig struct {
	Prefix          string
	Delimiter       string
	Suffix          string
	Suffix2			string
	DockerCertPath  string
	DockerHost      string
	K8sHost			string
	K8sToken		string
	DockerTLSVerify string
	UsageHint       string
	MachineName     string
	NoProxyVar      string
	NoProxyValue    string
}

func cmdEnv(c *cli.Context) {
	// Ensure that log messages always go to stderr when this command is
	// being run (it is intended to be run in a subshell)
	log.SetOutWriter(os.Stderr)

	if len(c.Args()) != 1 && !c.Bool("unset") {
		fatal(improperEnvArgsError)
	}

	h := getFirstArgHost(c)

	dockerHost, authOptions, err := runConnectionBoilerplate(h, c)
	if err != nil {
		fatalf("Error running connection boilerplate: %s", err)
	}

	mParts := strings.Split(dockerHost, "://")
	mParts = strings.Split(mParts[1], ":")

	k8sHost := fmt.Sprintf("https://%s:6443", mParts[0])
	k8sToken := h.HostOptions.KubernetesOptions.K8SToken

	userShell := c.String("shell")
	if userShell == "" {
		shell, err := detectShell()
		if err != nil {
			fatal(err)
		}
		userShell = shell
	}

	t := template.New("envConfig")

	usageHint := generateUsageHint(c.App.Name, c.Args().First(), userShell)

	shellCfg := &ShellConfig{
		DockerCertPath:  authOptions.CertDir,
		DockerHost:      dockerHost,
        K8sHost:         k8sHost,
        K8sToken:		 k8sToken,
		DockerTLSVerify: "1",
		UsageHint:       usageHint,
		MachineName:     h.Name,
	}

	if c.Bool("no-proxy") {
		ip, err := h.Driver.GetIP()
		if err != nil {
			fatalf("Error getting host IP: %s", err)
		}

		// first check for an existing lower case no_proxy var
		noProxyVar := "no_proxy"
		noProxyValue := os.Getenv("no_proxy")

		// otherwise default to allcaps HTTP_PROXY
		if noProxyValue == "" {
			noProxyVar = "NO_PROXY"
			noProxyValue = os.Getenv("NO_PROXY")
		}

		// add the docker host to the no_proxy list idempotently
		switch {
		case noProxyValue == "":
			noProxyValue = ip
		case strings.Contains(noProxyValue, ip):
			//ip already in no_proxy list, nothing to do
		default:
			noProxyValue = fmt.Sprintf("%s,%s", noProxyValue, ip)
		}

		shellCfg.NoProxyVar = noProxyVar
		shellCfg.NoProxyValue = noProxyValue
	}

	// unset vars
	if c.Bool("unset") {
		switch userShell {
		case "fish":
			shellCfg.Prefix = "set -e "
			shellCfg.Delimiter = ""
			shellCfg.Suffix = ";\n"
		case "powershell":
			shellCfg.Prefix = "Remove-Item Env:\\\\"
			shellCfg.Delimiter = ""
			shellCfg.Suffix = "\n"
		case "cmd":
			// since there is no way to unset vars in cmd just reset to empty
			shellCfg.DockerCertPath = ""
			shellCfg.DockerHost = ""
			shellCfg.DockerTLSVerify = ""
			shellCfg.Prefix = "set "
			shellCfg.Delimiter = "="
			shellCfg.Suffix = "\n"
		default:
			shellCfg.Prefix = "unset "
			shellCfg.Delimiter = " "
			shellCfg.Suffix = "\n"
		}

		tmpl, err := t.Parse(envTmpl)
		if err != nil {
			fatal(err)
		}

		if err := tmpl.Execute(os.Stdout, shellCfg); err != nil {
			fatal(err)
		}
		return
	}

	switch userShell {
	case "fish":
		shellCfg.Prefix = "set -x "
		shellCfg.Suffix = "\";\n"
		shellCfg.Delimiter = " \""
	case "powershell":
		shellCfg.Prefix = "$Env:"
		shellCfg.Suffix = "\"\n"
		shellCfg.Delimiter = " = \""
	case "cmd":
		shellCfg.Prefix = "set "
		shellCfg.Suffix = "\n"
		shellCfg.Delimiter = "="
	default:
		shellCfg.Prefix = "export "
		shellCfg.Suffix = "\"\n"
		shellCfg.Suffix2 = "\n"
		shellCfg.Delimiter = "=\""
	}

	tmpl, err := t.Parse(envTmpl)
	if err != nil {
		fatal(err)
	}

	if err := tmpl.Execute(os.Stdout, shellCfg); err != nil {
		fatal(err)
	}
}

func generateUsageHint(appName, machineName, userShell string) string {
	cmd := ""
	switch userShell {
	case "fish":
		if machineName != "" {
			cmd = fmt.Sprintf("eval (%s env %s)", appName, machineName)
		} else {
			cmd = fmt.Sprintf("eval (%s env)", appName)
		}
	case "powershell":
		if machineName != "" {
			cmd = fmt.Sprintf("%s env --shell=powershell %s | Invoke-Expression", appName, machineName)
		} else {
			cmd = fmt.Sprintf("%s env --shell=powershell | Invoke-Expression", appName)
		}
	case "cmd":
		cmd = "copy and paste the above values into your command prompt"
	default:
		if machineName != "" {
			cmd = fmt.Sprintf("eval \"$(%s env %s)\"", appName, machineName)
		} else {
			cmd = fmt.Sprintf("eval \"$(%s env)\"", appName)
		}
	}

	return fmt.Sprintf("# Run this command to configure your shell: \n# %s\n", cmd)
}
<commit_msg>Using machine name to define contexts<commit_after>package commands

import (
	"errors"
	"fmt"
	"os"
	"strings"
	"text/template"

	"github.com/docker/machine/cli"
	"github.com/docker/machine/libmachine/log"
)

const (
	envTmpl = `kubectl config set-cluster {{ .MachineName }} --server={{ .K8sHost }} --insecure-skip-tls-verify=true{{ .Suffix2 }}kubectl config set-credentials {{ .MachineName}} --token={{ .K8sToken }}{{ .Suffix2 }}kubectl config set-context {{ .MachineName }} --user={{ .MachineName}} --cluster={{ .MachineName }}{{ .Suffix2 }}kubectl config use-context {{ .MachineName}}{{ .Suffix2 }}{{ .Prefix }}DOCKER_TLS_VERIFY{{ .Delimiter }}{{ .DockerTLSVerify }}{{ .Suffix }}{{ .Prefix }}DOCKER_HOST{{ .Delimiter }}{{ .DockerHost }}{{ .Suffix }}{{ .Prefix }}DOCKER_CERT_PATH{{ .Delimiter }}{{ .DockerCertPath }}{{ .Suffix }}{{ .Prefix }}DOCKER_MACHINE_NAME{{ .Delimiter }}{{ .MachineName }}{{ .Suffix }}{{ if .NoProxyVar }}{{ .Prefix }}{{ .NoProxyVar }}{{ .Delimiter }}{{ .NoProxyValue }}{{ .Suffix }}{{end}}{{ .UsageHint }}`
)

var (
	improperEnvArgsError = errors.New("Error: Expected either one machine name, or -u flag to unset the variables in the arguments.")
)

type ShellConfig struct {
	Prefix          string
	Delimiter       string
	Suffix          string
	Suffix2			string
	DockerCertPath  string
	DockerHost      string
	K8sHost			string
	K8sToken		string
	DockerTLSVerify string
	UsageHint       string
	MachineName     string
	NoProxyVar      string
	NoProxyValue    string
}

func cmdEnv(c *cli.Context) {
	// Ensure that log messages always go to stderr when this command is
	// being run (it is intended to be run in a subshell)
	log.SetOutWriter(os.Stderr)

	if len(c.Args()) != 1 && !c.Bool("unset") {
		fatal(improperEnvArgsError)
	}

	h := getFirstArgHost(c)

	dockerHost, authOptions, err := runConnectionBoilerplate(h, c)
	if err != nil {
		fatalf("Error running connection boilerplate: %s", err)
	}

	mParts := strings.Split(dockerHost, "://")
	mParts = strings.Split(mParts[1], ":")

	k8sHost := fmt.Sprintf("https://%s:6443", mParts[0])
	k8sToken := h.HostOptions.KubernetesOptions.K8SToken

	userShell := c.String("shell")
	if userShell == "" {
		shell, err := detectShell()
		if err != nil {
			fatal(err)
		}
		userShell = shell
	}

	t := template.New("envConfig")

	usageHint := generateUsageHint(c.App.Name, c.Args().First(), userShell)

	shellCfg := &ShellConfig{
		DockerCertPath:  authOptions.CertDir,
		DockerHost:      dockerHost,
        K8sHost:         k8sHost,
        K8sToken:		 k8sToken,
		DockerTLSVerify: "1",
		UsageHint:       usageHint,
		MachineName:     h.Name,
	}

	if c.Bool("no-proxy") {
		ip, err := h.Driver.GetIP()
		if err != nil {
			fatalf("Error getting host IP: %s", err)
		}

		// first check for an existing lower case no_proxy var
		noProxyVar := "no_proxy"
		noProxyValue := os.Getenv("no_proxy")

		// otherwise default to allcaps HTTP_PROXY
		if noProxyValue == "" {
			noProxyVar = "NO_PROXY"
			noProxyValue = os.Getenv("NO_PROXY")
		}

		// add the docker host to the no_proxy list idempotently
		switch {
		case noProxyValue == "":
			noProxyValue = ip
		case strings.Contains(noProxyValue, ip):
			//ip already in no_proxy list, nothing to do
		default:
			noProxyValue = fmt.Sprintf("%s,%s", noProxyValue, ip)
		}

		shellCfg.NoProxyVar = noProxyVar
		shellCfg.NoProxyValue = noProxyValue
	}

	// unset vars
	if c.Bool("unset") {
		switch userShell {
		case "fish":
			shellCfg.Prefix = "set -e "
			shellCfg.Delimiter = ""
			shellCfg.Suffix = ";\n"
		case "powershell":
			shellCfg.Prefix = "Remove-Item Env:\\\\"
			shellCfg.Delimiter = ""
			shellCfg.Suffix = "\n"
		case "cmd":
			// since there is no way to unset vars in cmd just reset to empty
			shellCfg.DockerCertPath = ""
			shellCfg.DockerHost = ""
			shellCfg.DockerTLSVerify = ""
			shellCfg.Prefix = "set "
			shellCfg.Delimiter = "="
			shellCfg.Suffix = "\n"
		default:
			shellCfg.Prefix = "unset "
			shellCfg.Delimiter = " "
			shellCfg.Suffix = "\n"
		}

		tmpl, err := t.Parse(envTmpl)
		if err != nil {
			fatal(err)
		}

		if err := tmpl.Execute(os.Stdout, shellCfg); err != nil {
			fatal(err)
		}
		return
	}

	switch userShell {
	case "fish":
		shellCfg.Prefix = "set -x "
		shellCfg.Suffix = "\";\n"
		shellCfg.Delimiter = " \""
	case "powershell":
		shellCfg.Prefix = "$Env:"
		shellCfg.Suffix = "\"\n"
		shellCfg.Delimiter = " = \""
	case "cmd":
		shellCfg.Prefix = "set "
		shellCfg.Suffix = "\n"
		shellCfg.Delimiter = "="
	default:
		shellCfg.Prefix = "export "
		shellCfg.Suffix = "\"\n"
		shellCfg.Suffix2 = "\n"
		shellCfg.Delimiter = "=\""
	}

	tmpl, err := t.Parse(envTmpl)
	if err != nil {
		fatal(err)
	}

	if err := tmpl.Execute(os.Stdout, shellCfg); err != nil {
		fatal(err)
	}
}

func generateUsageHint(appName, machineName, userShell string) string {
	cmd := ""
	switch userShell {
	case "fish":
		if machineName != "" {
			cmd = fmt.Sprintf("eval (%s env %s)", appName, machineName)
		} else {
			cmd = fmt.Sprintf("eval (%s env)", appName)
		}
	case "powershell":
		if machineName != "" {
			cmd = fmt.Sprintf("%s env --shell=powershell %s | Invoke-Expression", appName, machineName)
		} else {
			cmd = fmt.Sprintf("%s env --shell=powershell | Invoke-Expression", appName)
		}
	case "cmd":
		cmd = "copy and paste the above values into your command prompt"
	default:
		if machineName != "" {
			cmd = fmt.Sprintf("eval \"$(%s env %s)\"", appName, machineName)
		} else {
			cmd = fmt.Sprintf("eval \"$(%s env)\"", appName)
		}
	}

	return fmt.Sprintf("# Run this command to configure your shell: \n# %s\n", cmd)
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	circleci "github.com/mtchavez/circlecigo"
	"github.com/urfave/cli"
)

// EnvCmd - get env settings for a project
var EnvCmd = cli.Command{
	Name:        "env",
	Usage:       "get env settings for a project",
	Flags:       envFlags,
	Action:      envListAction,
	Subcommands: envSubcommands,
}

var envFlags = []cli.Flag{
	userFlag,
	projectFlag,
}

var envSubcommands = []cli.Command{
	{
		Name:    "list",
		Aliases: []string{"ls"},
		Usage:   "all environment variables",
		Action:  envListAction,
		Flags:   envFlags,
	},
	{
		Name:   "get",
		Usage:  "value of an environment variable",
		Action: envGetAction,
		Flags:  append(envFlags, envVarFlag),
	},
	{
		Name:   "set",
		Usage:  "a value for an environment variable",
		Action: envSetAction,
		Flags:  append(envFlags, envVarFlag),
	},
	{
		Name:    "delete",
		Aliases: []string{"del", "rm"},
		Usage:   "an environment variable",
		Action:  envDeleteAction,
		Flags:   append(envFlags, envVarFlag),
	},
}

func envListAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	padding := 1
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)

	envVars, _ := client.EnvVars(context.String("user"), context.String("project"))
	fmt.Fprintln(writer, "Var\tValue\t")
	for _, envVar := range envVars {
		fmt.Fprintln(writer, fmt.Sprintf("%s\t%s\t", envVar.Name, envVar.Value))
	}
	writer.Flush()
	return nil
}

func envGetAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	padding := 1
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)

	envVar, _ := client.GetEnvVar(context.String("user"), context.String("project"), context.String("var"))
	fmt.Fprintln(writer, "Name\tValue\t")
	fmt.Fprintln(writer, fmt.Sprintf("%s\t%s\t", envVar.Name, envVar.Value))
	writer.Flush()
	return nil
}

func envSetAction(context *cli.Context) error {
	varInput := strings.Split(context.String("var"), "=")
	envName := ""
	envValue := ""
	if len(varInput) == 2 {
		envName = varInput[0]
		envValue = varInput[1]
	}
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	padding := 1
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)

	envVar, _ := client.AddEnvVar(context.String("user"), context.String("project"), envName, envValue)
	fmt.Fprintln(writer, "Name\tValue\t")
	fmt.Fprintln(writer, fmt.Sprintf("%s\t%s\t", envVar.Name, envVar.Value))
	writer.Flush()
	return nil
}

func envDeleteAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	varName := context.String("var")
	_, resp := client.DeleteEnvVar(context.String("user"), context.String("project"), varName)
	if resp.Success() {
		fmt.Printf("Removed %s\n", varName)
		return nil
	}
	fmt.Printf("Failed removing %s\n", varName)
	fmt.Println(string(resp.Body))
	return resp.Error
}
<commit_msg>Refactor: Change env command name to environ<commit_after>package commands

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	circleci "github.com/mtchavez/circlecigo"
	"github.com/urfave/cli"
)

// EnvCmd - get env settings for a project
var EnvCmd = cli.Command{
	Name:        "environ",
	Usage:       "get environment settings for a project",
	Flags:       envFlags,
	Action:      envListAction,
	Subcommands: envSubcommands,
}

var envFlags = []cli.Flag{
	userFlag,
	projectFlag,
}

var envSubcommands = []cli.Command{
	{
		Name:    "list",
		Aliases: []string{"ls"},
		Usage:   "all environment variables",
		Action:  envListAction,
		Flags:   envFlags,
	},
	{
		Name:   "get",
		Usage:  "value of an environment variable",
		Action: envGetAction,
		Flags:  append(envFlags, envVarFlag),
	},
	{
		Name:   "set",
		Usage:  "a value for an environment variable",
		Action: envSetAction,
		Flags:  append(envFlags, envVarFlag),
	},
	{
		Name:    "delete",
		Aliases: []string{"del", "rm"},
		Usage:   "an environment variable",
		Action:  envDeleteAction,
		Flags:   append(envFlags, envVarFlag),
	},
}

func envListAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	padding := 1
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)

	envVars, _ := client.EnvVars(context.String("user"), context.String("project"))
	fmt.Fprintln(writer, "Var\tValue\t")
	for _, envVar := range envVars {
		fmt.Fprintln(writer, fmt.Sprintf("%s\t%s\t", envVar.Name, envVar.Value))
	}
	writer.Flush()
	return nil
}

func envGetAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	padding := 1
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)

	envVar, _ := client.GetEnvVar(context.String("user"), context.String("project"), context.String("var"))
	fmt.Fprintln(writer, "Name\tValue\t")
	fmt.Fprintln(writer, fmt.Sprintf("%s\t%s\t", envVar.Name, envVar.Value))
	writer.Flush()
	return nil
}

func envSetAction(context *cli.Context) error {
	varInput := strings.Split(context.String("var"), "=")
	envName := ""
	envValue := ""
	if len(varInput) == 2 {
		envName = varInput[0]
		envValue = varInput[1]
	}
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	padding := 1
	writer := tabwriter.NewWriter(os.Stdout, 0, 8, padding, '\t', tabwriter.AlignRight)

	envVar, _ := client.AddEnvVar(context.String("user"), context.String("project"), envName, envValue)
	fmt.Fprintln(writer, "Name\tValue\t")
	fmt.Fprintln(writer, fmt.Sprintf("%s\t%s\t", envVar.Name, envVar.Value))
	writer.Flush()
	return nil
}

func envDeleteAction(context *cli.Context) error {
	token := context.GlobalString("token")
	client := circleci.NewClient(token)
	varName := context.String("var")
	_, resp := client.DeleteEnvVar(context.String("user"), context.String("project"), varName)
	if resp.Success() {
		fmt.Printf("Removed %s\n", varName)
		return nil
	}
	fmt.Printf("Failed removing %s\n", varName)
	fmt.Println(string(resp.Body))
	return resp.Error
}
<|endoftext|>