<commit_before>// Copyright 2015 The Vanadium 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 nacl

import (
	"flag"
	"fmt"
	"path/filepath"
	"runtime"

	"v.io/jiri/profiles"
	"v.io/jiri/tool"
	"v.io/x/lib/envvar"
)

const (
	profileName = "nacl"
	gitRemote   = "https://vanadium.googlesource.com/release.go.ppapi"
)

type versionSpec struct {
	gitRevision string
}

func init() {
	m := &Manager{
		versionInfo: profiles.NewVersionInfo(profileName, map[string]interface{}{
			"1": &versionSpec{"5e967194049bd1a6f097854f09fcbbbaa21afc05"},
			"2": &versionSpec{"5e967194049bd1a6f097854f09fcbbbaa21afc05"},
		}, "2"),
	}
	profiles.Register(profileName, m)
}

type Manager struct {
	root        string
	naclRoot    string
	versionInfo *profiles.VersionInfo
	spec        versionSpec
}

func (Manager) Name() string {
	return profileName
}

func (m Manager) String() string {
	return fmt.Sprintf("%s[%s]", profileName, m.versionInfo.Default())
}

func (m Manager) Root() string {
	return m.root
}

func (m *Manager) SetRoot(root string) {
	m.root = root
	m.naclRoot = filepath.Join(m.root, "profiles", "nacl")
}

func (m Manager) VersionInfo() *profiles.VersionInfo {
	return m.versionInfo
}

func (m *Manager) AddFlags(flags *flag.FlagSet, action profiles.Action) {
}

func (m *Manager) defaultTarget(ctx *tool.Context, action string, target *profiles.Target) error {
	if !target.IsSet() {
		def := *target
		target.Set("nacl=amd64p32-nacl")
		fmt.Fprintf(ctx.Stdout(), "Default target %v reinterpreted as: %v\n", def, target)
	} else {
		if target.Arch() != "amd64p32" && target.OS() != "nacl" {
			return fmt.Errorf("this profile can only be %v as amd64p32-nacl and not as %v", action, target)
		}
	}
	return nil
}

func (m *Manager) Install(ctx *tool.Context, target profiles.Target) error {
	if err := m.defaultTarget(ctx, "installed", &target); err != nil {
		return err
	}
	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
		return err
	}
	naclInstDir := filepath.Join(m.naclRoot, target.TargetSpecificDirname(), m.spec.gitRevision)

	if err := m.installNacl(ctx, target, m.spec); err != nil {
		return err
	}
	target.InstallationDir = naclInstDir
	target.Env.Vars = envvar.MergeSlices(target.Env.Vars, []string{
		"GOARCH=amd64p32",
		"GOOS=nacl",
		"GOROOT=" + naclInstDir,
	})
	profiles.InstallProfile(profileName, m.naclRoot)
	return profiles.AddProfileTarget(profileName, target)
}

func (m *Manager) Uninstall(ctx *tool.Context, target profiles.Target) error {
	// ignore errors to allow for older installs to be removed.
	m.defaultTarget(ctx, "uninstalled", &target)
	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
		return err
	}
	naclSrcDir := filepath.Join(m.naclRoot, m.spec.gitRevision)
	naclInstDir := filepath.Join(m.naclRoot, target.TargetSpecificDirname(), m.spec.gitRevision)
	if err := ctx.Run().RemoveAll(naclInstDir); err != nil {
		return err
	}
	if err := ctx.Run().RemoveAll(naclSrcDir); err != nil {
		return err
	}
	if profiles.RemoveProfileTarget(profileName, target) {
		return ctx.Run().RemoveAll(m.naclRoot)
	}
	return nil
}

// installNacl installs the nacl profile.
func (m *Manager) installNacl(ctx *tool.Context, target profiles.Target, spec versionSpec) error {
	switch runtime.GOOS {
	case "darwin":
	case "linux":
		if err := profiles.InstallPackages(ctx, []string{"g++", "libc6-i386", "zip"}); err != nil {
			return err
		}
	}

	naclSrcDir := filepath.Join(m.naclRoot, spec.gitRevision)
	naclInstDir := filepath.Join(m.naclRoot, "native", spec.gitRevision)

	cloneGoPpapiFn := func() error {
		tmpDir, err := ctx.Run().TempDir("", "")
		if err != nil {
			return err
		}
		defer ctx.Run().RemoveAll(tmpDir)

		if err := profiles.GitCloneRepo(ctx, gitRemote, spec.gitRevision, tmpDir, profiles.DefaultDirPerm); err != nil {
			return err
		}
		if err := ctx.Run().Chdir(tmpDir); err != nil {
			return err
		}

		if err := ctx.Run().MkdirAll(m.naclRoot, profiles.DefaultDirPerm); err != nil {
			return err
		}

		if ctx.Run().DirectoryExists(naclSrcDir) {
			ctx.Run().RemoveAll(naclSrcDir)
		}
		if err := ctx.Run().Rename(tmpDir, naclSrcDir); err != nil {
			return err
		}
		return nil
	}
	// Cloning is slow so we handle it as an atomic action and then create
	// a copy for the actual build.
	if err := profiles.AtomicAction(ctx, cloneGoPpapiFn, naclSrcDir, "Clone Go Ppapi repository"); err != nil {
		return err
	}

	// Compile the Go Ppapi compiler.
	compileGoPpapiFn := func() error {
		dir := filepath.Dir(naclInstDir)
		if err := ctx.Run().MkdirAll(dir, profiles.DefaultDirPerm); err != nil {
			return err
		}
		if err := profiles.RunCommand(ctx, nil, "cp", "-r", naclSrcDir, naclInstDir); err != nil {
			return err
		}
		goPpapiCompileScript := filepath.Join(naclInstDir, "src", "make-nacl-amd64p32.sh")
		if err := profiles.RunCommand(ctx, nil, goPpapiCompileScript); err != nil {
			return err
		}
		return nil
	}
	return profiles.AtomicAction(ctx, compileGoPpapiFn, naclInstDir, "Compile Go Ppapi compiler")
}
<commit_msg>TBR: v.io/x/devtools: fix the nacl profile.<commit_after>// Copyright 2015 The Vanadium 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 nacl

import (
	"flag"
	"fmt"
	"path/filepath"
	"runtime"

	"v.io/jiri/profiles"
	"v.io/jiri/tool"
	"v.io/x/lib/envvar"
)

const (
	profileName = "nacl"
	gitRemote   = "https://vanadium.googlesource.com/release.go.ppapi"
)

type versionSpec struct {
	gitRevision string
}

func init() {
	m := &Manager{
		versionInfo: profiles.NewVersionInfo(profileName, map[string]interface{}{
			"1": &versionSpec{"5e967194049bd1a6f097854f09fcbbbaa21afc05"},
			"2": &versionSpec{"5e967194049bd1a6f097854f09fcbbbaa21afc05"},
		}, "2"),
	}
	profiles.Register(profileName, m)
}

type Manager struct {
	root        string
	naclRoot    string
	versionInfo *profiles.VersionInfo
	spec        versionSpec
}

func (Manager) Name() string {
	return profileName
}

func (m Manager) String() string {
	return fmt.Sprintf("%s[%s]", profileName, m.versionInfo.Default())
}

func (m Manager) Root() string {
	return m.root
}

func (m *Manager) SetRoot(root string) {
	m.root = root
	m.naclRoot = filepath.Join(m.root, "profiles", "nacl")
}

func (m Manager) VersionInfo() *profiles.VersionInfo {
	return m.versionInfo
}

func (m *Manager) AddFlags(flags *flag.FlagSet, action profiles.Action) {
}

func (m *Manager) defaultTarget(ctx *tool.Context, action string, target *profiles.Target) error {
	if !target.IsSet() {
		def := *target
		target.Set("nacl=amd64p32-nacl")
		fmt.Fprintf(ctx.Stdout(), "Default target %v reinterpreted as: %v\n", def, target)
	} else {
		if target.Arch() != "amd64p32" && target.OS() != "nacl" {
			return fmt.Errorf("this profile can only be %v as amd64p32-nacl and not as %v", action, target)
		}
	}
	return nil
}

func (m *Manager) Install(ctx *tool.Context, target profiles.Target) error {
	if err := m.defaultTarget(ctx, "installed", &target); err != nil {
		return err
	}
	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
		return err
	}
	naclInstDir := filepath.Join(m.naclRoot, target.TargetSpecificDirname(), m.spec.gitRevision)

	if err := m.installNacl(ctx, target, m.spec, naclInstDir); err != nil {
		return err
	}
	target.InstallationDir = naclInstDir
	target.Env.Vars = envvar.MergeSlices(target.Env.Vars, []string{
		"GOARCH=amd64p32",
		"GOOS=nacl",
		"GOROOT=" + naclInstDir,
	})
	profiles.InstallProfile(profileName, m.naclRoot)
	return profiles.AddProfileTarget(profileName, target)
}

func (m *Manager) Uninstall(ctx *tool.Context, target profiles.Target) error {
	// ignore errors to allow for older installs to be removed.
	m.defaultTarget(ctx, "uninstalled", &target)
	if err := m.versionInfo.Lookup(target.Version(), &m.spec); err != nil {
		return err
	}
	naclSrcDir := filepath.Join(m.naclRoot, m.spec.gitRevision)
	naclInstDir := filepath.Join(m.naclRoot, target.TargetSpecificDirname(), m.spec.gitRevision)
	if err := ctx.Run().RemoveAll(naclInstDir); err != nil {
		return err
	}
	if err := ctx.Run().RemoveAll(naclSrcDir); err != nil {
		return err
	}
	if profiles.RemoveProfileTarget(profileName, target) {
		return ctx.Run().RemoveAll(m.naclRoot)
	}
	return nil
}

// installNacl installs the nacl profile.
func (m *Manager) installNacl(ctx *tool.Context, target profiles.Target, spec versionSpec, naclInstDir string) error {
	switch runtime.GOOS {
	case "darwin":
	case "linux":
		if err := profiles.InstallPackages(ctx, []string{"g++", "libc6-i386", "zip"}); err != nil {
			return err
		}
	}

	naclSrcDir := filepath.Join(m.naclRoot, spec.gitRevision)

	cloneGoPpapiFn := func() error {
		tmpDir, err := ctx.Run().TempDir("", "")
		if err != nil {
			return err
		}
		defer ctx.Run().RemoveAll(tmpDir)

		if err := profiles.GitCloneRepo(ctx, gitRemote, spec.gitRevision, tmpDir, profiles.DefaultDirPerm); err != nil {
			return err
		}
		if err := ctx.Run().Chdir(tmpDir); err != nil {
			return err
		}

		if err := ctx.Run().MkdirAll(m.naclRoot, profiles.DefaultDirPerm); err != nil {
			return err
		}

		if ctx.Run().DirectoryExists(naclSrcDir) {
			ctx.Run().RemoveAll(naclSrcDir)
		}
		if err := ctx.Run().Rename(tmpDir, naclSrcDir); err != nil {
			return err
		}
		return nil
	}
	// Cloning is slow so we handle it as an atomic action and then create
	// a copy for the actual build.
	if err := profiles.AtomicAction(ctx, cloneGoPpapiFn, naclSrcDir, "Clone Go Ppapi repository"); err != nil {
		return err
	}

	// Compile the Go Ppapi compiler.
	compileGoPpapiFn := func() error {
		dir := filepath.Dir(naclInstDir)
		if err := ctx.Run().MkdirAll(dir, profiles.DefaultDirPerm); err != nil {
			return err
		}
		if err := profiles.RunCommand(ctx, nil, "cp", "-r", naclSrcDir, naclInstDir); err != nil {
			return err
		}
		goPpapiCompileScript := filepath.Join(naclInstDir, "src", "make-nacl-amd64p32.sh")
		if err := profiles.RunCommand(ctx, nil, goPpapiCompileScript); err != nil {
			return err
		}
		return nil
	}
	return profiles.AtomicAction(ctx, compileGoPpapiFn, naclInstDir, "Compile Go Ppapi compiler")
}
<|endoftext|>


<commit_before>// +build !windows

package mains

import "os/exec"

func newCommand(command string) *exec.Cmd {
	return exec.Command("/bin/sh", "c", command)
}
<commit_msg>Fix: the lua function io.popen could not work on Linux<commit_after>// +build !windows

package mains

import "os/exec"

func newCommand(command string) *exec.Cmd {
	return exec.Command("/bin/sh", "-c", command)
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package model

import (
	"strings"

	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/util/types"
)

// SchemaState is the state for schema elements.
type SchemaState byte

const (
	// StateNone means this schema element is absent and can't be used.
	StateNone SchemaState = iota
	// StateDeleteOnly means we can only delete items for this schema element.
	StateDeleteOnly
	// StateWriteOnly means we can use any write operation on this schema element,
	// but outer can't read the changed data.
	StateWriteOnly
	// StateWriteReorganization means we are re-organizating whole data after write only state.
	StateWriteReorganization
	// StateDeleteReorganization means we are re-organizating whole data after delete only state.
	StateDeleteReorganization
	// StatePublic means this schema element is ok for all write and read operations.
	StatePublic
)

// String implements fmt.Stringer interface.
func (s SchemaState) String() string {
	switch s {
	case StateDeleteOnly:
		return "delete only"
	case StateWriteOnly:
		return "write only"
	case StateWriteReorganization:
		return "write reorganization"
	case StateDeleteReorganization:
		return "delete reorganization"
	case StatePublic:
		return "public"
	default:
		return "none"
	}
}

// ColumnInfo provides meta data describing of a table column.
type ColumnInfo struct {
	ID                 int64       `json:"id"`
	Name               CIStr       `json:"name"`
	Offset             int         `json:"offset"`
	OriginDefaultValue interface{} `json:"origin_default"`
	DefaultValue       interface{} `json:"default"`
	types.FieldType    `json:"type"`
	State              SchemaState `json:"state"`
	Comment            string      `json:"comment"`
}

// Clone clones ColumnInfo.
func (c *ColumnInfo) Clone() *ColumnInfo {
	nc := *c
	return &nc
}

// TableInfo provides meta data describing a DB table.
type TableInfo struct {
	ID      int64  `json:"id"`
	Name    CIStr  `json:"name"`
	Charset string `json:"charset"`
	Collate string `json:"collate"`
	// Columns are listed in the order in which they appear in the schema.
	Columns     []*ColumnInfo `json:"cols"`
	Indices     []*IndexInfo  `json:"index_info"`
	ForeignKeys []*FKInfo     `json:"fk_info"`
	State       SchemaState   `json:"state"`
	PKIsHandle  bool          `json:"pk_is_handle"`
	Comment     string        `json:"comment"`
	AutoIncID   int64         `json:"auto_inc_id"`
	MaxColumnID int64         `json:"max_col_id"`
	MaxIndexID  int64         `json:"max_idx_id"`
	// OldSchemaID :
	// Because auto increment ID has schemaID as prefix,
	// We need to save original schemaID to keep autoID unchanged
	// while renaming a table from one database to another.
	OldSchemaID int64 `json:"old_schema_id,omitempty"`
}

// Clone clones TableInfo.
func (t *TableInfo) Clone() *TableInfo {
	nt := *t
	nt.Columns = make([]*ColumnInfo, len(t.Columns))
	nt.Indices = make([]*IndexInfo, len(t.Indices))
	nt.ForeignKeys = make([]*FKInfo, len(t.ForeignKeys))

	for i := range t.Columns {
		nt.Columns[i] = t.Columns[i].Clone()
	}

	for i := range t.Indices {
		nt.Indices[i] = t.Indices[i].Clone()
	}

	for i := range t.ForeignKeys {
		nt.ForeignKeys[i] = t.ForeignKeys[i].Clone()
	}

	return &nt
}

// GetPkName will return the pk name if pk exists.
func (t *TableInfo) GetPkName() CIStr {
	if t.PKIsHandle {
		for _, colInfo := range t.Columns {
			if mysql.HasPriKeyFlag(colInfo.Flag) {
				return colInfo.Name
			}
		}
	}
	return CIStr{}
}

// IndexColumn provides index column info.
type IndexColumn struct {
	Name   CIStr `json:"name"`   // Index name
	Offset int   `json:"offset"` // Index offset
	Length int   `json:"length"` // Index length
}

// Clone clones IndexColumn.
func (i *IndexColumn) Clone() *IndexColumn {
	ni := *i
	return &ni
}

// IndexType is the type of index
type IndexType int

// String implements Stringer interface.
func (t IndexType) String() string {
	switch t {
	case IndexTypeBtree:
		return "BTREE"
	case IndexTypeHash:
		return "HASH"
	}
	return ""
}

// IndexTypes
const (
	IndexTypeBtree IndexType = iota + 1
	IndexTypeHash
)

// IndexInfo provides meta data describing a DB index.
// It corresponds to the statement `CREATE INDEX Name ON Table (Column);`
// See https://dev.mysql.com/doc/refman/5.7/en/create-index.html
type IndexInfo struct {
	ID      int64          `json:"id"`
	Name    CIStr          `json:"idx_name"`   // Index name.
	Table   CIStr          `json:"tbl_name"`   // Table name.
	Columns []*IndexColumn `json:"idx_cols"`   // Index columns.
	Unique  bool           `json:"is_unique"`  // Whether the index is unique.
	Primary bool           `json:"is_primary"` // Whether the index is primary key.
	State   SchemaState    `json:"state"`
	Comment string         `json:"comment"`    // Comment
	Tp      IndexType      `json:"index_type"` // Index type: Btree or Hash
}

// Clone clones IndexInfo.
func (index *IndexInfo) Clone() *IndexInfo {
	ni := *index
	ni.Columns = make([]*IndexColumn, len(index.Columns))
	for i := range index.Columns {
		ni.Columns[i] = index.Columns[i].Clone()
	}
	return &ni
}

// HasPrefixIndex returns whether any columns of this index uses prefix length.
func (index *IndexInfo) HasPrefixIndex() bool {
	for _, ic := range index.Columns {
		if ic.Length != types.UnspecifiedLength {
			return true
		}
	}
	return false
}

// FKInfo provides meta data describing a foreign key constraint.
type FKInfo struct {
	ID       int64       `json:"id"`
	Name     CIStr       `json:"fk_name"`
	RefTable CIStr       `json:"ref_table"`
	RefCols  []CIStr     `json:"ref_cols"`
	Cols     []CIStr     `json:"cols"`
	OnDelete int         `json:"on_delete"`
	OnUpdate int         `json:"on_update"`
	State    SchemaState `json:"state"`
}

// Clone clones FKInfo.
func (fk *FKInfo) Clone() *FKInfo {
	nfk := *fk

	nfk.RefCols = make([]CIStr, len(fk.RefCols))
	nfk.Cols = make([]CIStr, len(fk.Cols))
	copy(nfk.RefCols, fk.RefCols)
	copy(nfk.Cols, fk.Cols)

	return &nfk
}

// DBInfo provides meta data describing a DB.
type DBInfo struct {
	ID      int64        `json:"id"`      // Database ID
	Name    CIStr        `json:"db_name"` // DB name.
	Charset string       `json:"charset"`
	Collate string       `json:"collate"`
	Tables  []*TableInfo `json:"-"` // Tables in the DB.
	State   SchemaState  `json:"state"`
}

// Clone clones DBInfo.
func (db *DBInfo) Clone() *DBInfo {
	newInfo := *db
	newInfo.Tables = make([]*TableInfo, len(db.Tables))
	for i := range db.Tables {
		newInfo.Tables[i] = db.Tables[i].Clone()
	}
	return &newInfo
}

// CIStr is case insensitive string.
type CIStr struct {
	O string `json:"O"` // Original string.
	L string `json:"L"` // Lower case string.
}

// String implements fmt.Stringer interface.
func (cis CIStr) String() string {
	return cis.O
}

// NewCIStr creates a new CIStr.
func NewCIStr(s string) (cs CIStr) {
	cs.O = s
	cs.L = strings.ToLower(s)
	return
}
<commit_msg>add comment (#3328)<commit_after>// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package model

import (
	"strings"

	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/util/types"
)

// SchemaState is the state for schema elements.
type SchemaState byte

const (
	// StateNone means this schema element is absent and can't be used.
	StateNone SchemaState = iota
	// StateDeleteOnly means we can only delete items for this schema element.
	StateDeleteOnly
	// StateWriteOnly means we can use any write operation on this schema element,
	// but outer can't read the changed data.
	StateWriteOnly
	// StateWriteReorganization means we are re-organizating whole data after write only state.
	StateWriteReorganization
	// StateDeleteReorganization means we are re-organizating whole data after delete only state.
	StateDeleteReorganization
	// StatePublic means this schema element is ok for all write and read operations.
	StatePublic
)

// String implements fmt.Stringer interface.
func (s SchemaState) String() string {
	switch s {
	case StateDeleteOnly:
		return "delete only"
	case StateWriteOnly:
		return "write only"
	case StateWriteReorganization:
		return "write reorganization"
	case StateDeleteReorganization:
		return "delete reorganization"
	case StatePublic:
		return "public"
	default:
		return "none"
	}
}

// ColumnInfo provides meta data describing of a table column.
type ColumnInfo struct {
	ID                 int64       `json:"id"`
	Name               CIStr       `json:"name"`
	Offset             int         `json:"offset"`
	OriginDefaultValue interface{} `json:"origin_default"`
	DefaultValue       interface{} `json:"default"`
	types.FieldType    `json:"type"`
	State              SchemaState `json:"state"`
	Comment            string      `json:"comment"`
}

// Clone clones ColumnInfo.
func (c *ColumnInfo) Clone() *ColumnInfo {
	nc := *c
	return &nc
}

// TableInfo provides meta data describing a DB table.
type TableInfo struct {
	ID      int64  `json:"id"`
	Name    CIStr  `json:"name"`
	Charset string `json:"charset"`
	Collate string `json:"collate"`
	// Columns are listed in the order in which they appear in the schema.
	Columns     []*ColumnInfo `json:"cols"`
	Indices     []*IndexInfo  `json:"index_info"`
	ForeignKeys []*FKInfo     `json:"fk_info"`
	State       SchemaState   `json:"state"`
	PKIsHandle  bool          `json:"pk_is_handle"`
	Comment     string        `json:"comment"`
	AutoIncID   int64         `json:"auto_inc_id"`
	MaxColumnID int64         `json:"max_col_id"`
	MaxIndexID  int64         `json:"max_idx_id"`
	// OldSchemaID :
	// Because auto increment ID has schemaID as prefix,
	// We need to save original schemaID to keep autoID unchanged
	// while renaming a table from one database to another.
	OldSchemaID int64 `json:"old_schema_id,omitempty"`
}

// Clone clones TableInfo.
func (t *TableInfo) Clone() *TableInfo {
	nt := *t
	nt.Columns = make([]*ColumnInfo, len(t.Columns))
	nt.Indices = make([]*IndexInfo, len(t.Indices))
	nt.ForeignKeys = make([]*FKInfo, len(t.ForeignKeys))

	for i := range t.Columns {
		nt.Columns[i] = t.Columns[i].Clone()
	}

	for i := range t.Indices {
		nt.Indices[i] = t.Indices[i].Clone()
	}

	for i := range t.ForeignKeys {
		nt.ForeignKeys[i] = t.ForeignKeys[i].Clone()
	}

	return &nt
}

// GetPkName will return the pk name if pk exists.
func (t *TableInfo) GetPkName() CIStr {
	if t.PKIsHandle {
		for _, colInfo := range t.Columns {
			if mysql.HasPriKeyFlag(colInfo.Flag) {
				return colInfo.Name
			}
		}
	}
	return CIStr{}
}

// IndexColumn provides index column info.
type IndexColumn struct {
	Name   CIStr `json:"name"`   // Index name
	Offset int   `json:"offset"` // Index offset
	// Length of prefix when using column prefix
	// for indexing;
	// UnspecifedLength if not using prefix indexing
	Length int `json:"length"`
}

// Clone clones IndexColumn.
func (i *IndexColumn) Clone() *IndexColumn {
	ni := *i
	return &ni
}

// IndexType is the type of index
type IndexType int

// String implements Stringer interface.
func (t IndexType) String() string {
	switch t {
	case IndexTypeBtree:
		return "BTREE"
	case IndexTypeHash:
		return "HASH"
	}
	return ""
}

// IndexTypes
const (
	IndexTypeBtree IndexType = iota + 1
	IndexTypeHash
)

// IndexInfo provides meta data describing a DB index.
// It corresponds to the statement `CREATE INDEX Name ON Table (Column);`
// See https://dev.mysql.com/doc/refman/5.7/en/create-index.html
type IndexInfo struct {
	ID      int64          `json:"id"`
	Name    CIStr          `json:"idx_name"`   // Index name.
	Table   CIStr          `json:"tbl_name"`   // Table name.
	Columns []*IndexColumn `json:"idx_cols"`   // Index columns.
	Unique  bool           `json:"is_unique"`  // Whether the index is unique.
	Primary bool           `json:"is_primary"` // Whether the index is primary key.
	State   SchemaState    `json:"state"`
	Comment string         `json:"comment"`    // Comment
	Tp      IndexType      `json:"index_type"` // Index type: Btree or Hash
}

// Clone clones IndexInfo.
func (index *IndexInfo) Clone() *IndexInfo {
	ni := *index
	ni.Columns = make([]*IndexColumn, len(index.Columns))
	for i := range index.Columns {
		ni.Columns[i] = index.Columns[i].Clone()
	}
	return &ni
}

// HasPrefixIndex returns whether any columns of this index uses prefix length.
func (index *IndexInfo) HasPrefixIndex() bool {
	for _, ic := range index.Columns {
		if ic.Length != types.UnspecifiedLength {
			return true
		}
	}
	return false
}

// FKInfo provides meta data describing a foreign key constraint.
type FKInfo struct {
	ID       int64       `json:"id"`
	Name     CIStr       `json:"fk_name"`
	RefTable CIStr       `json:"ref_table"`
	RefCols  []CIStr     `json:"ref_cols"`
	Cols     []CIStr     `json:"cols"`
	OnDelete int         `json:"on_delete"`
	OnUpdate int         `json:"on_update"`
	State    SchemaState `json:"state"`
}

// Clone clones FKInfo.
func (fk *FKInfo) Clone() *FKInfo {
	nfk := *fk

	nfk.RefCols = make([]CIStr, len(fk.RefCols))
	nfk.Cols = make([]CIStr, len(fk.Cols))
	copy(nfk.RefCols, fk.RefCols)
	copy(nfk.Cols, fk.Cols)

	return &nfk
}

// DBInfo provides meta data describing a DB.
type DBInfo struct {
	ID      int64        `json:"id"`      // Database ID
	Name    CIStr        `json:"db_name"` // DB name.
	Charset string       `json:"charset"`
	Collate string       `json:"collate"`
	Tables  []*TableInfo `json:"-"` // Tables in the DB.
	State   SchemaState  `json:"state"`
}

// Clone clones DBInfo.
func (db *DBInfo) Clone() *DBInfo {
	newInfo := *db
	newInfo.Tables = make([]*TableInfo, len(db.Tables))
	for i := range db.Tables {
		newInfo.Tables[i] = db.Tables[i].Clone()
	}
	return &newInfo
}

// CIStr is case insensitive string.
type CIStr struct {
	O string `json:"O"` // Original string.
	L string `json:"L"` // Lower case string.
}

// String implements fmt.Stringer interface.
func (cis CIStr) String() string {
	return cis.O
}

// NewCIStr creates a new CIStr.
func NewCIStr(s string) (cs CIStr) {
	cs.O = s
	cs.L = strings.ToLower(s)
	return
}
<|endoftext|>


<commit_before>package provision

import (
  "bytes"
  "fmt"
	"io/ioutil"
	"path"
  "strings"
  "text/template"

  "github.com/docker/machine/libmachine/auth"
  "github.com/docker/machine/libmachine/cert"
  "github.com/docker/machine/libmachine/kubernetes"
  "github.com/docker/machine/libmachine/log"
)

func xferCert(p Provisioner, certPath string, targetPath string) error {
	certXferCmd := "printf '%%s' '%s' | sudo tee %s"

	certContents, err := ioutil.ReadFile(certPath)
	if err != nil {
		return err
	}

    if _, err := p.SSHCommand(fmt.Sprintf("mkdir -p %s", targetPath)); err != nil {
      return err
    }

    /*
     * TODO: Until we start dynamically generating the configuration file,
     * these must have a known naming convention on the machine.
     */
     _, certFile := path.Split(certPath)
     certFile = strings.Split(certFile, "_")[1]

	if _, err := p.SSHCommand(fmt.Sprintf(certXferCmd, string(certContents), path.Join(targetPath, certFile))); err != nil {
		return err
	}

	return nil
}

func fixPermissions(p Provisioner, certPath string, targetPath string) error {
	_, certFile := path.Split(certPath)

    /*
     * TODO: Until we start dynamically generating the configuration file,
     * these must have a known naming convention on the machine.
     */
     certFile = strings.Split(certFile, "_")[1]

	_, err := p.SSHCommand(fmt.Sprintf("sudo chmod 0400 %s", path.Join(targetPath, certFile)))
	if  err != nil {
		return err
	}

	return nil
}

func GenerateCertificates(p Provisioner, k8sOptions kubernetes.KubernetesOptions, authOptions auth.AuthOptions) (error) {
  /* Generate and install certificates. Then kick off kubernetes */
  driver := p.GetDriver()
  machine := driver.GetMachineName()
  bits := 2048  // Based on the initial configuration
  targetDir := k8sOptions.K8SCertPath
  ip, err := driver.GetIP()
  if err != nil {
    return fmt.Errorf("Error retrieving address: %s", err)
  }

  err = cert.GenerateCert(
    []string{ip, "localhost"},
    k8sOptions.K8SAPICert,
    k8sOptions.K8SAPIKey,
    authOptions.CaCertPath,
    authOptions.CaPrivateKeyPath,
    kubernetes.GenOrg(machine, "api"),
    bits)

  if err != nil {
    return fmt.Errorf("Error generating API cert: %s", err)
  }

  err = cert.GenerateCert(
    []string{""},
    k8sOptions.K8SAdminCert,
    k8sOptions.K8SAdminKey,
    authOptions.CaCertPath,
    authOptions.CaPrivateKeyPath,
    kubernetes.GenOrg(machine, "admin"),
    bits)

  if err != nil {
    return fmt.Errorf("Error generating Admin cert: %s", err)
  }

  err = cert.GenerateCert(
    []string{},
    k8sOptions.K8SProxyCert,
    k8sOptions.K8SProxyKey,
    authOptions.CaCertPath,
    authOptions.CaPrivateKeyPath,
    kubernetes.GenOrg(machine, "proxy"),
    bits)

  if err != nil {
    return fmt.Errorf("Error generating proxy cert: %s", err)
  }

  /* Copy certs into place */
  log.Info("Copying certs to the remote system...")

  /* Kick off the kubernetes run */
  if _, err := p.SSHCommand(fmt.Sprintf("mkdir -p %s", targetDir)); err != nil {
    return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%q,%s,%d' |sudo tee %s", k8sOptions.K8SToken, k8sOptions.K8SUser, 0, path.Join(targetDir, "tokenfile.txt"))); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAPIKey, targetDir + "/apiserver"); err != nil {
    return err
  }

  if err := fixPermissions(p, k8sOptions.K8SAPIKey, targetDir + "/apiserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAPICert, targetDir + "/apiserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SProxyCert, targetDir + "/proxyserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SProxyKey, targetDir + "/proxyserver"); err != nil {
    return err
  }

  if err := fixPermissions(p, k8sOptions.K8SProxyKey, targetDir + "/proxyserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAdminCert, targetDir + "/kubelet"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAdminKey, targetDir + "/kubelet"); err != nil {
    return err
  }

  if err := fixPermissions(p, k8sOptions.K8SAdminKey, targetDir + "/kubelet"); err != nil {
    return err
  }

  /* Copy the CA cert to a known location */
  caCertContents, err := ioutil.ReadFile(authOptions.CaCertPath)
  if err != nil {
    return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s/ca.pem", caCertContents, targetDir)); err != nil {
    return err
  }

  return nil
}

func configureKubernetes(p Provisioner, k8sOptions *kubernetes.KubernetesOptions, authOptions auth.AuthOptions) (error) {
    log.Info("Configuring kubernetes...")

  if _, err := p.SSHCommand("sudo /bin/sh /usr/local/etc/init.d/kubelet stop"); err != nil {
      log.Info("Errored while attempting to stop the kubelet: %s", err)
  }

  if err := GenerateCertificates(p, *k8sOptions, authOptions); err != nil {
    return err
  }

  /* Generate and install certificates. Then kick off kubernetes */
	driver := p.GetDriver()
	machine := driver.GetMachineName()
  targetDir := k8sOptions.K8SCertPath

  /* Generate and copy a new YAML file to the target */
  configFile, err := Generatek8sManifest(machine, targetDir)
  if err != nil {
      return err
  }

  kubeletConfig, err := GenerateKubeletConfig(machine, targetDir)
  if err != nil {
      return err
  }

  policyFile, err := GeneratePolicyFile(k8sOptions.K8SUser)
  if err != nil {
    return err
  }

  /* TOOD: The target manifest directory should be a parameter throughout here */
  /* Ensure that the kubernetes configuration directory exists */
  if _, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p /etc/kubernetes/manifests")); err != nil {
      return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s", kubeletConfig, "/etc/kubernetes/kubelet.kubeconfig")); err != nil {
      return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s", configFile, "/etc/kubernetes/manifests/kubernetes.yaml")); err != nil {
      return err
  }

  /* Generate the policy file */
  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s", policyFile, "/etc/kubernetes/policies/policy.jsonl")); err != nil {
      return err
  }

	/* Lastly, start the kubelet */
    if _, err := p.SSHCommand("sudo /bin/sh /usr/local/etc/init.d/kubelet start"); err != nil {
        return err
    }

    return nil
}

func GeneratePolicyFile(name string) (string, error) {
  type ConfigDetails struct {
    Username string
  }
  var result bytes.Buffer

  details := ConfigDetails{name}
  policyTmpl := `{"user":"{{.Username}}"}
{"user":"scheduler", "readonly": true, "resource": "pods"}
{"user":"scheduler", "resource": "bindings"}
{"user":"proxy", "resource": "services"}
{"user":"proxy", "resource": "endpoints"}
{"user":"kubelet",  "resource": "pods"}
{"user":"kubelet",  "resource": "nodes"}
{"user":"kubelet",  "readonly": true, "resource": "services"}
{"user":"kubelet",  "readonly": true, "resource": "endpoints"}
{"user":"kubelet", "resource": "events"}
}`

  t, err := template.New("PolicyTmpl").Parse(policyTmpl)
  if err != nil {
      return "", err
  }

  err = t.Execute(&result, details)

  return result.String(), err
}

func GenerateKubeletConfig(name string, targetDir string) (string, error) {
    type ConfigDetails struct {
        ClusterName   string
        CertDir       string
    }

    details := ConfigDetails{name, targetDir}
    var result bytes.Buffer

    kubeletConfigTmpl := `apiVersion: v1
kind: Config
clusters:
  - cluster:
      certificate-authority: {{.CertDir}}/ca.pem
      server: https://127.0.0.1:6443
    name: {{.ClusterName}}
contexts:
  - context:
      cluster: {{.ClusterName}}
      user: kubelet
    name: {{.ClusterName}}
users:
  - name: kubelet
    user:
      client-certificate: {{.CertDir}}/kubelet/cert.pem
      client-key: {{.CertDir}}/kubelet/key.pem`

    t, err := template.New("kubeletConfigTmpl").Parse(kubeletConfigTmpl)
    if err != nil {
        return "", err
    }

    err = t.Execute(&result, details)

    return result.String(), err
}

func Generatek8sManifest(name string, targetDir string) (string, error) {
    type ConfigDetails struct {
        ClusterName   string
        CertDir       string
    }

    details := ConfigDetails{name, targetDir}
    var result bytes.Buffer

    k8sConfigTmpl := `apiVersion: v1
kind: Pod
clusters:
  - cluster:
      certificate-authority: {{.CertDir}}/ca.pem
metadata:
  name: {{.ClusterName}}
spec:
  hostNetwork: true
  volumes:
    - name: "certs"
      hostPath:
        path: "{{.CertDir}}"
    - name: "policies"
      hostPath:
        path: "/etc/kubernetes/policies"
  containers:
    - name: "etcd"
      image: "b.gcr.io/kuar/etcd:2.1.1"
      args:
        - "--data-dir=/var/lib/etcd"
        - "--advertise-client-urls=http://127.0.0.1:2379"
        - "--listen-client-urls=http://127.0.0.1:2379"
        - "--listen-peer-urls=http://127.0.0.1:2380"
        - "--name=etcd"
    - name: "controller-manager"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      args:
        - "/hyperkube"
        - "controller-manager"
        - "--master=http://127.0.0.1:8080"
        - "--v=2"
    - name: "apiserver"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      volumeMounts:
        - name: "certs"
          mountPath: "{{.CertDir}}"
          readOnly: true
        - name: "policies"
          mountPath: "/etc/kubernetes/policies"
          readOnly: true
      args:
        - "/hyperkube"
        - "apiserver"
        - "--authorization-mode=AlwaysAllow"
        - "--client-ca-file=/var/run/kubernetes/ca.pem"
        - "--token-auth-file={{.CertDir}}/tokenfile.txt"
        - "--allow-privileged=true"
        - "--service-cluster-ip-range=10.0.20.0/24"
        - "--insecure-bind-address=0.0.0.0"
        - "--insecure-port=8080"
        - "--secure-port=6443"
        - "--etcd-servers=http://127.0.0.1:2379"
        - "--tls-cert-file={{.CertDir}}/apiserver/cert.pem"
        - "--tls-private-key-file={{.CertDir}}/apiserver/key.pem"
        - "--v=2"
    - name: "proxy"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      securityContext:
        privileged: true
      args:
        - "/hyperkube"
        - "proxy"
        - "--master=http://127.0.0.1:8080"
        - "--v=2"
    - name: "scheduler"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      args:
        - "/hyperkube"
        - "scheduler"
        - "--master=http://127.0.0.1:8080"
        - "--v=2"

`
    t, err := template.New("k8sConfig").Parse(k8sConfigTmpl)
    if err != nil {
        return "", err
    }

    err = t.Execute(&result, details)

    return result.String(), err
}
<commit_msg>Modified mkdir to be prefixed with sudo<commit_after>package provision

import (
  "bytes"
  "fmt"
	"io/ioutil"
	"path"
  "strings"
  "text/template"

  "github.com/docker/machine/libmachine/auth"
  "github.com/docker/machine/libmachine/cert"
  "github.com/docker/machine/libmachine/kubernetes"
  "github.com/docker/machine/libmachine/log"
)

func xferCert(p Provisioner, certPath string, targetPath string) error {
	certXferCmd := "printf '%%s' '%s' | sudo tee %s"

	certContents, err := ioutil.ReadFile(certPath)
	if err != nil {
		return err
	}

    if _, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", targetPath)); err != nil {
      return err
    }

    /*
     * TODO: Until we start dynamically generating the configuration file,
     * these must have a known naming convention on the machine.
     */
     _, certFile := path.Split(certPath)
     certFile = strings.Split(certFile, "_")[1]

	if _, err := p.SSHCommand(fmt.Sprintf(certXferCmd, string(certContents), path.Join(targetPath, certFile))); err != nil {
		return err
	}

	return nil
}

func fixPermissions(p Provisioner, certPath string, targetPath string) error {
	_, certFile := path.Split(certPath)

    /*
     * TODO: Until we start dynamically generating the configuration file,
     * these must have a known naming convention on the machine.
     */
     certFile = strings.Split(certFile, "_")[1]

	_, err := p.SSHCommand(fmt.Sprintf("sudo chmod 0400 %s", path.Join(targetPath, certFile)))
	if  err != nil {
		return err
	}

	return nil
}

func GenerateCertificates(p Provisioner, k8sOptions kubernetes.KubernetesOptions, authOptions auth.AuthOptions) (error) {
  /* Generate and install certificates. Then kick off kubernetes */
  driver := p.GetDriver()
  machine := driver.GetMachineName()
  bits := 2048  // Based on the initial configuration
  targetDir := k8sOptions.K8SCertPath
  ip, err := driver.GetIP()
  if err != nil {
    return fmt.Errorf("Error retrieving address: %s", err)
  }

  err = cert.GenerateCert(
    []string{ip, "localhost"},
    k8sOptions.K8SAPICert,
    k8sOptions.K8SAPIKey,
    authOptions.CaCertPath,
    authOptions.CaPrivateKeyPath,
    kubernetes.GenOrg(machine, "api"),
    bits)

  if err != nil {
    return fmt.Errorf("Error generating API cert: %s", err)
  }

  err = cert.GenerateCert(
    []string{""},
    k8sOptions.K8SAdminCert,
    k8sOptions.K8SAdminKey,
    authOptions.CaCertPath,
    authOptions.CaPrivateKeyPath,
    kubernetes.GenOrg(machine, "admin"),
    bits)

  if err != nil {
    return fmt.Errorf("Error generating Admin cert: %s", err)
  }

  err = cert.GenerateCert(
    []string{},
    k8sOptions.K8SProxyCert,
    k8sOptions.K8SProxyKey,
    authOptions.CaCertPath,
    authOptions.CaPrivateKeyPath,
    kubernetes.GenOrg(machine, "proxy"),
    bits)

  if err != nil {
    return fmt.Errorf("Error generating proxy cert: %s", err)
  }

  /* Copy certs into place */
  log.Info("Copying certs to the remote system...")

  /* Kick off the kubernetes run */
  if _, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", targetDir)); err != nil {
    return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%q,%s,%d' |sudo tee %s", k8sOptions.K8SToken, k8sOptions.K8SUser, 0, path.Join(targetDir, "tokenfile.txt"))); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAPIKey, targetDir + "/apiserver"); err != nil {
    return err
  }

  if err := fixPermissions(p, k8sOptions.K8SAPIKey, targetDir + "/apiserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAPICert, targetDir + "/apiserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SProxyCert, targetDir + "/proxyserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SProxyKey, targetDir + "/proxyserver"); err != nil {
    return err
  }

  if err := fixPermissions(p, k8sOptions.K8SProxyKey, targetDir + "/proxyserver"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAdminCert, targetDir + "/kubelet"); err != nil {
    return err
  }

  if err := xferCert(p, k8sOptions.K8SAdminKey, targetDir + "/kubelet"); err != nil {
    return err
  }

  if err := fixPermissions(p, k8sOptions.K8SAdminKey, targetDir + "/kubelet"); err != nil {
    return err
  }

  /* Copy the CA cert to a known location */
  caCertContents, err := ioutil.ReadFile(authOptions.CaCertPath)
  if err != nil {
    return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s/ca.pem", caCertContents, targetDir)); err != nil {
    return err
  }

  return nil
}

func configureKubernetes(p Provisioner, k8sOptions *kubernetes.KubernetesOptions, authOptions auth.AuthOptions) (error) {
    log.Info("Configuring kubernetes...")

  if _, err := p.SSHCommand("sudo /bin/sh /usr/local/etc/init.d/kubelet stop"); err != nil {
      log.Info("Errored while attempting to stop the kubelet: %s", err)
  }

  if err := GenerateCertificates(p, *k8sOptions, authOptions); err != nil {
    return err
  }

  /* Generate and install certificates. Then kick off kubernetes */
	driver := p.GetDriver()
	machine := driver.GetMachineName()
  targetDir := k8sOptions.K8SCertPath

  /* Generate and copy a new YAML file to the target */
  configFile, err := Generatek8sManifest(machine, targetDir)
  if err != nil {
      return err
  }

  kubeletConfig, err := GenerateKubeletConfig(machine, targetDir)
  if err != nil {
      return err
  }

  policyFile, err := GeneratePolicyFile(k8sOptions.K8SUser)
  if err != nil {
    return err
  }

  /* TOOD: The target manifest directory should be a parameter throughout here */
  /* Ensure that the kubernetes configuration directory exists */
  if _, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p /etc/kubernetes/manifests")); err != nil {
      return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s", kubeletConfig, "/etc/kubernetes/kubelet.kubeconfig")); err != nil {
      return err
  }

  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s", configFile, "/etc/kubernetes/manifests/kubernetes.yaml")); err != nil {
      return err
  }

  /* Generate the policy file */
  if _, err := p.SSHCommand(fmt.Sprintf("printf '%%s' '%s' | sudo tee %s", policyFile, "/etc/kubernetes/policies/policy.jsonl")); err != nil {
      return err
  }

	/* Lastly, start the kubelet */
    if _, err := p.SSHCommand("sudo /bin/sh /usr/local/etc/init.d/kubelet start"); err != nil {
        return err
    }

    return nil
}

func GeneratePolicyFile(name string) (string, error) {
  type ConfigDetails struct {
    Username string
  }
  var result bytes.Buffer

  details := ConfigDetails{name}
  policyTmpl := `{"user":"{{.Username}}"}
{"user":"scheduler", "readonly": true, "resource": "pods"}
{"user":"scheduler", "resource": "bindings"}
{"user":"proxy", "resource": "services"}
{"user":"proxy", "resource": "endpoints"}
{"user":"kubelet",  "resource": "pods"}
{"user":"kubelet",  "resource": "nodes"}
{"user":"kubelet",  "readonly": true, "resource": "services"}
{"user":"kubelet",  "readonly": true, "resource": "endpoints"}
{"user":"kubelet", "resource": "events"}
}`

  t, err := template.New("PolicyTmpl").Parse(policyTmpl)
  if err != nil {
      return "", err
  }

  err = t.Execute(&result, details)

  return result.String(), err
}

func GenerateKubeletConfig(name string, targetDir string) (string, error) {
    type ConfigDetails struct {
        ClusterName   string
        CertDir       string
    }

    details := ConfigDetails{name, targetDir}
    var result bytes.Buffer

    kubeletConfigTmpl := `apiVersion: v1
kind: Config
clusters:
  - cluster:
      certificate-authority: {{.CertDir}}/ca.pem
      server: https://127.0.0.1:6443
    name: {{.ClusterName}}
contexts:
  - context:
      cluster: {{.ClusterName}}
      user: kubelet
    name: {{.ClusterName}}
users:
  - name: kubelet
    user:
      client-certificate: {{.CertDir}}/kubelet/cert.pem
      client-key: {{.CertDir}}/kubelet/key.pem`

    t, err := template.New("kubeletConfigTmpl").Parse(kubeletConfigTmpl)
    if err != nil {
        return "", err
    }

    err = t.Execute(&result, details)

    return result.String(), err
}

func Generatek8sManifest(name string, targetDir string) (string, error) {
    type ConfigDetails struct {
        ClusterName   string
        CertDir       string
    }

    details := ConfigDetails{name, targetDir}
    var result bytes.Buffer

    k8sConfigTmpl := `apiVersion: v1
kind: Pod
clusters:
  - cluster:
      certificate-authority: {{.CertDir}}/ca.pem
metadata:
  name: {{.ClusterName}}
spec:
  hostNetwork: true
  volumes:
    - name: "certs"
      hostPath:
        path: "{{.CertDir}}"
    - name: "policies"
      hostPath:
        path: "/etc/kubernetes/policies"
  containers:
    - name: "etcd"
      image: "b.gcr.io/kuar/etcd:2.1.1"
      args:
        - "--data-dir=/var/lib/etcd"
        - "--advertise-client-urls=http://127.0.0.1:2379"
        - "--listen-client-urls=http://127.0.0.1:2379"
        - "--listen-peer-urls=http://127.0.0.1:2380"
        - "--name=etcd"
    - name: "controller-manager"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      args:
        - "/hyperkube"
        - "controller-manager"
        - "--master=http://127.0.0.1:8080"
        - "--v=2"
    - name: "apiserver"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      volumeMounts:
        - name: "certs"
          mountPath: "{{.CertDir}}"
          readOnly: true
        - name: "policies"
          mountPath: "/etc/kubernetes/policies"
          readOnly: true
      args:
        - "/hyperkube"
        - "apiserver"
        - "--authorization-mode=AlwaysAllow"
        - "--client-ca-file=/var/run/kubernetes/ca.pem"
        - "--token-auth-file={{.CertDir}}/tokenfile.txt"
        - "--allow-privileged=true"
        - "--service-cluster-ip-range=10.0.20.0/24"
        - "--insecure-bind-address=0.0.0.0"
        - "--insecure-port=8080"
        - "--secure-port=6443"
        - "--etcd-servers=http://127.0.0.1:2379"
        - "--tls-cert-file={{.CertDir}}/apiserver/cert.pem"
        - "--tls-private-key-file={{.CertDir}}/apiserver/key.pem"
        - "--v=2"
    - name: "proxy"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      securityContext:
        privileged: true
      args:
        - "/hyperkube"
        - "proxy"
        - "--master=http://127.0.0.1:8080"
        - "--v=2"
    - name: "scheduler"
      image: "gcr.io/google_containers/hyperkube:v1.0.3"
      args:
        - "/hyperkube"
        - "scheduler"
        - "--master=http://127.0.0.1:8080"
        - "--v=2"

`
    t, err := template.New("k8sConfig").Parse(k8sConfigTmpl)
    if err != nil {
        return "", err
    }

    err = t.Execute(&result, details)

    return result.String(), err
}
<|endoftext|>


<commit_before>package process

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"github.com/taskcluster/generic-worker/runtime"
	"github.com/taskcluster/runlib/platform"
	"github.com/taskcluster/runlib/subprocess"
	"github.com/taskcluster/runlib/win32"
)

type Verdict int

const (
	SUCCESS               = Verdict(0)
	FAIL                  = Verdict(1)
	CRASH                 = Verdict(2)
	TIME_LIMIT_EXCEEDED   = Verdict(3)
	MEMORY_LIMIT_EXCEEDED = Verdict(4)
	IDLE                  = Verdict(5)
	SECURITY_VIOLATION    = Verdict(6)
)

func (v Verdict) String() string {
	switch v {
	case SUCCESS:
		return "SUCCEEDED"
	case FAIL:
		return "FAILED"
	case CRASH:
		return "CRASHED"
	case TIME_LIMIT_EXCEEDED:
		return "TIME_LIMIT_EXCEEDED"
	case MEMORY_LIMIT_EXCEEDED:
		return "MEMORY_LIMIT_EXCEEDED"
	case IDLE:
		return "IDLENESS_LIMIT_EXCEEDED"
	case SECURITY_VIOLATION:
		return "SECURITY_VIOLATION"
	}
	return "FAILED"
}

func GetVerdict(r *Result) Verdict {
	switch {
	case r.SuccessCode == 0 && r.ExitCode == 0:
		return SUCCESS
	case r.SuccessCode == 0 && r.ExitCode != 0:
		return FAIL
	case r.SuccessCode&(subprocess.EF_PROCESS_LIMIT_HIT|subprocess.EF_PROCESS_LIMIT_HIT_POST) != 0:
		return SECURITY_VIOLATION
	case r.SuccessCode&(subprocess.EF_INACTIVE|subprocess.EF_TIME_LIMIT_HARD) != 0:
		return IDLE
	case r.SuccessCode&(subprocess.EF_TIME_LIMIT_HIT|subprocess.EF_TIME_LIMIT_HIT_POST) != 0:
		return TIME_LIMIT_EXCEEDED
	case r.SuccessCode&(subprocess.EF_MEMORY_LIMIT_HIT|subprocess.EF_MEMORY_LIMIT_HIT_POST) != 0:
		return MEMORY_LIMIT_EXCEEDED
	default:
		return CRASH
	}
}

type Command struct {
	*subprocess.Subprocess
	Deadline time.Time
}

type Result struct {
	*subprocess.SubprocessResult
	SystemError error
}

type DesktopSession struct {
	User        *runtime.OSUser
	LoginInfo   *subprocess.LoginInfo
	Desktop     *platform.Desktop
	OrigDesktop *platform.Desktop
}

func (r *Result) Succeeded() bool {
	return GetVerdict(r) == SUCCESS
}

func (r *Result) Failed() bool {
	return r.SystemError == nil && GetVerdict(r) != SUCCESS
}

func (r *Result) FailureCause() error {
	return fmt.Errorf("%v\n\nExit code: %v", r.Error, r.ExitCode)
}

func (r *Result) Crashed() bool {
	return r.SystemError != nil
}

func (r *Result) CrashCause() error {
	return r.SystemError
}

func (r *Result) String() string {
	if r.SystemError != nil {
		return fmt.Sprintf(`WORKER CRASH!!
%v`, r.SystemError)
	}
	if r.SubprocessResult != nil {
		return fmt.Sprintf(`   Exit Code: %v
Success Code: 0x%X
   User Time: %v
 Kernel Time: %v
   Wall Time: %v
 Peak Memory: %v
      Result: %v`, r.ExitCode, r.SuccessCode, r.UserTime, r.KernelTime, r.WallTime, r.PeakMemory, GetVerdict(r))
	}
	return fmt.Sprintf("Worker in unknown state: %#v", r)
}

func (c *Command) String() string {
	return *c.Cmd.CommandLine
}

func (c *Command) Execute() (r *Result) {
	if !c.Deadline.IsZero() {
		c.HardTimeLimit = c.Deadline.Sub(time.Now())
		if c.HardTimeLimit < 0 {
			log.Printf("WARNING: Deadline %v exceeded before command %v has been executed!", c.Deadline, c)
			// this is a hack to simulate a failure
			return &Result{
				SubprocessResult: &subprocess.SubprocessResult{
					SuccessCode: 0,
					ExitCode:    1,
				},
				SystemError: nil,
			}
		}
	}
	result, err := c.Subprocess.Execute()
	return &Result{
		SubprocessResult: result,
		SystemError:      err,
	}
}

func NewDesktopSession(username, password string) (loginInfo *subprocess.LoginInfo, origDesktop, newDesktop *platform.Desktop, err error) {
	// if running as current user, no login nor desktop required
	if username == "" {
		return
	}
	origDesktop, newDesktop, err = platform.CreateDesktop()
	if err != nil {
		return
	}
	loginInfo, err = subprocess.NewLoginInfo(username, password)
	if err != nil {
		return
	}
	err = newDesktop.Display()
	if err != nil {
		log.Printf("Could not display the newly created desktop, despite successfully logging in:\n%v", err)
	}
	return
}

func NewCommand(commandLine string, workingDirectory *string, env *[]string, deadline time.Time) (*Command, error) {
	if deadline.IsZero() {
		log.Print("No deadline!")
	} else {
		log.Printf("Deadline: %v", deadline)
	}
	hToken, err := win32.InteractiveUserToken()
	if err != nil {
		return nil, err
	}
	loginInfo := &subprocess.LoginInfo{
		HUser: hToken,
	}
	command := &Command{
		Subprocess: &subprocess.Subprocess{
			TimeQuantum: time.Second / 4,
			Cmd: &subprocess.CommandLine{
				ApplicationName: nil,
				CommandLine:     &commandLine,
				Parameters:      nil,
			},
			CurrentDirectory:    workingDirectory,
			TimeLimit:           0,
			HardTimeLimit:       0,
			MemoryLimit:         0,
			CheckIdleness:       false,
			RestrictUi:          false,
			ProcessAffinityMask: 0,
			NoJob:               true,
			Environment:         env,
			StdIn: &subprocess.Redirect{
				Mode: subprocess.REDIRECT_NONE,
			},
			StdOut:        nil,
			StdErr:        nil,
			JoinStdOutErr: true,
			Options: &subprocess.PlatformOptions{
				Desktop: "",
			},
			Login: loginInfo,
		},
		Deadline: deadline,
	}
	log.Printf("Created command: %v", commandLine)
	return command, nil
}

// For now, I don't see a simple way to terminate the process outside of the
// subprocess library.  However, we can set a time limit, so the only thing we
// can't do is kill a process in response to cancelling of a task. That wasn't
// implemented before, so we haven't lost anything, over the old
// implementation. However, at some point, we should find a way to kill the
// process for when we want to cancel tasks.
func (c *Command) Kill() error {
	return nil
}

func (c *Command) DirectOutput(writer io.Writer) {
	r, w, err := os.Pipe()
	if err != nil {
		panic(err)
	}
	c.StdOut = &subprocess.Redirect{
		Mode: subprocess.REDIRECT_PIPE,
		Pipe: w,
	}
	go func() {
		io.Copy(writer, r)
	}()
}
<commit_msg>First try<commit_after>package process

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"github.com/taskcluster/generic-worker/runtime"
	"github.com/taskcluster/runlib/platform"
	"github.com/taskcluster/runlib/subprocess"
	"github.com/taskcluster/runlib/win32"
)

type Verdict int

const (
	SUCCESS               = Verdict(0)
	FAIL                  = Verdict(1)
	CRASH                 = Verdict(2)
	TIME_LIMIT_EXCEEDED   = Verdict(3)
	MEMORY_LIMIT_EXCEEDED = Verdict(4)
	IDLE                  = Verdict(5)
	SECURITY_VIOLATION    = Verdict(6)
)

func (v Verdict) String() string {
	switch v {
	case SUCCESS:
		return "SUCCEEDED"
	case FAIL:
		return "FAILED"
	case CRASH:
		return "CRASHED"
	case TIME_LIMIT_EXCEEDED:
		return "TIME_LIMIT_EXCEEDED"
	case MEMORY_LIMIT_EXCEEDED:
		return "MEMORY_LIMIT_EXCEEDED"
	case IDLE:
		return "IDLENESS_LIMIT_EXCEEDED"
	case SECURITY_VIOLATION:
		return "SECURITY_VIOLATION"
	}
	return "FAILED"
}

func GetVerdict(r *Result) Verdict {
	switch {
	case r.SuccessCode == 0 && r.ExitCode == 0:
		return SUCCESS
	case r.SuccessCode == 0 && r.ExitCode != 0:
		return FAIL
	case r.SuccessCode&(subprocess.EF_PROCESS_LIMIT_HIT|subprocess.EF_PROCESS_LIMIT_HIT_POST) != 0:
		return SECURITY_VIOLATION
	case r.SuccessCode&(subprocess.EF_INACTIVE|subprocess.EF_TIME_LIMIT_HARD) != 0:
		return IDLE
	case r.SuccessCode&(subprocess.EF_TIME_LIMIT_HIT|subprocess.EF_TIME_LIMIT_HIT_POST) != 0:
		return TIME_LIMIT_EXCEEDED
	case r.SuccessCode&(subprocess.EF_MEMORY_LIMIT_HIT|subprocess.EF_MEMORY_LIMIT_HIT_POST) != 0:
		return MEMORY_LIMIT_EXCEEDED
	default:
		return CRASH
	}
}

type Command struct {
	*subprocess.Subprocess
	Deadline time.Time
}

type Result struct {
	*subprocess.SubprocessResult
	SystemError error
}

type DesktopSession struct {
	User        *runtime.OSUser
	LoginInfo   *subprocess.LoginInfo
	Desktop     *platform.Desktop
	OrigDesktop *platform.Desktop
}

func (r *Result) Succeeded() bool {
	return GetVerdict(r) == SUCCESS
}

func (r *Result) Failed() bool {
	return r.SystemError == nil && GetVerdict(r) != SUCCESS
}

func (r *Result) FailureCause() error {
	return fmt.Errorf("%v\n\nExit code: %v", r.Error, r.ExitCode)
}

func (r *Result) Crashed() bool {
	return r.SystemError != nil
}

func (r *Result) CrashCause() error {
	return r.SystemError
}

func (r *Result) String() string {
	if r.SystemError != nil {
		return fmt.Sprintf(`WORKER CRASH!!
%v`, r.SystemError)
	}
	if r.SubprocessResult != nil {
		return fmt.Sprintf(`   Exit Code: %v
Success Code: 0x%X
   User Time: %v
 Kernel Time: %v
   Wall Time: %v
 Peak Memory: %v
      Result: %v`, r.ExitCode, r.SuccessCode, r.UserTime, r.KernelTime, r.WallTime, r.PeakMemory, GetVerdict(r))
	}
	return fmt.Sprintf("Worker in unknown state: %#v", r)
}

func (c *Command) String() string {
	return *c.Cmd.CommandLine
}

func (c *Command) Execute() (r *Result) {
	if !c.Deadline.IsZero() {
		c.HardTimeLimit = c.Deadline.Sub(time.Now())
		if c.HardTimeLimit < 0 {
			log.Printf("WARNING: Deadline %v exceeded before command %v has been executed!", c.Deadline, c)
			// this is a hack to simulate a failure
			return &Result{
				SubprocessResult: &subprocess.SubprocessResult{
					SuccessCode: 0,
					ExitCode:    1,
				},
				SystemError: nil,
			}
		}
	}
	result, err := c.Subprocess.Execute()
	return &Result{
		SubprocessResult: result,
		SystemError:      err,
	}
}

func NewDesktopSession(username, password string) (loginInfo *subprocess.LoginInfo, origDesktop, newDesktop *platform.Desktop, err error) {
	// if running as current user, no login nor desktop required
	if username == "" {
		return
	}
	origDesktop, newDesktop, err = platform.CreateDesktop()
	if err != nil {
		return
	}
	loginInfo, err = subprocess.NewLoginInfo(username, password)
	if err != nil {
		return
	}
	err = newDesktop.Display()
	if err != nil {
		log.Printf("Could not display the newly created desktop, despite successfully logging in:\n%v", err)
	}
	return
}

func NewCommand(commandLine string, workingDirectory *string, env *[]string, deadline time.Time) (*Command, error) {
	if deadline.IsZero() {
		log.Print("No deadline!")
	} else {
		log.Printf("Deadline: %v", deadline)
	}
	hToken, err := win32.InteractiveUserToken(time.Minute)
	if err != nil {
		return nil, err
	}
	loginInfo := &subprocess.LoginInfo{
		HUser: hToken,
	}
	command := &Command{
		Subprocess: &subprocess.Subprocess{
			TimeQuantum: time.Second / 4,
			Cmd: &subprocess.CommandLine{
				ApplicationName: nil,
				CommandLine:     &commandLine,
				Parameters:      nil,
			},
			CurrentDirectory:    workingDirectory,
			TimeLimit:           0,
			HardTimeLimit:       0,
			MemoryLimit:         0,
			CheckIdleness:       false,
			RestrictUi:          false,
			ProcessAffinityMask: 0,
			NoJob:               true,
			Environment:         env,
			StdIn: &subprocess.Redirect{
				Mode: subprocess.REDIRECT_NONE,
			},
			StdOut:        nil,
			StdErr:        nil,
			JoinStdOutErr: true,
			Options: &subprocess.PlatformOptions{
				Desktop: "",
			},
			Login: loginInfo,
		},
		Deadline: deadline,
	}
	log.Printf("Created command: %v", commandLine)
	return command, nil
}

// For now, I don't see a simple way to terminate the process outside of the
// subprocess library.  However, we can set a time limit, so the only thing we
// can't do is kill a process in response to cancelling of a task. That wasn't
// implemented before, so we haven't lost anything, over the old
// implementation. However, at some point, we should find a way to kill the
// process for when we want to cancel tasks.
func (c *Command) Kill() error {
	return nil
}

func (c *Command) DirectOutput(writer io.Writer) {
	r, w, err := os.Pipe()
	if err != nil {
		panic(err)
	}
	c.StdOut = &subprocess.Redirect{
		Mode: subprocess.REDIRECT_PIPE,
		Pipe: w,
	}
	go func() {
		io.Copy(writer, r)
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go9p/p"
	"code.google.com/p/go9p/p/srv"
	"strings"
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"unicode/utf8"
)

type M9Player struct {
	playlist []string
	position int
	queue []string

	player *M9Play
	song *string

	actions chan func()
}

type M9Play struct {
	Song string
	proc *os.Process
	killed bool
}

func (player *M9Play) Kill() {
	player.killed = true
	player.proc.Signal(os.Interrupt)
}

var m9 *M9Player

func (m9 *M9Player) spawn(song string) {
	path, err := exec.LookPath("m9play")
	if err != nil {
		fmt.Printf("couldn't find m9play: %s\n", err)
		return
	}
	proc, err := os.StartProcess(path, []string{"m9play", song}, new(os.ProcAttr))
	if err != nil {
		fmt.Printf("couldn't spawn player: %s\n", err)
		return
	}
	player := M9Play{song, proc, false}
	m9.player = &player
	events <- "Play " + song
	go func() {
		proc.Wait()
		if player.killed {
			return
		}
		if len(m9.queue) == 0 && len(m9.playlist) > 0 {
			m9.position = (m9.position + 1) % len(m9.playlist)
		}
		m9.player = nil
		m9.Play("")
	}()
}

func (m9 *M9Player) state() string {
	player := m9.player
	if player != nil {
		return "Play " + player.Song
	} else {
		if len(m9.queue) > 0 {
			return "Stop " + m9.queue[0]
		} else if len(m9.playlist) > 0 {
			return "Stop " + m9.playlist[m9.position]
		}
		return "Stop"
	}
}

func (m9 *M9Player) Add(song string) {
	m9.playlist = append(m9.playlist, song)
	if m9.player == nil && len(m9.queue) == 0 && len(m9.playlist) == 1 {
		/* We are stopped, the queue is empty and this was the first song added to the list.
		 * Update /event to reflect the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Clear() {
	m9.playlist = make([]string, 0)
}

func (m9 *M9Player) Enqueue(song string) {
	m9.queue = append(m9.queue, song)
	if m9.player != nil && len(m9.queue) == 1 {
		/* We are stopped, and this was the first item added to the queue.
		 * Update /event to show the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Play(song string) {
	player := m9.player
	if player != nil {
		/* already playing; stop the current player first */
		m9.player = nil
		player.Kill()
	}
	if song != "" {
		m9.spawn(song)
	} else {
		if len(m9.queue) > 0 {
			m9.spawn(m9.queue[0])
			m9.queue = m9.queue[1:]
		} else if len(m9.playlist) > 0 {
			m9.spawn(m9.playlist[m9.position])
		}
	}
}

func (m9 *M9Player) Skip(n int) {
	if len(m9.playlist) == 0 {
		return
	}
	m9.position += n
	m9.position %= len(m9.playlist)
	if m9.position < 0 {
		m9.position += len(m9.playlist)
	}
	if m9.player != nil {
		m9.Play("")
	} else {
		events <- m9.state()
	}
}

func (m9 *M9Player) Stop() {
	player := m9.player
	m9.player = nil
	if player != nil {
		player.Kill()
	}
	events <- m9.state()
}

func play(song string) {
	m9.Play(song)
}

func skip(amount string) error {
	if amount == "" {
		m9.Skip(1)
		return nil
	}
	i, err := strconv.Atoi(amount)
	if err != nil {
		return err
	}
	m9.Skip(i)
	return nil
}

func stop() {
	m9.Stop()
}

var events chan string
var register chan chan string

func eventer() {
	listeners := make([]chan string, 0)
	for {
		select {
		case ev := <- events:
			for i := range(listeners) {
				listeners[i] <- ev
			}
			listeners = make([]chan string, 0)

		case l := <- register:
			listeners = append(listeners, l)
		}
	}
}

func waitForEvent() string {
	c := make(chan string)
	register <- c
	ev := <- c
	return ev
}

var net = flag.String("net", "unix", "network type")
var addr = flag.String("addr", "/tmp/ns.sqweek.:0/m9u", "network address")

type CtlFile struct {
	srv.File
}
type SongListFile struct {
	srv.File
	rd map[*srv.Fid] []byte
	wr map[*srv.Fid] *PartialLine
	SongAdded func(string)
}
type ListFile struct {
	SongListFile
}
type QueueFile struct {
	SongListFile
}
type EventFile struct {
	srv.File
}

func (*CtlFile) Write(fid *srv.FFid, b []byte, offset uint64) (n int, err error) {
	cmd := string(b)
	if strings.HasPrefix(cmd, "play") {
		play(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "skip") {
		err = skip(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "stop") {
		stop()
	} else {
		err = m9err("/ctl", "write", "syntax error")
	}
	if err == nil {
		n = len(b)
	}
	return n, err
}

func mkbuf(lst []string) []byte {
	buflen := 0
	for i := range(lst) {
		buflen += 1 + len([]byte(lst[i]))
	}
	buf := make([]byte, buflen)
	j := 0
	for i := range(lst) {
		j += copy(buf[j:], lst[i])
		buf[j] = '\n'
		j++
	}
	return buf
}

type PartialLine struct {
	leftover []byte
}

func (part *PartialLine) append(bytes []byte) string {
	if part.leftover == nil {
		return string(bytes)
	}
	left := part.leftover
	part.leftover = nil
	return string(left) + string(bytes)
}

func (lstfile *ListFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		lstfile.wr[fid.Fid] = new(PartialLine)
		if mode & p.OTRUNC != 0 {
			m9.Clear()
		}
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		lstfile.rd[fid.Fid] = mkbuf(m9.playlist)
	}
	return nil
}

type M9Error struct {
	file string
	op string
	msg string
}

func (e *M9Error) Error() string {
	return e.file + ": " + e.op + ": " + e.msg
}

func m9err(file string, op string, msg string) *M9Error {
	err := new(M9Error)
	err.file = file
	err.op = op
	err.msg = msg
	return err
}

func (slf *SongListFile) Write(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	prefix, ok := slf.wr[fid.Fid]
	if !ok {
		return 0, m9err(fid.F.Name, "write", "bad state")
	}
	i := 0
	for {
		j := bytes.IndexByte(b[i:], '\n')
		if j == -1 {
			break
		}
		song := prefix.append(b[i:i+j])
		slf.SongAdded(song)
		//m9.Add(song)
		i += j+1
	}
	if i < len(b) {
		prefix.leftover = b[i:]
	}
	return len(b), nil
}

func min(a uint64, b uint64) uint64 {
	if a < b {
		return a
	}
	return b
}

func (slf *SongListFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	buf, ok := slf.rd[fid.Fid]
	if !ok {
		return 0, m9err(fid.F.Name, "read", "bad state")
	}
	remaining := uint64(len(buf)) - offset
	n := min(remaining, uint64(len(b)))
	copy(b, buf[offset:offset + n])
	return int(n), nil
}

func (slf *SongListFile) Clunk(fid *srv.FFid) error {
	delete(slf.rd, fid.Fid)
	delete(slf.wr, fid.Fid)
	return nil
}

func (qf *QueueFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		qf.wr[fid.Fid] = new(PartialLine)
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		qf.rd[fid.Fid] = mkbuf(m9.queue)
	}
	return nil
}

func (*EventFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	var ev string
	if offset == 0 {
		ev = m9.state()
	} else {
		ev = waitForEvent()
	}
	buf := []byte(ev)
	for len(buf) > len(b) - 1 {
		_, size := utf8.DecodeLastRune(buf)
		buf = buf[:len(buf)-size]
	}
	copy(b[:len(buf)], buf)
	b[len(buf)] = byte('\n')
	return len(buf)+1, nil
}

func (slf *SongListFile) init(f func(string)) {
	slf.wr = make(map[*srv.Fid]*PartialLine)
	slf.rd = make(map[*srv.Fid] []byte)
	slf.SongAdded = f
}

func main() {
	var err error
	flag.Parse()

	uid := p.OsUsers.Uid2User(os.Geteuid())
	gid := p.OsUsers.Gid2Group(os.Getegid())
	fmt.Printf("uid = %d  gid = %d\n", os.Geteuid(), os.Getegid())

	events = make(chan string)
	register = make(chan chan string)

	m9 = new(M9Player)

	go eventer()

	root := new(srv.File)
	root.Add(nil, "/", uid, gid, p.DMDIR|0555, nil)
	ctl := new(CtlFile)
	ctl.Add(root, "ctl", uid, gid, 0644, ctl)
	list := new(ListFile)
	list.init(func(song string) {m9.Add(song)})
	list.Add(root, "list", uid, gid, 0644, list)
	queue := new(QueueFile)
	queue.init(func(song string) {m9.Enqueue(song)})
	queue.Add(root, "queue", uid, gid, 0644, queue)
	event := new(EventFile)
	event.Add(root, "event", uid, gid, 0444, event)

	s := srv.NewFileSrv(root)
	s.Start(s)
	err = s.StartNetListener(*net, *addr)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
	}

	return
}

<commit_msg>Cleanup error messages. Allow wstat.<commit_after>package main

import (
	"code.google.com/p/go9p/p"
	"code.google.com/p/go9p/p/srv"
	"strings"
	"errors"
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"unicode/utf8"
)

type M9Player struct {
	playlist []string
	position int
	queue []string

	player *M9Play
	song *string

	actions chan func()
}

type M9Play struct {
	Song string
	proc *os.Process
	killed bool
}

func (player *M9Play) Kill() {
	player.killed = true
	player.proc.Signal(os.Interrupt)
}

var m9 *M9Player

func (m9 *M9Player) spawn(song string) {
	path, err := exec.LookPath("m9play")
	if err != nil {
		fmt.Printf("couldn't find m9play: %s\n", err)
		return
	}
	proc, err := os.StartProcess(path, []string{"m9play", song}, new(os.ProcAttr))
	if err != nil {
		fmt.Printf("couldn't spawn player: %s\n", err)
		return
	}
	player := M9Play{song, proc, false}
	m9.player = &player
	events <- "Play " + song
	go func() {
		proc.Wait()
		if player.killed {
			return
		}
		if len(m9.queue) == 0 && len(m9.playlist) > 0 {
			m9.position = (m9.position + 1) % len(m9.playlist)
		}
		m9.player = nil
		m9.Play("")
	}()
}

/* instantaneous state */
func (m9 *M9Player) state() string {
	player := m9.player
	if player != nil {
		return "Play " + player.Song
	} else {
		if len(m9.queue) > 0 {
			return "Stop " + m9.queue[0]
		} else if len(m9.playlist) > 0 {
			return "Stop " + m9.playlist[m9.position]
		}
		return "Stop"
	}
}

func (m9 *M9Player) Add(song string) {
	m9.playlist = append(m9.playlist, song)
	if m9.player == nil && len(m9.queue) == 0 && len(m9.playlist) == 1 {
		/* We are stopped, the queue is empty and this was the first song added to the list.
		 * Update /event to reflect the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Clear() {
	m9.playlist = make([]string, 0)
}

func (m9 *M9Player) Enqueue(song string) {
	m9.queue = append(m9.queue, song)
	if m9.player != nil && len(m9.queue) == 1 {
		/* We are stopped, and this was the first item added to the queue.
		 * Update /event to show the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Play(song string) {
	player := m9.player
	if player != nil {
		/* already playing; stop the current player first */
		m9.player = nil
		player.Kill()
	}
	if song != "" {
		m9.spawn(song)
	} else {
		if len(m9.queue) > 0 {
			m9.spawn(m9.queue[0])
			m9.queue = m9.queue[1:]
		} else if len(m9.playlist) > 0 {
			m9.spawn(m9.playlist[m9.position])
		}
	}
}

func (m9 *M9Player) Skip(n int) {
	if len(m9.playlist) == 0 {
		return
	}
	m9.position += n
	m9.position %= len(m9.playlist)
	if m9.position < 0 {
		m9.position += len(m9.playlist)
	}
	if m9.player != nil {
		m9.Play("")
	} else {
		events <- m9.state()
	}
}

func (m9 *M9Player) Stop() {
	player := m9.player
	m9.player = nil
	if player != nil {
		player.Kill()
	}
	events <- m9.state()
}

func play(song string) {
	m9.Play(song)
}

func skip(amount string) error {
	if amount == "" {
		m9.Skip(1)
		return nil
	}
	i, err := strconv.Atoi(amount)
	if err != nil {
		return err
	}
	m9.Skip(i)
	return nil
}

func stop() {
	m9.Stop()
}

var events chan string
var register chan chan string

func eventer() {
	listeners := make([]chan string, 0)
	for {
		select {
		case ev := <- events:
			for i := range(listeners) {
				listeners[i] <- ev
			}
			listeners = make([]chan string, 0)

		case l := <- register:
			listeners = append(listeners, l)
		}
	}
}

func waitForEvent() string {
	c := make(chan string)
	register <- c
	ev := <- c
	return ev
}

var net = flag.String("net", "unix", "network type")
var addr = flag.String("addr", "/tmp/ns.sqweek.:0/m9u", "network address")

type CtlFile struct {
	srv.File
}
type SongListFile struct {
	srv.File
	rd map[*srv.Fid] []byte
	wr map[*srv.Fid] *PartialLine
	SongAdded func(string)
}
type ListFile struct {
	SongListFile
}
type QueueFile struct {
	SongListFile
}
type EventFile struct {
	srv.File
}

func (*CtlFile) Write(fid *srv.FFid, b []byte, offset uint64) (n int, err error) {
	cmd := string(b)
	if strings.HasPrefix(cmd, "play") {
		play(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "skip") {
		err = skip(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "stop") {
		stop()
	} else {
		err = errors.New("ill-formed control message")
	}
	if err == nil {
		n = len(b)
	}
	return n, err
}

func (*CtlFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	return nil
}

func mkbuf(lst []string) []byte {
	buflen := 0
	for i := range(lst) {
		buflen += 1 + len([]byte(lst[i]))
	}
	buf := make([]byte, buflen)
	j := 0
	for i := range(lst) {
		j += copy(buf[j:], lst[i])
		buf[j] = '\n'
		j++
	}
	return buf
}

type PartialLine struct {
	leftover []byte
}

func (part *PartialLine) append(bytes []byte) string {
	if part.leftover == nil {
		return string(bytes)
	}
	left := part.leftover
	part.leftover = nil
	return string(left) + string(bytes)
}

func (lstfile *ListFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		lstfile.wr[fid.Fid] = new(PartialLine)
		if mode & p.OTRUNC != 0 {
			m9.Clear()
		}
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		lstfile.rd[fid.Fid] = mkbuf(m9.playlist)
	}
	return nil
}

func (slf *SongListFile) Write(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	prefix, ok := slf.wr[fid.Fid]
	if !ok {
		return 0, errors.New("internal state corrupted")
	}
	i := 0
	for {
		j := bytes.IndexByte(b[i:], '\n')
		if j == -1 {
			break
		}
		song := prefix.append(b[i:i+j])
		slf.SongAdded(song)
		//m9.Add(song)
		i += j+1
	}
	if i < len(b) {
		prefix.leftover = b[i:]
	}
	return len(b), nil
}

func (slf *SongListFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	return nil
}

func min(a uint64, b uint64) uint64 {
	if a < b {
		return a
	}
	return b
}

func (slf *SongListFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	buf, ok := slf.rd[fid.Fid]
	if !ok {
		return 0, errors.New("internal state corrupted")
	}
	remaining := uint64(len(buf)) - offset
	n := min(remaining, uint64(len(b)))
	copy(b, buf[offset:offset + n])
	return int(n), nil
}

func (slf *SongListFile) Clunk(fid *srv.FFid) error {
	delete(slf.rd, fid.Fid)
	delete(slf.wr, fid.Fid)
	return nil
}

func (qf *QueueFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		qf.wr[fid.Fid] = new(PartialLine)
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		qf.rd[fid.Fid] = mkbuf(m9.queue)
	}
	return nil
}

func (*EventFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	var ev string
	if offset == 0 {
		ev = m9.state()
	} else {
		ev = waitForEvent()
	}
	buf := []byte(ev)
	for len(buf) > len(b) - 1 {
		_, size := utf8.DecodeLastRune(buf)
		buf = buf[:len(buf)-size]
	}
	copy(b[:len(buf)], buf)
	b[len(buf)] = byte('\n')
	return len(buf)+1, nil
}

func (*EventFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
	return nil
}

func (slf *SongListFile) init(f func(string)) {
	slf.wr = make(map[*srv.Fid]*PartialLine)
	slf.rd = make(map[*srv.Fid] []byte)
	slf.SongAdded = f
}

func main() {
	var err error
	flag.Parse()

	uid := p.OsUsers.Uid2User(os.Geteuid())
	gid := p.OsUsers.Gid2Group(os.Getegid())
	fmt.Printf("uid = %d  gid = %d\n", os.Geteuid(), os.Getegid())

	events = make(chan string)
	register = make(chan chan string)

	m9 = new(M9Player)

	go eventer()

	root := new(srv.File)
	root.Add(nil, "/", uid, gid, p.DMDIR|0555, nil)
	ctl := new(CtlFile)
	ctl.Add(root, "ctl", uid, gid, 0644, ctl)
	list := new(ListFile)
	list.init(func(song string) {m9.Add(song)})
	list.Add(root, "list", uid, gid, 0644, list)
	queue := new(QueueFile)
	queue.init(func(song string) {m9.Enqueue(song)})
	queue.Add(root, "queue", uid, gid, 0644, queue)
	event := new(EventFile)
	event.Add(root, "event", uid, gid, 0444, event)

	s := srv.NewFileSrv(root)
	s.Start(s)
	err = s.StartNetListener(*net, *addr)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
	}

	return
}

<|endoftext|>


<commit_before>package model

import (
	"github.com/jinzhu/gorm"
	"github.com/ChaosXu/nerv/lib/db"
	"fmt"
	"time"
	"github.com/ChaosXu/nerv/lib/log"
)

func init() {
	db.Models["Topology"] = topologyDesc()
}

func topologyDesc() *db.ModelDescriptor {
	return &db.ModelDescriptor{
		Type: &Topology{},
		New: func() interface{} {
			return &Topology{}
		},
		NewSlice:func() interface{} {
			return &[]Topology{}
		},
	}
}

type traverseCallback func(node *Node, template *ServiceTemplate) (<-chan error, <-chan bool)

//Topology which has been deployed by the service template
type Topology struct {
	gorm.Model
	Status
	Name     string //topology name
	Template string //service template name
	Version  int32  //service template version
	Nodes    []*Node
}

//Install the topology and start to serve
func (p *Topology) Install() error {
	log.LogCodeLine()
	return p.postTraverse("contained", p.installNode)
}

//Uninstall the topology
func (p *Topology) Uninstall() error {
	log.LogCodeLine()
	return p.preTraverse("contained", p.uninstallNode)
}

//Configure the topology for start
func (p *Topology) Configure() {

}

//Start the Topology
func (p *Topology) Start() {

}

//Stop the Topology
func (p *Topology) Stop() {

}

func (p *Topology) preTraverse(depType string, fn traverseCallback) error {
	lock := GetLock("Topology", p.ID)
	if !lock.TryLock() {
		return fmt.Errorf("topology is doing. ID=%d", p.ID)
	}
	defer lock.Unlock()

	tnodes := []*Node{}
	db.DB.Where("topology_id =?", p.ID).Preload("Links").Find(&tnodes)
	p.Nodes = tnodes

	template := ServiceTemplate{}
	if err := db.DB.Where("name=? and version=?", p.Template, p.Version).Preload("Nodes").Preload("Nodes.Parameters").First(&template).Error; err != nil {
		return err
	}

	dones := []<-chan error{}
	timeouts := []<-chan bool{}

	for _, node := range p.Nodes {
		done, timeout := p.preTraverseNode(depType, node, &template, fn)
		dones = append(dones, done)
		timeouts = append(timeouts, timeout)
	}

	var err error = nil
	for i, done := range dones {
		select {
		case e := <-done:
			if e != nil {
				err = e
			}
		case <-timeouts[i]:
		}
	}

	if err == nil {
		p.RunStatus = RunStatusGreen
	} else {
		p.Error = err.Error()
		p.RunStatus = RunStatusRed
	}
	db.DB.Save(p)
	return err
}

func (p *Topology) postTraverse(depType string, fn traverseCallback) error {
	lock := GetLock("Topology", p.ID)
	if !lock.TryLock() {
		return fmt.Errorf("topology is doing. ID=%d", p.ID)
	}
	defer lock.Unlock()

	tnodes := []*Node{}
	db.DB.Where("topology_id =?", p.ID).Preload("Links").Find(&tnodes)
	p.Nodes = tnodes

	template := ServiceTemplate{}
	if err := db.DB.Where("name=? and version=?", p.Template, p.Version).Preload("Nodes").Preload("Nodes.Parameters").First(&template).Error; err != nil {
		return err
	}

	dones := []<-chan error{}
	timeouts := []<-chan bool{}

	for _, node := range p.Nodes {
		done, timeout := p.postTraverseNode(depType, node, &template, fn)
		dones = append(dones, done)
		timeouts = append(timeouts, timeout)
	}

	var err error = nil
	for i, done := range dones {
		select {
		case e := <-done:
			if e != nil {
				err = e
			}
		case <-timeouts[i]:
		}
	}

	if err == nil {
		p.RunStatus = RunStatusGreen
	} else {
		p.Error = err.Error()
		p.RunStatus = RunStatusRed
	}
	db.DB.Save(p)
	return err
}

func (p *Topology) uninstallNode(node *Node, template *ServiceTemplate) (<-chan error, <-chan bool) {
	done := make(chan error, 1)
	timeout := make(chan bool, 1)

	go func() {
		time.Sleep(30 * time.Second)
		timeout <- true
	}()

	go func() {
		nodeTemplate := template.findTemplate(node.Template)
		if nodeTemplate != nil {
			if err := node.Execute("Delete", nodeTemplate); err != nil {
				done <- err
			} else {
				done <- nil
			}
		} else {
			node.RunStatus = RunStatusRed
			err := fmt.Errorf("template %s of node %s isn't exist", node.Template, node.Name)
			node.Error = err.Error()
			db.DB.Save(node)
			done <- err
		}
	}()

	return done, timeout
}

func (p *Topology) installNode(node *Node, template *ServiceTemplate) (<-chan error, <-chan bool) {
	done := make(chan error, 1)
	timeout := make(chan bool, 1)

	go func() {
		time.Sleep(30 * time.Second)
		timeout <- true
	}()

	go func() {
		nodeTemplate := template.findTemplate(node.Template)
		if nodeTemplate != nil {
			if err := node.Execute("Create", nodeTemplate); err != nil {
				done <- err
			} else {
				done <- nil
			}
		} else {
			node.RunStatus = RunStatusRed
			err := fmt.Errorf("template %s of node %s isn't exist", node.Template, node.Name)
			node.Error = err.Error()
			db.DB.Save(node)
			done <- err
		}
	}()

	return done, timeout
}

func (p *Topology) preTraverseNode(depType string, parent *Node, template *ServiceTemplate, fn traverseCallback) (<-chan error, <-chan bool) {
	err, timeout := fn(parent, template)
	select {
	case e := <-err:
		ec := make(chan error, 1)
		ec <- e
		return ec, timeout
	case <-timeout:
	}

	var childErr error = nil
	var childTimeout bool

	links := parent.FindLinksByType(depType)
	if links != nil && len(links) > 0 {
		dones := []<-chan error{}
		timeouts := []<-chan bool{}
		for _, link := range links {
			node := p.getNode(link.Target)
			done, timeout := p.preTraverseNode(depType, node, template, fn)
			dones = append(dones, done)
			timeouts = append(timeouts, timeout)
		}

		for i, done := range dones {
			select {
			case e := <-done:
				if e != nil {
					childErr = e
				}
			case t := <-timeouts[i]:
				if t {
					childTimeout = t
				}
			}
		}
	}

	dc := make(chan error, 1)
	tc := make(chan bool, 1)

	if err == nil {
		dc <- childErr
	}
	if timeout == nil {
		tc <- childTimeout
	}
	return dc, tc
}

func (p *Topology) postTraverseNode(depType string, parent *Node, template *ServiceTemplate, fn traverseCallback) (<-chan error, <-chan bool) {
	links := parent.FindLinksByType(depType)
	if links != nil && len(links) > 0 {
		dones := []<-chan error{}
		timeouts := []<-chan bool{}
		for _, link := range links {
			node := p.getNode(link.Target)
			done, timeout := p.postTraverseNode(depType, node, template, fn)
			dones = append(dones, done)
			timeouts = append(timeouts, timeout)
		}

		var err error = nil
		for i, done := range dones {
			select {
			case e := <-done:
				if e != nil {
					err = e
				}
			case <-timeouts[i]:
			}
		}

		if err == nil {
			return fn(parent, template)
		} else {
			done := make(chan error, 1)
			timeout := make(chan bool, 1)
			done <- err
			return done, timeout
		}
	} else {
		return fn(parent, template)
	}
}

//Only used to add host node
func (p *Topology) addNode(node *Node) {
	p.Nodes = append(p.Nodes, node)
}

func (p *Topology) putNode(node *Node) {
	old := p.getNode(node.Name)
	if old == nil {
		p.Nodes = append(p.Nodes, node)
	}
}

func (p *Topology) getNode(name string) *Node {
	for _, node := range p.Nodes {
		if node.Name == name {
			return node
		}
	}
	return nil
}

func (p *Topology) getNodes(name string) []*Node {
	nodes := []*Node{}
	for _, node := range p.Nodes {
		if node.Name == name {
			nodes = append(nodes, node)
		}
	}
	return nodes
}
<commit_msg>Refactor topology<commit_after>package model

import (
	"github.com/jinzhu/gorm"
	"github.com/ChaosXu/nerv/lib/db"
	"fmt"
	"time"
	"github.com/ChaosXu/nerv/lib/log"
)

func init() {
	db.Models["Topology"] = topologyDesc()
}

func topologyDesc() *db.ModelDescriptor {
	return &db.ModelDescriptor{
		Type: &Topology{},
		New: func() interface{} {
			return &Topology{}
		},
		NewSlice:func() interface{} {
			return &[]Topology{}
		},
	}
}

type traverseCallback func(node *Node, template *ServiceTemplate) (<-chan error, <-chan bool)

//Topology which has been deployed by the service template
type Topology struct {
	gorm.Model
	Status
	Name     string //topology name
	Template string //service template name
	Version  int32  //service template version
	Nodes    []*Node
}

//Install the topology and start to serve
func (p *Topology) Install() error {
	log.LogCodeLine()
	return p.postTraverse("contained", "Create")
}

//Uninstall the topology
func (p *Topology) Uninstall() error {
	log.LogCodeLine()
	return p.preTraverse("contained", "Delete")
}

//Configure the topology for start
func (p *Topology) Configure() error {
	return fmt.Errorf("TBD")
}

//Start the Topology
func (p *Topology) Start() error {
	log.LogCodeLine()
	return p.postTraverse("contained", "Start")
}

//Stop the Topology
func (p *Topology) Stop() error {
	log.LogCodeLine()
	return p.preTraverse("contained", "Stop")
}

func (p *Topology) preTraverse(depType string, operation string) error {
	lock := GetLock("Topology", p.ID)
	if !lock.TryLock() {
		return fmt.Errorf("topology is doing. ID=%d", p.ID)
	}
	defer lock.Unlock()

	tnodes := []*Node{}
	db.DB.Where("topology_id =?", p.ID).Preload("Links").Find(&tnodes)
	p.Nodes = tnodes

	template := ServiceTemplate{}
	if err := db.DB.Where("name=? and version=?", p.Template, p.Version).Preload("Nodes").Preload("Nodes.Parameters").First(&template).Error; err != nil {
		return err
	}

	dones := []<-chan error{}
	timeouts := []<-chan bool{}

	for _, node := range p.Nodes {
		done, timeout := p.preTraverseNode(depType, node, &template, operation)
		dones = append(dones, done)
		timeouts = append(timeouts, timeout)
	}

	var err error = nil
	for i, done := range dones {
		select {
		case e := <-done:
			if e != nil {
				err = e
			}
		case <-timeouts[i]:
		}
	}

	if err == nil {
		p.RunStatus = RunStatusGreen
	} else {
		p.Error = err.Error()
		p.RunStatus = RunStatusRed
	}
	db.DB.Save(p)
	return err
}

func (p *Topology) postTraverse(depType string, operation string) error {
	lock := GetLock("Topology", p.ID)
	if !lock.TryLock() {
		return fmt.Errorf("topology is doing. ID=%d", p.ID)
	}
	defer lock.Unlock()

	tnodes := []*Node{}
	db.DB.Where("topology_id =?", p.ID).Preload("Links").Find(&tnodes)
	p.Nodes = tnodes

	template := ServiceTemplate{}
	if err := db.DB.Where("name=? and version=?", p.Template, p.Version).Preload("Nodes").Preload("Nodes.Parameters").First(&template).Error; err != nil {
		return err
	}

	dones := []<-chan error{}
	timeouts := []<-chan bool{}

	for _, node := range p.Nodes {
		done, timeout := p.postTraverseNode(depType, node, &template, operation)
		dones = append(dones, done)
		timeouts = append(timeouts, timeout)
	}

	var err error = nil
	for i, done := range dones {
		select {
		case e := <-done:
			if e != nil {
				err = e
			}
		case <-timeouts[i]:
		}
	}

	if err == nil {
		p.RunStatus = RunStatusGreen
	} else {
		p.Error = err.Error()
		p.RunStatus = RunStatusRed
	}
	db.DB.Save(p)
	return err
}

func (p *Topology) executeNode(operation string, node *Node, template *ServiceTemplate) (<-chan error, <-chan bool) {
	done := make(chan error, 1)
	timeout := make(chan bool, 1)

	go func() {
		time.Sleep(30 * time.Second)
		timeout <- true
	}()

	go func() {
		nodeTemplate := template.findTemplate(node.Template)
		if nodeTemplate != nil {
			if err := node.Execute(operation, nodeTemplate); err != nil {
				done <- err
			} else {
				done <- nil
			}
		} else {
			node.RunStatus = RunStatusRed
			err := fmt.Errorf("template %s of node %s isn't exist", node.Template, node.Name)
			node.Error = err.Error()
			db.DB.Save(node)
			done <- err
		}
	}()

	return done, timeout
}

func (p *Topology) preTraverseNode(depType string, parent *Node, template *ServiceTemplate, operation string) (<-chan error, <-chan bool) {
	err, timeout := p.executeNode(operation, parent, template)
	select {
	case e := <-err:
		ec := make(chan error, 1)
		ec <- e
		return ec, timeout
	case <-timeout:
	}

	var childErr error = nil
	var childTimeout bool

	links := parent.FindLinksByType(depType)
	if links != nil && len(links) > 0 {
		dones := []<-chan error{}
		timeouts := []<-chan bool{}
		for _, link := range links {
			node := p.getNode(link.Target)
			done, timeout := p.preTraverseNode(depType, node, template, operation)
			dones = append(dones, done)
			timeouts = append(timeouts, timeout)
		}

		for i, done := range dones {
			select {
			case e := <-done:
				if e != nil {
					childErr = e
				}
			case t := <-timeouts[i]:
				if t {
					childTimeout = t
				}
			}
		}
	}

	dc := make(chan error, 1)
	tc := make(chan bool, 1)

	if err == nil {
		dc <- childErr
	}
	if timeout == nil {
		tc <- childTimeout
	}
	return dc, tc
}

func (p *Topology) postTraverseNode(depType string, parent *Node, template *ServiceTemplate, operation string) (<-chan error, <-chan bool) {
	links := parent.FindLinksByType(depType)
	if links != nil && len(links) > 0 {
		dones := []<-chan error{}
		timeouts := []<-chan bool{}
		for _, link := range links {
			node := p.getNode(link.Target)
			done, timeout := p.postTraverseNode(depType, node, template, operation)
			dones = append(dones, done)
			timeouts = append(timeouts, timeout)
		}

		var err error = nil
		for i, done := range dones {
			select {
			case e := <-done:
				if e != nil {
					err = e
				}
			case <-timeouts[i]:
			}
		}

		if err == nil {
			return p.executeNode(operation, parent, template)
		} else {
			done := make(chan error, 1)
			timeout := make(chan bool, 1)
			done <- err
			return done, timeout
		}
	} else {
		return p.executeNode(operation, parent, template)
	}
}

//Only used to add host node
func (p *Topology) addNode(node *Node) {
	p.Nodes = append(p.Nodes, node)
}

func (p *Topology) putNode(node *Node) {
	old := p.getNode(node.Name)
	if old == nil {
		p.Nodes = append(p.Nodes, node)
	}
}

func (p *Topology) getNode(name string) *Node {
	for _, node := range p.Nodes {
		if node.Name == name {
			return node
		}
	}
	return nil
}

func (p *Topology) getNodes(name string) []*Node {
	nodes := []*Node{}
	for _, node := range p.Nodes {
		if node.Name == name {
			nodes = append(nodes, node)
		}
	}
	return nodes
}
<|endoftext|>


<commit_before>package wellington

import (
	"fmt"
	"log"
	"path/filepath"
	"strings"
	"sync"

	"github.com/wellington/spritewell"
	"gopkg.in/fsnotify.v1"
)

// Sets the default size of the slice holding the top level files for a
// sass partial in SafePartialMap.M
const MaxTopLevel int = 20

// BuildArgs holds universal arguments for a build that the parser
// uses during the initial build and the filewatcher passes back to
// the parser on any file changes.
type BuildArgs struct {
	Imgs, Sprites spritewell.SafeImageMap
	Dir           string
	BuildDir      string
	Includes      string
	Font          string
	Gen           string
	Style         int
	Comments      bool
}

// Watcher holds all data needed to kick off a build of the css when a
// file changes.
// FileWatcher is the object that triggers builds when a file changes.
// PartialMap contains a mapping of partials to top level files.
// Dirs contains all directories that have top level files.
// GlobalBuildArgs contains build args that apply to all sass files.
type Watcher struct {
	FileWatcher *fsnotify.Watcher
	PartialMap  *SafePartialMap
	Dirs        []string
	BArgs       *BuildArgs
}

// SafePartialMap is a thread safe map of partial sass files to top
// level files. The file watcher will detect changes in a partial and
// kick off builds for all top level files that contain that partial.
type SafePartialMap struct {
	sync.RWMutex
	M map[string][]string
}

// NewPartialMap creates a initialized SafeParitalMap with with capacity 100
func NewPartialMap() *SafePartialMap {
	spm := &SafePartialMap{
		M: make(map[string][]string, 100)}
	return spm
}

// AddRelation links a partial Sass file with the top level file by
// adding a thread safe entry into partialMap.M.
func (p *SafePartialMap) AddRelation(mainfile string, subfile string) {
	p.Lock()
	//check to see if the map exists, if not initialize the top level map
	if _, ok := p.M[subfile]; !ok {
		p.M[subfile] = make([]string, 0, MaxTopLevel)
	}

	p.M[subfile] = appendUnique(p.M[subfile], mainfile)
	p.Unlock()
}

// FileWatch is the main entry point into filewatcher and sets up the
// SW object that begins monitoring for file changes and triggering
// top level sass rebuilds.
func FileWatch(p *SafePartialMap, bargs *BuildArgs, dirs []string) {
	var fswatcher *fsnotify.Watcher
	fswatcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	defer fswatcher.Close()
	w := Watcher{
		FileWatcher: fswatcher,
		PartialMap:  p,
		Dirs:        dirs,
		BArgs:       bargs,
	}
	w.watchFiles()
	w.startWatching()

}

func (w *Watcher) startWatching() {
	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-w.FileWatcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					w.rebuild(event.Name)
				}
			case err := <-(*w.FileWatcher).Errors:
				fmt.Println("error:", err)
			}
		}
	}()
	<-done
}

// rebuild is notified about sass file updates and looks
// for the file in the partial map.  It also checks
// for whether the file is a non-partial, no _ at beginning,
// and requests the file be rebuilt directly.
func (w *Watcher) rebuild(eventFileName string) {
	// Top level file modified, rebuild it directly
	if !strings.HasPrefix(filepath.Base(eventFileName), "_") {
		LoadAndBuild(eventFileName, w.BArgs, w.PartialMap)
	}
	w.PartialMap.RLock()
	for _, sassPath := range w.PartialMap.M[eventFileName] {
		// TODO: do this in a new goroutine
		LoadAndBuild(sassPath, w.BArgs, w.PartialMap)
	}
	w.PartialMap.RUnlock()

}

func (w *Watcher) watchFiles() {
	//Watch the dirs of all sass partials
	w.PartialMap.RLock()
	for k := range w.PartialMap.M {
		w.watch(filepath.Dir(k))
	}
	w.PartialMap.RUnlock()

	//Watch the dirs of all top level files
	for k := range w.Dirs {
		w.watch(w.Dirs[k])
	}
}

func (w *Watcher) watch(fpath string) {
	if len(fpath) > 0 {
		if err := (w.FileWatcher).Add(fpath); nil != err {
			log.Fatalln(err)
		}
	}
}

func appendUnique(slice []string, s string) []string {
	for _, ele := range slice {
		if ele == s {
			return slice
		}
	}
	return append(slice, s)
}
<commit_msg>Removing a deadlock, removing unneeded pointer dereferences.<commit_after>package wellington

import (
	"fmt"
	"log"
	"path/filepath"
	"strings"
	"sync"

	"github.com/wellington/spritewell"
	"gopkg.in/fsnotify.v1"
)

// Sets the default size of the slice holding the top level files for a
// sass partial in SafePartialMap.M
const MaxTopLevel int = 20

// BuildArgs holds universal arguments for a build that the parser
// uses during the initial build and the filewatcher passes back to
// the parser on any file changes.
type BuildArgs struct {
	Imgs, Sprites spritewell.SafeImageMap
	Dir           string
	BuildDir      string
	Includes      string
	Font          string
	Gen           string
	Style         int
	Comments      bool
}

// Watcher holds all data needed to kick off a build of the css when a
// file changes.
// FileWatcher is the object that triggers builds when a file changes.
// PartialMap contains a mapping of partials to top level files.
// Dirs contains all directories that have top level files.
// GlobalBuildArgs contains build args that apply to all sass files.
type Watcher struct {
	FileWatcher *fsnotify.Watcher
	PartialMap  *SafePartialMap
	Dirs        []string
	BArgs       *BuildArgs
}

// SafePartialMap is a thread safe map of partial sass files to top
// level files. The file watcher will detect changes in a partial and
// kick off builds for all top level files that contain that partial.
type SafePartialMap struct {
	sync.RWMutex
	M map[string][]string
}

// NewPartialMap creates a initialized SafeParitalMap with with capacity 100
func NewPartialMap() *SafePartialMap {
	spm := &SafePartialMap{
		M: make(map[string][]string, 100)}
	return spm
}

// AddRelation links a partial Sass file with the top level file by
// adding a thread safe entry into partialMap.M.
func (p *SafePartialMap) AddRelation(mainfile string, subfile string) {
	p.Lock()
	//check to see if the map exists, if not initialize the top level map
	if _, ok := p.M[subfile]; !ok {
		p.M[subfile] = make([]string, 0, MaxTopLevel)
	}

	p.M[subfile] = appendUnique(p.M[subfile], mainfile)
	p.Unlock()
}

// FileWatch is the main entry point into filewatcher and sets up the
// SW object that begins monitoring for file changes and triggering
// top level sass rebuilds.
func FileWatch(p *SafePartialMap, bargs *BuildArgs, dirs []string) {
	var fswatcher *fsnotify.Watcher
	fswatcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	defer fswatcher.Close()
	w := Watcher{
		FileWatcher: fswatcher,
		PartialMap:  p,
		Dirs:        dirs,
		BArgs:       bargs,
	}
	w.watchFiles()
	w.startWatching()

}

func (w *Watcher) startWatching() {
	for {
		select {
		default:
			// only called when w.FileWatcher.Events is set to nil.
		case event := <-w.FileWatcher.Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				w.rebuild(event.Name)
			}
		case err := <-w.FileWatcher.Errors:
			fmt.Println("error:", err)
		}
	}
}

// rebuild is notified about sass file updates and looks
// for the file in the partial map.  It also checks
// for whether the file is a non-partial, no _ at beginning,
// and requests the file be rebuilt directly.
func (w *Watcher) rebuild(eventFileName string) {
	// Top level file modified, rebuild it directly
	if !strings.HasPrefix(filepath.Base(eventFileName), "_") {
		LoadAndBuild(eventFileName, w.BArgs, w.PartialMap)
	}
	w.PartialMap.RLock()
	t := w.PartialMap.M[eventFileName]
	w.PartialMap.RUnlock()
	for _, sassPath := range t {
		// TODO: do this in a new goroutine
		LoadAndBuild(sassPath, w.BArgs, w.PartialMap)
	}
}

func (w *Watcher) watchFiles() {
	//Watch the dirs of all sass partials
	w.PartialMap.RLock()
	for k := range w.PartialMap.M {
		w.watch(filepath.Dir(k))
	}
	w.PartialMap.RUnlock()

	//Watch the dirs of all top level files
	for k := range w.Dirs {
		w.watch(w.Dirs[k])
	}
}

func (w *Watcher) watch(fpath string) {
	if len(fpath) > 0 {
		if err := w.FileWatcher.Add(fpath); nil != err {
			log.Fatalln(err)
		}
	}
}

func appendUnique(slice []string, s string) []string {
	for _, ele := range slice {
		if ele == s {
			return slice
		}
	}
	return append(slice, s)
}
<|endoftext|>