<commit_before>package core

import (
	"path/filepath"

	"github.com/gobwas/glob"
	"github.com/spf13/afero"
	"gopkg.in/ini.v1"
)

var syntaxOpts = map[string]func(string, *ini.Section, *Config){
	"BasedOnStyles": func(label string, sec *ini.Section, cfg *Config) {
		pat, err := glob.Compile(label)
		if _, found := cfg.SecToPat[label]; !found && CheckError(err, cfg.Debug) {
			cfg.SecToPat[label] = pat
		}
		sStyles := mergeValues(sec.Key("BasedOnStyles").ValueWithShadows())

		cfg.Styles = append(cfg.Styles, sStyles...)
		cfg.SBaseStyles[label] = sStyles
	},
	"IgnorePatterns": func(label string, sec *ini.Section, cfg *Config) {
		cfg.BlockIgnores[label] = mergeValues(sec.Key("IgnorePatterns").ValueWithShadows())
	},
	"BlockIgnores": func(label string, sec *ini.Section, cfg *Config) {
		cfg.TokenIgnores[label] = mergeValues(sec.Key("BlockIgnores").ValueWithShadows())
	},
	"TokenIgnores": func(label string, sec *ini.Section, cfg *Config) {
		cfg.TokenIgnores[label] = mergeValues(sec.Key("TokenIgnores").ValueWithShadows())
	},
	"Parser": func(label string, sec *ini.Section, cfg *Config) {
		cfg.Parsers[label] = sec.Key("Parser").String()
	},
	"Transform": func(label string, sec *ini.Section, cfg *Config) {
		canidate := sec.Key("Transform").String()
		abs, _ := filepath.Abs(canidate)
		cfg.Stylesheets[label] = filepath.FromSlash(abs)
	},
}

var globalOpts = map[string]func(*ini.Section, *Config, []string){
	"BasedOnStyles": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.GBaseStyles = mergeValues(sec.Key("BasedOnStyles").ValueWithShadows())
		cfg.Styles = append(cfg.Styles, cfg.GBaseStyles...)
	},
	"IgnorePatterns": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.BlockIgnores["*"] = mergeValues(sec.Key("IgnorePatterns").ValueWithShadows())
	},
	"BlockIgnores": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.BlockIgnores["*"] = mergeValues(sec.Key("IgnorePatterns").ValueWithShadows())
	},
	"TokenIgnores": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.TokenIgnores["*"] = mergeValues(sec.Key("TokenIgnores").ValueWithShadows())
	},
}

var coreOpts = map[string]func(*ini.Section, *Config, []string){
	"StylesPath": func(sec *ini.Section, cfg *Config, args []string) {
		paths := sec.Key("StylesPath").ValueWithShadows()
		if cfg.Local && len(paths) == 2 {
			basePath := DeterminePath(args[0], filepath.FromSlash(paths[1]))
			mockPath := DeterminePath(args[1], filepath.FromSlash(paths[0]))
			if basePath != mockPath {
				baseFs := cfg.FsWrapper.Fs
				mockFs := afero.NewMemMapFs()
				if CheckError(CopyDir(baseFs, basePath, mockFs, mockPath), cfg.Debug) {
					cfg.FsWrapper.Fs = afero.NewCopyOnWriteFs(baseFs, mockFs)
					cfg.FallbackPath = mockPath
				}
			}
		}
		cfg.StylesPath = cfg.FallbackPath
		if cfg.StylesPath == "" {
			canidate := filepath.FromSlash(sec.Key("StylesPath").MustString(""))
			cfg.StylesPath = DeterminePath(cfg.Path, canidate)
		}
	},
	"MinAlertLevel": func(sec *ini.Section, cfg *Config, args []string) {
		if !StringInSlice(cfg.AlertLevel, AlertLevels) {
			level := sec.Key("MinAlertLevel").In("suggestion", AlertLevels)
			cfg.MinAlertLevel = LevelToInt[level]
		}
	},
	"IgnoredScopes": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.IgnoredScopes = mergeValues(sec.Key("IgnoredScopes").ValueWithShadows())
	},
	"WordTemplate": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.WordTemplate = sec.Key("WordTemplate").String()
	},
	"SkippedScopes": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.SkippedScopes = mergeValues(sec.Key("SkippedScopes").ValueWithShadows())
	},
	"IgnoredClasses": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.IgnoredClasses = mergeValues(sec.Key("IgnoredClasses").ValueWithShadows())
	},
	"Project": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.Project = sec.Key("Project").String()
		loadVocab(cfg.Project, cfg)
	},
	"Vocab": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.Project = sec.Key("Vocab").String()
		loadVocab(cfg.Project, cfg)
	},
	"LTPath": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.LTPath = sec.Key("LTPath").String()
	},
	"SphinxBuildPath": func(sec *ini.Section, cfg *Config, args []string) {
		canidate := filepath.FromSlash(sec.Key("SphinxBuildPath").MustString(""))
		cfg.SphinxBuild = DeterminePath(cfg.Path, canidate)
	},
	"SphinxAutoBuild": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.SphinxAuto = sec.Key("SphinxAutoBuild").MustString("")
	},
}
<commit_msg>fix: ensure `BlockIgnores` are already respected<commit_after>package core

import (
	"path/filepath"

	"github.com/gobwas/glob"
	"github.com/spf13/afero"
	"gopkg.in/ini.v1"
)

var syntaxOpts = map[string]func(string, *ini.Section, *Config){
	"BasedOnStyles": func(label string, sec *ini.Section, cfg *Config) {
		pat, err := glob.Compile(label)
		if _, found := cfg.SecToPat[label]; !found && CheckError(err, cfg.Debug) {
			cfg.SecToPat[label] = pat
		}
		sStyles := mergeValues(sec.Key("BasedOnStyles").ValueWithShadows())

		cfg.Styles = append(cfg.Styles, sStyles...)
		cfg.SBaseStyles[label] = sStyles
	},
	"IgnorePatterns": func(label string, sec *ini.Section, cfg *Config) {
		cfg.BlockIgnores[label] = mergeValues(sec.Key("IgnorePatterns").ValueWithShadows())
	},
	"BlockIgnores": func(label string, sec *ini.Section, cfg *Config) {
		cfg.BlockIgnores[label] = mergeValues(sec.Key("BlockIgnores").ValueWithShadows())
	},
	"TokenIgnores": func(label string, sec *ini.Section, cfg *Config) {
		cfg.TokenIgnores[label] = mergeValues(sec.Key("TokenIgnores").ValueWithShadows())
	},
	"Parser": func(label string, sec *ini.Section, cfg *Config) {
		cfg.Parsers[label] = sec.Key("Parser").String()
	},
	"Transform": func(label string, sec *ini.Section, cfg *Config) {
		canidate := sec.Key("Transform").String()
		abs, _ := filepath.Abs(canidate)
		cfg.Stylesheets[label] = filepath.FromSlash(abs)
	},
}

var globalOpts = map[string]func(*ini.Section, *Config, []string){
	"BasedOnStyles": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.GBaseStyles = mergeValues(sec.Key("BasedOnStyles").ValueWithShadows())
		cfg.Styles = append(cfg.Styles, cfg.GBaseStyles...)
	},
	"IgnorePatterns": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.BlockIgnores["*"] = mergeValues(sec.Key("IgnorePatterns").ValueWithShadows())
	},
	"BlockIgnores": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.BlockIgnores["*"] = mergeValues(sec.Key("BlockIgnores").ValueWithShadows())
	},
	"TokenIgnores": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.TokenIgnores["*"] = mergeValues(sec.Key("TokenIgnores").ValueWithShadows())
	},
}

var coreOpts = map[string]func(*ini.Section, *Config, []string){
	"StylesPath": func(sec *ini.Section, cfg *Config, args []string) {
		paths := sec.Key("StylesPath").ValueWithShadows()
		if cfg.Local && len(paths) == 2 {
			basePath := DeterminePath(args[0], filepath.FromSlash(paths[1]))
			mockPath := DeterminePath(args[1], filepath.FromSlash(paths[0]))
			if basePath != mockPath {
				baseFs := cfg.FsWrapper.Fs
				mockFs := afero.NewMemMapFs()
				if CheckError(CopyDir(baseFs, basePath, mockFs, mockPath), cfg.Debug) {
					cfg.FsWrapper.Fs = afero.NewCopyOnWriteFs(baseFs, mockFs)
					cfg.FallbackPath = mockPath
				}
			}
		}
		cfg.StylesPath = cfg.FallbackPath
		if cfg.StylesPath == "" {
			canidate := filepath.FromSlash(sec.Key("StylesPath").MustString(""))
			cfg.StylesPath = DeterminePath(cfg.Path, canidate)
		}
	},
	"MinAlertLevel": func(sec *ini.Section, cfg *Config, args []string) {
		if !StringInSlice(cfg.AlertLevel, AlertLevels) {
			level := sec.Key("MinAlertLevel").In("suggestion", AlertLevels)
			cfg.MinAlertLevel = LevelToInt[level]
		}
	},
	"IgnoredScopes": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.IgnoredScopes = mergeValues(sec.Key("IgnoredScopes").ValueWithShadows())
	},
	"WordTemplate": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.WordTemplate = sec.Key("WordTemplate").String()
	},
	"SkippedScopes": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.SkippedScopes = mergeValues(sec.Key("SkippedScopes").ValueWithShadows())
	},
	"IgnoredClasses": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.IgnoredClasses = mergeValues(sec.Key("IgnoredClasses").ValueWithShadows())
	},
	"Project": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.Project = sec.Key("Project").String()
		loadVocab(cfg.Project, cfg)
	},
	"Vocab": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.Project = sec.Key("Vocab").String()
		loadVocab(cfg.Project, cfg)
	},
	"LTPath": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.LTPath = sec.Key("LTPath").String()
	},
	"SphinxBuildPath": func(sec *ini.Section, cfg *Config, args []string) {
		canidate := filepath.FromSlash(sec.Key("SphinxBuildPath").MustString(""))
		cfg.SphinxBuild = DeterminePath(cfg.Path, canidate)
	},
	"SphinxAutoBuild": func(sec *ini.Section, cfg *Config, args []string) {
		cfg.SphinxAuto = sec.Key("SphinxAutoBuild").MustString("")
	},
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chromium 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 proccache implements a simple in-memory cache that can be injected
// into a context and shared by all request handlers executing within a process.
// Same can be achieved by using global variables, but global state complicates
// unit testing.
package proccache

import (
	"sync"
	"time"

	"github.com/luci/luci-go/common/clock"
	"golang.org/x/net/context"
)

// Getter is returned by Cached. See Cached for more details.
type Getter func(c context.Context) (interface{}, error)

// Maker is used by GetOrMake to make new cache item if previous one has
// expired. It returns a value to put in the cache along with its expiration
// duration (or 0 if it doesn't expire).
type Maker func() (interface{}, time.Duration, error)

// Warmer is used by Cached to make a new cache item if previous one has
// expired. It returns a value to put in the cache along with its expiration
// duration (or 0 if it doesn't expire). Unlike Maker, it doesn't expect the
// context and the key to be in the function closure and accepts them
// explicitly.
type Warmer func(c context.Context, key interface{}) (interface{}, time.Duration, error)

// Entry is returned by Get. It is a stored value along with its expiration
// time (that may have already passed). Zero expiration time means the item
// doesn't expire.
type Entry struct {
	Value interface{}
	Exp   time.Time
}

// Cache holds a mapping key -> (value, expiration time). The mapping is never
// cleared automatically, store only O(1) number of items there.
type Cache struct {
	lock  sync.Mutex
	cache map[interface{}]Entry
}

// Put adds a new item to the cache or replaces an existing one.
func (c *Cache) Put(key, value interface{}, exp time.Time) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.cache == nil {
		c.cache = make(map[interface{}]Entry, 1)
	}
	c.cache[key] = Entry{value, exp}
}

// Get returns a stored item or nil if no such item.
func (c *Cache) Get(key interface{}) *Entry {
	c.lock.Lock()
	defer c.lock.Unlock()
	if e, ok := c.cache[key]; ok {
		return &e
	}
	return nil
}

// Mutate reads an entry, passes it to the callback, and writes back whatever
// callback returns. All under the lock. If such entry doesn't exist, callback
// receives nil. If callback returns nil, entry is removed from the cache.
func (c *Cache) Mutate(key interface{}, callback func(*Entry) *Entry) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.cache == nil {
		c.cache = make(map[interface{}]Entry, 1)
	}
	var changed *Entry
	if e, ok := c.cache[key]; ok {
		changed = callback(&e)
	} else {
		changed = callback(nil)
	}
	if changed == nil {
		delete(c.cache, key)
	} else {
		c.cache[key] = *changed
	}
}

type contextKey int

// Use injects instance of Cache in the context.
func Use(c context.Context, cache *Cache) context.Context {
	return context.WithValue(c, contextKey(0), cache)
}

// GetCache grabs instance of Cache stored in the context, or creates a new one
// if nothing is stored.
func GetCache(c context.Context) *Cache {
	if c, ok := c.Value(contextKey(0)).(*Cache); ok && c != nil {
		return c
	}
	return &Cache{}
}

// Put grabs an instance of Cache from the context and puts an item in it. Zero
// expiration duration means the item doesn't expire. If context doesn't have
// a cache installed, Put is noop.
func Put(c context.Context, key, value interface{}, exp time.Duration) {
	expTs := time.Time{}
	if exp != 0 {
		expTs = clock.Now(c).Add(exp)
	}
	GetCache(c).Put(key, value, expTs)
}

// Get returns an item from cache in the context if it hasn't expired yet. If
// there's no such item or it has expired, returns (nil, false).
func Get(c context.Context, key interface{}) (value interface{}, ok bool) {
	e := GetCache(c).Get(key)
	if e != nil && (e.Exp.IsZero() || clock.Now(c).Before(e.Exp)) {
		return e.Value, true
	}
	return nil, false
}

// Add atomically adds an item to the cache if it's not currently cached
// or existing item has expired.
//
// Returns (true, new item) if the item was added, (false, existing item) if
// the item existed before.
func Add(c context.Context, key, value interface{}, exp time.Duration) (added bool, out interface{}) {
	now := clock.Now(c)
	GetCache(c).Mutate(key, func(existing *Entry) *Entry {
		if existing != nil && (existing.Exp.IsZero() || now.Before(existing.Exp)) {
			out = existing.Value
			return existing
		}
		expTs := time.Time{}
		if exp != 0 {
			expTs = now.Add(exp)
		}
		added = true
		out = value
		return &Entry{value, expTs}
	})
	return
}

// GetOrMake attempts to grab an item from the cache. If it's not there, it
// calls a supplied `maker` callback to generate it and uses Add to place it in
// the cache. If some concurrent operation managed to put an item in the cache
// in between the calls, existing item is returned and return value of `maker`
// is discarded. Returns error only if `maker` returns error.
func GetOrMake(c context.Context, key interface{}, maker Maker) (interface{}, error) {
	if value, ok := Get(c, key); ok {
		return value, nil
	}
	value, exp, err := maker()
	if err != nil {
		return nil, err
	}
	_, value = Add(c, key, value, exp)
	return value, nil
}

// Cached returns a getter function that extracts an item from the cache (if it
// is there) or calls a supplied callback to initialize and put it there.
// Intended to be used like a decorator for top level functions. The getter
// returns a error only if cache initialization callback (Warmer) returns
// a error. Warmer callback is not protected by any locks, and it may be called
// concurrently (and the cache will have the result of invocation that finished
// first, whatever it may be). Implement synchronization inside the callback
// itself if needed.
func Cached(key interface{}, warmer Warmer) Getter {
	return func(c context.Context) (interface{}, error) {
		return GetOrMake(c, key, func() (interface{}, time.Duration, error) {
			return warmer(c, key)
		})
	}
}
<commit_msg>Use RWLock in proccache.<commit_after>// Copyright 2015 The Chromium 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 proccache implements a simple in-memory cache that can be injected
// into a context and shared by all request handlers executing within a process.
// Same can be achieved by using global variables, but global state complicates
// unit testing.
package proccache

import (
	"sync"
	"time"

	"github.com/luci/luci-go/common/clock"
	"golang.org/x/net/context"
)

// Getter is returned by Cached. See Cached for more details.
type Getter func(c context.Context) (interface{}, error)

// Maker is used by GetOrMake to make new cache item if previous one has
// expired. It returns a value to put in the cache along with its expiration
// duration (or 0 if it doesn't expire).
type Maker func() (interface{}, time.Duration, error)

// Warmer is used by Cached to make a new cache item if previous one has
// expired. It returns a value to put in the cache along with its expiration
// duration (or 0 if it doesn't expire). Unlike Maker, it doesn't expect the
// context and the key to be in the function closure and accepts them
// explicitly.
type Warmer func(c context.Context, key interface{}) (interface{}, time.Duration, error)

// Entry is returned by Get. It is a stored value along with its expiration
// time (that may have already passed). Zero expiration time means the item
// doesn't expire.
type Entry struct {
	Value interface{}
	Exp   time.Time
}

// Cache holds a mapping key -> (value, expiration time). The mapping is never
// cleared automatically, store only O(1) number of items there.
type Cache struct {
	lock  sync.RWMutex
	cache map[interface{}]Entry
}

// Put adds a new item to the cache or replaces an existing one.
func (c *Cache) Put(key, value interface{}, exp time.Time) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.cache == nil {
		c.cache = make(map[interface{}]Entry, 1)
	}
	c.cache[key] = Entry{value, exp}
}

// Get returns a stored item or nil if no such item.
func (c *Cache) Get(key interface{}) *Entry {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if e, ok := c.cache[key]; ok {
		return &e
	}
	return nil
}

// Mutate reads an entry, passes it to the callback, and writes back whatever
// callback returns. All under the lock. If such entry doesn't exist, callback
// receives nil. If callback returns nil, entry is removed from the cache.
func (c *Cache) Mutate(key interface{}, callback func(*Entry) *Entry) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.cache == nil {
		c.cache = make(map[interface{}]Entry, 1)
	}
	var changed *Entry
	if e, ok := c.cache[key]; ok {
		changed = callback(&e)
	} else {
		changed = callback(nil)
	}
	if changed == nil {
		delete(c.cache, key)
	} else {
		c.cache[key] = *changed
	}
}

type contextKey int

// Use injects instance of Cache in the context.
func Use(c context.Context, cache *Cache) context.Context {
	return context.WithValue(c, contextKey(0), cache)
}

// GetCache grabs instance of Cache stored in the context, or creates a new one
// if nothing is stored.
func GetCache(c context.Context) *Cache {
	if c, ok := c.Value(contextKey(0)).(*Cache); ok && c != nil {
		return c
	}
	return &Cache{}
}

// Put grabs an instance of Cache from the context and puts an item in it. Zero
// expiration duration means the item doesn't expire. If context doesn't have
// a cache installed, Put is noop.
func Put(c context.Context, key, value interface{}, exp time.Duration) {
	expTs := time.Time{}
	if exp != 0 {
		expTs = clock.Now(c).Add(exp)
	}
	GetCache(c).Put(key, value, expTs)
}

// Get returns an item from cache in the context if it hasn't expired yet. If
// there's no such item or it has expired, returns (nil, false).
func Get(c context.Context, key interface{}) (value interface{}, ok bool) {
	e := GetCache(c).Get(key)
	if e != nil && (e.Exp.IsZero() || clock.Now(c).Before(e.Exp)) {
		return e.Value, true
	}
	return nil, false
}

// Add atomically adds an item to the cache if it's not currently cached
// or existing item has expired.
//
// Returns (true, new item) if the item was added, (false, existing item) if
// the item existed before.
func Add(c context.Context, key, value interface{}, exp time.Duration) (added bool, out interface{}) {
	now := clock.Now(c)
	GetCache(c).Mutate(key, func(existing *Entry) *Entry {
		if existing != nil && (existing.Exp.IsZero() || now.Before(existing.Exp)) {
			out = existing.Value
			return existing
		}
		expTs := time.Time{}
		if exp != 0 {
			expTs = now.Add(exp)
		}
		added = true
		out = value
		return &Entry{value, expTs}
	})
	return
}

// GetOrMake attempts to grab an item from the cache. If it's not there, it
// calls a supplied `maker` callback to generate it and uses Add to place it in
// the cache. If some concurrent operation managed to put an item in the cache
// in between the calls, existing item is returned and return value of `maker`
// is discarded. Returns error only if `maker` returns error.
func GetOrMake(c context.Context, key interface{}, maker Maker) (interface{}, error) {
	if value, ok := Get(c, key); ok {
		return value, nil
	}
	value, exp, err := maker()
	if err != nil {
		return nil, err
	}
	_, value = Add(c, key, value, exp)
	return value, nil
}

// Cached returns a getter function that extracts an item from the cache (if it
// is there) or calls a supplied callback to initialize and put it there.
// Intended to be used like a decorator for top level functions. The getter
// returns a error only if cache initialization callback (Warmer) returns
// a error. Warmer callback is not protected by any locks, and it may be called
// concurrently (and the cache will have the result of invocation that finished
// first, whatever it may be). Implement synchronization inside the callback
// itself if needed.
func Cached(key interface{}, warmer Warmer) Getter {
	return func(c context.Context) (interface{}, error) {
		return GetOrMake(c, key, func() (interface{}, time.Duration, error) {
			return warmer(c, key)
		})
	}
}
<|endoftext|>


<commit_before>package tester

import (
	"fmt"
	"regexp"
	"sync"
	"time"
)

// Container represents information about container in pod
type Container struct {
	Ns            string
	PodName       string
	ContainerName string
}

// StateUpdate contains information about changes in container's State
type StateUpdate struct {
	Ready      bool
	RestartCnt int32
}

// State stores information about container's state
type State struct {
	Ready      bool
	RestartCnt int32
	ReadySince time.Time
}

// ContainerAndState aggregates Container and State
type ContainerAndState struct {
	Container Container
	State     State
}

// IgnoreContainersByRegexp provides ignore function which checks containers against patterns
func IgnoreContainersByRegexp(podPattern, nsPattern, containerPattern string) (IgnoringContainersFunc, error) {
	var podRegexp, nsRegexp, containerRegexp *regexp.Regexp
	var err error
	if podPattern != "" {
		podRegexp, err = regexp.Compile(podPattern)
		if err != nil {
			return nil, err
		}
	}
	if nsPattern != "" {
		nsRegexp, err = regexp.Compile(nsPattern)
		if err != nil {
			return nil, err
		}
	}

	if containerPattern != "" {
		containerRegexp, err = regexp.Compile(containerPattern)
		if err != nil {
			return nil, err
		}
	}

	return func(c Container) bool {
		match := true
		anyComparison := false
		if podRegexp != nil {
			match = match && podRegexp.MatchString(c.PodName)
			anyComparison = true
		}
		if nsRegexp != nil {
			match = match && nsRegexp.MatchString(c.Ns)
			anyComparison = true

		}

		if containerRegexp != nil {
			match = match && containerRegexp.MatchString(c.ContainerName)
			anyComparison = true
		}

		ignored := match && anyComparison
		if ignored {
			fmt.Printf("Ignoring container %+v\n", c)
		}
		return ignored
	}, nil

}

// IgnoringContainersFunc defines function for ignoring containers
type IgnoringContainersFunc func(c Container) bool

// NewClusterState creates ClusterState
func NewClusterState(ignoreFunc IgnoringContainersFunc) *ClusterState {
	if ignoreFunc == nil {
		ignoreFunc = func(c Container) bool {
			return false
		}
	}
	cs := &ClusterState{
		containers:         make(map[Container]State),
		timeProvider:       time.Now,
		isContainerIgnored: ignoreFunc,
	}

	return cs
}

// ClusterState contains information about all containers in cluster
type ClusterState struct {
	mtx                sync.Mutex
	containers         map[Container]State
	timeProvider       func() time.Time
	isContainerIgnored IgnoringContainersFunc
}

// ForgetPod removes all information about pod state
func (cs *ClusterState) ForgetPod(ns, podName string) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	for container := range cs.containers {
		if container.Ns == ns && container.PodName == podName {
			fmt.Printf("Forgetting about pod: [ns: %s, podName: %s]\n", ns, podName)
			delete(cs.containers, container)
		}
	}
}

// UpdateState update state of the container
func (cs *ClusterState) UpdateState(c Container, update StateUpdate) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	state, ex := cs.containers[c]

	if !ex {
		var since time.Time
		if update.Ready {
			since = cs.timeProvider()
		}
		state = State{
			Ready:      update.Ready,
			RestartCnt: update.RestartCnt,
			ReadySince: since,
		}
		cs.containers[c] = state
		return
	}

	var since time.Time
	if !update.Ready {
		since = time.Time{}
	} else {
		if state.Ready == false || update.RestartCnt != state.RestartCnt {
			since = cs.timeProvider()
		} else {
			since = state.ReadySince
		}

	}
	state.RestartCnt = update.RestartCnt
	state.Ready = update.Ready
	state.ReadySince = since

	cs.containers[c] = state

}

// GetUnstableContainers returns all Containers which are not ready or restarts in recent requiredStabilityPeriod
func (cs *ClusterState) GetUnstableContainers(requiredStabilityPeriod time.Duration) []ContainerAndState {
	cs.mtx.Lock()
	cs.mtx.Unlock()

	unstable := make([]ContainerAndState, 0)

	for c, s := range cs.containers {
		if cs.isContainerIgnored(c) {
			continue
		}
		if !s.Ready || cs.timeProvider().Sub(s.ReadySince) < requiredStabilityPeriod {
			unstable = append(unstable, ContainerAndState{
				Container: c,
				State:     s,
			})
		}
	}
	return unstable
}
<commit_msg>Fix concurrent map operations in watch-pods (#669)<commit_after>package tester

import (
	"fmt"
	"regexp"
	"sync"
	"time"
)

// Container represents information about container in pod
type Container struct {
	Ns            string
	PodName       string
	ContainerName string
}

// StateUpdate contains information about changes in container's State
type StateUpdate struct {
	Ready      bool
	RestartCnt int32
}

// State stores information about container's state
type State struct {
	Ready      bool
	RestartCnt int32
	ReadySince time.Time
}

// ContainerAndState aggregates Container and State
type ContainerAndState struct {
	Container Container
	State     State
}

// IgnoreContainersByRegexp provides ignore function which checks containers against patterns
func IgnoreContainersByRegexp(podPattern, nsPattern, containerPattern string) (IgnoringContainersFunc, error) {
	var podRegexp, nsRegexp, containerRegexp *regexp.Regexp
	var err error
	if podPattern != "" {
		podRegexp, err = regexp.Compile(podPattern)
		if err != nil {
			return nil, err
		}
	}
	if nsPattern != "" {
		nsRegexp, err = regexp.Compile(nsPattern)
		if err != nil {
			return nil, err
		}
	}

	if containerPattern != "" {
		containerRegexp, err = regexp.Compile(containerPattern)
		if err != nil {
			return nil, err
		}
	}

	return func(c Container) bool {
		match := true
		anyComparison := false
		if podRegexp != nil {
			match = match && podRegexp.MatchString(c.PodName)
			anyComparison = true
		}
		if nsRegexp != nil {
			match = match && nsRegexp.MatchString(c.Ns)
			anyComparison = true

		}

		if containerRegexp != nil {
			match = match && containerRegexp.MatchString(c.ContainerName)
			anyComparison = true
		}

		ignored := match && anyComparison
		if ignored {
			fmt.Printf("Ignoring container %+v\n", c)
		}
		return ignored
	}, nil

}

// IgnoringContainersFunc defines function for ignoring containers
type IgnoringContainersFunc func(c Container) bool

// NewClusterState creates ClusterState
func NewClusterState(ignoreFunc IgnoringContainersFunc) *ClusterState {
	if ignoreFunc == nil {
		ignoreFunc = func(c Container) bool {
			return false
		}
	}
	cs := &ClusterState{
		containers:         make(map[Container]State),
		timeProvider:       time.Now,
		isContainerIgnored: ignoreFunc,
	}

	return cs
}

// ClusterState contains information about all containers in cluster
type ClusterState struct {
	mtx                sync.Mutex
	containers         map[Container]State
	timeProvider       func() time.Time
	isContainerIgnored IgnoringContainersFunc
}

// ForgetPod removes all information about pod state
func (cs *ClusterState) ForgetPod(ns, podName string) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	for container := range cs.containers {
		if container.Ns == ns && container.PodName == podName {
			fmt.Printf("Forgetting about pod: [ns: %s, podName: %s]\n", ns, podName)
			delete(cs.containers, container)
		}
	}
}

// UpdateState update state of the container
func (cs *ClusterState) UpdateState(c Container, update StateUpdate) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	state, ex := cs.containers[c]

	if !ex {
		var since time.Time
		if update.Ready {
			since = cs.timeProvider()
		}
		state = State{
			Ready:      update.Ready,
			RestartCnt: update.RestartCnt,
			ReadySince: since,
		}
		cs.containers[c] = state
		return
	}

	var since time.Time
	if !update.Ready {
		since = time.Time{}
	} else {
		if state.Ready == false || update.RestartCnt != state.RestartCnt {
			since = cs.timeProvider()
		} else {
			since = state.ReadySince
		}

	}
	state.RestartCnt = update.RestartCnt
	state.Ready = update.Ready
	state.ReadySince = since

	cs.containers[c] = state

}

// GetUnstableContainers returns all Containers which are not ready or restarts in recent requiredStabilityPeriod
func (cs *ClusterState) GetUnstableContainers(requiredStabilityPeriod time.Duration) []ContainerAndState {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()

	unstable := make([]ContainerAndState, 0)

	for c, s := range cs.containers {
		if cs.isContainerIgnored(c) {
			continue
		}
		if !s.Ready || cs.timeProvider().Sub(s.ReadySince) < requiredStabilityPeriod {
			unstable = append(unstable, ContainerAndState{
				Container: c,
				State:     s,
			})
		}
	}
	return unstable
}
<|endoftext|>


<commit_before>package analyze

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/bazelbuild/buildtools/edit"
	"github.com/bazelbuild/rules_typescript/ts_auto_deps/workspace"
	"github.com/golang/protobuf/proto"

	appb "github.com/bazelbuild/buildtools/build_proto"
)

// pkgCacheEntry represents a set of loaded rules and a mapping from alias
// to rules from a package.
type pkgCacheEntry struct {
	// rules is all rules in a package.
	rules []*appb.Rule
	// aliases is a map from an alias label to the actual rule of the alias.
	aliases map[string]*appb.Rule
}

// QueryBasedTargetLoader uses Bazel query to load targets from BUILD files.
type QueryBasedTargetLoader struct {
	workdir     string
	bazelBinary string

	// pkgCache is a mapping from a package to all of the rules in said
	// package along with a map from aliases to actual rules.
	//
	// Keys are of the form of "<visibility>|<package>" where visibility
	// is the package that rules in package must be visible to and package
	// is the actual package that has been loaded and cached.
	//
	// Since a new target loader is constructed for each directory being
	// analyzed in the "-recursive" case, these caches will be garbage
	// collected between directories.
	pkgCache map[string]*pkgCacheEntry
	// labelCache is a mapping from a label to its loaded rule.
	labelCache map[string]*appb.Rule

	// queryCount is the total number of queries executed by the target loader.
	queryCount int
}

// NewQueryBasedTargetLoader constructs a new QueryBasedTargetLoader rooted
// in workdir.
func NewQueryBasedTargetLoader(workdir, bazelBinary string) *QueryBasedTargetLoader {
	return &QueryBasedTargetLoader{
		workdir:     workdir,
		bazelBinary: bazelBinary,

		pkgCache:   make(map[string]*pkgCacheEntry),
		labelCache: make(map[string]*appb.Rule),
	}
}

// LoadLabels uses Bazel query to load targets associated with labels from BUILD
// files.
func (q *QueryBasedTargetLoader) LoadLabels(pkg string, labels []string) (map[string]*appb.Rule, error) {
	var labelCacheMisses []string
	for _, label := range labels {
		if _, ok := q.labelCache[labelCacheKey(pkg, label)]; !ok {
			labelCacheMisses = append(labelCacheMisses, label)
		}
	}
	if len(labelCacheMisses) > 0 {
		var queries []string
		if pkg == "" {
			queries = labelCacheMisses
		} else {
			for _, label := range labelCacheMisses {
				queries = append(queries, fmt.Sprintf("visible(%s:*, %s)", pkg, label))
			}
		}
		r, err := q.batchQuery(queries)
		if err != nil {
			return nil, err
		}
		for _, target := range r.GetTarget() {
			label, err := q.ruleLabel(target)
			if err != nil {
				return nil, err
			}
			q.labelCache[labelCacheKey(pkg, label)] = target.GetRule()
		}
		for _, label := range labelCacheMisses {
			key := labelCacheKey(pkg, label)
			if _, ok := q.labelCache[key]; !ok {
				// Set to nil so the result exists in the cache and is not
				// loaded again. If the nil is not added at the appropriate
				// cache key, LoadLabels will attempt to load it again when
				// next requested instead of getting a cache hit.
				q.labelCache[key] = nil
			}
		}
	}
	labelToRule := make(map[string]*appb.Rule)
	for _, label := range labels {
		labelToRule[label] = q.labelCache[labelCacheKey(pkg, label)]
	}
	return labelToRule, nil
}

func labelCacheKey(currentPkg, label string) string {
	return currentPkg + "^" + label
}

// LoadImportPaths uses Bazel Query to load targets associated with import
// paths from BUILD files.
func (q *QueryBasedTargetLoader) LoadImportPaths(ctx context.Context, currentPkg, workspaceRoot string, paths []string) (map[string]*appb.Rule, error) {
	debugf("loading imports visible to %q relative to %q: %q", currentPkg, workspaceRoot, paths)
	results := make(map[string]*appb.Rule)

	addedPaths := make(map[string]bool)
	var possiblePaths []string
	for _, path := range paths {
		if strings.HasPrefix(path, "goog:") {
			// 'goog:' imports are resolved using an sstable.
			results[path] = nil
			continue
		}
		if !strings.HasPrefix(path, "@") {
			if _, ok := addedPaths[path]; !ok {
				addedPaths[path] = true

				// If the path has a suffix of ".ngfactory" or ".ngsummary", it might
				// be an Angular AOT generated file. We can infer the target as we
				// infer its corresponding ngmodule target by simply stripping the
				// ".ngfactory" / ".ngsummary" suffix
				path = strings.TrimSuffix(strings.TrimSuffix(path, ".ngsummary"), ".ngfactory")
				path = strings.TrimPrefix(path, workspace.Name()+"/")

				possiblePaths = append(possiblePaths, pathWithExtensions(path)...)
				possiblePaths = append(possiblePaths, pathWithExtensions(filepath.Join(path, "index"))...)
			}
		}
	}

	r, err := q.batchQuery(possiblePaths)
	if err != nil {
		return nil, err
	}
	var sourceFileLabels, generators []string
	generatorsToFiles := make(map[string][]*appb.GeneratedFile)
	for _, target := range r.GetTarget() {
		label, err := q.fileLabel(target)
		if err != nil {
			return nil, err
		}
		switch target.GetType() {
		case appb.Target_GENERATED_FILE:
			file := target.GetGeneratedFile()
			generator := file.GetGeneratingRule()

			generators = append(generators, generator)
			generatorsToFiles[generator] = append(generatorsToFiles[generator], file)
		case appb.Target_SOURCE_FILE:
			sourceFileLabels = append(sourceFileLabels, label)
		}
	}

	labelToRule := make(map[string]*appb.Rule)
	for len(generators) > 0 {
		generatorToRule, err := q.LoadLabels(currentPkg, generators)
		if err != nil {
			return nil, err
		}
		var newGenerators []string
		for label, rule := range generatorToRule {
			_, _, target := edit.ParseLabel(label)
			if generator := stringAttribute(rule, "generator_name"); generator != "" && generator != target {
				// Located rule is also a generated rule. Look for the rule
				// that generates it.
				_, pkg, _ := edit.ParseLabel(label)
				newLabel := "//" + pkg + ":" + generator
				newGenerators = append(newGenerators, newLabel)
				generatorsToFiles[newLabel] = generatorsToFiles[label]
			} else {
				for _, generated := range generatorsToFiles[label] {
					labelToRule[generated.GetName()] = rule
				}
			}
		}
		generators = newGenerators
	}

	sourceLabelToRule, err := q.loadRulesIncludingSourceFiles(workspaceRoot, sourceFileLabels)
	if err != nil {
		return nil, err
	}
	for label, rule := range sourceLabelToRule {
		labelToRule[label] = rule
	}

	for label, rule := range labelToRule {
		_, pkg, file := edit.ParseLabel(label)
		// Trim "/index" suffixes that were added to path in the queries above.
		pathWithoutExtension := strings.TrimSuffix(filepath.Join(pkg, stripTSExtension(file)), string(filepath.Separator)+"index")
		for _, path := range paths {
			if pathWithoutExtension == strings.TrimSuffix(path, string(filepath.Separator)+"index") {
				results[path] = rule
			} else if pathWithoutExtension == strings.TrimSuffix(path, ".ngsummary") {
				results[path] = rule
			} else if pathWithoutExtension == strings.TrimSuffix(path, ".ngfactory") {
				results[path] = rule
			}
		}
	}

	return results, nil
}

func (q *QueryBasedTargetLoader) ruleLabel(target *appb.Target) (string, error) {
	if t := target.GetType(); t != appb.Target_RULE {
		return "", fmt.Errorf("target contains object of type %q instead of type %q", t, appb.Target_RULE)
	}
	return target.GetRule().GetName(), nil
}

func (q *QueryBasedTargetLoader) fileLabel(target *appb.Target) (string, error) {
	switch t := target.GetType(); t {
	case appb.Target_GENERATED_FILE:
		return target.GetGeneratedFile().GetName(), nil
	case appb.Target_SOURCE_FILE:
		return target.GetSourceFile().GetName(), nil
	default:
		return "", fmt.Errorf("target contains object of type %q instead of type %q or %q", t, appb.Target_SOURCE_FILE, appb.Target_GENERATED_FILE)
	}
}

// loadRuleIncludingSourceFiles loads all rules which include labels in
// sourceFileLabels, Returns a map from source file label to the rule which
// includes it.
func (q *QueryBasedTargetLoader) loadRulesIncludingSourceFiles(workspaceRoot string, sourceFileLabels []string) (map[string]*appb.Rule, error) {
	pkgToLabels := make(map[string][]string)
	queries := make([]string, 0, len(sourceFileLabels))
	for _, label := range sourceFileLabels {
		_, pkg, file := edit.ParseLabel(label)
		pkgToLabels[pkg] = append(pkgToLabels[pkg], label)
		// Query for all targets in the package which use file.
		queries = append(queries, fmt.Sprintf("attr('srcs', %s, //%s:*)", file, pkg))
	}
	r, err := q.batchQuery(queries)
	if err != nil {
		return nil, err
	}
	labelToRule := make(map[string]*appb.Rule)
	for _, target := range r.GetTarget() {
		label, err := q.ruleLabel(target)
		if err != nil {
			return nil, err
		}
		rule := target.GetRule()
		_, pkg, _ := edit.ParseLabel(label)
		labels := pkgToLabels[pkg]
		for _, src := range listAttribute(rule, "srcs") {
			for _, l := range labels {
				if src == l {
					labelToRule[l] = rule
					break
				}
			}
		}
	}
	return labelToRule, nil
}

// batchQuery runs a set of queries with a single call to Bazel query and the
// '--keep_going' flag.
func (q *QueryBasedTargetLoader) batchQuery(queries []string) (*appb.QueryResult, error) {
	// Join all of the queries with a '+' character according to Bazel's
	// syntax for running multiple queries.
	return q.query("--keep_going", strings.Join(queries, "+"))
}

func (q *QueryBasedTargetLoader) query(args ...string) (*appb.QueryResult, error) {
	n := len(args)
	if n < 1 {
		return nil, fmt.Errorf("expected at least one argument")
	}
	query := args[n-1]
	if query == "" {
		// An empty query was provided so return an empty result without
		// making a call to Bazel.
		return &appb.QueryResult{}, nil
	}
	var stdout, stderr bytes.Buffer
	args = append([]string{"query", "--output=proto"}, args...)
	q.queryCount++
	debugf("executing query #%d in %q: %s %s %q", q.queryCount, q.workdir, q.bazelBinary, strings.Join(args[:len(args)-1], " "), query)
	cmd := exec.Command(q.bazelBinary, args...)
	cmd.Dir = q.workdir
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	startTime := time.Now()
	if err := cmd.Run(); err != nil {
		// Exit status 3 is a direct result of one or more queries in a set of
		// queries not returning a result while running with the '--keep_going'
		// flag. Since one query failing to return a result does not hinder the
		// other queries from returning a result, ignore these errors.
		if err.Error() != "exit status 3" {
			// The error provided as a result is less useful than the contents of
			// stderr for debugging.
			return nil, fmt.Errorf(stderr.String())
		}
	}
	debugf("query #%d took %v", q.queryCount, time.Since(startTime))
	var result appb.QueryResult
	if err := proto.Unmarshal(stdout.Bytes(), &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// dedupeLabels returns a new set of labels with no duplicates.
func dedupeLabels(labels []string) []string {
	addedLabels := make(map[string]bool)
	var uniqueLabels []string
	for _, label := range labels {
		if _, added := addedLabels[label]; !added {
			addedLabels[label] = true
			uniqueLabels = append(uniqueLabels, label)
		}
	}
	return uniqueLabels
}

// isTazeManagedRuleClass checks if a class is a ts_auto_deps-managed rule class.
func isTazeManagedRuleClass(class string) bool {
	for _, c := range []string{
		"ts_library",
		// TODO(alexeagle): Add ts_declaration once it can be determined
		// if they are unused.
		"ng_module",
		"js_library",
	} {
		if c == class {
			return true
		}
	}
	return false
}

// typeScriptRules returns all TypeScript rules in rules.
func typeScriptRules(rules []*appb.Rule) []*appb.Rule {
	var tsRules []*appb.Rule
	for _, rule := range rules {
		for _, supportedRuleClass := range []string{
			"ts_library",
			"ts_declaration",
			"ng_module",
		} {
			if rule.GetRuleClass() == supportedRuleClass {
				tsRules = append(tsRules, rule)
				break
			}
		}
	}
	return tsRules
}

// resolveAgainstModuleRoot resolves imported against moduleRoot and moduleName.
func resolveAgainstModuleRoot(label, moduleRoot, moduleName, imported string) string {
	if moduleRoot == "" && moduleName == "" {
		return imported
	}
	trim := strings.TrimPrefix(imported, moduleName)
	if trim == imported {
		return imported
	}
	_, pkg, _ := edit.ParseLabel(label)
	return filepath.Join(pkg, moduleRoot, trim)
}

// parsePackageName parses and returns the scope and package of imported. For
// example, "@foo/bar" would have a scope of "@foo" and a package of "bar".
func parsePackageName(imported string) (string, string) {
	firstSlash := strings.Index(imported, "/")
	if firstSlash == -1 {
		return imported, ""
	}
	afterSlash := imported[firstSlash+1:]
	if secondSlash := strings.Index(afterSlash, "/"); secondSlash > -1 {
		return imported[:firstSlash], afterSlash[:secondSlash]
	}
	return imported[:firstSlash], afterSlash
}
<commit_msg>No external change<commit_after>package analyze

import (
	"bytes"
	"context"
	"fmt"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/bazelbuild/buildtools/edit"
	"github.com/bazelbuild/rules_typescript/ts_auto_deps/workspace"
	"github.com/golang/protobuf/proto"

	appb "github.com/bazelbuild/buildtools/build_proto"
)

// pkgCacheEntry represents a set of loaded rules and a mapping from alias
// to rules from a package.
type pkgCacheEntry struct {
	// rules is all rules in a package.
	rules []*appb.Rule
	// aliases is a map from an alias label to the actual rule of the alias.
	aliases map[string]*appb.Rule
}

// QueryBasedTargetLoader uses Bazel query to load targets from BUILD files.
type QueryBasedTargetLoader struct {
	workdir     string
	bazelBinary string

	// pkgCache is a mapping from a package to all of the rules in said
	// package along with a map from aliases to actual rules.
	//
	// Keys are of the form of "<visibility>|<package>" where visibility
	// is the package that rules in package must be visible to and package
	// is the actual package that has been loaded and cached.
	//
	// Since a new target loader is constructed for each directory being
	// analyzed in the "-recursive" case, these caches will be garbage
	// collected between directories.
	pkgCache map[string]*pkgCacheEntry
	// labelCache is a mapping from a label to its loaded rule.
	labelCache map[string]*appb.Rule

	// queryCount is the total number of queries executed by the target loader.
	queryCount int
}

// NewQueryBasedTargetLoader constructs a new QueryBasedTargetLoader rooted
// in workdir.
func NewQueryBasedTargetLoader(workdir, bazelBinary string) *QueryBasedTargetLoader {
	return &QueryBasedTargetLoader{
		workdir:     workdir,
		bazelBinary: bazelBinary,

		pkgCache:   make(map[string]*pkgCacheEntry),
		labelCache: make(map[string]*appb.Rule),
	}
}

// LoadLabels uses Bazel query to load targets associated with labels from BUILD
// files.
func (q *QueryBasedTargetLoader) LoadLabels(pkg string, labels []string) (map[string]*appb.Rule, error) {
	var labelCacheMisses []string
	for _, label := range labels {
		if _, ok := q.labelCache[labelCacheKey(pkg, label)]; !ok {
			labelCacheMisses = append(labelCacheMisses, label)
		}
	}
	if len(labelCacheMisses) > 0 {
		var queries []string
		if pkg == "" {
			queries = labelCacheMisses
		} else {
			for _, label := range labelCacheMisses {
				queries = append(queries, fmt.Sprintf("visible(%s:*, %s)", pkg, label))
			}
		}
		r, err := q.batchQuery(queries)
		if err != nil {
			return nil, err
		}
		for _, target := range r.GetTarget() {
			label, err := q.ruleLabel(target)
			if err != nil {
				return nil, err
			}
			q.labelCache[labelCacheKey(pkg, label)] = target.GetRule()
		}
		for _, label := range labelCacheMisses {
			key := labelCacheKey(pkg, label)
			if _, ok := q.labelCache[key]; !ok {
				// Set to nil so the result exists in the cache and is not
				// loaded again. If the nil is not added at the appropriate
				// cache key, LoadLabels will attempt to load it again when
				// next requested instead of getting a cache hit.
				q.labelCache[key] = nil
			}
		}
	}
	labelToRule := make(map[string]*appb.Rule)
	for _, label := range labels {
		labelToRule[label] = q.labelCache[labelCacheKey(pkg, label)]
	}
	return labelToRule, nil
}

func labelCacheKey(currentPkg, label string) string {
	return currentPkg + "^" + label
}

// LoadImportPaths uses Bazel Query to load targets associated with import
// paths from BUILD files.
func (q *QueryBasedTargetLoader) LoadImportPaths(ctx context.Context, currentPkg, workspaceRoot string, paths []string) (map[string]*appb.Rule, error) {
	debugf("loading imports visible to %q relative to %q: %q", currentPkg, workspaceRoot, paths)
	results := make(map[string]*appb.Rule)

	addedPaths := make(map[string]bool)
	var possiblePaths []string
	for _, path := range paths {
		if strings.HasPrefix(path, "goog:") {
			// 'goog:' imports are resolved using an sstable.
			results[path] = nil
			continue
		}
		if !strings.HasPrefix(path, "@") {
			if _, ok := addedPaths[path]; !ok {
				addedPaths[path] = true

				// If the path has a suffix of ".ngfactory" or ".ngsummary", it might
				// be an Angular AOT generated file. We can infer the target as we
				// infer its corresponding ngmodule target by simply stripping the
				// ".ngfactory" / ".ngsummary" suffix
				path = strings.TrimSuffix(strings.TrimSuffix(path, ".ngsummary"), ".ngfactory")
				path = strings.TrimPrefix(path, workspace.Name()+"/")

				possiblePaths = append(possiblePaths, pathWithExtensions(path)...)
				possiblePaths = append(possiblePaths, pathWithExtensions(filepath.Join(path, "index"))...)
			}
		}
	}

	r, err := q.batchQuery(possiblePaths)
	if err != nil {
		return nil, err
	}
	var sourceFileLabels, generators []string
	generatorsToFiles := make(map[string][]*appb.GeneratedFile)
	for _, target := range r.GetTarget() {
		label, err := q.fileLabel(target)
		if err != nil {
			return nil, err
		}
		switch target.GetType() {
		case appb.Target_GENERATED_FILE:
			file := target.GetGeneratedFile()
			generator := file.GetGeneratingRule()

			generators = append(generators, generator)
			generatorsToFiles[generator] = append(generatorsToFiles[generator], file)
		case appb.Target_SOURCE_FILE:
			sourceFileLabels = append(sourceFileLabels, label)
		}
	}

	labelToRule := make(map[string]*appb.Rule)
	for len(generators) > 0 {
		generatorToRule, err := q.LoadLabels(currentPkg, generators)
		if err != nil {
			return nil, err
		}
		var newGenerators []string
		for label, rule := range generatorToRule {
			_, _, target := edit.ParseLabel(label)

			if generator := stringAttribute(rule, "generator_name"); generator != "" && generator != target {
				// Located rule is also a generated rule. Look for the rule
				// that generates it.
				_, pkg, _ := edit.ParseLabel(label)
				newLabel := "//" + pkg + ":" + generator
				newGenerators = append(newGenerators, newLabel)
				generatorsToFiles[newLabel] = generatorsToFiles[label]
			} else {
				for _, generated := range generatorsToFiles[label] {
					labelToRule[generated.GetName()] = rule
				}
			}
		}
		generators = newGenerators
	}

	sourceLabelToRule, err := q.loadRulesIncludingSourceFiles(workspaceRoot, sourceFileLabels)
	if err != nil {
		return nil, err
	}
	for label, rule := range sourceLabelToRule {
		labelToRule[label] = rule
	}

	for label, rule := range labelToRule {
		_, pkg, file := edit.ParseLabel(label)
		// Trim "/index" suffixes that were added to path in the queries above.
		pathWithoutExtension := strings.TrimSuffix(filepath.Join(pkg, stripTSExtension(file)), string(filepath.Separator)+"index")
		for _, path := range paths {
			if pathWithoutExtension == strings.TrimSuffix(path, string(filepath.Separator)+"index") {
				results[path] = rule
			} else if pathWithoutExtension == strings.TrimSuffix(path, ".ngsummary") {
				results[path] = rule
			} else if pathWithoutExtension == strings.TrimSuffix(path, ".ngfactory") {
				results[path] = rule
			}
		}
	}

	return results, nil
}

func (q *QueryBasedTargetLoader) ruleLabel(target *appb.Target) (string, error) {
	if t := target.GetType(); t != appb.Target_RULE {
		return "", fmt.Errorf("target contains object of type %q instead of type %q", t, appb.Target_RULE)
	}
	return target.GetRule().GetName(), nil
}

func (q *QueryBasedTargetLoader) fileLabel(target *appb.Target) (string, error) {
	switch t := target.GetType(); t {
	case appb.Target_GENERATED_FILE:
		return target.GetGeneratedFile().GetName(), nil
	case appb.Target_SOURCE_FILE:
		return target.GetSourceFile().GetName(), nil
	default:
		return "", fmt.Errorf("target contains object of type %q instead of type %q or %q", t, appb.Target_SOURCE_FILE, appb.Target_GENERATED_FILE)
	}
}

// loadRuleIncludingSourceFiles loads all rules which include labels in
// sourceFileLabels, Returns a map from source file label to the rule which
// includes it.
func (q *QueryBasedTargetLoader) loadRulesIncludingSourceFiles(workspaceRoot string, sourceFileLabels []string) (map[string]*appb.Rule, error) {
	pkgToLabels := make(map[string][]string)
	queries := make([]string, 0, len(sourceFileLabels))
	for _, label := range sourceFileLabels {
		_, pkg, file := edit.ParseLabel(label)
		pkgToLabels[pkg] = append(pkgToLabels[pkg], label)
		// Query for all targets in the package which use file.
		queries = append(queries, fmt.Sprintf("attr('srcs', %s, //%s:*)", file, pkg))
	}
	r, err := q.batchQuery(queries)
	if err != nil {
		return nil, err
	}
	labelToRule := make(map[string]*appb.Rule)
	for _, target := range r.GetTarget() {
		label, err := q.ruleLabel(target)
		if err != nil {
			return nil, err
		}
		rule := target.GetRule()
		_, pkg, _ := edit.ParseLabel(label)
		labels := pkgToLabels[pkg]
		for _, src := range listAttribute(rule, "srcs") {
			for _, l := range labels {
				if src == l {
					labelToRule[l] = rule
					break
				}
			}
		}
	}
	return labelToRule, nil
}

// batchQuery runs a set of queries with a single call to Bazel query and the
// '--keep_going' flag.
func (q *QueryBasedTargetLoader) batchQuery(queries []string) (*appb.QueryResult, error) {
	// Join all of the queries with a '+' character according to Bazel's
	// syntax for running multiple queries.
	return q.query("--keep_going", strings.Join(queries, "+"))
}

func (q *QueryBasedTargetLoader) query(args ...string) (*appb.QueryResult, error) {
	n := len(args)
	if n < 1 {
		return nil, fmt.Errorf("expected at least one argument")
	}
	query := args[n-1]
	if query == "" {
		// An empty query was provided so return an empty result without
		// making a call to Bazel.
		return &appb.QueryResult{}, nil
	}
	var stdout, stderr bytes.Buffer
	args = append([]string{"query", "--output=proto"}, args...)
	q.queryCount++
	debugf("executing query #%d in %q: %s %s %q", q.queryCount, q.workdir, q.bazelBinary, strings.Join(args[:len(args)-1], " "), query)
	cmd := exec.Command(q.bazelBinary, args...)
	cmd.Dir = q.workdir
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	startTime := time.Now()
	if err := cmd.Run(); err != nil {
		// Exit status 3 is a direct result of one or more queries in a set of
		// queries not returning a result while running with the '--keep_going'
		// flag. Since one query failing to return a result does not hinder the
		// other queries from returning a result, ignore these errors.
		if err.Error() != "exit status 3" {
			// The error provided as a result is less useful than the contents of
			// stderr for debugging.
			return nil, fmt.Errorf(stderr.String())
		}
	}
	debugf("query #%d took %v", q.queryCount, time.Since(startTime))
	var result appb.QueryResult
	if err := proto.Unmarshal(stdout.Bytes(), &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// dedupeLabels returns a new set of labels with no duplicates.
func dedupeLabels(labels []string) []string {
	addedLabels := make(map[string]bool)
	var uniqueLabels []string
	for _, label := range labels {
		if _, added := addedLabels[label]; !added {
			addedLabels[label] = true
			uniqueLabels = append(uniqueLabels, label)
		}
	}
	return uniqueLabels
}

// isTazeManagedRuleClass checks if a class is a ts_auto_deps-managed rule class.
func isTazeManagedRuleClass(class string) bool {
	for _, c := range []string{
		"ts_library",
		// TODO(alexeagle): Add ts_declaration once it can be determined
		// if they are unused.
		"ng_module",
		"js_library",
	} {
		if c == class {
			return true
		}
	}
	return false
}

// typeScriptRules returns all TypeScript rules in rules.
func typeScriptRules(rules []*appb.Rule) []*appb.Rule {
	var tsRules []*appb.Rule
	for _, rule := range rules {
		for _, supportedRuleClass := range []string{
			"ts_library",
			"ts_declaration",
			"ng_module",
		} {
			if rule.GetRuleClass() == supportedRuleClass {
				tsRules = append(tsRules, rule)
				break
			}
		}
	}
	return tsRules
}

// resolveAgainstModuleRoot resolves imported against moduleRoot and moduleName.
func resolveAgainstModuleRoot(label, moduleRoot, moduleName, imported string) string {
	if moduleRoot == "" && moduleName == "" {
		return imported
	}
	trim := strings.TrimPrefix(imported, moduleName)
	if trim == imported {
		return imported
	}
	_, pkg, _ := edit.ParseLabel(label)
	return filepath.Join(pkg, moduleRoot, trim)
}

// parsePackageName parses and returns the scope and package of imported. For
// example, "@foo/bar" would have a scope of "@foo" and a package of "bar".
func parsePackageName(imported string) (string, string) {
	firstSlash := strings.Index(imported, "/")
	if firstSlash == -1 {
		return imported, ""
	}
	afterSlash := imported[firstSlash+1:]
	if secondSlash := strings.Index(afterSlash, "/"); secondSlash > -1 {
		return imported[:firstSlash], afterSlash[:secondSlash]
	}
	return imported[:firstSlash], afterSlash
}
<|endoftext|>


<commit_before>package lb

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/mikebeyer/clc-sdk/sdk/clc"
	"github.com/mikebeyer/clc-sdk/sdk/lb"
)

func Commands(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "load-balancer",
		Aliases: []string{"lb"},
		Usage:   "load balancer api",
		Subcommands: []cli.Command{
			get(client),
			create(client),
			getPool(client),
			createPool(client),
			getNode(client),
			updateNode(client),
		},
	}
}

func get(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "get",
		Aliases: []string{"g"},
		Usage:   "get load balancer details",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "all", Usage: "list all load balancers for location"},
			cli.StringFlag{Name: "id", Usage: "load balancer id"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" {
				fmt.Printf("location flag is required.\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			if c.Bool("all") || c.String("id") == "" {
				resp, err := client.LB.GetAll(c.String("location"))
				if err != nil {
					fmt.Printf("failed to get %s\n", c.Args().First())
					return
				}
				b, err := json.MarshalIndent(resp, "", "  ")
				if err != nil {
					fmt.Printf("%s\n", err)
					return
				}
				fmt.Printf("%s\n", b)
				return
			}
			resp, err := client.LB.Get(c.String("location"), c.String("id"))
			if err != nil {
				fmt.Printf("failed to get %s\n", c.Args().First())
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func create(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "create",
		Aliases: []string{"c"},
		Usage:   "create shared load balancer",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "name, n", Usage: "load balancer name [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "description, d", Usage: "load balancer description"},
		},
		Action: func(c *cli.Context) {
			name := c.String("name")
			loc := c.String("location")
			if name == "" || loc == "" {
				fmt.Printf("missing required flags to create load balancer. [use --help to show required flags]\n")
				return
			}

			lb := lb.LoadBalancer{Name: name, Description: c.String("description")}
			resp, err := client.LB.Create(loc, lb)
			if err != nil {
				fmt.Printf("failed to create load balancer [%s] in %s\n", name, loc)
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func getPool(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "get-pool",
		Aliases: []string{"gp"},
		Usage:   "get load balancer pool details",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "all", Usage: "list all load balancers for location"},
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" || c.String("id") == "" {
				fmt.Printf("missing required flags to get pool. [use --help to show required flags]\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			if c.Bool("all") || c.String("pool") == "" {
				resp, err := client.LB.GetAllPools(c.String("location"), c.String("id"))
				if err != nil {
					fmt.Printf("failed to get %s\n", c.Args().First())
					return
				}
				b, err := json.MarshalIndent(resp, "", "  ")
				if err != nil {
					fmt.Printf("%s\n", err)
					return
				}
				fmt.Printf("%s\n", b)
				return
			}
			resp, err := client.LB.GetPool(c.String("location"), c.String("id"), c.String("pool"))
			if err != nil {
				fmt.Printf("failed to get %s\n", c.Args().First())
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func createPool(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "create-pool",
		Aliases: []string{"cp"},
		Usage:   "create load balancer pool",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.IntFlag{Name: "port", Usage: "pool port [required]"},
			cli.BoolFlag{Name: "sticky", Usage: "use stick persistence"},
			cli.BoolFlag{Name: "standard", Usage: "use standard persistence [default]"},
			cli.BoolFlag{Name: "least-connection, lc", Usage: "use least-connection load balacing"},
			cli.BoolFlag{Name: "round-robin, rr", Usage: "use round-robin load balacing [default]"},
		},
		Before: func(c *cli.Context) error {
			if c.Bool("sticky") && c.Bool("standard") {
				fmt.Println("only one of sticky and standard can be selected")
				return fmt.Errorf("")
			}

			if c.Bool("least-connection") && c.Bool("round-robin") {
				fmt.Println("only one of least-connection and round-robin can be selected")
				return fmt.Errorf("")
			}

			if c.String("id") == "" || c.String("location") == "" || c.Int("port") == 0 {
				fmt.Println("missing required flags, --help for more details")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			pool := lb.Pool{Port: c.Int("port")}
			if c.Bool("sticky") {
				pool.Persistence = lb.Sticky
			} else {
				pool.Persistence = lb.Standard
			}

			if c.Bool("least-connection") {
				pool.Method = lb.LeastConn
			} else {
				pool.Method = lb.RoundRobin
			}

			resp, err := client.LB.CreatePool(c.String("location"), c.String("id"), pool)
			if err != nil {
				fmt.Printf("failed to create load balancer pool for [%s] in %s\n", c.String("id"), c.String("location"))
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func getNode(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "get-node",
		Aliases: []string{"gn"},
		Usage:   "get load balancer node details",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id [required]"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" || c.String("id") == "" || c.String("pool") == "" {
				fmt.Printf("missing required flags to get pool. [use --help to show required flags]\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			resp, err := client.LB.GetAllNodes(c.String("location"), c.String("id"), c.String("pool"))
			if err != nil {
				fmt.Printf("failed to get %s\n", c.Args().First())
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func updateNode(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "update-node",
		Aliases: []string{"un"},
		Usage:   "update load balancer node details",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id [required]"},
			cli.StringSliceFlag{Name: "host, h", Usage: "node hostname and port (ex. 10.10.10.10:8080)"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" || c.String("id") == "" || c.String("pool") == "" {
				fmt.Printf("missing required flags to get pool. [use --help to show required flags]\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			nodes := make([]lb.Node, len(c.StringSlice("host")))
			for i, v := range c.StringSlice("host") {
				split := strings.Split(v, ":")
				port, err := strconv.Atoi(split[1])
				if err != nil {
					fmt.Printf("failed parsing %s", v)
					return
				}
				node := lb.Node{
					IPaddress:   split[0],
					PrivatePort: port,
				}
				nodes[i] = node
			}
			err := client.LB.UpdateNodes(c.String("location"), c.String("id"), c.String("pool"), nodes...)
			if err != nil {
				fmt.Printf("failed to update nodes\n")
				return
			}
			fmt.Printf("nodes updates for pool %s\n", c.String("pool"))
		},
	}
}
<commit_msg>added updated pool to cli<commit_after>package lb

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/mikebeyer/clc-sdk/sdk/clc"
	"github.com/mikebeyer/clc-sdk/sdk/lb"
)

func Commands(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "load-balancer",
		Aliases: []string{"lb"},
		Usage:   "load balancer api",
		Subcommands: []cli.Command{
			get(client),
			create(client),
			getPool(client),
			createPool(client),
			updatePool(client),
			getNode(client),
			updateNode(client),
		},
	}
}

func get(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "get",
		Aliases: []string{"g"},
		Usage:   "get load balancer details",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "all", Usage: "list all load balancers for location"},
			cli.StringFlag{Name: "id", Usage: "load balancer id"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" {
				fmt.Printf("location flag is required.\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			if c.Bool("all") || c.String("id") == "" {
				resp, err := client.LB.GetAll(c.String("location"))
				if err != nil {
					fmt.Printf("failed to get %s\n", c.Args().First())
					return
				}
				b, err := json.MarshalIndent(resp, "", "  ")
				if err != nil {
					fmt.Printf("%s\n", err)
					return
				}
				fmt.Printf("%s\n", b)
				return
			}
			resp, err := client.LB.Get(c.String("location"), c.String("id"))
			if err != nil {
				fmt.Printf("failed to get %s\n", c.Args().First())
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func create(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "create",
		Aliases: []string{"c"},
		Usage:   "create shared load balancer",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "name, n", Usage: "load balancer name [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "description, d", Usage: "load balancer description"},
		},
		Action: func(c *cli.Context) {
			name := c.String("name")
			loc := c.String("location")
			if name == "" || loc == "" {
				fmt.Printf("missing required flags to create load balancer. [use --help to show required flags]\n")
				return
			}

			lb := lb.LoadBalancer{Name: name, Description: c.String("description")}
			resp, err := client.LB.Create(loc, lb)
			if err != nil {
				fmt.Printf("failed to create load balancer [%s] in %s\n", name, loc)
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func getPool(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "get-pool",
		Aliases: []string{"gp"},
		Usage:   "get load balancer pool details",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "all", Usage: "list all load balancers for location"},
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" || c.String("id") == "" {
				fmt.Printf("missing required flags to get pool. [use --help to show required flags]\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			if c.Bool("all") || c.String("pool") == "" {
				resp, err := client.LB.GetAllPools(c.String("location"), c.String("id"))
				if err != nil {
					fmt.Printf("failed to get %s\n", c.Args().First())
					return
				}
				b, err := json.MarshalIndent(resp, "", "  ")
				if err != nil {
					fmt.Printf("%s\n", err)
					return
				}
				fmt.Printf("%s\n", b)
				return
			}
			resp, err := client.LB.GetPool(c.String("location"), c.String("id"), c.String("pool"))
			if err != nil {
				fmt.Printf("failed to get %s\n", c.Args().First())
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func createPool(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "create-pool",
		Aliases: []string{"cp"},
		Usage:   "create load balancer pool",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.IntFlag{Name: "port", Usage: "pool port [required]"},
			cli.BoolFlag{Name: "sticky", Usage: "use stick persistence"},
			cli.BoolFlag{Name: "standard", Usage: "use standard persistence [default]"},
			cli.BoolFlag{Name: "least-connection, lc", Usage: "use least-connection load balacing"},
			cli.BoolFlag{Name: "round-robin, rr", Usage: "use round-robin load balacing [default]"},
		},
		Before: func(c *cli.Context) error {
			if c.Bool("sticky") && c.Bool("standard") {
				fmt.Println("only one of sticky and standard can be selected")
				return fmt.Errorf("")
			}

			if c.Bool("least-connection") && c.Bool("round-robin") {
				fmt.Println("only one of least-connection and round-robin can be selected")
				return fmt.Errorf("")
			}

			if c.String("id") == "" || c.String("location") == "" || c.Int("port") == 0 {
				fmt.Println("missing required flags, --help for more details")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			pool := lb.Pool{Port: c.Int("port")}
			if c.Bool("sticky") {
				pool.Persistence = lb.Sticky
			} else {
				pool.Persistence = lb.Standard
			}

			if c.Bool("least-connection") {
				pool.Method = lb.LeastConn
			} else {
				pool.Method = lb.RoundRobin
			}

			resp, err := client.LB.CreatePool(c.String("location"), c.String("id"), pool)
			if err != nil {
				fmt.Printf("failed to create load balancer pool for [%s] in %s\n", c.String("id"), c.String("location"))
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func updatePool(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "update-pool",
		Aliases: []string{"up"},
		Usage:   "update load balancer pool",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id [required]"},
			cli.BoolFlag{Name: "sticky", Usage: "use stick persistence"},
			cli.BoolFlag{Name: "standard", Usage: "use standard persistence [default]"},
			cli.BoolFlag{Name: "least-connection, lc", Usage: "use least-connection load balacing"},
			cli.BoolFlag{Name: "round-robin, rr", Usage: "use round-robin load balacing [default]"},
		},
		Before: func(c *cli.Context) error {
			if c.Bool("sticky") && c.Bool("standard") {
				fmt.Println("only one of sticky and standard can be selected")
				return fmt.Errorf("")
			}

			if c.Bool("least-connection") && c.Bool("round-robin") {
				fmt.Println("only one of least-connection and round-robin can be selected")
				return fmt.Errorf("")
			}

			if c.String("id") == "" || c.String("location") == "" || c.String("pool") == "" {
				fmt.Println("missing required flags, --help for more details")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			pool := lb.Pool{}
			if c.Bool("sticky") {
				pool.Persistence = lb.Sticky
			} else {
				pool.Persistence = lb.Standard
			}

			if c.Bool("least-connection") {
				pool.Method = lb.LeastConn
			} else {
				pool.Method = lb.RoundRobin
			}

			err := client.LB.UpdatePool(c.String("location"), c.String("id"), c.String("pool"), pool)
			if err != nil {
				fmt.Printf("failed to update load balancer pool [%s] in %s\n", c.String("pool"), c.String("location"))
				return
			}
			fmt.Printf("successfully updated pool [%s]\n", c.String("pool"))
		},
	}
}
func getNode(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "get-node",
		Aliases: []string{"gn"},
		Usage:   "get load balancer node details",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id [required]"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" || c.String("id") == "" || c.String("pool") == "" {
				fmt.Printf("missing required flags to get pool. [use --help to show required flags]\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			resp, err := client.LB.GetAllNodes(c.String("location"), c.String("id"), c.String("pool"))
			if err != nil {
				fmt.Printf("failed to get %s\n", c.Args().First())
				return
			}
			b, err := json.MarshalIndent(resp, "", "  ")
			if err != nil {
				fmt.Printf("%s\n", err)
				return
			}
			fmt.Printf("%s\n", b)
		},
	}
}

func updateNode(client *clc.Client) cli.Command {
	return cli.Command{
		Name:    "update-node",
		Aliases: []string{"un"},
		Usage:   "update load balancer node details",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "id", Usage: "load balancer id [required]"},
			cli.StringFlag{Name: "location, l", Usage: "load balancer location [required]"},
			cli.StringFlag{Name: "pool, p", Usage: "load balancer pool id [required]"},
			cli.StringSliceFlag{Name: "host, h", Usage: "node hostname and port (ex. 10.10.10.10:8080)"},
		},
		Before: func(c *cli.Context) error {
			if c.String("location") == "" || c.String("id") == "" || c.String("pool") == "" {
				fmt.Printf("missing required flags to get pool. [use --help to show required flags]\n")
				return fmt.Errorf("")
			}
			return nil
		},
		Action: func(c *cli.Context) {
			nodes := make([]lb.Node, len(c.StringSlice("host")))
			for i, v := range c.StringSlice("host") {
				split := strings.Split(v, ":")
				port, err := strconv.Atoi(split[1])
				if err != nil {
					fmt.Printf("failed parsing %s", v)
					return
				}
				node := lb.Node{
					IPaddress:   split[0],
					PrivatePort: port,
				}
				nodes[i] = node
			}
			err := client.LB.UpdateNodes(c.String("location"), c.String("id"), c.String("pool"), nodes...)
			if err != nil {
				fmt.Printf("failed to update nodes\n")
				return
			}
			fmt.Printf("nodes updates for pool %s\n", c.String("pool"))
		},
	}
}
<|endoftext|>


<commit_before>package proto

import (
	pb "code.google.com/p/goprotobuf/proto"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func NewMixer(path string) *Mixer {
	version, err := ioutil.ReadFile(path + "/VERSION")

	if err != nil {
		panic("Couldn't find a version for mixer:" + path + ":" + err.Error())
	}

	versionStr := strings.TrimSpace(string(version))

	_, name := filepath.Split(path)

	return &Mixer{
		Name:      pb.String(name),
		Version:   pb.String(versionStr),
		Assets:    make([]*File, 0),
		Features:  make([]*Feature, 0),
		Recipes:   make([]*Recipe, 0),
		Rewriters: make([]*File, 0),
		Package:   nil,
	}

}

func ResolveMixer(name string) *Mixer {
	path := os.ExpandEnv("$GOHATTAN_DATA/mixers/")

	if !strings.Contains(name, ".mxr") {
		name = name + ".mxr"
	}

	fullPath := filepath.Join(path, name)
	return OpenMixer(fullPath)
}

func (m *Mixer) Write(path string) (outputPath string) {

	name := pb.GetString(m.Name)
	version := pb.GetString(m.Version)
	outputFilename := filepath.Join(path, name+"-"+version+".mxr")

	bytes, err := pb.Marshal(m)

	if err != nil {
		panic("Could not marshal mixer (" + name + "), " + err.Error())
	}

	bytes = Encrypt(bytes)

	ioutil.WriteFile(outputFilename, bytes, 0644)

	return outputFilename
}

func OpenMixer(location string) (m *Mixer) {

	data, err := ioutil.ReadFile(location)

	if err != nil {
		panic("Could not find mixer file:" + location)
	}

	data = Decrypt(data)

	thisMixer := &Mixer{}
	err = pb.Unmarshal(data, thisMixer)

	if err != nil {
		panic("Error unmarshalling mixer at:" + location)
	}

	return thisMixer
}

func (m *Mixer) Inspect(printFunctions bool) {
	fmt.Printf(" \tFeatures: \n\t\t%v\n", m.Features)

	println("\tAssets:")
	for _, asset := range m.Assets {
		fmt.Printf("\t\t -- %v\n", pb.GetString(asset.Path))
	}

	println("\tRecipes:")
	for _, recipe := range m.Recipes {
		fmt.Printf("\t\t -- Recipe (%v) contains (%v) files.\n", pb.GetString(recipe.Name), len(recipe.Files))
	}

	println("\tRewriters:")
	for _, rewriter := range m.Rewriters {
		fmt.Printf("\t\t -- %v\n", pb.GetString(rewriter.Path))
	}

	println("\tRoot Package:")
	fmt.Printf(m.packageSummary(printFunctions))
}

func (m *Mixer) Unpack(path string) {
	fullPath := filepath.Join(path, pb.GetString(m.Name)+"-"+pb.GetString(m.Version))
	err := os.MkdirAll(fullPath, 0755)
	if err != nil {
		panic(err.Error())
	}

	err = m.unpackFiles(filepath.Join(fullPath, "assets"), m.Assets)
	if err != nil {
		panic(err)
	}

	for _, recipe := range m.Recipes {
		err = m.unpackFiles(filepath.Join(fullPath, "recipes", pb.GetString(recipe.Name)), recipe.Files)
		if err != nil {
			panic(err)
		}
	}

	err = m.unpackFiles(filepath.Join(fullPath, "rewriters"), m.Rewriters)
	if err != nil {
		panic(err)
	}

	summary := m.packageSummary(true)
	packageFile := filepath.Join(fullPath, "package-summary.txt")

	err = ioutil.WriteFile(packageFile, []byte(summary), 0644)

	if err != nil {
		fmt.Printf("Warning : Couldn't write package summary")
	}

	fmt.Printf("* %v\n", packageFile)
}

func (m *Mixer) unpackFiles(path string, files []*File) (err error) {
	fl := &FileList{
		RootDirectory: path,
		Files:         files,
	}

	err = fl.Unpack(true)

	return
}

func (m *Mixer) packageSummary(printFunctions bool) string {
	summary := ""
	if m.Package != nil {
		summary += fmt.Sprintf("\t\t -- Name: %v\n", pb.GetString(m.Package.Name))
		summary += fmt.Sprintf("\t\t -- Types: %v\n", m.Package.Types)
		summary += fmt.Sprintf("\t\t -- Dependencies:  %v\n", m.Package.Dependencies)
		if printFunctions {
			summary += fmt.Sprintf("\t\t -- Functions (%v):\n", len(m.Package.Functions))
			for _, function := range m.Package.Functions {
				summary += fmt.Sprintf("\t\t\t %v\n", function.Stub(m.Package))
			}
		}
	}
	return summary
}

/* Dummy encryption for now */

func Encrypt(data []byte) []byte {
	for index, b := range data {
		data[index] = rot13(b)
	}
	return data
}

func Decrypt(data []byte) []byte {
	for index, b := range data {
		data[index] = rot13(b)
	}
	return data
}

func rot13(b byte) byte {
	if 'a' <= b && b <= 'z' {
		b = 'a' + ((b-'a')+13)%26
	}
	if 'A' <= b && b <= 'Z' {
		b = 'A' + ((b-'A')+13)%26
	}
	return b
}
<commit_msg>Cleanup the write function and return any errors<commit_after>package proto

import (
	pb "code.google.com/p/goprotobuf/proto"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func NewMixer(path string) *Mixer {
	version, err := ioutil.ReadFile(path + "/VERSION")

	if err != nil {
		panic("Couldn't find a version for mixer:" + path + ":" + err.Error())
	}

	versionStr := strings.TrimSpace(string(version))

	_, name := filepath.Split(path)

	return &Mixer{
		Name:      pb.String(name),
		Version:   pb.String(versionStr),
		Assets:    make([]*File, 0),
		Features:  make([]*Feature, 0),
		Recipes:   make([]*Recipe, 0),
		Rewriters: make([]*File, 0),
		Package:   nil,
	}

}

func ResolveMixer(name string) *Mixer {
	path := os.ExpandEnv("$GOHATTAN_DATA/mixers/")

	if !strings.Contains(name, ".mxr") {
		name = name + ".mxr"
	}

	fullPath := filepath.Join(path, name)
	return OpenMixer(fullPath)
}

func (m *Mixer) Write(path string) (outputPath string, err error) {

	name := pb.GetString(m.Name)
	version := pb.GetString(m.Version)
	outputPath = filepath.Join(path, name+"-"+version+".mxr")

	bytes, err := pb.Marshal(m)
	if err != nil {
		return
	}

	bytes = Encrypt(bytes)

	err = ioutil.WriteFile(outputPath, bytes, 0644)
	if err != nil {
		return
	}

	return
}

func OpenMixer(location string) (m *Mixer) {

	data, err := ioutil.ReadFile(location)

	if err != nil {
		panic("Could not find mixer file:" + location)
	}

	data = Decrypt(data)

	thisMixer := &Mixer{}
	err = pb.Unmarshal(data, thisMixer)

	if err != nil {
		panic("Error unmarshalling mixer at:" + location)
	}

	return thisMixer
}

func (m *Mixer) Inspect(printFunctions bool) {
	fmt.Printf(" \tFeatures: \n\t\t%v\n", m.Features)

	println("\tAssets:")
	for _, asset := range m.Assets {
		fmt.Printf("\t\t -- %v\n", pb.GetString(asset.Path))
	}

	println("\tRecipes:")
	for _, recipe := range m.Recipes {
		fmt.Printf("\t\t -- Recipe (%v) contains (%v) files.\n", pb.GetString(recipe.Name), len(recipe.Files))
	}

	println("\tRewriters:")
	for _, rewriter := range m.Rewriters {
		fmt.Printf("\t\t -- %v\n", pb.GetString(rewriter.Path))
	}

	println("\tRoot Package:")
	fmt.Printf(m.packageSummary(printFunctions))
}

func (m *Mixer) Unpack(path string) {
	fullPath := filepath.Join(path, pb.GetString(m.Name)+"-"+pb.GetString(m.Version))
	err := os.MkdirAll(fullPath, 0755)
	if err != nil {
		panic(err.Error())
	}

	err = m.unpackFiles(filepath.Join(fullPath, "assets"), m.Assets)
	if err != nil {
		panic(err)
	}

	for _, recipe := range m.Recipes {
		err = m.unpackFiles(filepath.Join(fullPath, "recipes", pb.GetString(recipe.Name)), recipe.Files)
		if err != nil {
			panic(err)
		}
	}

	err = m.unpackFiles(filepath.Join(fullPath, "rewriters"), m.Rewriters)
	if err != nil {
		panic(err)
	}

	summary := m.packageSummary(true)
	packageFile := filepath.Join(fullPath, "package-summary.txt")

	err = ioutil.WriteFile(packageFile, []byte(summary), 0644)

	if err != nil {
		fmt.Printf("Warning : Couldn't write package summary")
	}

	fmt.Printf("* %v\n", packageFile)
}

func (m *Mixer) unpackFiles(path string, files []*File) (err error) {
	fl := &FileList{
		RootDirectory: path,
		Files:         files,
	}

	err = fl.Unpack(true)

	return
}

func (m *Mixer) packageSummary(printFunctions bool) string {
	summary := ""
	if m.Package != nil {
		summary += fmt.Sprintf("\t\t -- Name: %v\n", pb.GetString(m.Package.Name))
		summary += fmt.Sprintf("\t\t -- Types: %v\n", m.Package.Types)
		summary += fmt.Sprintf("\t\t -- Dependencies:  %v\n", m.Package.Dependencies)
		if printFunctions {
			summary += fmt.Sprintf("\t\t -- Functions (%v):\n", len(m.Package.Functions))
			for _, function := range m.Package.Functions {
				summary += fmt.Sprintf("\t\t\t %v\n", function.Stub(m.Package))
			}
		}
	}
	return summary
}

/* Dummy encryption for now */

func Encrypt(data []byte) []byte {
	for index, b := range data {
		data[index] = rot13(b)
	}
	return data
}

func Decrypt(data []byte) []byte {
	for index, b := range data {
		data[index] = rot13(b)
	}
	return data
}

func rot13(b byte) byte {
	if 'a' <= b && b <= 'z' {
		b = 'a' + ((b-'a')+13)%26
	}
	if 'A' <= b && b <= 'Z' {
		b = 'A' + ((b-'A')+13)%26
	}
	return b
}
<|endoftext|>


<commit_before>package counter

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestCounter_Init(t *testing.T) {
	tmpfile, _ := ioutil.TempFile("", "test")
	defer os.Remove(tmpfile.Name())

	_, e := Init("", 0, 10)
	if e == nil {
		t.Error("Init did not fail with wrong filename")
	}

	_, e = Init(tmpfile.Name(), 0, 10)
	if e != nil {
		t.Error("Init failed with correct params")
	}

}

func TestCounter_Inc(t *testing.T) {
	tmpfile, _ := ioutil.TempFile("", "test")
	defer os.Remove(tmpfile.Name())

	c, _ := Init(tmpfile.Name(), 0, 2)
	ch := make(chan int)
	c.Inc(ch)
	if n := <-ch; n != 1 {
		t.Error("Incorrect increment", n)
	}
	time.Sleep(time.Second * 1)
	c.Inc(ch)
	if n := <-ch; n != 2 {
		t.Error("Incorrect increment", n)
	}
	time.Sleep(time.Second * 3)
	c.Inc(ch)
	if n := <-ch; n != 1 {
		t.Error("Incorrect increment", n)
	}
}
<commit_msg>adding example and bench test for sake of documents and historical performance check<commit_after>package counter

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestCounter_Init(t *testing.T) {
	tmpfile, _ := ioutil.TempFile("", "test")
	defer os.Remove(tmpfile.Name())

	_, e := Init("", 0, 10)
	if e == nil {
		t.Error("Init did not fail with wrong filename")
	}

	_, e = Init(tmpfile.Name(), 0, 10)
	if e != nil {
		t.Error("Init failed with correct params")
	}

}

func TestCounter_Inc(t *testing.T) {
	tmpfile, _ := ioutil.TempFile("", "test")
	defer os.Remove(tmpfile.Name())

	c, _ := Init(tmpfile.Name(), 0, 2)
	ch := make(chan int)
	c.Inc(ch)
	if n := <-ch; n != 1 {
		t.Error("Incorrect increment", n)
	}
	time.Sleep(time.Second * 1)
	c.Inc(ch)
	if n := <-ch; n != 2 {
		t.Error("Incorrect increment", n)
	}
	time.Sleep(time.Second * 3)
	c.Inc(ch)
	if n := <-ch; n != 1 {
		t.Error("Incorrect increment", n)
	}
}

func ExampleCounter() {
	tmpfile, _ := ioutil.TempFile("", "test")
	defer os.Remove(tmpfile.Name())

	c, _ := Init(tmpfile.Name(), 0, 2)
	ch := make(chan int)
	c.Inc(ch)
	fmt.Println(<-ch)
	c.Inc(ch)
	fmt.Println(<-ch)
	c.Inc(ch)
	fmt.Println(<-ch)
	// Output:
	// 1
	// 2
	// 3
}

func BenchmarkInc(b *testing.B) {
	tmpfile, _ := ioutil.TempFile("", "test")
	defer os.Remove(tmpfile.Name())

	c, _ := Init(tmpfile.Name(), 0, 2)
	ch := make(chan int)
	for i := 0; i < b.N; i++ {
		c.Inc(ch)
		<-ch
	}
}
<|endoftext|>