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

package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"text/tabwriter"

	"github.com/sdboyer/gps"
)

const statusShortHelp = `Report the status of the project's dependencies`
const statusLongHelp = `
With no arguments, print the status of each dependency of the project.

  PROJECT     Import path
  CONSTRAINT  Version constraint, from the manifest
  VERSION     Version chosen, from the lock
  REVISION    VCS revision of the chosen version
  LATEST      Latest VCS revision available
  PKGS USED   Number of packages from this project that are actually used

With one or more explicitly specified packages, or with the -detailed flag,
print an extended status output for each dependency of the project.

  TODO    Another column description
  FOOBAR  Another column description

Status returns exit code zero if all dependencies are in a "good state".
`

func (cmd *statusCommand) Name() string      { return "status" }
func (cmd *statusCommand) Args() string      { return "[package...]" }
func (cmd *statusCommand) ShortHelp() string { return statusShortHelp }
func (cmd *statusCommand) LongHelp() string  { return statusLongHelp }

func (cmd *statusCommand) Register(fs *flag.FlagSet) {
	fs.BoolVar(&cmd.detailed, "detailed", false, "report more detailed status")
	fs.BoolVar(&cmd.json, "json", false, "output in JSON format")
	fs.StringVar(&cmd.template, "f", "", "output in text/template format")
	fs.BoolVar(&cmd.dot, "dot", false, "output the dependency graph in GraphViz format")
	fs.BoolVar(&cmd.old, "old", false, "only show out-of-date dependencies")
	fs.BoolVar(&cmd.missing, "missing", false, "only show missing dependencies")
	fs.BoolVar(&cmd.unused, "unused", false, "only show unused dependencies")
	fs.BoolVar(&cmd.modified, "modified", false, "only show modified dependencies")
}

type statusCommand struct {
	detailed bool
	json     bool
	template string
	dot      bool
	old      bool
	missing  bool
	unused   bool
	modified bool
}

func (cmd *statusCommand) Run(args []string) error {
	p, err := hoardContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := hoardContext.sourceManager()
	if err != nil {
		return err
	}
	sm.UseDefaultSignalHandling()
	defer sm.Release()

	if cmd.detailed {
		return runStatusDetailed(p, sm, args)
	}
	return runStatusAll(p, sm)
}

// BasicStatus contains all the information reported about a single dependency
// in the summary/list status output mode.
type BasicStatus struct {
	ProjectRoot  string
	Constraint   gps.Constraint
	Version      gps.UnpairedVersion
	Revision     gps.Revision
	Latest       gps.Version
	PackageCount int
}

type MissingStatus struct {
	ProjectRoot     string
	MissingPackages string
}

func runStatusAll(p *project, sm *gps.SourceMgr) error {
	if p.l == nil {
		// TODO if we have no lock file, do...other stuff
		return nil
	}

	// In the background, warm caches of version lists for all the projects in
	// the lock. The SourceMgr coordinates access to this information - if the
	// main goroutine asks for the version list while the background goroutine's
	// request is in flight (or vice versa), both calls are folded together and
	// are fulfilled from the same network response, and the same on-disk
	// repository cache.
	for _, proj := range p.l.Projects() {
		id := proj.Ident()
		go sm.ListVersions(id)
	}

	// While the network churns on ListVersions() requests, statically analyze
	// code from the current project.
	ptree, err := gps.ListPackages(p.absroot, string(p.importroot))
	if err != nil {
		return fmt.Errorf("analysis of local packages failed: %v", err)
	}

	// Set up a solver in order to check the InputHash.
	params := gps.SolveParameters{
		RootDir:         p.absroot,
		RootPackageTree: ptree,
		Manifest:        p.m,
		// Locks aren't a part of the input hash check, so we can omit it.
	}
	if *verbose {
		params.Trace = true
		params.TraceLogger = log.New(os.Stderr, "", 0)
	}

	s, err := gps.Prepare(params, sm)
	if err != nil {
		return fmt.Errorf("could not set up solver for input hashing: %s", err)
	}

	cm := collectConstraints(ptree, p, sm)

	// Get the project list and sort it so that the printed output users see is
	// deterministically ordered. (This may be superfluous if the lock is always
	// written in alpha order, but it doesn't hurt to double down.)
	slp := p.l.Projects()
	sort.Sort(sortedLockedProjects(slp))

	if bytes.Equal(s.HashInputs(), p.l.Memo) {
		// If these are equal, we're guaranteed that the lock is a transitively
		// complete picture of all deps. That eliminates the need for at least
		// some checks.

		tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
		fmt.Fprintf(tw, "PROJECT\tCONSTRAINT\tVERSION\tREVISION\tLATEST\tPKGS USED\n")

		for _, proj := range slp {
			bs := BasicStatus{
				ProjectRoot:  string(proj.Ident().ProjectRoot),
				PackageCount: len(proj.Packages()),
			}

			// Split apart the version from the lock into its constituent parts
			switch tv := proj.Version().(type) {
			case gps.UnpairedVersion:
				bs.Version = tv
			case gps.Revision:
				bs.Revision = tv
			case gps.PairedVersion:
				bs.Version = tv.Unpair()
				bs.Revision = tv.Underlying()
			}

			// Check if the manifest has an override for this project. If so,
			// set that as the constraint.
			if pp, has := p.m.Ovr[proj.Ident().ProjectRoot]; has && pp.Constraint != nil {
				// TODO note somehow that it's overridden
				bs.Constraint = pp.Constraint
			} else {
				bs.Constraint = gps.Any()
				for _, c := range cm[bs.ProjectRoot] {
					bs.Constraint = c.Intersect(bs.Constraint)
				}
			}

			// Only if we have a non-rev and non-plain version do/can we display
			// anything wrt the version's updateability.
			if bs.Version != nil && bs.Version.Type() != gps.IsVersion {
				c, has := p.m.Dependencies[proj.Ident().ProjectRoot]
				if !has {
					c.Constraint = gps.Any()
				}
				// TODO: This constraint is only the constraint imposed by the
				// current project, not by any transitive deps. As a result,
				// transitive project deps will always show "any" here.
				bs.Constraint = c.Constraint

				vl, err := sm.ListVersions(proj.Ident())
				if err == nil {
					gps.SortForUpgrade(vl)

					for _, v := range vl {
						// Because we've sorted the version list for
						// upgrade, the first version we encounter that
						// matches our constraint will be what we want.
						if c.Constraint.Matches(v) {
							// For branch constraints this should be the
							// most recent revision on the selected
							// branch.
							if tv, ok := v.(gps.PairedVersion); ok && v.Type() == gps.IsBranch {
								bs.Latest = tv.Underlying()
							} else {
								bs.Latest = v
							}
							break
						}
					}
				}
			}

			var constraint string
			if v, ok := bs.Constraint.(gps.Version); ok {
				constraint = formatVersion(v)
			} else {
				constraint = bs.Constraint.String()
			}

			fmt.Fprintf(tw,
				"%s\t%s\t%s\t%s\t%s\t%d\t\n",
				bs.ProjectRoot,
				constraint,
				formatVersion(bs.Version),
				formatVersion(bs.Revision),
				formatVersion(bs.Latest),
				bs.PackageCount,
			)
		}

		tw.Flush()
	} else {
		// Hash digest mismatch may indicate that some deps are no longer
		// needed, some are missing, or that some constraints or source
		// locations have changed.
		//
		// It's possible for digests to not match, but still have a correct
		// lock.
		tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
		fmt.Fprintf(tw, "PROJECT\tMISSING PACKAGES\n")

		external := ptree.ExternalReach(true, false, nil).ListExternalImports()
		roots := make(map[gps.ProjectRoot][]string)
		var errs []string
		for _, e := range external {
			root, err := sm.DeduceProjectRoot(e)
			if err != nil {
				errs = append(errs, string(root))
				continue
			}

			roots[root] = append(roots[root], e)
		}

	outer:
		for root, pkgs := range roots {
			// TODO also handle the case where the project is present, but there
			// are items missing from just the package list
			for _, lp := range slp {
				if lp.Ident().ProjectRoot == root {
					continue outer
				}
			}

			fmt.Fprintf(tw,
				"%s\t%s\t\n",
				root,
				pkgs,
			)
		}
		tw.Flush()
	}

	return nil
}

func formatVersion(v gps.Version) string {
	if v == nil {
		return ""
	}
	switch v.Type() {
	case gps.IsBranch:
		return "branch " + v.String()
	case gps.IsRevision:
		r := v.String()
		if len(r) > 7 {
			r = r[:7]
		}
		return r
	}
	return v.String()
}

func runStatusDetailed(p *project, sm *gps.SourceMgr, args []string) error {
	// TODO
	return fmt.Errorf("not implemented")
}

func collectConstraints(ptree gps.PackageTree, p *project, sm *gps.SourceMgr) map[string][]gps.Constraint {
	// TODO
	return map[string][]gps.Constraint{}
}
<commit_msg>dep: dedent and return early<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"text/tabwriter"

	"github.com/sdboyer/gps"
)

const statusShortHelp = `Report the status of the project's dependencies`
const statusLongHelp = `
With no arguments, print the status of each dependency of the project.

  PROJECT     Import path
  CONSTRAINT  Version constraint, from the manifest
  VERSION     Version chosen, from the lock
  REVISION    VCS revision of the chosen version
  LATEST      Latest VCS revision available
  PKGS USED   Number of packages from this project that are actually used

With one or more explicitly specified packages, or with the -detailed flag,
print an extended status output for each dependency of the project.

  TODO    Another column description
  FOOBAR  Another column description

Status returns exit code zero if all dependencies are in a "good state".
`

func (cmd *statusCommand) Name() string      { return "status" }
func (cmd *statusCommand) Args() string      { return "[package...]" }
func (cmd *statusCommand) ShortHelp() string { return statusShortHelp }
func (cmd *statusCommand) LongHelp() string  { return statusLongHelp }

func (cmd *statusCommand) Register(fs *flag.FlagSet) {
	fs.BoolVar(&cmd.detailed, "detailed", false, "report more detailed status")
	fs.BoolVar(&cmd.json, "json", false, "output in JSON format")
	fs.StringVar(&cmd.template, "f", "", "output in text/template format")
	fs.BoolVar(&cmd.dot, "dot", false, "output the dependency graph in GraphViz format")
	fs.BoolVar(&cmd.old, "old", false, "only show out-of-date dependencies")
	fs.BoolVar(&cmd.missing, "missing", false, "only show missing dependencies")
	fs.BoolVar(&cmd.unused, "unused", false, "only show unused dependencies")
	fs.BoolVar(&cmd.modified, "modified", false, "only show modified dependencies")
}

type statusCommand struct {
	detailed bool
	json     bool
	template string
	dot      bool
	old      bool
	missing  bool
	unused   bool
	modified bool
}

func (cmd *statusCommand) Run(args []string) error {
	p, err := hoardContext.loadProject("")
	if err != nil {
		return err
	}

	sm, err := hoardContext.sourceManager()
	if err != nil {
		return err
	}
	sm.UseDefaultSignalHandling()
	defer sm.Release()

	if cmd.detailed {
		return runStatusDetailed(p, sm, args)
	}
	return runStatusAll(p, sm)
}

// BasicStatus contains all the information reported about a single dependency
// in the summary/list status output mode.
type BasicStatus struct {
	ProjectRoot  string
	Constraint   gps.Constraint
	Version      gps.UnpairedVersion
	Revision     gps.Revision
	Latest       gps.Version
	PackageCount int
}

type MissingStatus struct {
	ProjectRoot     string
	MissingPackages string
}

func runStatusAll(p *project, sm *gps.SourceMgr) error {
	if p.l == nil {
		// TODO if we have no lock file, do...other stuff
		return nil
	}

	// In the background, warm caches of version lists for all the projects in
	// the lock. The SourceMgr coordinates access to this information - if the
	// main goroutine asks for the version list while the background goroutine's
	// request is in flight (or vice versa), both calls are folded together and
	// are fulfilled from the same network response, and the same on-disk
	// repository cache.
	for _, proj := range p.l.Projects() {
		id := proj.Ident()
		go sm.ListVersions(id)
	}

	// While the network churns on ListVersions() requests, statically analyze
	// code from the current project.
	ptree, err := gps.ListPackages(p.absroot, string(p.importroot))
	if err != nil {
		return fmt.Errorf("analysis of local packages failed: %v", err)
	}

	// Set up a solver in order to check the InputHash.
	params := gps.SolveParameters{
		RootDir:         p.absroot,
		RootPackageTree: ptree,
		Manifest:        p.m,
		// Locks aren't a part of the input hash check, so we can omit it.
	}
	if *verbose {
		params.Trace = true
		params.TraceLogger = log.New(os.Stderr, "", 0)
	}

	s, err := gps.Prepare(params, sm)
	if err != nil {
		return fmt.Errorf("could not set up solver for input hashing: %s", err)
	}

	cm := collectConstraints(ptree, p, sm)

	// Get the project list and sort it so that the printed output users see is
	// deterministically ordered. (This may be superfluous if the lock is always
	// written in alpha order, but it doesn't hurt to double down.)
	slp := p.l.Projects()
	sort.Sort(sortedLockedProjects(slp))

	tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
	defer tw.Flush()

	if bytes.Equal(s.HashInputs(), p.l.Memo) {
		// If these are equal, we're guaranteed that the lock is a transitively
		// complete picture of all deps. That eliminates the need for at least
		// some checks.

		fmt.Fprintf(tw, "PROJECT\tCONSTRAINT\tVERSION\tREVISION\tLATEST\tPKGS USED\n")

		for _, proj := range slp {
			bs := BasicStatus{
				ProjectRoot:  string(proj.Ident().ProjectRoot),
				PackageCount: len(proj.Packages()),
			}

			// Split apart the version from the lock into its constituent parts
			switch tv := proj.Version().(type) {
			case gps.UnpairedVersion:
				bs.Version = tv
			case gps.Revision:
				bs.Revision = tv
			case gps.PairedVersion:
				bs.Version = tv.Unpair()
				bs.Revision = tv.Underlying()
			}

			// Check if the manifest has an override for this project. If so,
			// set that as the constraint.
			if pp, has := p.m.Ovr[proj.Ident().ProjectRoot]; has && pp.Constraint != nil {
				// TODO note somehow that it's overridden
				bs.Constraint = pp.Constraint
			} else {
				bs.Constraint = gps.Any()
				for _, c := range cm[bs.ProjectRoot] {
					bs.Constraint = c.Intersect(bs.Constraint)
				}
			}

			// Only if we have a non-rev and non-plain version do/can we display
			// anything wrt the version's updateability.
			if bs.Version != nil && bs.Version.Type() != gps.IsVersion {
				c, has := p.m.Dependencies[proj.Ident().ProjectRoot]
				if !has {
					c.Constraint = gps.Any()
				}
				// TODO: This constraint is only the constraint imposed by the
				// current project, not by any transitive deps. As a result,
				// transitive project deps will always show "any" here.
				bs.Constraint = c.Constraint

				vl, err := sm.ListVersions(proj.Ident())
				if err == nil {
					gps.SortForUpgrade(vl)

					for _, v := range vl {
						// Because we've sorted the version list for
						// upgrade, the first version we encounter that
						// matches our constraint will be what we want.
						if c.Constraint.Matches(v) {
							// For branch constraints this should be the
							// most recent revision on the selected
							// branch.
							if tv, ok := v.(gps.PairedVersion); ok && v.Type() == gps.IsBranch {
								bs.Latest = tv.Underlying()
							} else {
								bs.Latest = v
							}
							break
						}
					}
				}
			}

			var constraint string
			if v, ok := bs.Constraint.(gps.Version); ok {
				constraint = formatVersion(v)
			} else {
				constraint = bs.Constraint.String()
			}

			fmt.Fprintf(tw,
				"%s\t%s\t%s\t%s\t%s\t%d\t\n",
				bs.ProjectRoot,
				constraint,
				formatVersion(bs.Version),
				formatVersion(bs.Revision),
				formatVersion(bs.Latest),
				bs.PackageCount,
			)
		}

		return nil
	}

	// Hash digest mismatch may indicate that some deps are no longer
	// needed, some are missing, or that some constraints or source
	// locations have changed.
	//
	// It's possible for digests to not match, but still have a correct
	// lock.
	fmt.Fprintf(tw, "PROJECT\tMISSING PACKAGES\n")

	external := ptree.ExternalReach(true, false, nil).ListExternalImports()
	roots := make(map[gps.ProjectRoot][]string)
	var errs []string
	for _, e := range external {
		root, err := sm.DeduceProjectRoot(e)
		if err != nil {
			errs = append(errs, string(root))
			continue
		}

		roots[root] = append(roots[root], e)
	}

outer:
	for root, pkgs := range roots {
		// TODO also handle the case where the project is present, but there
		// are items missing from just the package list
		for _, lp := range slp {
			if lp.Ident().ProjectRoot == root {
				continue outer
			}
		}

		fmt.Fprintf(tw,
			"%s\t%s\t\n",
			root,
			pkgs,
		)
	}

	return nil
}

func formatVersion(v gps.Version) string {
	if v == nil {
		return ""
	}
	switch v.Type() {
	case gps.IsBranch:
		return "branch " + v.String()
	case gps.IsRevision:
		r := v.String()
		if len(r) > 7 {
			r = r[:7]
		}
		return r
	}
	return v.String()
}

func runStatusDetailed(p *project, sm *gps.SourceMgr, args []string) error {
	// TODO
	return fmt.Errorf("not implemented")
}

func collectConstraints(ptree gps.PackageTree, p *project, sm *gps.SourceMgr) map[string][]gps.Constraint {
	// TODO
	return map[string][]gps.Constraint{}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"sync"
	"time"

	"github.com/jasonsoft/napnap"
)

type responseWriterWithLength struct {
	http.ResponseWriter
	length int
}

func (w *responseWriterWithLength) Write(b []byte) (n int, err error) {
	n, err = w.ResponseWriter.Write(b)
	w.length += n
	return
}

func (w *responseWriterWithLength) Length() int {
	return w.length
}

type status struct {
	sync.Mutex
	RequestCount int64     `json:"request_count"`
	NetworkIn    int64     `json:"network_in"`
	NetworkOut   int64     `json:"network_out"`
	StartAt      time.Time `json:"start_at"`
}

func newStatusMiddleware() *status {
	now := time.Now().UTC()
	return &status{
		StartAt: now,
	}
}

func (s *status) Invoke(c *napnap.Context, next napnap.HandlerFunc) {
	s.Lock()
	s.RequestCount++
	if c.Request.ContentLength > 0 {
		s.NetworkIn += c.Request.ContentLength
	}
	s.Unlock()

	lengthWriter := &responseWriterWithLength{c.Writer, 0}
	c.Writer = lengthWriter
	next(c)

	s.Lock()
	s.NetworkOut += int64(lengthWriter.length)
	s.Unlock()
}
<commit_msg>refactoring status middleware<commit_after>package main

import (
	"sync"
	"time"

	"github.com/jasonsoft/napnap"
)

type status struct {
	sync.Mutex
	RequestCount int64     `json:"request_count"`
	NetworkIn    int64     `json:"network_in"`
	NetworkOut   int64     `json:"network_out"`
	StartAt      time.Time `json:"start_at"`
}

func newStatusMiddleware() *status {
	now := time.Now().UTC()
	return &status{
		StartAt: now,
	}
}

func (s *status) Invoke(c *napnap.Context, next napnap.HandlerFunc) {
	s.Lock()
	s.RequestCount++
	if c.Request.ContentLength > 0 {
		s.NetworkIn += c.Request.ContentLength
	}
	s.Unlock()

	next(c)

	s.Lock()
	s.NetworkOut += int64(c.Writer.ContentLength())
	s.Unlock()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/xiaonanln/goworld"
	"github.com/xiaonanln/goworld/engine/common"
	"github.com/xiaonanln/goworld/engine/entity"
	"github.com/xiaonanln/goworld/engine/gwlog"
)

// Account entity for login process
type Account struct {
	entity.Entity // Entity type should always inherit entity.Entity
	username      string
	logining      bool
}

func (a *Account) OnInit() {

}

func (a *Account) OnCreated() {
	//gwlog.Info("%s created: client=%v", a, a.GetClient())
}

func (a *Account) Register_Client(username string, password string) {
	gwlog.Debug("Register %s %s", username, password)
	goworld.GetKVDB("password$"+username, func(val string, err error) {
		if err != nil {
			a.CallClient("ShowError", "服务器错误："+err.Error())
			return
		}

		if val != "" {
			a.CallClient("ShowError", "这个账号已经存在")
			return
		}
		goworld.PutKVDB("password$"+username, password, func(err error) {
			avatarID := goworld.CreateEntityLocally("Avatar") // 创建一个Avatar对象然后立刻销毁，产生一次存盘
			avatar := goworld.GetEntity(avatarID)
			avatar.Attrs.Set("name", username)
			avatar.Destroy()
			goworld.PutKVDB("avatarID$"+username, string(avatarID), func(err error) {
				a.CallClient("ShowInfo", "注册成功，请点击登录")
			})
		})
	})
}

func (a *Account) Login_Client(username string, password string) {
	if a.logining {
		// logining
		gwlog.Error("%s is already logining", a)
		return
	}

	gwlog.Info("%s logining with username %s password %s ...", a, username, password)
	a.logining = true
	goworld.GetKVDB("password$"+username, func(correctPassword string, err error) {
		if err != nil {
			a.logining = false
			a.CallClient("ShowError", "服务器错误："+err.Error())
			return
		}

		if password != correctPassword {
			a.logining = false
			a.CallClient("ShowError", "密码错误")
			return
		}

		goworld.GetKVDB("avatarID$"+username, func(_avatarID string, err error) {
			if err != nil {
				a.logining = false
				a.CallClient("ShowError", "服务器错误："+err.Error())
				return
			}
			avatarID := common.EntityID(_avatarID)
			goworld.LoadEntityAnywhere("Avatar", avatarID)
			a.Call(avatarID, "GetSpaceID", a.ID)
		})
	})
}

func (a *Account) OnGetAvatarSpaceID(avatarID common.EntityID, spaceID common.EntityID) {
	// avatar may be in the same space with account, check again
	avatar := goworld.GetEntity(avatarID)
	if avatar != nil {
		a.onAvatarEntityFound(avatar)
		return
	}

	a.Attrs.Set("loginAvatarID", avatarID)
	a.EnterSpace(spaceID, entity.Position{})
}

func (a *Account) onAvatarEntityFound(avatar *entity.Entity) {
	a.logining = false
	a.GiveClientTo(avatar)
}

func (a *Account) OnClientDisconnected() {
	a.Destroy()
}

func (a *Account) OnMigrateIn() {
	loginAvatarID := common.EntityID(a.Attrs.GetStr("loginAvatarID"))
	avatar := goworld.GetEntity(loginAvatarID)
	gwlog.Debug("%s migrating in, attrs=%v, loginAvatarID=%s, avatar=%v, client=%s", a, a.Attrs.ToMap(), loginAvatarID, avatar, a.GetClient())

	if avatar != nil {
		a.onAvatarEntityFound(avatar)
	} else {
		// failed
		a.CallClient("ShowError", "登录失败，请重试")
		a.logining = false
	}
}

func (a *Account) OnMigrateOut() {
	gwlog.Debug("%s migrating out ...", a)
}
<commit_msg>chatroom demo server works<commit_after>package main

import (
	"github.com/xiaonanln/goworld"
	"github.com/xiaonanln/goworld/engine/common"
	"github.com/xiaonanln/goworld/engine/entity"
	"github.com/xiaonanln/goworld/engine/gwlog"
)

// Account entity for login process
type Account struct {
	entity.Entity // Entity type should always inherit entity.Entity
	username      string
	logining      bool
}

func (a *Account) OnInit() {

}

func (a *Account) OnCreated() {
	//gwlog.Info("%s created: client=%v", a, a.GetClient())
}

func (a *Account) Register_Client(username string, password string) {
	gwlog.Debug("Register %s %s", username, password)
	goworld.GetKVDB("password$"+username, func(val string, err error) {
		if err != nil {
			a.CallClient("ShowError", "服务器错误："+err.Error())
			return
		}

		if val != "" {
			a.CallClient("ShowError", "这个账号已经存在")
			return
		}
		goworld.PutKVDB("password$"+username, password, func(err error) {
			avatarID := goworld.CreateEntityLocally("Avatar") // 创建一个Avatar对象然后立刻销毁，产生一次存盘
			avatar := goworld.GetEntity(avatarID)
			avatar.Attrs.Set("name", username)
			avatar.Destroy()
			goworld.PutKVDB("avatarID$"+username, string(avatarID), func(err error) {
				a.CallClient("ShowInfo", "注册成功，请点击登录")
			})
		})
	})
}

func (a *Account) Login_Client(username string, password string) {
	if a.logining {
		// logining
		gwlog.Error("%s is already logining", a)
		return
	}

	gwlog.Info("%s logining with username %s password %s ...", a, username, password)
	a.logining = true
	goworld.GetKVDB("password$"+username, func(correctPassword string, err error) {
		if err != nil {
			a.logining = false
			a.CallClient("ShowError", "服务器错误："+err.Error())
			return
		}

		if correctPassword == "" {
			a.logining = false
			a.CallClient("ShowError", "账号不存在")
			return
		}

		if password != correctPassword {
			a.logining = false
			a.CallClient("ShowError", "密码错误")
			return
		}

		goworld.GetKVDB("avatarID$"+username, func(_avatarID string, err error) {
			if err != nil {
				a.logining = false
				a.CallClient("ShowError", "服务器错误："+err.Error())
				return
			}
			avatarID := common.EntityID(_avatarID)
			goworld.LoadEntityAnywhere("Avatar", avatarID)
			a.Call(avatarID, "GetSpaceID", a.ID)
		})
	})
}

func (a *Account) OnGetAvatarSpaceID(avatarID common.EntityID, spaceID common.EntityID) {
	// avatar may be in the same space with account, check again
	avatar := goworld.GetEntity(avatarID)
	if avatar != nil {
		a.onAvatarEntityFound(avatar)
		return
	}

	a.Attrs.Set("loginAvatarID", avatarID)
	a.EnterSpace(spaceID, entity.Position{})
}

func (a *Account) onAvatarEntityFound(avatar *entity.Entity) {
	a.logining = false
	a.GiveClientTo(avatar)
}

func (a *Account) OnClientDisconnected() {
	a.Destroy()
}

func (a *Account) OnMigrateIn() {
	loginAvatarID := common.EntityID(a.Attrs.GetStr("loginAvatarID"))
	avatar := goworld.GetEntity(loginAvatarID)
	gwlog.Debug("%s migrating in, attrs=%v, loginAvatarID=%s, avatar=%v, client=%s", a, a.Attrs.ToMap(), loginAvatarID, avatar, a.GetClient())

	if avatar != nil {
		a.onAvatarEntityFound(avatar)
	} else {
		// failed
		a.CallClient("ShowError", "登录失败，请重试")
		a.logining = false
	}
}

func (a *Account) OnMigrateOut() {
	gwlog.Debug("%s migrating out ...", a)
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package tests_test

import (
	"flag"
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	k8sv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	v1 "kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/tests"
)

var _ = Describe("Subresource Api", func() {

	flag.Parse()

	virtCli, err := kubecli.GetKubevirtClient()
	tests.PanicOnError(err)

	manual := v1.RunStrategyManual
	restartOnError := v1.RunStrategyRerunOnFailure

	BeforeEach(func() {
		tests.BeforeTestCleanup()
	})

	Describe("Rbac Authorization", func() {
		resource := "virtualmachineinstances"

		Context("with correct permissions", func() {
			It("should be allowed to access subresource endpoint", func() {
				testClientJob(virtCli, true, resource)
			}, 15)
		})
		Context("Without permissions", func() {
			It("should not be able to access subresource endpoint", func() {
				testClientJob(virtCli, false, resource)
			}, 15)
		})
	})

	Describe("Rbac Authorization For Version Command", func() {
		resource := "version"

		Context("with authenticated user", func() {
			It("should be allowed to access subresource version endpoint", func() {
				testClientJob(virtCli, true, resource)
			}, 15)
		})
		Context("Without permissions", func() {
			It("should be able to access subresource version endpoint", func() {
				testClientJob(virtCli, false, resource)
			}, 15)
		})
	})

	Describe("VirtualMachine subresource", func() {
		Context("with a restart endpoint", func() {
			It("should restart a VM", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).NotTo(HaveOccurred())

				tests.StartVirtualMachine(vm)
				vmi, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())
				Expect(vmi.Status.Phase).To(Equal(v1.Running))

				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).NotTo(HaveOccurred())

				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil || vmi.UID == newVMI.UID {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})

			It("should return an error when VM is not running", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).NotTo(HaveOccurred())

				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).To(HaveOccurred())
			})

			It("should return an error when VM has not been found but VMI is running", func() {
				vmi := tests.NewRandomVMI()
				tests.RunVMIAndExpectLaunch(vmi, 60)

				err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vmi.Name)
				Expect(err).To(HaveOccurred())
			})
		})

		Context("With manual RunStrategy", func() {
			It("Should restart when VM is not running", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm.Spec.RunStrategy = &manual
				vm.Spec.Running = nil

				By("Creating VM")
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).ToNot(HaveOccurred())

				By("Starting VM via Restart subresource")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				By("Waiting for VMI to start")
				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})

			It("Should restart when VM is running", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm.Spec.RunStrategy = &manual
				vm.Spec.Running = nil

				By("Creating VM")
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).ToNot(HaveOccurred())

				By("Starting VM via Restart subresource")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				By("Waiting for VMI to start")
				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))

				vmi, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())

				By("Restarting VM")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil || vmi.UID == newVMI.UID {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})
		})

		Context("With RunStrategy RerunOnFailure", func() {
			It("Should restart the VM", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm.Spec.RunStrategy = &restartOnError
				vm.Spec.Running = nil

				By("Creating VM")
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).ToNot(HaveOccurred())

				By("Waiting for VMI to start")
				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))

				vmi, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())

				By("Restarting VM")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil || vmi.UID == newVMI.UID {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})
		})
	})

	Describe("the openapi spec for the subresources", func() {
		It("should be aggregated into the the apiserver openapi spec", func() {
			Eventually(func() string {
				spec, err := virtCli.RestClient().Get().AbsPath("/openapi/v2").DoRaw()
				Expect(err).ToNot(HaveOccurred())
				return string(spec)
			}, 60*time.Second, 1*time.Second).Should(ContainSubstring("subresources.kubevirt.io"))
		})
	})
})

func testClientJob(virtCli kubecli.KubevirtClient, withServiceAccount bool, resource string) {
	namespace := tests.NamespaceTestDefault
	expectedPhase := k8sv1.PodFailed
	name := "subresource-access-tester"
	job := &k8sv1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: name,
			Labels: map[string]string{
				v1.AppLabel: tests.SubresourceTestLabel,
			},
		},
		Spec: k8sv1.PodSpec{
			RestartPolicy: k8sv1.RestartPolicyNever,
			Containers: []k8sv1.Container{
				{
					Name:    name,
					Image:   fmt.Sprintf("%s/subresource-access-test:%s", tests.KubeVirtRepoPrefix, tests.KubeVirtVersionTag),
					Command: []string{"/subresource-access-test", resource},
				},
			},
		},
	}

	if withServiceAccount {
		job.Spec.ServiceAccountName = tests.SubresourceServiceAccountName
		expectedPhase = k8sv1.PodSucceeded
	} else if resource == "version" {
		expectedPhase = k8sv1.PodSucceeded
	}

	pod, err := virtCli.CoreV1().Pods(namespace).Create(job)
	Expect(err).ToNot(HaveOccurred())

	getStatus := func() k8sv1.PodPhase {
		pod, err := virtCli.CoreV1().Pods(namespace).Get(pod.Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())
		return pod.Status.Phase
	}

	Eventually(getStatus, 30, 0.5).Should(Equal(expectedPhase))
}
<commit_msg>match vm restart tests with polarion<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package tests_test

import (
	"flag"
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	k8sv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	v1 "kubevirt.io/kubevirt/pkg/api/v1"
	"kubevirt.io/kubevirt/pkg/kubecli"
	"kubevirt.io/kubevirt/tests"
)

var _ = Describe("Subresource Api", func() {

	flag.Parse()

	virtCli, err := kubecli.GetKubevirtClient()
	tests.PanicOnError(err)

	manual := v1.RunStrategyManual
	restartOnError := v1.RunStrategyRerunOnFailure

	BeforeEach(func() {
		tests.BeforeTestCleanup()
	})

	Describe("Rbac Authorization", func() {
		resource := "virtualmachineinstances"

		Context("with correct permissions", func() {
			It("should be allowed to access subresource endpoint", func() {
				testClientJob(virtCli, true, resource)
			}, 15)
		})
		Context("Without permissions", func() {
			It("should not be able to access subresource endpoint", func() {
				testClientJob(virtCli, false, resource)
			}, 15)
		})
	})

	Describe("Rbac Authorization For Version Command", func() {
		resource := "version"

		Context("with authenticated user", func() {
			It("should be allowed to access subresource version endpoint", func() {
				testClientJob(virtCli, true, resource)
			}, 15)
		})
		Context("Without permissions", func() {
			It("should be able to access subresource version endpoint", func() {
				testClientJob(virtCli, false, resource)
			}, 15)
		})
	})

	Describe("[rfe_id:1177][crit:medium][vendor:cnv-qe@redhat.com][level:component] VirtualMachine subresource", func() {
		Context("with a restart endpoint", func() {
			It("[test_id:1304] should restart a VM", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).NotTo(HaveOccurred())

				tests.StartVirtualMachine(vm)
				vmi, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())
				Expect(vmi.Status.Phase).To(Equal(v1.Running))

				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).NotTo(HaveOccurred())

				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil || vmi.UID == newVMI.UID {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})

			It("should return an error when VM is not running", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).NotTo(HaveOccurred())

				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).To(HaveOccurred())
			})

			It("should return an error when VM has not been found but VMI is running", func() {
				vmi := tests.NewRandomVMI()
				tests.RunVMIAndExpectLaunch(vmi, 60)

				err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vmi.Name)
				Expect(err).To(HaveOccurred())
			})
		})

		Context("With manual RunStrategy", func() {
			It("Should restart when VM is not running", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm.Spec.RunStrategy = &manual
				vm.Spec.Running = nil

				By("Creating VM")
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).ToNot(HaveOccurred())

				By("Starting VM via Restart subresource")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				By("Waiting for VMI to start")
				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})

			It("Should restart when VM is running", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm.Spec.RunStrategy = &manual
				vm.Spec.Running = nil

				By("Creating VM")
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).ToNot(HaveOccurred())

				By("Starting VM via Restart subresource")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				By("Waiting for VMI to start")
				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))

				vmi, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())

				By("Restarting VM")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil || vmi.UID == newVMI.UID {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})
		})

		Context("With RunStrategy RerunOnFailure", func() {
			It("Should restart the VM", func() {
				vm := tests.NewRandomVirtualMachine(tests.NewRandomVMI(), false)
				vm.Spec.RunStrategy = &restartOnError
				vm.Spec.Running = nil

				By("Creating VM")
				vm, err := virtCli.VirtualMachine(tests.NamespaceTestDefault).Create(vm)
				Expect(err).ToNot(HaveOccurred())

				By("Waiting for VMI to start")
				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))

				vmi, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
				Expect(err).NotTo(HaveOccurred())

				By("Restarting VM")
				err = virtCli.VirtualMachine(tests.NamespaceTestDefault).Restart(vm.Name)
				Expect(err).ToNot(HaveOccurred())

				Eventually(func() v1.VirtualMachineInstancePhase {
					newVMI, err := virtCli.VirtualMachineInstance(tests.NamespaceTestDefault).Get(vm.Name, &metav1.GetOptions{})
					if err != nil || vmi.UID == newVMI.UID {
						return v1.VmPhaseUnset
					}
					return newVMI.Status.Phase
				}, 90*time.Second, 1*time.Second).Should(Equal(v1.Running))
			})
		})
	})

	Describe("the openapi spec for the subresources", func() {
		It("should be aggregated into the the apiserver openapi spec", func() {
			Eventually(func() string {
				spec, err := virtCli.RestClient().Get().AbsPath("/openapi/v2").DoRaw()
				Expect(err).ToNot(HaveOccurred())
				return string(spec)
			}, 60*time.Second, 1*time.Second).Should(ContainSubstring("subresources.kubevirt.io"))
		})
	})
})

func testClientJob(virtCli kubecli.KubevirtClient, withServiceAccount bool, resource string) {
	namespace := tests.NamespaceTestDefault
	expectedPhase := k8sv1.PodFailed
	name := "subresource-access-tester"
	job := &k8sv1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: name,
			Labels: map[string]string{
				v1.AppLabel: tests.SubresourceTestLabel,
			},
		},
		Spec: k8sv1.PodSpec{
			RestartPolicy: k8sv1.RestartPolicyNever,
			Containers: []k8sv1.Container{
				{
					Name:    name,
					Image:   fmt.Sprintf("%s/subresource-access-test:%s", tests.KubeVirtRepoPrefix, tests.KubeVirtVersionTag),
					Command: []string{"/subresource-access-test", resource},
				},
			},
		},
	}

	if withServiceAccount {
		job.Spec.ServiceAccountName = tests.SubresourceServiceAccountName
		expectedPhase = k8sv1.PodSucceeded
	} else if resource == "version" {
		expectedPhase = k8sv1.PodSucceeded
	}

	pod, err := virtCli.CoreV1().Pods(namespace).Create(job)
	Expect(err).ToNot(HaveOccurred())

	getStatus := func() k8sv1.PodPhase {
		pod, err := virtCli.CoreV1().Pods(namespace).Get(pod.Name, metav1.GetOptions{})
		Expect(err).ToNot(HaveOccurred())
		return pod.Status.Phase
	}

	Eventually(getStatus, 30, 0.5).Should(Equal(expectedPhase))
}
<|endoftext|>


<commit_before>package main

// #cgo LDFLAGS: -lm
// #include "tclled.h"
import "C"
import "errors"

type Strand struct {
	device   C.int
	buffer   *_Ctype_tcl_buffer
	ledCount int
}

func (s *Strand) Connect(ledCount int) error {
	s.ledCount = ledCount
	s.device = C.open_device()

	if s.device <= 0 {
		return errors.New("Device init failed")
	}

	C.set_gamma(2.2, 2.2, 2.2)
	spiStatus := C.spi_init(s.device)
	if spiStatus != 0 {
		return errors.New("SPI init failed")
	}

	s.buffer = &C.tcl_buffer{}
	tclStatus := C.tcl_init(s.buffer, C.int(s.ledCount))
	if tclStatus != 0 {
		return errors.New("TCL init failed")
	}

	for i := 0; i < ledCount; i++ {
		s.SetColor(i, 0, 0, 0)
	}
	s.Save()

	return nil
}

func (s *Strand) Free() error {
	C.tcl_free(s.buffer)
	C.close_device(s.device)

	return nil
}

func (s *Strand) SetColor(ledNumber int, r int, g int, b int) {
	C.write_gamma_color_to_buffer(s.buffer, C.int(ledNumber), C.uint8_t(r), C.uint8_t(g), C.uint8_t(b))
}

func (s *Strand) Save() {
	C.send_buffer(s.device, s.buffer)
}
<commit_msg>Add new struct C syntax to fix for go 1.3<commit_after>package main

// #cgo LDFLAGS: -lm
// #include "tclled.h"
import "C"
import "errors"

type Strand struct {
	device   C.int
	buffer   *C.struct__tcl_buffer
	ledCount int
}

func (s *Strand) Connect(ledCount int) error {
	s.ledCount = ledCount
	s.device = C.open_device()

	if s.device <= 0 {
		return errors.New("Device init failed")
	}

	C.set_gamma(2.2, 2.2, 2.2)
	spiStatus := C.spi_init(s.device)
	if spiStatus != 0 {
		return errors.New("SPI init failed")
	}

	s.buffer = &C.struct__tcl_buffer{}
	tclStatus := C.tcl_init(s.buffer, C.int(s.ledCount))
	if tclStatus != 0 {
		return errors.New("TCL init failed")
	}

	for i := 0; i < ledCount; i++ {
		s.SetColor(i, 0, 0, 0)
	}
	s.Save()

	return nil
}

func (s *Strand) Free() error {
	C.tcl_free(s.buffer)
	C.close_device(s.device)

	return nil
}

func (s *Strand) SetColor(ledNumber int, r int, g int, b int) {
	C.write_gamma_color_to_buffer(s.buffer, C.int(ledNumber), C.uint8_t(r), C.uint8_t(g), C.uint8_t(b))
}

func (s *Strand) Save() {
	C.send_buffer(s.device, s.buffer)
}
<|endoftext|>


<commit_before>package stream

import (
	"bufio"
	"github.com/diggs/glog"
	"github.com/diggs/go-backoff"
	"net/http"
	"time"
)

/**
TODO:
Fix up close
Args for headers and SSL
Tests
**/

const (
	STREAM_INACTIVITY_TIMEOUT_SECONDS int = 90
)

type httpStream struct {
	Url                 string
	Data                chan []byte
	Exit                chan bool
	tcpBackoff          *backoff.Backoff
	httpBackoff         *backoff.Backoff
	httpThrottleBackoff *backoff.Backoff
}

func (s *httpStream) Connect() error {
	go s.enterReadStreamLoop()
	return nil
}

func (s *httpStream) Close() {
	s.Exit <- true
}

func (s *httpStream) resetBackoffs() {
	s.tcpBackoff.Reset()
	s.httpBackoff.Reset()
	s.httpThrottleBackoff.Reset()
}

func (s *httpStream) connect() (*http.Response, error) {

	glog.Debugf("Establishing connection to %s...", s.Url)

	client := &http.Client{}

	req, err := http.NewRequest("GET", s.Url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func (s *httpStream) enterReadStreamLoop() {

	glog.Debug("Entering read stream loop...")
	for {
		select {
		case <-s.Exit:
			glog.Debug("Exit signalled; leaving read stream loop.")
			return
		default:
			resp, err := s.connect()
			// TODO Differentiate between transient tcp/ip errors and fatal errors (such as malformed url etc.)
			if err != nil {
				glog.Debugf("Encountered error establishing connection: %v", err)
				glog.Debugf("Backing off %d milliseconds", s.tcpBackoff.GetBackoffDuration()/time.Millisecond)
				s.tcpBackoff.Backoff()
				continue
			}

			switch resp.StatusCode {
			case 200, 304:
				glog.Debug("Connection established...")
				s.resetBackoffs()
				s.enterReadLineLoop(resp)
			case 420:
				glog.Debug("Encountered 420 backoff code")
				glog.Debugf("Backing off %d minute(s)", s.httpThrottleBackoff.GetBackoffDuration()/time.Minute)
				s.httpThrottleBackoff.Backoff()
			default:
				// TODO: Fatal errors... 401 etc.
				glog.Debugf("Encountered %v status code", resp.StatusCode)
				glog.Debugf("Backing off %d second(s)", s.httpBackoff.GetBackoffDuration()/time.Second)
				s.httpBackoff.Backoff()
			}
			resp.Body.Close()
		}
	}
}

func (s *httpStream) enterReadLineLoop(resp *http.Response) {

	glog.Debug("Entering read line loop...")

	scanner := bufio.NewScanner(resp.Body)
	for {
		lineCh, errCh := s.readLine(resp, scanner)
		select {
		case data := <-lineCh:
			glog.Debugf("Read line from stream: %d bytes.", len(data))
			if len(data) > 0 { // drop empty heartbeat lines
				s.Data <- data
			}
		case <-s.Exit:
			glog.Debug("Exit signalled; leaving readLine loop.")
			return
		case err := <-errCh:
			glog.Debugf("Stream error; leaving readLine loop: %v", err)
			return
		case <-time.After(time.Duration(STREAM_INACTIVITY_TIMEOUT_SECONDS) * time.Second):
			glog.Debugf("Stream inactive for %d seconds; leaving readLine loop.", STREAM_INACTIVITY_TIMEOUT_SECONDS)
			return
		}
	}
}

func (s *httpStream) readLine(resp *http.Response, scanner *bufio.Scanner) (chan []byte, chan error) {
	glog.Debug("Scanning for line...")
	lineCh := make(chan []byte)
	errCh := make(chan error)
	go func() {
		if ok := scanner.Scan(); !ok {
			errCh <- scanner.Err()
			return
		}
		lineCh <- scanner.Bytes()[:]
	}()
	return lineCh, errCh
}

func NewStream(url string, autoConnect bool) *httpStream {
	s := httpStream{}
	s.Url = url
	s.Data = make(chan []byte)
	s.Exit = make(chan bool)
	// Back off linearly, starting at 250ms, capping at 16 seconds
	s.tcpBackoff = backoff.NewLinear(250*time.Millisecond, 16*time.Second)
	// Back off exponentially, starting at 5 seconds, capping at 320 seconds
	s.httpBackoff = backoff.NewExponential(5*time.Second, 320*time.Second)
	// Back off exponentially, starting at 1 minute, with no cap
	s.httpThrottleBackoff = backoff.NewExponential(time.Minute, 0)
	if autoConnect {
		s.Connect()
	}
	return &s
}
<commit_msg>Implement Close() properly using waitGroup to ensure final line is delivered to receiver.<commit_after>package stream

import (
	"bufio"
	"github.com/diggs/glog"
	"github.com/diggs/go-backoff"
	"net/http"
	"sync"
	"time"
)

/**
TODO:
Args for headers and SSL
Tests
**/

const (
	STREAM_INACTIVITY_TIMEOUT_SECONDS int = 90
)

type httpStream struct {
	Url                 string
	Data                chan []byte
	Exit                chan bool
	exiting             bool
	waitGroup           *sync.WaitGroup
	tcpBackoff          *backoff.Backoff
	httpBackoff         *backoff.Backoff
	httpThrottleBackoff *backoff.Backoff
}

func (s *httpStream) Connect() error {
	go s.enterReadStreamLoop()
	return nil
}

func (s *httpStream) Close() {
	if s.exiting {
		return
	}
	s.exiting = true
	close(s.Exit)
	go func() {
		s.waitGroup.Wait()
		close(s.Data)
	}()
}

func (s *httpStream) resetBackoffs() {
	s.tcpBackoff.Reset()
	s.httpBackoff.Reset()
	s.httpThrottleBackoff.Reset()
}

func (s *httpStream) connect() (*http.Response, error) {

	glog.Debugf("Establishing connection to %s...", s.Url)

	client := &http.Client{}
	req, err := http.NewRequest("GET", s.Url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func (s *httpStream) enterReadStreamLoop() {
	s.waitGroup.Add(1)
	defer s.waitGroup.Done()

	glog.Debug("Entering read stream loop...")
	for {
		select {
		case <-s.Exit:
			glog.Debug("Exit signalled; leaving read stream loop.")
			return
		default:
			resp, err := s.connect()
			defer resp.Body.Close()
			// TODO Differentiate between transient tcp/ip errors and fatal errors (such as malformed url etc.)
			if err != nil {
				glog.Debugf("Encountered error establishing connection: %v", err)
				glog.Debugf("Backing off %d milliseconds", s.tcpBackoff.NextDuration/time.Millisecond)
				s.tcpBackoff.Backoff()
				continue
			}
			switch resp.StatusCode {
			case 200, 304:
				glog.Debug("Connection established...")
				s.resetBackoffs()
				s.enterReadLineLoop(resp)
			case 420:
				glog.Debug("Encountered 420 backoff code")
				glog.Debugf("Backing off %d minute(s)", s.httpThrottleBackoff.NextDuration/time.Minute)
				s.httpThrottleBackoff.Backoff()
			default:
				// TODO: Fatal errors... 401 etc.
				glog.Debugf("Encountered %v status code", resp.StatusCode)
				glog.Debugf("Backing off %d second(s)", s.httpBackoff.NextDuration/time.Second)
				s.httpBackoff.Backoff()
			}
		}
	}
}

func (s *httpStream) enterReadLineLoop(resp *http.Response) {

	glog.Debug("Entering read line loop...")

	scanner := bufio.NewScanner(resp.Body)
	for {
		lineCh, errCh := s.readLine(resp, scanner)
		select {
		case data := <-lineCh:
			glog.Debugf("Read line from stream: %d bytes.", len(data))
			if len(data) > 0 { // drop empty heartbeat lines
				s.Data <- data
			}
		case <-s.Exit:
			glog.Debug("Exit signalled; leaving readLine loop.")
			return
		case err := <-errCh:
			glog.Debugf("Stream error; leaving readLine loop: %v", err)
			return
		case <-time.After(time.Duration(STREAM_INACTIVITY_TIMEOUT_SECONDS) * time.Second):
			glog.Debugf("Stream inactive for %d seconds; leaving readLine loop.", STREAM_INACTIVITY_TIMEOUT_SECONDS)
			return
		}
	}
}

func (s *httpStream) readLine(resp *http.Response, scanner *bufio.Scanner) (chan []byte, chan error) {
	glog.Debug("Scanning for line...")
	lineCh := make(chan []byte)
	errCh := make(chan error)
	go func() {
		if ok := scanner.Scan(); !ok {
			errCh <- scanner.Err()
			return
		}
		lineCh <- scanner.Bytes()[:]
	}()
	return lineCh, errCh
}

func NewStream(url string, autoConnect bool) *httpStream {
	s := httpStream{}
	s.Url = url
	s.Data = make(chan []byte)
	s.Exit = make(chan bool)
	s.waitGroup = &sync.WaitGroup{}
	// Back off linearly, starting at 250ms, capping at 16 seconds
	s.tcpBackoff = backoff.NewLinear(250*time.Millisecond, 16*time.Second)
	// Back off exponentially, starting at 5 seconds, capping at 320 seconds
	s.httpBackoff = backoff.NewExponential(5*time.Second, 320*time.Second)
	// Back off exponentially, starting at 1 minute, with no cap
	s.httpThrottleBackoff = backoff.NewExponential(time.Minute, 0)
	if autoConnect {
		s.Connect()
	}
	return &s
}
<|endoftext|>


<commit_before>package common

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"unicode"

	"github.com/ryanuber/columnize"
)

type errfunc func() error

// AppRoot returns the app root path
func AppRoot(appName string) string {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	return fmt.Sprintf("%v/%v", dokkuRoot, appName)
}

// AppHostRoot returns the app root path
func AppHostRoot(appName string) string {
	dokkuHostRoot := MustGetEnv("DOKKU_HOST_ROOT")
	return fmt.Sprintf("%v/%v", dokkuHostRoot, appName)
}

// AskForDestructiveConfirmation checks for confirmation on destructive actions
func AskForDestructiveConfirmation(name string, objectType string) error {
	LogWarn("WARNING: Potentially Destructive Action")
	LogWarn(fmt.Sprintf("This command will destroy %v %v.", objectType, name))
	LogWarn(fmt.Sprintf("To proceed, type \"%v\"", name))
	fmt.Print("> ")
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		return err
	}

	if response != name {
		LogStderr("Confirmation did not match test. Aborted.")
		os.Exit(1)
		return nil
	}

	return nil
}

// CommandUsage outputs help for a command
func CommandUsage(helpHeader string, helpContent string) {
	config := columnize.DefaultConfig()
	config.Delim = ","
	config.Prefix = "    "
	config.Empty = ""
	content := strings.Split(helpContent, "\n")[1:]
	fmt.Println(helpHeader)
	fmt.Println(columnize.Format(content, config))
}

// GetAppScheduler fetches the scheduler for a given application
func GetAppScheduler(appName string) string {
	if appName == "--global" {
		appName = ""
	}

	b, _ := PlugnTriggerOutput("config-get", []string{appName, "DOKKU_SCHEDULER"}...)
	value := strings.TrimSpace(string(b[:]))
	if value != "" {
		return value
	}

	return GetGlobalScheduler()
}

// GetGlobalScheduler fetchs the global scheduler
func GetGlobalScheduler() string {
	b, _ := PlugnTriggerOutput("config-get-global", []string{"DOKKU_SCHEDULER"}...)
	value := strings.TrimSpace(string(b[:]))
	if value != "" {
		return value
	}

	return "docker-local"
}

// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (string, error) {
	b, err := PlugnTriggerOutput("deployed-app-repository", []string{appName}...)
	if err != nil {
		return "", err
	}
	imageRemoteRepository := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-tag", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageTag := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-repo", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageRepo := strings.TrimSpace(string(b[:]))

	if newImageRepo != "" {
		imageRepo = newImageRepo
	}
	if newImageTag != "" {
		imageTag = newImageTag
	}
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}
	if imageTag == "" {
		imageTag = "latest"
	}

	imageName := fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
	if !VerifyImage(imageName) {
		return "", fmt.Errorf("App image (%s) not found", imageName)
	}
	return imageName, nil
}

// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
	return strings.Join([]string{"dokku", appName}, "/")
}

// GetAppContainerIDs returns a list of docker container ids for given app and optional container_type
func GetAppContainerIDs(appName string, containerType string) ([]string, error) {
	var containerIDs []string
	appRoot := AppRoot(appName)
	containerFilePath := fmt.Sprintf("%v/CONTAINER", appRoot)
	_, err := os.Stat(containerFilePath)
	if !os.IsNotExist(err) {
		containerIDs = append(containerIDs, ReadFirstLine(containerFilePath))
	}

	containerPattern := fmt.Sprintf("%v/CONTAINER.*", appRoot)
	if containerType != "" {
		containerPattern = fmt.Sprintf("%v/CONTAINER.%v.*", appRoot, containerType)
		if strings.Contains(".", containerType) {
			containerPattern = fmt.Sprintf("%v/CONTAINER.%v", appRoot, containerType)
		}
	}

	files, _ := filepath.Glob(containerPattern)
	for _, containerFile := range files {
		containerIDs = append(containerIDs, ReadFirstLine(containerFile))
	}

	return containerIDs, nil
}

// GetAppRunningContainerIDs return a list of running docker container ids for given app and optional container_type
func GetAppRunningContainerIDs(appName string, containerType string) ([]string, error) {
	var runningContainerIDs []string
	if !IsDeployed(appName) {
		LogFail(fmt.Sprintf("App %v has not been deployed", appName))
	}

	containerIDs, err := GetAppContainerIDs(appName, containerType)
	if err != nil {
		return runningContainerIDs, nil
	}
	for _, containerID := range containerIDs {
		if ContainerIsRunning(containerID) {
			runningContainerIDs = append(runningContainerIDs, containerID)
		}
	}

	return runningContainerIDs, nil
}

// GetRunningImageTag retrieves current image tag for a given app and returns empty string if no deployed containers are found
func GetRunningImageTag(appName string) (string, error) {
	containerIDs, err := GetAppContainerIDs(appName, "")
	if err != nil {
		return "", err
	}

	for _, containerID := range containerIDs {
		if image, err := DockerInspect(containerID, "{{ .Config.Image }}"); err == nil {
			return strings.Split(image, ":")[1], nil
		}
	}

	return "", errors.New("No image tag found")
}

// DokkuApps returns a list of all local apps
func DokkuApps() (apps []string, err error) {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	files, err := ioutil.ReadDir(dokkuRoot)
	if err != nil {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	for _, f := range files {
		appRoot := AppRoot(f.Name())
		if !DirectoryExists(appRoot) {
			continue
		}
		if strings.HasPrefix(f.Name(), ".") {
			continue
		}
		apps = append(apps, f.Name())
	}

	if len(apps) == 0 {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	return
}

// GetAppImageName returns image identifier for a given app, tag tuple. validate if tag is presented
func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}

	if imageTag == "" {
		imageName = fmt.Sprintf("%v:latest", imageRepo)
	} else {
		imageName = fmt.Sprintf("%v:%v", imageRepo, imageTag)
		if !VerifyImage(imageName) {
			LogFail(fmt.Sprintf("App image (%s) not found", imageName))
		}
	}
	return
}

// IsDeployed returns true if given app has a running container
func IsDeployed(appName string) bool {
	scheduler := GetAppScheduler(appName)
	_, err := PlugnTriggerOutput("scheduler-is-deployed", []string{scheduler, appName}...)
	return err == nil
}

// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		LogFail(fmt.Sprintf("%s not set!", key))
	}
	return
}

// GetenvWithDefault returns env variable or defaultValue if it's not set
func GetenvWithDefault(key string, defaultValue string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		val = defaultValue
	}
	return
}

// ParseReportArgs splits out flags from non-flags for input into report commands
func ParseReportArgs(pluginName string, arguments []string) ([]string, string, error) {
	osArgs := []string{}
	infoFlags := []string{}
	for _, argument := range arguments {
		if strings.HasPrefix(argument, "--") {
			infoFlags = append(infoFlags, argument)
		} else {
			osArgs = append(osArgs, argument)
		}
	}

	if len(infoFlags) == 0 {
		return osArgs, "", nil
	}
	if len(infoFlags) == 1 {
		return osArgs, infoFlags[0], nil
	}
	return osArgs, "", fmt.Errorf("%s:report command allows only a single flag", pluginName)
}

// ReportSingleApp is an internal function that displays a report for an app
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
	flags := []string{}
	for key := range infoFlags {
		flags = append(flags, key)
	}
	sort.Strings(flags)

	if len(infoFlag) == 0 {
		LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
		for _, k := range flags {
			v := infoFlags[k]
			prefix := "--"
			if trimPrefix {
				prefix = fmt.Sprintf("--%v-", reportType)
			}

			key := strings.Replace(strings.Replace(strings.TrimPrefix(k, prefix), "-", " ", -1), ".", " ", -1)

			if uppercaseFirstCharacter {
				key = UcFirst(key)
			}

			LogVerbose(fmt.Sprintf("%s%s", RightPad(fmt.Sprintf("%s:", key), 31, " "), v))
		}
		return nil
	}

	for _, k := range flags {
		if infoFlag == k {
			v := infoFlags[k]
			fmt.Println(v)
			return nil
		}
	}

	keys := reflect.ValueOf(infoFlags).MapKeys()
	strkeys := make([]string, len(keys))
	for i := 0; i < len(keys); i++ {
		strkeys[i] = keys[i].String()
	}

	return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))
}

// RightPad right-pads the string with pad up to len runes
func RightPad(str string, length int, pad string) string {
	return str + times(pad, length-len(str))
}

// ShiftString removes the first and returns that entry as well as the rest of the list
func ShiftString(a []string) (string, []string) {
	if len(a) == 0 {
		return "", a
	}

	return a[0], a[1:]
}

// StripInlineComments removes bash-style comment from input line
func StripInlineComments(text string) string {
	b := []byte(text)
	re := regexp.MustCompile("(?s)#.*")
	b = re.ReplaceAll(b, nil)
	return strings.TrimSpace(string(b))
}

// SuppressOutput suppresses the output of a function unless there is an error
func SuppressOutput(f errfunc) error {
	rescueStdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	err := f()

	w.Close()
	out, _ := ioutil.ReadAll(r)
	os.Stdout = rescueStdout

	if err != nil {
		fmt.Printf(string(out[:]))
	}

	return err
}

// ToBool returns a bool value for a given string
func ToBool(s string) bool {
	return s == "true"
}

// ToInt returns an int value for a given string
func ToInt(s string, defaultValue int) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		return defaultValue
	}

	return i
}

// UcFirst uppercases the first character in a string
func UcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}

// IsValidAppName verifies that the app name matches naming restrictions
func IsValidAppName(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:_A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, colons, or underscores")
}

// isValidAppNameOld verifies that the app name matches the old naming restrictions
func isValidAppNameOld(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, or colons")
}

// VerifyAppName checks if an app conforming to either the old or new
// naming conventions exists
func VerifyAppName(appName string) error {
	newErr := IsValidAppName(appName)
	oldErr := isValidAppNameOld(appName)
	if newErr != nil && oldErr != nil {
		return newErr
	}

	appRoot := AppRoot(appName)
	if !DirectoryExists(appRoot) {
		return fmt.Errorf("App %s does not exist", appName)
	}

	return nil
}

func times(str string, n int) (out string) {
	for i := 0; i < n; i++ {
		out += str
	}
	return
}
<commit_msg>refactor: parallelize scheduler retrieval<commit_after>package common

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"unicode"

	"github.com/ryanuber/columnize"
)

type errfunc func() error

// AppRoot returns the app root path
func AppRoot(appName string) string {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	return fmt.Sprintf("%v/%v", dokkuRoot, appName)
}

// AppHostRoot returns the app root path
func AppHostRoot(appName string) string {
	dokkuHostRoot := MustGetEnv("DOKKU_HOST_ROOT")
	return fmt.Sprintf("%v/%v", dokkuHostRoot, appName)
}

// AskForDestructiveConfirmation checks for confirmation on destructive actions
func AskForDestructiveConfirmation(name string, objectType string) error {
	LogWarn("WARNING: Potentially Destructive Action")
	LogWarn(fmt.Sprintf("This command will destroy %v %v.", objectType, name))
	LogWarn(fmt.Sprintf("To proceed, type \"%v\"", name))
	fmt.Print("> ")
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		return err
	}

	if response != name {
		LogStderr("Confirmation did not match test. Aborted.")
		os.Exit(1)
		return nil
	}

	return nil
}

// CommandUsage outputs help for a command
func CommandUsage(helpHeader string, helpContent string) {
	config := columnize.DefaultConfig()
	config.Delim = ","
	config.Prefix = "    "
	config.Empty = ""
	content := strings.Split(helpContent, "\n")[1:]
	fmt.Println(helpHeader)
	fmt.Println(columnize.Format(content, config))
}

// GetAppScheduler fetches the scheduler for a given application
func GetAppScheduler(appName string) string {
	appScheduler := ""
	globalScheduler := ""

	var wg sync.WaitGroup

	if appName != "--global" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			b, _ := PlugnTriggerOutput("config-get", []string{appName, "DOKKU_SCHEDULER"}...)
			value := strings.TrimSpace(string(b[:]))
			if value != "" {
				appScheduler = value
			}
		}()
	}
	wg.Add(1)
	go func() {
		defer wg.Done()
		globalScheduler = GetGlobalScheduler()
	}()

	wg.Wait()

	if appScheduler == "" {
		appScheduler = globalScheduler
	}
	return appScheduler
}

// GetGlobalScheduler fetchs the global scheduler
func GetGlobalScheduler() string {
	b, _ := PlugnTriggerOutput("config-get-global", []string{"DOKKU_SCHEDULER"}...)
	value := strings.TrimSpace(string(b[:]))
	if value != "" {
		return value
	}

	return "docker-local"
}

// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (string, error) {
	b, err := PlugnTriggerOutput("deployed-app-repository", []string{appName}...)
	if err != nil {
		return "", err
	}
	imageRemoteRepository := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-tag", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageTag := strings.TrimSpace(string(b[:]))

	b, err = PlugnTriggerOutput("deployed-app-image-repo", []string{appName}...)
	if err != nil {
		return "", err
	}
	newImageRepo := strings.TrimSpace(string(b[:]))

	if newImageRepo != "" {
		imageRepo = newImageRepo
	}
	if newImageTag != "" {
		imageTag = newImageTag
	}
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}
	if imageTag == "" {
		imageTag = "latest"
	}

	imageName := fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
	if !VerifyImage(imageName) {
		return "", fmt.Errorf("App image (%s) not found", imageName)
	}
	return imageName, nil
}

// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
	return strings.Join([]string{"dokku", appName}, "/")
}

// GetAppContainerIDs returns a list of docker container ids for given app and optional container_type
func GetAppContainerIDs(appName string, containerType string) ([]string, error) {
	var containerIDs []string
	appRoot := AppRoot(appName)
	containerFilePath := fmt.Sprintf("%v/CONTAINER", appRoot)
	_, err := os.Stat(containerFilePath)
	if !os.IsNotExist(err) {
		containerIDs = append(containerIDs, ReadFirstLine(containerFilePath))
	}

	containerPattern := fmt.Sprintf("%v/CONTAINER.*", appRoot)
	if containerType != "" {
		containerPattern = fmt.Sprintf("%v/CONTAINER.%v.*", appRoot, containerType)
		if strings.Contains(".", containerType) {
			containerPattern = fmt.Sprintf("%v/CONTAINER.%v", appRoot, containerType)
		}
	}

	files, _ := filepath.Glob(containerPattern)
	for _, containerFile := range files {
		containerIDs = append(containerIDs, ReadFirstLine(containerFile))
	}

	return containerIDs, nil
}

// GetAppRunningContainerIDs return a list of running docker container ids for given app and optional container_type
func GetAppRunningContainerIDs(appName string, containerType string) ([]string, error) {
	var runningContainerIDs []string
	if !IsDeployed(appName) {
		LogFail(fmt.Sprintf("App %v has not been deployed", appName))
	}

	containerIDs, err := GetAppContainerIDs(appName, containerType)
	if err != nil {
		return runningContainerIDs, nil
	}
	for _, containerID := range containerIDs {
		if ContainerIsRunning(containerID) {
			runningContainerIDs = append(runningContainerIDs, containerID)
		}
	}

	return runningContainerIDs, nil
}

// GetRunningImageTag retrieves current image tag for a given app and returns empty string if no deployed containers are found
func GetRunningImageTag(appName string) (string, error) {
	containerIDs, err := GetAppContainerIDs(appName, "")
	if err != nil {
		return "", err
	}

	for _, containerID := range containerIDs {
		if image, err := DockerInspect(containerID, "{{ .Config.Image }}"); err == nil {
			return strings.Split(image, ":")[1], nil
		}
	}

	return "", errors.New("No image tag found")
}

// DokkuApps returns a list of all local apps
func DokkuApps() (apps []string, err error) {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	files, err := ioutil.ReadDir(dokkuRoot)
	if err != nil {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	for _, f := range files {
		appRoot := AppRoot(f.Name())
		if !DirectoryExists(appRoot) {
			continue
		}
		if strings.HasPrefix(f.Name(), ".") {
			continue
		}
		apps = append(apps, f.Name())
	}

	if len(apps) == 0 {
		err = fmt.Errorf("You haven't deployed any applications yet")
		return
	}

	return
}

// GetAppImageName returns image identifier for a given app, tag tuple. validate if tag is presented
func GetAppImageName(appName, imageTag, imageRepo string) (imageName string) {
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}

	if imageTag == "" {
		imageName = fmt.Sprintf("%v:latest", imageRepo)
	} else {
		imageName = fmt.Sprintf("%v:%v", imageRepo, imageTag)
		if !VerifyImage(imageName) {
			LogFail(fmt.Sprintf("App image (%s) not found", imageName))
		}
	}
	return
}

// IsDeployed returns true if given app has a running container
func IsDeployed(appName string) bool {
	scheduler := GetAppScheduler(appName)
	_, err := PlugnTriggerOutput("scheduler-is-deployed", []string{scheduler, appName}...)
	return err == nil
}

// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		LogFail(fmt.Sprintf("%s not set!", key))
	}
	return
}

// GetenvWithDefault returns env variable or defaultValue if it's not set
func GetenvWithDefault(key string, defaultValue string) (val string) {
	val = os.Getenv(key)
	if val == "" {
		val = defaultValue
	}
	return
}

// ParseReportArgs splits out flags from non-flags for input into report commands
func ParseReportArgs(pluginName string, arguments []string) ([]string, string, error) {
	osArgs := []string{}
	infoFlags := []string{}
	for _, argument := range arguments {
		if strings.HasPrefix(argument, "--") {
			infoFlags = append(infoFlags, argument)
		} else {
			osArgs = append(osArgs, argument)
		}
	}

	if len(infoFlags) == 0 {
		return osArgs, "", nil
	}
	if len(infoFlags) == 1 {
		return osArgs, infoFlags[0], nil
	}
	return osArgs, "", fmt.Errorf("%s:report command allows only a single flag", pluginName)
}

// ReportSingleApp is an internal function that displays a report for an app
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
	flags := []string{}
	for key := range infoFlags {
		flags = append(flags, key)
	}
	sort.Strings(flags)

	if len(infoFlag) == 0 {
		LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
		for _, k := range flags {
			v := infoFlags[k]
			prefix := "--"
			if trimPrefix {
				prefix = fmt.Sprintf("--%v-", reportType)
			}

			key := strings.Replace(strings.Replace(strings.TrimPrefix(k, prefix), "-", " ", -1), ".", " ", -1)

			if uppercaseFirstCharacter {
				key = UcFirst(key)
			}

			LogVerbose(fmt.Sprintf("%s%s", RightPad(fmt.Sprintf("%s:", key), 31, " "), v))
		}
		return nil
	}

	for _, k := range flags {
		if infoFlag == k {
			v := infoFlags[k]
			fmt.Println(v)
			return nil
		}
	}

	keys := reflect.ValueOf(infoFlags).MapKeys()
	strkeys := make([]string, len(keys))
	for i := 0; i < len(keys); i++ {
		strkeys[i] = keys[i].String()
	}

	return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))
}

// RightPad right-pads the string with pad up to len runes
func RightPad(str string, length int, pad string) string {
	return str + times(pad, length-len(str))
}

// ShiftString removes the first and returns that entry as well as the rest of the list
func ShiftString(a []string) (string, []string) {
	if len(a) == 0 {
		return "", a
	}

	return a[0], a[1:]
}

// StripInlineComments removes bash-style comment from input line
func StripInlineComments(text string) string {
	b := []byte(text)
	re := regexp.MustCompile("(?s)#.*")
	b = re.ReplaceAll(b, nil)
	return strings.TrimSpace(string(b))
}

// SuppressOutput suppresses the output of a function unless there is an error
func SuppressOutput(f errfunc) error {
	rescueStdout := os.Stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	err := f()

	w.Close()
	out, _ := ioutil.ReadAll(r)
	os.Stdout = rescueStdout

	if err != nil {
		fmt.Printf(string(out[:]))
	}

	return err
}

// ToBool returns a bool value for a given string
func ToBool(s string) bool {
	return s == "true"
}

// ToInt returns an int value for a given string
func ToInt(s string, defaultValue int) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		return defaultValue
	}

	return i
}

// UcFirst uppercases the first character in a string
func UcFirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}

// IsValidAppName verifies that the app name matches naming restrictions
func IsValidAppName(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:_A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, colons, or underscores")
}

// isValidAppNameOld verifies that the app name matches the old naming restrictions
func isValidAppNameOld(appName string) error {
	if appName == "" {
		return errors.New("Please specify an app to run the command on")
	}

	r, _ := regexp.Compile("^[a-z0-9][^/:A-Z]*$")
	if r.MatchString(appName) {
		return nil
	}

	return errors.New("App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, or colons")
}

// VerifyAppName checks if an app conforming to either the old or new
// naming conventions exists
func VerifyAppName(appName string) error {
	newErr := IsValidAppName(appName)
	oldErr := isValidAppNameOld(appName)
	if newErr != nil && oldErr != nil {
		return newErr
	}

	appRoot := AppRoot(appName)
	if !DirectoryExists(appRoot) {
		return fmt.Errorf("App %s does not exist", appName)
	}

	return nil
}

func times(str string, n int) (out string) {
	for i := 0; i < n; i++ {
		out += str
	}
	return
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"

	"github.com/dokku/dokku/plugins/common"
	configenv "github.com/dokku/dokku/plugins/config/src/configenv"
	columnize "github.com/ryanuber/columnize"
)

//GetWithDefault gets a value from a config. If appName is empty the global config is used.
func GetWithDefault(appName string, key string, defaultValue string) string {
	env, err := loadConfig(appName)
	if err != nil {
		return defaultValue
	}
	return env.GetDefault(key, defaultValue)
}

// GetWithDefault returns the value set for a given key, returning defaultValue if none found
// func GetWithDefault(appName string, key string, defaultValue string) (value string) {
// 	value = defaultValue

// 	envFile := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName, "ENV"}, "/")
// 	lines, err := common.FileToSlice(envFile)
// 	if err != nil {
// 		return
// 	}
// 	prefix := fmt.Sprintf("export %v=", key)
// 	for _, line := range lines {
// 		if !strings.HasPrefix(line, prefix) {
// 			continue
// 		}
// 		value = strings.TrimPrefix(line, prefix)
// 		if strings.HasPrefix(value, "'") && strings.HasSuffix(value, "'") {
// 			value = strings.TrimPrefix(strings.TrimSuffix(value, "'"), "'")
// 		}
// 	}
// 	return
// }

//HasKey determines if the config given by appName has a value for the given key
func HasKey(appName string, key string) bool {
	env, err := loadConfig(appName)
	if err != nil {
		return false
	}
	_, ok := env.Get(key)
	return ok
}

//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) {
	global := appName == ""
	env := GetConfig(appName, false)

	keys := make([]string, 0, len(entries))

	for k, v := range entries {
		env.Set(k, v)
		keys = append(keys, k)
	}

	if len(entries) != 0 {
		common.LogInfo1("Setting config vars")
		fmt.Println(PrettyPrintEnvEntries("       ", entries))
		env.Write()
		args := append([]string{appName, "set"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}

	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		Restart(appName)
	}
}

//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) {
	global := appName == ""
	env := GetConfig(appName, false)
	var changed = false
	for _, k := range keys {
		common.LogInfo1(fmt.Sprintf("Unsetting %s", k))
		env.Unset(k)
		changed = true
	}

	if changed {
		env.Write()
		args := append([]string{appName, "unset"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}

	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		Restart(appName)
	}
}

//PrettyPrintEnvEntries in columns
func PrettyPrintEnvEntries(prefix string, entries map[string]string) string {
	colConfig := columnize.DefaultConfig()
	colConfig.Prefix = prefix
	colConfig.Delim = "\x00"
	lines := make([]string, 0, len(entries))
	for k, v := range entries {
		lines = append(lines, fmt.Sprintf("%s:\x00%s", k, v))
	}
	return columnize.Format(lines, colConfig)
}

//GetCommonArgs extracts common positional args (appName and keys)
func GetCommonArgs(global bool, args []string) (string, []string) {
	nextArg := 0
	appName := ""
	if !global {
		if len(args) > 0 {
			appName = args[0]
		}
		if appName == "" {
			common.LogFail("Please specify an app or --global")
		} else {
			nextArg++
		}
	}
	keys := args[nextArg:]
	return appName, keys
}

//GetConfig for the given app (global config if appName is empty). Merge with global config if merged is true.
func GetConfig(appName string, merged bool) *configenv.Env {
	env, err := loadConfig(appName)
	if err != nil {
		common.LogFail(err.Error())
	}
	if appName != "" && merged {
		global, err := configenv.LoadGlobal()
		if err != nil {
			common.LogFail(err.Error())
		}
		global.Merge(env)
		return global
	}
	return env
}

//Restart trigger restart on app
func Restart(appName string) {
	common.LogInfo1(fmt.Sprintf("Restarting app %s", appName))
	common.PlugnTrigger("app-restart", appName)
}

func loadConfig(appName string) (*configenv.Env, error) {
	if appName == "" || appName == "--global" {
		return configenv.LoadGlobal()
	}
	return configenv.LoadApp(appName)
}
<commit_msg>[config] remove old code<commit_after>package config

import (
	"fmt"

	"github.com/dokku/dokku/plugins/common"
	configenv "github.com/dokku/dokku/plugins/config/src/configenv"
	columnize "github.com/ryanuber/columnize"
)

//GetWithDefault gets a value from a config. If appName is empty the global config is used.
func GetWithDefault(appName string, key string, defaultValue string) string {
	env, err := loadConfig(appName)
	if err != nil {
		return defaultValue
	}
	return env.GetDefault(key, defaultValue)
}

//HasKey determines if the config given by appName has a value for the given key
func HasKey(appName string, key string) bool {
	env, err := loadConfig(appName)
	if err != nil {
		return false
	}
	_, ok := env.Get(key)
	return ok
}

//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) {
	global := appName == ""
	env := GetConfig(appName, false)

	keys := make([]string, 0, len(entries))

	for k, v := range entries {
		env.Set(k, v)
		keys = append(keys, k)
	}

	if len(entries) != 0 {
		common.LogInfo1("Setting config vars")
		fmt.Println(PrettyPrintEnvEntries("       ", entries))
		env.Write()
		args := append([]string{appName, "set"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}

	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		Restart(appName)
	}
}

//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) {
	global := appName == ""
	env := GetConfig(appName, false)
	var changed = false
	for _, k := range keys {
		common.LogInfo1(fmt.Sprintf("Unsetting %s", k))
		env.Unset(k)
		changed = true
	}

	if changed {
		env.Write()
		args := append([]string{appName, "unset"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}

	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		Restart(appName)
	}
}

//PrettyPrintEnvEntries in columns
func PrettyPrintEnvEntries(prefix string, entries map[string]string) string {
	colConfig := columnize.DefaultConfig()
	colConfig.Prefix = prefix
	colConfig.Delim = "\x00"
	lines := make([]string, 0, len(entries))
	for k, v := range entries {
		lines = append(lines, fmt.Sprintf("%s:\x00%s", k, v))
	}
	return columnize.Format(lines, colConfig)
}

//GetCommonArgs extracts common positional args (appName and keys)
func GetCommonArgs(global bool, args []string) (string, []string) {
	nextArg := 0
	appName := ""
	if !global {
		if len(args) > 0 {
			appName = args[0]
		}
		if appName == "" {
			common.LogFail("Please specify an app or --global")
		} else {
			nextArg++
		}
	}
	keys := args[nextArg:]
	return appName, keys
}

//GetConfig for the given app (global config if appName is empty). Merge with global config if merged is true.
func GetConfig(appName string, merged bool) *configenv.Env {
	env, err := loadConfig(appName)
	if err != nil {
		common.LogFail(err.Error())
	}
	if appName != "" && merged {
		global, err := configenv.LoadGlobal()
		if err != nil {
			common.LogFail(err.Error())
		}
		global.Merge(env)
		return global
	}
	return env
}

//Restart trigger restart on app
func Restart(appName string) {
	common.LogInfo1(fmt.Sprintf("Restarting app %s", appName))
	common.PlugnTrigger("app-restart", appName)
}

func loadConfig(appName string) (*configenv.Env, error) {
	if appName == "" || appName == "--global" {
		return configenv.LoadGlobal()
	}
	return configenv.LoadApp(appName)
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"regexp"

	"github.com/dokku/dokku/plugins/common"
)

//Get retreives a value from a config. If appName is empty the global config is used.
func Get(appName string, key string) (value string, ok bool) {
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return "", false
	}
	if err = validateKey(key); err != nil {
		return "", false
	}
	return env.Get(key)
}

//GetWithDefault gets a value from a config. If appName is empty the global config is used. If the appName or key do not exist defaultValue is returned.
func GetWithDefault(appName string, key string, defaultValue string) (value string) {
	value, ok := Get(appName, key)
	if !ok {
		return defaultValue
	}
	return value
}

//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	keys := make([]string, 0, len(entries))
	for k := range entries {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for k, v := range entries {
		env.Set(k, v)
		keys = append(keys, k)
	}
	if len(entries) != 0 {
		common.LogInfo1("Setting config vars")
		fmt.Println(prettyPrintEnvEntries("       ", entries))
		env.Write()
		args := append([]string{appName, "set"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	var changed = false
	for _, k := range keys {
		if err = validateKey(k); err != nil {
			return
		}
		common.LogInfo1(fmt.Sprintf("Unsetting %s", k))
		env.Unset(k)
		changed = true
	}
	if changed {
		env.Write()
		args := append([]string{appName, "unset"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

func triggerRestart(appName string) {
	common.LogInfo1(fmt.Sprintf("Restarting app %s", appName))
	common.PlugnTrigger("app-restart", appName)
}

func loadAppOrGlobalEnv(appName string) (env *Env, err error) {
	if appName == "" || appName == "--global" {
		return LoadGlobalEnv()
	}
	return LoadAppEnv(appName)
}

func validateKey(key string) error {
	r, _ := regexp.Compile("^[a-zA-Z_][a-zA-Z0-9_]*$")
	if !r.MatchString(key) {
		return fmt.Errorf("Invalid key name: '%s'", key)
	}
	return nil
}
<commit_msg>[config] let user know if they are unsetting a non-existent config key<commit_after>package config

import (
	"fmt"
	"regexp"

	"github.com/dokku/dokku/plugins/common"
)

//Get retreives a value from a config. If appName is empty the global config is used.
func Get(appName string, key string) (value string, ok bool) {
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return "", false
	}
	if err = validateKey(key); err != nil {
		return "", false
	}
	return env.Get(key)
}

//GetWithDefault gets a value from a config. If appName is empty the global config is used. If the appName or key do not exist defaultValue is returned.
func GetWithDefault(appName string, key string, defaultValue string) (value string) {
	value, ok := Get(appName, key)
	if !ok {
		return defaultValue
	}
	return value
}

//SetMany variables in the environment. If appName is empty the global config is used. If restart is true the app is restarted.
func SetMany(appName string, entries map[string]string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	keys := make([]string, 0, len(entries))
	for k := range entries {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for k, v := range entries {
		env.Set(k, v)
		keys = append(keys, k)
	}
	if len(entries) != 0 {
		common.LogInfo1("Setting config vars")
		fmt.Println(prettyPrintEnvEntries("       ", entries))
		env.Write()
		args := append([]string{appName, "set"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

//UnsetMany a value in a config. If appName is empty the global config is used. If restart is true the app is restarted.
func UnsetMany(appName string, keys []string, restart bool) (err error) {
	global := appName == ""
	env, err := loadAppOrGlobalEnv(appName)
	if err != nil {
		return
	}
	var changed = false
	for _, k := range keys {
		if err = validateKey(k); err != nil {
			return
		}
	}
	for _, k := range keys {
		if _, hasKey := env.Map()[k]; hasKey {
			common.LogInfo1(fmt.Sprintf("Unsetting %s", k))
			env.Unset(k)
			changed = true
		} else {
			common.LogInfo1(fmt.Sprintf("Skipping %s, it is not set in the environment", k))
		}
	}
	if changed {
		env.Write()
		args := append([]string{appName, "unset"}, keys...)
		common.PlugnTrigger("post-config-update", args...)
	}
	if !global && restart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) {
		triggerRestart(appName)
	}
	return
}

func triggerRestart(appName string) {
	common.LogInfo1(fmt.Sprintf("Restarting app %s", appName))
	common.PlugnTrigger("app-restart", appName)
}

func loadAppOrGlobalEnv(appName string) (env *Env, err error) {
	if appName == "" || appName == "--global" {
		return LoadGlobalEnv()
	}
	return LoadAppEnv(appName)
}

func validateKey(key string) error {
	r, _ := regexp.Compile("^[a-zA-Z_][a-zA-Z0-9_]*$")
	if !r.MatchString(key) {
		return fmt.Errorf("Invalid key name: '%s'", key)
	}
	return nil
}
<|endoftext|>


<commit_before>package resources

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

type EC2VPNConnection struct {
	svc   *ec2.EC2
	id    string
	state string
}

func init() {
	register("EC2VPNConnection", ListEC2VPNConnections)
}

func ListEC2VPNConnections(sess *session.Session) ([]Resource, error) {
	svc := ec2.New(sess)

	params := &ec2.DescribeVpnConnectionsInput{}
	resp, err := svc.DescribeVpnConnections(params)
	if err != nil {
		return nil, err
	}

	resources := make([]Resource, 0)
	for _, out := range resp.VpnConnections {
		resources = append(resources, &EC2VPNConnection{
			svc:   svc,
			id:    *out.VpnConnectionId,
			state: *out.State,
		})
	}

	return resources, nil
}

func (v *EC2VPNConnection) Filter() error {
	if v.state == "deleted" {
		return fmt.Errorf("already deleted")
	}
	return nil
}

func (v *EC2VPNConnection) Remove() error {
	params := &ec2.DeleteVpnConnectionInput{
		VpnConnectionId: &v.id,
	}

	_, err := v.svc.DeleteVpnConnection(params)
	if err != nil {
		return err
	}

	return nil
}

func (v *EC2VPNConnection) String() string {
	return v.id
}
<commit_msg>Added tags list for vpn connections<commit_after>package resources

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/rebuy-de/aws-nuke/pkg/types"
)

type EC2VPNConnection struct {
	svc   *ec2.EC2
	conn  *ec2.VpnConnection
}

func init() {
	register("EC2VPNConnection", ListEC2VPNConnections)
}

func ListEC2VPNConnections(sess *session.Session) ([]Resource, error) {
	svc := ec2.New(sess)

	params := &ec2.DescribeVpnConnectionsInput{}
	resp, err := svc.DescribeVpnConnections(params)
	if err != nil {
		return nil, err
	}

	resources := make([]Resource, 0)
	for _, out := range resp.VpnConnections {
		resources = append(resources, &EC2VPNConnection{
			svc:   svc,
			conn:  out,
		})
	}

	return resources, nil
}

func (v *EC2VPNConnection) Filter() error {
	if *v.conn.State == "deleted" {
		return fmt.Errorf("already deleted")
	}
	return nil
}

func (v *EC2VPNConnection) Remove() error {
	params := &ec2.DeleteVpnConnectionInput{
		VpnConnectionId: v.conn.VpnConnectionId,
	}

	_, err := v.svc.DeleteVpnConnection(params)
	if err != nil {
		return err
	}

	return nil
}

func (v *EC2VPNConnection) Properties() types.Properties {
	properties := types.NewProperties()
	for _, tagValue := range v.conn.Tags {
		properties.SetTag(tagValue.Key, tagValue.Value)
	}
	return properties
}

func (v *EC2VPNConnection) String() string {
	return *v.conn.VpnConnectionId
}
<|endoftext|>