<commit_before>package db

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

	"github.com/cheggaaa/pb"
	"github.com/go-redis/redis"
	c "github.com/kotakanbe/go-cve-dictionary/config"
	"github.com/kotakanbe/go-cve-dictionary/jvn"
	log "github.com/kotakanbe/go-cve-dictionary/log"
	"github.com/kotakanbe/go-cve-dictionary/models"
	"github.com/kotakanbe/go-cve-dictionary/nvd"
)

/**
# Redis Data Structure

- HASH
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │NO │    HASH    │  FIELD   │  VALUE   │             PURPOSE             │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │ 1 │ CVE#$CVEID │NVD or JVN│ $CVEJSON │     TO GET CVEJSON BY CVEID     │
  ├───┼────────────┼──────────┼──────────┼─────────────────────────────────┤
  │ 2 │  CVE#CPE   │ $CPENAME │ $CPEJSON │    TO GET CPEJSON BY CPENAME    │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘

- ZINDE  X
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │NO │    KEY     │  SCORE   │  MEMBER  │             PURPOSE             │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │ 3 │CVE#$CPENAME│    0     │  $CVEID  │TO GET RELATED []CVEID BY CPENAME│
  ├───┼────────────┼──────────┼──────────┼─────────────────────────────────┤
  │ 4 │CVE#CPENAME │    0     │ $CPENAME │   TO GET ALL CPENAME QUICKLY    │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘
**/

const (
	dialectRedis  = "redis"
	hashKeyPrefix = "CVE#"
)

// RedisDriver is Driver for Redis
type RedisDriver struct {
	name string
	conn *redis.Client
}

// Name return db name
func (r *RedisDriver) Name() string {
	return r.name
}

// NewRedis return Redis driver
func NewRedis(dbType, dbpath string, debugSQL bool) (driver *RedisDriver, err error) {
	driver = &RedisDriver{
		name: dbType,
	}
	log.Debugf("Opening DB (%s).", driver.Name())
	if err = driver.OpenDB(dbType, dbpath, debugSQL); err != nil {
		return
	}

	return
}

// OpenDB opens Database
func (r *RedisDriver) OpenDB(dbType, dbPath string, debugSQL bool) (err error) {
	var option *redis.Options
	if option, err = redis.ParseURL(dbPath); err != nil {
		log.Error(err)
		return fmt.Errorf("Failed to Parse Redis URL. dbpath: %s, err: %s", dbPath, err)
	}
	r.conn = redis.NewClient(option)
	if err = r.conn.Ping().Err(); err != nil {
		return fmt.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %s", dbType, dbPath, err)
	}
	return nil
}

// CloseDB close Database
func (r *RedisDriver) CloseDB() (err error) {
	if err = r.conn.Close(); err != nil {
		log.Errorf("Failed to close DB. Type: %s. err: %s", r.name, err)
		return
	}
	return
}

// Get Select Cve information from DB.
func (r *RedisDriver) Get(cveID string) *models.CveDetail {
	// Avoid null slice being null in JSON
	emptyCveDetail := models.CveDetail{
		Nvd: models.Nvd{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		},
		Jvn: models.Jvn{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		},
	}

	var result *redis.StringStringMapCmd
	if result = r.conn.HGetAll(hashKeyPrefix + cveID); result.Err() != nil {
		log.Error(result.Err())
		return &emptyCveDetail
	}

	jvn := models.Jvn{
		References: []models.Reference{},
		Cpes:       []models.Cpe{},
	}

	var err error
	if j, ok := result.Val()["Jvn"]; ok {
		if err = json.Unmarshal([]byte(j), &jvn); err != nil {
			log.Errorf("Failed to Unmarshal json. err : %s", err)
			return &emptyCveDetail
		}
	}

	nvd := models.Nvd{
		References: []models.Reference{},
		Cpes:       []models.Cpe{},
	}
	if j, ok := result.Val()["Nvd"]; ok {
		if err = json.Unmarshal([]byte(j), &nvd); err != nil {
			log.Errorf("Failed to Unmarshal json. err : %s", err)
			return &emptyCveDetail
		}
	}

	cveDetail := &models.CveDetail{
		CveID: cveID,
		Nvd:   nvd,
		Jvn:   jvn,
	}

	return cveDetail
}

// GetMulti Select Cves information from DB.
func (r *RedisDriver) GetMulti(cveIDs []string) (cveDetails map[string]*models.CveDetail) {
	pipe := r.conn.Pipeline()
	cveDetails = map[string]*models.CveDetail{}
	for _, cveID := range cveIDs {
		// Avoid null slice being null in JSON
		emptyCveDetail := models.CveDetail{
			Nvd: models.Nvd{
				References: []models.Reference{},
				Cpes:       []models.Cpe{},
			},
			Jvn: models.Jvn{
				References: []models.Reference{},
				Cpes:       []models.Cpe{},
			},
		}
		var result *redis.StringStringMapCmd
		if result = pipe.HGetAll(hashKeyPrefix + cveID); result.Err() != nil {
			log.Error(result.Err())
			cveDetails[cveID] = &emptyCveDetail
			continue
		}
		jvn := models.Jvn{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		}
		var err error
		if j, ok := result.Val()["Jvn"]; ok {
			if err = json.Unmarshal([]byte(j), &jvn); err != nil {
				log.Errorf("Failed to Unmarshal json. err : %s", err)
			}
		}

		nvd := models.Nvd{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		}
		if j, ok := result.Val()["Nvd"]; ok {
			if err = json.Unmarshal([]byte(j), &nvd); err != nil {
				log.Errorf("Failed to Unmarshal json. err : %s", err)
			}
		}

		cveDetail := &models.CveDetail{
			CveID: cveID,
			Nvd:   nvd,
			Jvn:   jvn,
		}
		cveDetails[cveID] = cveDetail
	}
	return cveDetails
}

// GetByCpeName Select Cve information from DB.
func (r *RedisDriver) GetByCpeName(cpeName string) (details []*models.CveDetail) {
	var result *redis.StringSliceCmd
	if result = r.conn.ZRange(hashKeyPrefix+cpeName, 0, -1); result.Err() != nil {
		log.Error(result.Err())
		return details
	}

	for _, v := range result.Val() {
		details = append(details, r.Get(v))
	}
	return
}

// InsertJvn insert items fetched from JVN.
func (r *RedisDriver) InsertJvn(items []jvn.Item) error {
	log.Info("Inserting fetched CVEs...")

	cves := convertJvn(items)
	if err := r.insertIntoJvn(cves); err != nil {
		return err
	}
	return nil
}

// InsertIntoJvn inserts Cve Information into DB
func (r *RedisDriver) insertIntoJvn(cves []models.CveDetail) error {
	var err error
	var refreshedJvns []string
	bar := pb.New(len(cves))
	if c.Conf.Quiet {
		bar.Output = ioutil.Discard
	} else {
		bar.Output = os.Stderr
	}
	bar.Start()

	for chunked := range chunkSlice(cves, 10) {
		var pipe redis.Pipeliner
		pipe = r.conn.Pipeline()
		for _, c := range chunked {
			bar.Increment()

			var jj []byte
			c.Jvn.CveID = c.CveID
			if jj, err = json.Marshal(c.Jvn); err != nil {
				return fmt.Errorf("Failed to marshal json. err: %s", err)
			}
			refreshedJvns = append(refreshedJvns, c.CveID)
			if result := pipe.HSet(hashKeyPrefix+c.CveID, "Jvn", string(jj)); result.Err() != nil {
				return fmt.Errorf("Failed to HSet CVE. err: %s", result.Err())
			}

			for _, cpe := range c.Jvn.Cpes {
				var jc []byte
				if jc, err = json.Marshal(cpe); err != nil {
					return fmt.Errorf("Failed to marshal json. err: %s", err)
				}

				if result := pipe.HSet(hashKeyPrefix+"Cpe", cpe.CpeName, jc); result.Err() != nil {
					return fmt.Errorf("Failed to HSet cpe. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+cpe.CpeName,
					redis.Z{Score: 0, Member: c.CveID},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe name. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+"CpeName",
					redis.Z{Score: 0, Member: cpe.CpeName},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe. err: %s", result.Err())
				}
			}
		}
		if _, err = pipe.Exec(); err != nil {
			return fmt.Errorf("Failed to exec pipeline. err: %s", err)
		}
	}
	bar.Finish()
	log.Infof("Refreshed %d Jvns.", len(refreshedJvns))
	//  log.Debugf("%v", refreshedJvns)
	return nil
}

// CountNvd count nvd table
func (r *RedisDriver) CountNvd() (int, error) {
	var result *redis.StringSliceCmd
	if result = r.conn.Keys(hashKeyPrefix + "CVE*"); result.Err() != nil {
		return 0, result.Err()
	}
	return len(result.Val()), nil
}

// InsertNvd inserts CveInformation into DB
func (r *RedisDriver) InsertNvd(entries []nvd.Entry) error {
	log.Info("Inserting CVEs...")

	cves := convertNvd(entries)
	if err := r.insertIntoNvd(cves); err != nil {
		return err
	}
	return nil
}

// insertIntoNvd inserts CveInformation into DB
func (r *RedisDriver) insertIntoNvd(cves []models.CveDetail) error {
	var err error
	var refreshedNvds []string
	bar := pb.New(len(cves))
	if c.Conf.Quiet {
		bar.Output = ioutil.Discard
	} else {
		bar.Output = os.Stderr
	}
	bar.Start()

	for chunked := range chunkSlice(cves, 10) {
		var pipe redis.Pipeliner
		pipe = r.conn.Pipeline()
		for _, c := range chunked {
			bar.Increment()

			var jn []byte
			c.Nvd.CveID = c.CveID
			if jn, err = json.Marshal(c.Nvd); err != nil {
				return fmt.Errorf("Failed to marshal json. err: %s", err)
			}
			refreshedNvds = append(refreshedNvds, c.CveID)
			if result := pipe.HSet(hashKeyPrefix+c.CveID, "Nvd", string(jn)); result.Err() != nil {
				return fmt.Errorf("Failed to HSet CVE. err: %s", result.Err())
			}

			for _, cpe := range c.Nvd.Cpes {
				var jc []byte
				if jc, err = json.Marshal(cpe); err != nil {
					return fmt.Errorf("Failed to marshal json. err: %s", err)
				}

				if result := pipe.HSet(hashKeyPrefix+"Cpe", cpe.CpeName, jc); result.Err() != nil {
					return fmt.Errorf("Failed to HSet cpe. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+cpe.CpeName,
					redis.Z{Score: 0, Member: c.CveID},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe name. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+"CpeName",
					redis.Z{Score: 0, Member: cpe.CpeName},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe. err: %s", result.Err())
				}
			}
		}
		if _, err = pipe.Exec(); err != nil {
			return fmt.Errorf("Failed to exec pipeline. err: %s", err)
		}
	}
	bar.Finish()

	log.Infof("Refreshed %d Nvds.", len(refreshedNvds))
	//  log.Debugf("%v", refreshedNvds)
	return nil
}
<commit_msg>fix multi get (#79)<commit_after>package db

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

	"github.com/cheggaaa/pb"
	"github.com/go-redis/redis"
	c "github.com/kotakanbe/go-cve-dictionary/config"
	"github.com/kotakanbe/go-cve-dictionary/jvn"
	log "github.com/kotakanbe/go-cve-dictionary/log"
	"github.com/kotakanbe/go-cve-dictionary/models"
	"github.com/kotakanbe/go-cve-dictionary/nvd"
)

/**
# Redis Data Structure

- HASH
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │NO │    HASH    │  FIELD   │  VALUE   │             PURPOSE             │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │ 1 │ CVE#$CVEID │NVD or JVN│ $CVEJSON │     TO GET CVEJSON BY CVEID     │
  ├───┼────────────┼──────────┼──────────┼─────────────────────────────────┤
  │ 2 │  CVE#CPE   │ $CPENAME │ $CPEJSON │    TO GET CPEJSON BY CPENAME    │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘

- ZINDE  X
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │NO │    KEY     │  SCORE   │  MEMBER  │             PURPOSE             │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘
  ┌───┬────────────┬──────────┬──────────┬─────────────────────────────────┐
  │ 3 │CVE#$CPENAME│    0     │  $CVEID  │TO GET RELATED []CVEID BY CPENAME│
  ├───┼────────────┼──────────┼──────────┼─────────────────────────────────┤
  │ 4 │CVE#CPENAME │    0     │ $CPENAME │   TO GET ALL CPENAME QUICKLY    │
  └───┴────────────┴──────────┴──────────┴─────────────────────────────────┘
**/

const (
	dialectRedis  = "redis"
	hashKeyPrefix = "CVE#"
)

// RedisDriver is Driver for Redis
type RedisDriver struct {
	name string
	conn *redis.Client
}

// Name return db name
func (r *RedisDriver) Name() string {
	return r.name
}

// NewRedis return Redis driver
func NewRedis(dbType, dbpath string, debugSQL bool) (driver *RedisDriver, err error) {
	driver = &RedisDriver{
		name: dbType,
	}
	log.Debugf("Opening DB (%s).", driver.Name())
	if err = driver.OpenDB(dbType, dbpath, debugSQL); err != nil {
		return
	}

	return
}

// OpenDB opens Database
func (r *RedisDriver) OpenDB(dbType, dbPath string, debugSQL bool) (err error) {
	var option *redis.Options
	if option, err = redis.ParseURL(dbPath); err != nil {
		log.Error(err)
		return fmt.Errorf("Failed to Parse Redis URL. dbpath: %s, err: %s", dbPath, err)
	}
	r.conn = redis.NewClient(option)
	if err = r.conn.Ping().Err(); err != nil {
		return fmt.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %s", dbType, dbPath, err)
	}
	return nil
}

// CloseDB close Database
func (r *RedisDriver) CloseDB() (err error) {
	if err = r.conn.Close(); err != nil {
		log.Errorf("Failed to close DB. Type: %s. err: %s", r.name, err)
		return
	}
	return
}

// Get Select Cve information from DB.
func (r *RedisDriver) Get(cveID string) *models.CveDetail {
	// Avoid null slice being null in JSON
	emptyCveDetail := models.CveDetail{
		Nvd: models.Nvd{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		},
		Jvn: models.Jvn{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		},
	}

	var result *redis.StringStringMapCmd
	if result = r.conn.HGetAll(hashKeyPrefix + cveID); result.Err() != nil {
		log.Error(result.Err())
		return &emptyCveDetail
	}

	jvn := models.Jvn{
		References: []models.Reference{},
		Cpes:       []models.Cpe{},
	}

	var err error
	if j, ok := result.Val()["Jvn"]; ok {
		if err = json.Unmarshal([]byte(j), &jvn); err != nil {
			log.Errorf("Failed to Unmarshal json. err : %s", err)
			return &emptyCveDetail
		}
	}

	nvd := models.Nvd{
		References: []models.Reference{},
		Cpes:       []models.Cpe{},
	}
	if j, ok := result.Val()["Nvd"]; ok {
		if err = json.Unmarshal([]byte(j), &nvd); err != nil {
			log.Errorf("Failed to Unmarshal json. err : %s", err)
			return &emptyCveDetail
		}
	}

	cveDetail := &models.CveDetail{
		CveID: cveID,
		Nvd:   nvd,
		Jvn:   jvn,
	}

	return cveDetail
}

// GetMulti Select Cves information from DB.
func (r *RedisDriver) GetMulti(cveIDs []string) (cveDetails map[string]*models.CveDetail) {
	var err error
	cveDetails = map[string]*models.CveDetail{}
	pipe := r.conn.Pipeline()
	rs := map[string]*redis.StringStringMapCmd{}
	for _, cveID := range cveIDs {
		rs[cveID] = pipe.HGetAll(hashKeyPrefix + cveID)
	}
	if _, err = pipe.Exec(); err != nil {
		if err != redis.Nil {
			log.Errorf("Failed to get multi cve json. err : %s", err)
			return cveDetails
		}
	}

	for cveID, result := range rs {
		jvn := models.Jvn{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		}
		if j, ok := result.Val()["Jvn"]; ok {
			if err = json.Unmarshal([]byte(j), &jvn); err != nil {
				log.Errorf("Failed to Unmarshal json. err : %s", err)
			}
		}

		nvd := models.Nvd{
			References: []models.Reference{},
			Cpes:       []models.Cpe{},
		}
		if j, ok := result.Val()["Nvd"]; ok {
			if err = json.Unmarshal([]byte(j), &nvd); err != nil {
				log.Errorf("Failed to Unmarshal json. err : %s", err)
			}
		}

		cveDetail := &models.CveDetail{
			CveID: cveID,
			Nvd:   nvd,
			Jvn:   jvn,
		}
		cveDetails[cveID] = cveDetail
	}
	return cveDetails
}

// GetByCpeName Select Cve information from DB.
func (r *RedisDriver) GetByCpeName(cpeName string) (details []*models.CveDetail) {
	var result *redis.StringSliceCmd
	if result = r.conn.ZRange(hashKeyPrefix+cpeName, 0, -1); result.Err() != nil {
		log.Error(result.Err())
		return details
	}

	for _, v := range result.Val() {
		details = append(details, r.Get(v))
	}
	return
}

// InsertJvn insert items fetched from JVN.
func (r *RedisDriver) InsertJvn(items []jvn.Item) error {
	log.Info("Inserting fetched CVEs...")

	cves := convertJvn(items)
	if err := r.insertIntoJvn(cves); err != nil {
		return err
	}
	return nil
}

// InsertIntoJvn inserts Cve Information into DB
func (r *RedisDriver) insertIntoJvn(cves []models.CveDetail) error {
	var err error
	var refreshedJvns []string
	bar := pb.New(len(cves))
	if c.Conf.Quiet {
		bar.Output = ioutil.Discard
	} else {
		bar.Output = os.Stderr
	}
	bar.Start()

	for chunked := range chunkSlice(cves, 10) {
		var pipe redis.Pipeliner
		pipe = r.conn.Pipeline()
		for _, c := range chunked {
			bar.Increment()

			var jj []byte
			c.Jvn.CveID = c.CveID
			if jj, err = json.Marshal(c.Jvn); err != nil {
				return fmt.Errorf("Failed to marshal json. err: %s", err)
			}
			refreshedJvns = append(refreshedJvns, c.CveID)
			if result := pipe.HSet(hashKeyPrefix+c.CveID, "Jvn", string(jj)); result.Err() != nil {
				return fmt.Errorf("Failed to HSet CVE. err: %s", result.Err())
			}

			for _, cpe := range c.Jvn.Cpes {
				var jc []byte
				if jc, err = json.Marshal(cpe); err != nil {
					return fmt.Errorf("Failed to marshal json. err: %s", err)
				}

				if result := pipe.HSet(hashKeyPrefix+"Cpe", cpe.CpeName, jc); result.Err() != nil {
					return fmt.Errorf("Failed to HSet cpe. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+cpe.CpeName,
					redis.Z{Score: 0, Member: c.CveID},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe name. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+"CpeName",
					redis.Z{Score: 0, Member: cpe.CpeName},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe. err: %s", result.Err())
				}
			}
		}
		if _, err = pipe.Exec(); err != nil {
			return fmt.Errorf("Failed to exec pipeline. err: %s", err)
		}
	}
	bar.Finish()
	log.Infof("Refreshed %d Jvns.", len(refreshedJvns))
	//  log.Debugf("%v", refreshedJvns)
	return nil
}

// CountNvd count nvd table
func (r *RedisDriver) CountNvd() (int, error) {
	var result *redis.StringSliceCmd
	if result = r.conn.Keys(hashKeyPrefix + "CVE*"); result.Err() != nil {
		return 0, result.Err()
	}
	return len(result.Val()), nil
}

// InsertNvd inserts CveInformation into DB
func (r *RedisDriver) InsertNvd(entries []nvd.Entry) error {
	log.Info("Inserting CVEs...")

	cves := convertNvd(entries)
	if err := r.insertIntoNvd(cves); err != nil {
		return err
	}
	return nil
}

// insertIntoNvd inserts CveInformation into DB
func (r *RedisDriver) insertIntoNvd(cves []models.CveDetail) error {
	var err error
	var refreshedNvds []string
	bar := pb.New(len(cves))
	if c.Conf.Quiet {
		bar.Output = ioutil.Discard
	} else {
		bar.Output = os.Stderr
	}
	bar.Start()

	for chunked := range chunkSlice(cves, 10) {
		var pipe redis.Pipeliner
		pipe = r.conn.Pipeline()
		for _, c := range chunked {
			bar.Increment()

			var jn []byte
			c.Nvd.CveID = c.CveID
			if jn, err = json.Marshal(c.Nvd); err != nil {
				return fmt.Errorf("Failed to marshal json. err: %s", err)
			}
			refreshedNvds = append(refreshedNvds, c.CveID)
			if result := pipe.HSet(hashKeyPrefix+c.CveID, "Nvd", string(jn)); result.Err() != nil {
				return fmt.Errorf("Failed to HSet CVE. err: %s", result.Err())
			}

			for _, cpe := range c.Nvd.Cpes {
				var jc []byte
				if jc, err = json.Marshal(cpe); err != nil {
					return fmt.Errorf("Failed to marshal json. err: %s", err)
				}

				if result := pipe.HSet(hashKeyPrefix+"Cpe", cpe.CpeName, jc); result.Err() != nil {
					return fmt.Errorf("Failed to HSet cpe. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+cpe.CpeName,
					redis.Z{Score: 0, Member: c.CveID},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe name. err: %s", result.Err())
				}
				if result := pipe.ZAdd(
					hashKeyPrefix+"CpeName",
					redis.Z{Score: 0, Member: cpe.CpeName},
				); result.Err() != nil {
					return fmt.Errorf("Failed to ZAdd cpe. err: %s", result.Err())
				}
			}
		}
		if _, err = pipe.Exec(); err != nil {
			return fmt.Errorf("Failed to exec pipeline. err: %s", err)
		}
	}
	bar.Finish()

	log.Infof("Refreshed %d Nvds.", len(refreshedNvds))
	//  log.Debugf("%v", refreshedNvds)
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package mm

import (
	"github.com/percona/cloud-protocol/proto"
	"github.com/percona/cloud-tools/data"
	"github.com/percona/cloud-tools/pct"
	"math"
	"time"
)

type Aggregator struct {
	logger         *pct.Logger
	interval       int64
	collectionChan chan *Collection
	spool          data.Spooler
	// --
	sync    *pct.SyncChan
	running bool
}

func NewAggregator(logger *pct.Logger, interval int64, collectionChan chan *Collection, spool data.Spooler) *Aggregator {
	a := &Aggregator{
		logger:         logger,
		interval:       interval,
		collectionChan: collectionChan,
		spool:          spool,
		// --
		sync: pct.NewSyncChan(),
	}
	return a
}

/////////////////////////////////////////////////////////////////////////////
// Interface
/////////////////////////////////////////////////////////////////////////////

// @goroutine[0]
func (a *Aggregator) Start() {
	a.running = true // XXX: not guarded
	go a.run()
}

// @goroutine[0]
func (a *Aggregator) Stop() {
	a.sync.Stop()
	a.sync.Wait()
}

// @goroutine[0]
func (a *Aggregator) IsRunning() bool {
	return a.running // XXX: not guarded
}

/////////////////////////////////////////////////////////////////////////////
// Implementation
/////////////////////////////////////////////////////////////////////////////

// @goroutine[1]
func (a *Aggregator) run() {
	defer func() {
		a.running = false // XXX: not guarded
		a.sync.Done()
	}()

	var curInterval int64
	var startTs time.Time
	cur := []*InstanceStats{}

	for {
		select {
		case collection := <-a.collectionChan:
			interval := (collection.Ts / a.interval) * a.interval
			if curInterval == 0 {
				curInterval = interval
				startTs = GoTime(a.interval, interval)
			}
			if interval > curInterval {
				// Metrics for next interval have arrived.  Process and spool
				// the current interval, then advance to this interval.
				a.report(startTs, cur)

				// Init next stats based on current ones to avoid re-creating them.
				// todo: what if metrics from an instance aren't collected?
				next := make([]*InstanceStats, len(cur))
				for n := range cur {
					i := &InstanceStats{
						ServiceInstance: cur[n].ServiceInstance,
						Stats:           make(map[string]*Stats),
					}
					next[n] = i
				}
				cur = next
				curInterval = interval
				startTs = GoTime(a.interval, interval)
			} else if interval < curInterval {
				// collection arrived late
			}

			// Each collection is from a specific service instance ("it").
			// Find the stats for this instance, create if they don't exist.
			var is *InstanceStats
			for _, i := range cur {
				if collection.Service == i.Service && collection.InstanceId == i.InstanceId {
					is = i
					break
				}
			}

			if is == nil {
				// New service instance, create stats for it.
				is = &InstanceStats{
					ServiceInstance: proto.ServiceInstance{
						Service:    collection.Service,
						InstanceId: collection.InstanceId,
					},
					Stats: make(map[string]*Stats),
				}
				cur = append(cur, is)
			}

			// Add each metric in the collection to its Stats.
			for _, metric := range collection.Metrics {
				stats, haveStats := is.Stats[metric.Name]
				if !haveStats {
					// New metric, create stats for it.
					var err error
					stats, err = NewStats(metric.Type)
					if err != nil {
						a.logger.Error(metric.Name, "invalid:", err.Error())
						continue
					}
					is.Stats[metric.Name] = stats
				}
				stats.Add(&metric, collection.Ts)
			}
		case <-a.sync.StopChan:
			return
		}
	}
}

// @goroutine[1]
func (a *Aggregator) report(startTs time.Time, is []*InstanceStats) {
	a.logger.Info("Summarize metrics for", startTs)
	for _, i := range is {
		for _, s := range i.Stats {
			s.Summarize()
		}
	}
	report := &Report{
		Ts:       startTs,
		Duration: uint(a.interval),
		Stats:    is,
	}
	a.spool.Write("mm", report)
}

func GoTime(interval, unixTs int64) time.Time {
	// Calculate seconds (d) from begin to next interval.
	i := float64(interval)
	t := float64(unixTs)
	d := int64(i - math.Mod(t, i))
	if d != interval {
		/**
		 * unixTs is not an interval, so it's after the interval's start ts.
		 * E.g. if i=60 and unxiTs (t)=130, then t falls between intervals:
		 *   120
		 *   130  =t
		 *   180  d=50
		 * Real begin is 120, so decrease t by 10: i - d.
		 */
		unixTs = unixTs - (interval - d)
	}
	return time.Unix(int64(unixTs), 0).UTC()
}
<commit_msg>Add debug back to mm/aggregator.<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package mm

import (
	"github.com/percona/cloud-protocol/proto"
	"github.com/percona/cloud-tools/data"
	"github.com/percona/cloud-tools/pct"
	"math"
	"time"
)

type Aggregator struct {
	logger         *pct.Logger
	interval       int64
	collectionChan chan *Collection
	spool          data.Spooler
	// --
	sync    *pct.SyncChan
	running bool
}

func NewAggregator(logger *pct.Logger, interval int64, collectionChan chan *Collection, spool data.Spooler) *Aggregator {
	a := &Aggregator{
		logger:         logger,
		interval:       interval,
		collectionChan: collectionChan,
		spool:          spool,
		// --
		sync: pct.NewSyncChan(),
	}
	return a
}

/////////////////////////////////////////////////////////////////////////////
// Interface
/////////////////////////////////////////////////////////////////////////////

// @goroutine[0]
func (a *Aggregator) Start() {
	a.running = true // XXX: not guarded
	go a.run()
}

// @goroutine[0]
func (a *Aggregator) Stop() {
	a.sync.Stop()
	a.sync.Wait()
}

// @goroutine[0]
func (a *Aggregator) IsRunning() bool {
	return a.running // XXX: not guarded
}

/////////////////////////////////////////////////////////////////////////////
// Implementation
/////////////////////////////////////////////////////////////////////////////

// @goroutine[1]
func (a *Aggregator) run() {
	defer func() {
		a.running = false // XXX: not guarded
		a.sync.Done()
	}()

	var curInterval int64
	var startTs time.Time
	cur := []*InstanceStats{}

	for {
		select {
		case collection := <-a.collectionChan:
			interval := (collection.Ts / a.interval) * a.interval
			if curInterval == 0 {
				curInterval = interval
				startTs = GoTime(a.interval, interval)
				a.logger.Debug("Start first interval", startTs)
			}
			if interval > curInterval {
				// Metrics for next interval have arrived.  Process and spool
				// the current interval, then advance to this interval.
				a.report(startTs, cur)

				// Init next stats based on current ones to avoid re-creating them.
				// todo: what if metrics from an instance aren't collected?
				next := make([]*InstanceStats, len(cur))
				for n := range cur {
					i := &InstanceStats{
						ServiceInstance: cur[n].ServiceInstance,
						Stats:           make(map[string]*Stats),
					}
					next[n] = i
				}
				cur = next
				curInterval = interval
				startTs = GoTime(a.interval, interval)
				a.logger.Debug("Start interval", startTs)
			} else if interval < curInterval {
				t := GoTime(a.interval, interval)
				a.logger.Info("Lost collection for interval", t, "; current interval is", startTs)
			}

			// Each collection is from a specific service instance ("it").
			// Find the stats for this instance, create if they don't exist.
			var is *InstanceStats
			for _, i := range cur {
				if collection.Service == i.Service && collection.InstanceId == i.InstanceId {
					is = i
					break
				}
			}

			if is == nil {
				// New service instance, create stats for it.
				is = &InstanceStats{
					ServiceInstance: proto.ServiceInstance{
						Service:    collection.Service,
						InstanceId: collection.InstanceId,
					},
					Stats: make(map[string]*Stats),
				}
				cur = append(cur, is)
			}

			// Add each metric in the collection to its Stats.
			for _, metric := range collection.Metrics {
				stats, haveStats := is.Stats[metric.Name]
				if !haveStats {
					// New metric, create stats for it.
					var err error
					stats, err = NewStats(metric.Type)
					if err != nil {
						a.logger.Error(metric.Name, "invalid:", err.Error())
						continue
					}
					is.Stats[metric.Name] = stats
				}
				stats.Add(&metric, collection.Ts)
			}
		case <-a.sync.StopChan:
			return
		}
	}
}

// @goroutine[1]
func (a *Aggregator) report(startTs time.Time, is []*InstanceStats) {
	a.logger.Info("Summarize metrics for", startTs)
	for _, i := range is {
		for _, s := range i.Stats {
			s.Summarize()
		}
	}
	report := &Report{
		Ts:       startTs,
		Duration: uint(a.interval),
		Stats:    is,
	}
	a.spool.Write("mm", report)
}

func GoTime(interval, unixTs int64) time.Time {
	// Calculate seconds (d) from begin to next interval.
	i := float64(interval)
	t := float64(unixTs)
	d := int64(i - math.Mod(t, i))
	if d != interval {
		/**
		 * unixTs is not an interval, so it's after the interval's start ts.
		 * E.g. if i=60 and unxiTs (t)=130, then t falls between intervals:
		 *   120
		 *   130  =t
		 *   180  d=50
		 * Real begin is 120, so decrease t by 10: i - d.
		 */
		unixTs = unixTs - (interval - d)
	}
	return time.Unix(int64(unixTs), 0).UTC()
}
<|endoftext|>


<commit_before>package web

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/craigfurman/woodhouse-ci/blockingio"
	"github.com/craigfurman/woodhouse-ci/jobs"
	"github.com/craigfurman/woodhouse-ci/web/helpers"

	"github.com/gorilla/mux"
)

//go:generate counterfeiter -o fake_job_service/fake_job_service.go . JobService
type JobService interface {
	RunJob(id string) error
	Save(job *jobs.Job) error
	FindBuild(jobId string, buildNumber int) (jobs.Build, error)
	Stream(jobId string, buildNumber int, streamOffset int64) (*blockingio.BlockingReader, error)
}

type Handler struct {
	*mux.Router

	templates map[string]*template.Template
}

func New(jobService JobService, templateDir string) *Handler {
	templates := parseTemplates(templateDir)
	router := mux.NewRouter()

	handler := &Handler{
		Router:    router,
		templates: templates,
	}

	router.HandleFunc("/jobs", func(w http.ResponseWriter, r *http.Request) {
		handler.renderTemplate("list_jobs", nil, w)
	}).Methods("GET")

	router.HandleFunc("/jobs/new", func(w http.ResponseWriter, r *http.Request) {
		handler.renderTemplate("create_job", nil, w)
	}).Methods("GET")

	router.HandleFunc("/jobs", func(w http.ResponseWriter, r *http.Request) {
		job := jobs.Job{
			Name:    r.FormValue("name"),
			Command: r.FormValue("command"),
		}

		if err := jobService.Save(&job); err != nil {
			handler.renderErrPage("saving job", err, w, r)
			return
		}

		if err := jobService.RunJob(job.ID); err == nil {
			http.Redirect(w, r, fmt.Sprintf("/jobs/%s/builds/1", job.ID), 302)
		} else {
			handler.renderErrPage("running job", err, w, r)
		}
	}).Methods("POST")

	router.HandleFunc("/jobs/{jobId}/builds/{buildId}", func(w http.ResponseWriter, r *http.Request) {
		jobId := mux.Vars(r)["jobId"]
		buildIdStr := mux.Vars(r)["buildId"]
		buildId, err := strconv.Atoi(buildIdStr)
		must(err)
		if runningJob, err := jobService.FindBuild(jobId, buildId); err == nil {
			buildView := helpers.PresentableJob(runningJob)
			buildView.BuildNumber = buildIdStr
			buildView.BytesAlreadyReceived = len(runningJob.Output)
			handler.renderTemplate("job_output", buildView, w)
		} else {
			handler.renderErrPage("retrieving job", err, w, r)
		}
	}).Methods("GET")

	router.HandleFunc("/jobs/{jobId}/builds/{buildId}/output", func(w http.ResponseWriter, r *http.Request) {
		jobId := mux.Vars(r)["jobId"]
		buildId, err := strconv.Atoi(mux.Vars(r)["buildId"])
		must(err)

		must(r.ParseForm())
		streamOffset, err := strconv.Atoi(r.Form.Get("offset"))
		must(err)

		streamer, err := jobService.Stream(jobId, buildId, int64(streamOffset))
		must(err)

		w.Header().Set("Content-Type", "text/event-stream\n\n")

		for {
			_, err := w.Write([]byte("event: output\n"))
			must(err)
			bytes, done := streamer.Next()
			_, err = w.Write([]byte(fmt.Sprintf("data: %s", helpers.SanitisedHTML(string(bytes)))))
			must(err)
			_, err = w.Write([]byte("\n\n"))
			must(err)

			w.(http.Flusher).Flush()

			if done {
				break
			}
		}

		build, err := jobService.FindBuild(jobId, buildId)
		must(err)

		w.Write([]byte(fmt.Sprintf("event: end\ndata: %s\n\n", helpers.Message(build))))
	}).Methods("GET")

	return handler
}

type Error struct {
	Error string
}

func (handler Handler) renderErrPage(message string, err error, w http.ResponseWriter, r *http.Request) {
	log.Printf("Error: %s: %v", message, err)
	w.WriteHeader(500)
	handler.renderTemplate("error", Error{Error: err.Error()}, w)
}

func (h Handler) renderTemplate(name string, pageObject interface{}, w http.ResponseWriter) {
	w.Header().Set("Content-Type", "text/html")
	must(h.templates[name].Execute(w, pageObject))
}

func parseTemplates(templateDir string) map[string]*template.Template {
	templates := make(map[string]*template.Template)

	layout := filepath.Join(templateDir, "layouts", "layout.html")
	views, err := filepath.Glob(fmt.Sprintf("%s/views/*.html", templateDir))
	must(err)

	for _, view := range views {
		viewName := strings.Split(filepath.Base(view), ".")[0]
		templates[viewName] = template.Must(template.ParseFiles(layout, view))
	}
	return templates
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>DRY out SSE message composition<commit_after>package web

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/craigfurman/woodhouse-ci/blockingio"
	"github.com/craigfurman/woodhouse-ci/jobs"
	"github.com/craigfurman/woodhouse-ci/web/helpers"

	"github.com/gorilla/mux"
)

//go:generate counterfeiter -o fake_job_service/fake_job_service.go . JobService
type JobService interface {
	RunJob(id string) error
	Save(job *jobs.Job) error
	FindBuild(jobId string, buildNumber int) (jobs.Build, error)
	Stream(jobId string, buildNumber int, streamOffset int64) (*blockingio.BlockingReader, error)
}

type Handler struct {
	*mux.Router

	templates map[string]*template.Template
}

func New(jobService JobService, templateDir string) *Handler {
	templates := parseTemplates(templateDir)
	router := mux.NewRouter()

	handler := &Handler{
		Router:    router,
		templates: templates,
	}

	router.HandleFunc("/jobs", func(w http.ResponseWriter, r *http.Request) {
		handler.renderTemplate("list_jobs", nil, w)
	}).Methods("GET")

	router.HandleFunc("/jobs/new", func(w http.ResponseWriter, r *http.Request) {
		handler.renderTemplate("create_job", nil, w)
	}).Methods("GET")

	router.HandleFunc("/jobs", func(w http.ResponseWriter, r *http.Request) {
		job := jobs.Job{
			Name:    r.FormValue("name"),
			Command: r.FormValue("command"),
		}

		if err := jobService.Save(&job); err != nil {
			handler.renderErrPage("saving job", err, w, r)
			return
		}

		if err := jobService.RunJob(job.ID); err == nil {
			http.Redirect(w, r, fmt.Sprintf("/jobs/%s/builds/1", job.ID), 302)
		} else {
			handler.renderErrPage("running job", err, w, r)
		}
	}).Methods("POST")

	router.HandleFunc("/jobs/{jobId}/builds/{buildId}", func(w http.ResponseWriter, r *http.Request) {
		jobId := mux.Vars(r)["jobId"]
		buildIdStr := mux.Vars(r)["buildId"]
		buildId, err := strconv.Atoi(buildIdStr)
		must(err)
		if runningJob, err := jobService.FindBuild(jobId, buildId); err == nil {
			buildView := helpers.PresentableJob(runningJob)
			buildView.BuildNumber = buildIdStr
			buildView.BytesAlreadyReceived = len(runningJob.Output)
			handler.renderTemplate("job_output", buildView, w)
		} else {
			handler.renderErrPage("retrieving job", err, w, r)
		}
	}).Methods("GET")

	router.HandleFunc("/jobs/{jobId}/builds/{buildId}/output", func(w http.ResponseWriter, r *http.Request) {
		jobId := mux.Vars(r)["jobId"]
		buildId, err := strconv.Atoi(mux.Vars(r)["buildId"])
		must(err)

		must(r.ParseForm())
		streamOffset, err := strconv.Atoi(r.Form.Get("offset"))
		must(err)

		streamer, err := jobService.Stream(jobId, buildId, int64(streamOffset))
		must(err)

		w.Header().Set("Content-Type", "text/event-stream\n\n")

		for {
			bytes, done := streamer.Next()
			_, err = w.Write([]byte(eventMessage("output", helpers.SanitisedHTML(string(bytes)))))
			must(err)

			w.(http.Flusher).Flush()

			if done {
				break
			}
		}

		build, err := jobService.FindBuild(jobId, buildId)
		must(err)

		w.Write([]byte(eventMessage("end", helpers.Message(build))))
	}).Methods("GET")

	return handler
}

func eventMessage(eventName, data string) string {
	return fmt.Sprintf("event: %s\ndata: %s\n\n", eventName, data)
}

type Error struct {
	Error string
}

func (handler Handler) renderErrPage(message string, err error, w http.ResponseWriter, r *http.Request) {
	log.Printf("Error: %s: %v", message, err)
	w.WriteHeader(500)
	handler.renderTemplate("error", Error{Error: err.Error()}, w)
}

func (h Handler) renderTemplate(name string, pageObject interface{}, w http.ResponseWriter) {
	w.Header().Set("Content-Type", "text/html")
	must(h.templates[name].Execute(w, pageObject))
}

func parseTemplates(templateDir string) map[string]*template.Template {
	templates := make(map[string]*template.Template)

	layout := filepath.Join(templateDir, "layouts", "layout.html")
	views, err := filepath.Glob(fmt.Sprintf("%s/views/*.html", templateDir))
	must(err)

	for _, view := range views {
		viewName := strings.Split(filepath.Base(view), ".")[0]
		templates[viewName] = template.Must(template.ParseFiles(layout, view))
	}
	return templates
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package user

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/pkg/errors"
	"github.com/xchapter7x/lo"
)

func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error {
	lo.G.Debugf("Uaa Users %+v", uaaUsers)
	origin := m.LdapConfig.Origin
	if m.LdapConfig.Enabled {
		ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %+v", ldapUsers)
		for _, inputUser := range ldapUsers {
			userToUse := m.UpdateUserInfo(inputUser)
			userID := userToUse.UserID
			userList := uaaUsers.GetByName(userID)
			if len(userList) == 0 {
				lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
				if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil {
					lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error())
					continue
				} else {
					uaaUsers.Add(uaa.User{
						Username:   userID,
						ExternalID: userToUse.UserDN,
						Origin:     m.LdapConfig.Origin,
						Email:      userToUse.Email,
						GUID:       userGUID,
					})
				}
			}
			if !roleUsers.HasUserForOrigin(userID, origin) {
				user := uaaUsers.GetByNameAndOrigin(userID, origin)
				if user == nil {
					return fmt.Errorf("Unabled to find user %s for origin %s", userID, origin)
				}
				if err := usersInput.AddUser(usersInput, user.Username, user.GUID); err != nil {
					return errors.Wrap(err, fmt.Sprintf("User %s with origin %s", user.Username, user.Origin))
				}
			} else {
				lo.G.Debugf("User[%s] found in role", userID)
				roleUsers.RemoveUserForOrigin(userID, origin)
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	return nil
}

func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) {
	var ldapUsers []ldap.User
	for _, groupName := range usersInput.UniqueLdapGroupNames() {
		userDNList, err := m.LdapMgr.GetUserDNs(groupName)
		if err != nil {
			return nil, err
		}
		for _, userDN := range userDNList {
			lo.G.Debugf("Checking for userDN %s", userDN)
			uaaUser := uaaUsers.GetByExternalID(userDN)
			if uaaUser != nil {
				lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username)
				ldapUsers = append(ldapUsers, ldap.User{
					UserID: uaaUser.Username,
					UserDN: userDN,
					Email:  uaaUser.Email,
				})
			} else {
				lo.G.Infof("UserDN [%s] not found in UAA, executing ldap lookup", userDN)
				user, err := m.LdapMgr.GetUserByDN(userDN)
				if err != nil {
					return nil, err
				}
				if user != nil {
					ldapUsers = append(ldapUsers, *user)
				} else {
					lo.G.Infof("UserDN %s not found in ldap", userDN)
				}
			}
		}
	}
	for _, userID := range usersInput.LdapUsers {
		userList := uaaUsers.GetByName(userID)
		if len(userList) > 0 {
			lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID)
			for _, uaaUser := range userList {
				if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) {
					ldapUsers = append(ldapUsers, ldap.User{
						UserID: userID,
						UserDN: uaaUser.ExternalID,
						Email:  uaaUser.Email,
					})
				}
			}
		} else {
			lo.G.Infof("User [%s] not found in UAA, executing ldap lookup", userID)
			user, err := m.LdapMgr.GetUserByID(userID)
			if err != nil {
				return nil, err
			}
			if user != nil {
				ldapUsers = append(ldapUsers, *user)
			} else {
				lo.G.Infof("User %s not found in ldap", userID)
			}
		}
	}
	return ldapUsers, nil
}

func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User {
	userID := strings.ToLower(user.UserID)
	externalID := user.UserDN
	email := user.Email
	if email == "" {
		email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
	}

	return ldap.User{
		UserID: userID,
		UserDN: externalID,
		Email:  email,
	}
}
<commit_msg>remove unneccessary logging<commit_after>package user

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/pkg/errors"
	"github.com/xchapter7x/lo"
)

func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error {
	origin := m.LdapConfig.Origin
	if m.LdapConfig.Enabled {
		ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %+v", ldapUsers)
		for _, inputUser := range ldapUsers {
			userToUse := m.UpdateUserInfo(inputUser)
			userID := userToUse.UserID
			userList := uaaUsers.GetByName(userID)
			if len(userList) == 0 {
				lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
				if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil {
					lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error())
					continue
				} else {
					uaaUsers.Add(uaa.User{
						Username:   userID,
						ExternalID: userToUse.UserDN,
						Origin:     m.LdapConfig.Origin,
						Email:      userToUse.Email,
						GUID:       userGUID,
					})
				}
			}
			if !roleUsers.HasUserForOrigin(userID, origin) {
				user := uaaUsers.GetByNameAndOrigin(userID, origin)
				if user == nil {
					return fmt.Errorf("Unabled to find user %s for origin %s", userID, origin)
				}
				if err := usersInput.AddUser(usersInput, user.Username, user.GUID); err != nil {
					return errors.Wrap(err, fmt.Sprintf("User %s with origin %s", user.Username, user.Origin))
				}
			} else {
				lo.G.Debugf("User[%s] found in role", userID)
				roleUsers.RemoveUserForOrigin(userID, origin)
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	return nil
}

func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) {
	var ldapUsers []ldap.User
	for _, groupName := range usersInput.UniqueLdapGroupNames() {
		userDNList, err := m.LdapMgr.GetUserDNs(groupName)
		if err != nil {
			return nil, err
		}
		for _, userDN := range userDNList {
			lo.G.Debugf("Checking for userDN %s", userDN)
			uaaUser := uaaUsers.GetByExternalID(userDN)
			if uaaUser != nil {
				lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username)
				ldapUsers = append(ldapUsers, ldap.User{
					UserID: uaaUser.Username,
					UserDN: userDN,
					Email:  uaaUser.Email,
				})
			} else {
				lo.G.Infof("UserDN [%s] not found in UAA, executing ldap lookup", userDN)
				user, err := m.LdapMgr.GetUserByDN(userDN)
				if err != nil {
					return nil, err
				}
				if user != nil {
					ldapUsers = append(ldapUsers, *user)
				} else {
					lo.G.Infof("UserDN %s not found in ldap", userDN)
				}
			}
		}
	}
	for _, userID := range usersInput.LdapUsers {
		userList := uaaUsers.GetByName(userID)
		if len(userList) > 0 {
			lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID)
			for _, uaaUser := range userList {
				if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) {
					ldapUsers = append(ldapUsers, ldap.User{
						UserID: userID,
						UserDN: uaaUser.ExternalID,
						Email:  uaaUser.Email,
					})
				}
			}
		} else {
			lo.G.Infof("User [%s] not found in UAA, executing ldap lookup", userID)
			user, err := m.LdapMgr.GetUserByID(userID)
			if err != nil {
				return nil, err
			}
			if user != nil {
				ldapUsers = append(ldapUsers, *user)
			} else {
				lo.G.Infof("User %s not found in ldap", userID)
			}
		}
	}
	return ldapUsers, nil
}

func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User {
	userID := strings.ToLower(user.UserID)
	externalID := user.UserDN
	email := user.Email
	if email == "" {
		email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
	}

	return ldap.User{
		UserID: userID,
		UserDN: externalID,
		Email:  email,
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mobile

import (
	"github.com/hajimehoshi/ebiten"
)

// Start starts the game and returns immediately.
//
// Different from ebiten.Run, this invokes only the game loop and not the main (UI) loop.
func Start(f func(*ebiten.Image) error, width, height int, scale float64, title string) error {
	return start(f, width, height, scale, title)
}

// Update updates and renders the game.
//
// This should be called on every frame.
//
// On Android, this should be called at onDrawFrame of Renderer (used by GLSurfaceView).
//
// On iOS, this should be called at glkView:drawInRect: of GLKViewDelegate.
func Update() error {
	return update()
}

// UpdateTouchesOnAndroid updates the touch state on Android.
//
// This should be called with onTouchEvent of GLSurfaceView like this:
//
//     @Override
//     public boolean onTouchEvent(MotionEvent e) {
//         for (int i = 0; i < e.getPointerCount(); i++) {
//             int id = e.getPointerId(i);
//             int x = (int)e.getX(i);
//             int y = (int)e.getY(i);
//             YourGame.UpdateTouchesOnAndroid(e.getActionMasked(), id, x, y);
//         }
//         return true;
//     }
func UpdateTouchesOnAndroid(action int, id int, x, y int) {
	updateTouchesOnAndroid(action, id, x, y)
}

func UpdateTouchesOnIOS(phase int, ptr int64, x, y int) {
	updateTouchesOnIOSImpl(phase, ptr, x, y)
}
<commit_msg>mobile: Update a comment about units<commit_after>// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mobile

import (
	"github.com/hajimehoshi/ebiten"
)

// Start starts the game and returns immediately.
//
// Different from ebiten.Run, this invokes only the game loop and not the main (UI) loop.
//
// On Android, width/height is in pixels (px).
//
// On iOS, width/height is in points.
func Start(f func(*ebiten.Image) error, width, height int, scale float64, title string) error {
	return start(f, width, height, scale, title)
}

// Update updates and renders the game.
//
// This should be called on every frame.
//
// On Android, this should be called at onDrawFrame of Renderer (used by GLSurfaceView).
//
// On iOS, this should be called at glkView:drawInRect: of GLKViewDelegate.
func Update() error {
	return update()
}

// UpdateTouchesOnAndroid updates the touch state on Android.
//
// This should be called with onTouchEvent of GLSurfaceView like this:
//
//     @Override
//     public boolean onTouchEvent(MotionEvent e) {
//         for (int i = 0; i < e.getPointerCount(); i++) {
//             int id = e.getPointerId(i);
//             int x = (int)e.getX(i);
//             int y = (int)e.getY(i);
//             YourGame.UpdateTouchesOnAndroid(e.getActionMasked(), id, x, y);
//         }
//         return true;
//     }
func UpdateTouchesOnAndroid(action int, id int, x, y int) {
	updateTouchesOnAndroid(action, id, x, y)
}

func UpdateTouchesOnIOS(phase int, ptr int64, x, y int) {
	updateTouchesOnIOSImpl(phase, ptr, x, y)
}
<|endoftext|>


<commit_before>package user

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/pkg/errors"
	"github.com/xchapter7x/lo"
)

func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error {
	origin := m.LdapConfig.Origin
	if m.LdapConfig.Enabled {
		ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %+v", ldapUsers)
		for _, inputUser := range ldapUsers {
			userToUse := m.UpdateUserInfo(inputUser)
			userID := userToUse.UserID
			userList := uaaUsers.GetByName(userID)
			if len(userList) == 0 {
				lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
				if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil {
					lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error())
					continue
				} else {
					uaaUsers.Add(uaa.User{
						Username:   userID,
						ExternalID: userToUse.UserDN,
						Origin:     m.LdapConfig.Origin,
						Email:      userToUse.Email,
						GUID:       userGUID,
					})
				}
			}
			if !roleUsers.HasUserForOrigin(userID, origin) {
				user := uaaUsers.GetByNameAndOrigin(userID, origin)
				if user == nil {
					return fmt.Errorf("Unabled to find user %s for origin %s", userID, origin)
				}
				if err := usersInput.AddUser(usersInput, user.Username, user.GUID); err != nil {
					return errors.Wrap(err, fmt.Sprintf("User %s with origin %s", user.Username, user.Origin))
				}
			} else {
				lo.G.Debugf("User[%s] found in role", userID)
				roleUsers.RemoveUserForOrigin(userID, origin)
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	return nil
}

func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) {
	var ldapUsers []ldap.User
	for _, groupName := range usersInput.UniqueLdapGroupNames() {
		userDNList, err := m.LdapMgr.GetUserDNs(groupName)
		if err != nil {
			return nil, err
		}
		for _, userDN := range userDNList {
			lo.G.Debugf("Checking for userDN %s", userDN)
			uaaUser := uaaUsers.GetByExternalID(userDN)
			if uaaUser != nil {
				lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username)
				ldapUsers = append(ldapUsers, ldap.User{
					UserID: uaaUser.Username,
					UserDN: userDN,
					Email:  uaaUser.Email,
				})
			} else {
				lo.G.Debugf("UserDN [%s] not found in UAA, executing ldap lookup", userDN)
				user, err := m.LdapMgr.GetUserByDN(userDN)
				if err != nil {
					return nil, err
				}
				if user != nil {
					ldapUsers = append(ldapUsers, *user)
				} else {
					lo.G.Infof("UserDN %s not found in ldap", userDN)
				}
			}
		}
	}
	for _, userID := range usersInput.LdapUsers {
		userList := uaaUsers.GetByName(userID)
		if len(userList) > 0 {
			lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID)
			for _, uaaUser := range userList {
				lo.G.Debugf("Checking if UserID [%s] with origin [%s] and externalID [%s] matches ldap origin", uaaUser.Username, uaaUser.Origin, uaaUser.ExternalID)
				if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) {
					ldapUsers = append(ldapUsers, ldap.User{
						UserID: userID,
						UserDN: uaaUser.ExternalID,
						Email:  uaaUser.Email,
					})
				}
			}
		} else {
			lo.G.Debugf("User [%s] not found in UAA, executing ldap lookup", userID)
			user, err := m.LdapMgr.GetUserByID(userID)
			if err != nil {
				return nil, err
			}
			if user != nil {
				ldapUsers = append(ldapUsers, *user)
			} else {
				lo.G.Infof("User %s not found in ldap", userID)
			}
		}
	}

	uniqueLDAPUsers := make(map[string]ldap.User)
	for _, ldapUser := range ldapUsers {
		uniqueLDAPUsers[strings.ToUpper(ldapUser.UserDN)] = ldapUser
	}

	ldapUsersToReturn := []ldap.User{}

	for _, uniqueLDAPUser := range uniqueLDAPUsers {
		ldapUsersToReturn = append(ldapUsersToReturn, uniqueLDAPUser)
	}
	return ldapUsersToReturn, nil
}

func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User {
	userID := strings.ToLower(user.UserID)
	externalID := user.UserDN
	email := user.Email
	if email == "" {
		email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
	}

	return ldap.User{
		UserID: userID,
		UserDN: externalID,
		Email:  email,
	}
}
<commit_msg>update to log information about ldap users origin<commit_after>package user

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/pkg/errors"
	"github.com/xchapter7x/lo"
)

func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error {
	origin := m.LdapConfig.Origin
	if m.LdapConfig.Enabled {
		ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %+v", ldapUsers)
		for _, inputUser := range ldapUsers {
			userToUse := m.UpdateUserInfo(inputUser)
			userID := userToUse.UserID
			userList := uaaUsers.GetByName(userID)
			if len(userList) == 0 {
				lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
				if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil {
					lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error())
					continue
				} else {
					uaaUsers.Add(uaa.User{
						Username:   userID,
						ExternalID: userToUse.UserDN,
						Origin:     m.LdapConfig.Origin,
						Email:      userToUse.Email,
						GUID:       userGUID,
					})
				}
			}
			if !roleUsers.HasUserForOrigin(userID, origin) {
				user := uaaUsers.GetByNameAndOrigin(userID, origin)
				if user == nil {
					return fmt.Errorf("Unabled to find user %s for origin %s", userID, origin)
				}
				if err := usersInput.AddUser(usersInput, user.Username, user.GUID); err != nil {
					return errors.Wrap(err, fmt.Sprintf("User %s with origin %s", user.Username, user.Origin))
				}
			} else {
				lo.G.Debugf("User[%s] found in role", userID)
				roleUsers.RemoveUserForOrigin(userID, origin)
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	return nil
}

func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) {
	var ldapUsers []ldap.User
	for _, groupName := range usersInput.UniqueLdapGroupNames() {
		userDNList, err := m.LdapMgr.GetUserDNs(groupName)
		if err != nil {
			return nil, err
		}
		for _, userDN := range userDNList {
			lo.G.Debugf("Checking for userDN %s", userDN)
			uaaUser := uaaUsers.GetByExternalID(userDN)
			if uaaUser != nil {
				lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username)
				ldapUsers = append(ldapUsers, ldap.User{
					UserID: uaaUser.Username,
					UserDN: userDN,
					Email:  uaaUser.Email,
				})
			} else {
				lo.G.Debugf("UserDN [%s] not found in UAA, executing ldap lookup", userDN)
				user, err := m.LdapMgr.GetUserByDN(userDN)
				if err != nil {
					return nil, err
				}
				if user != nil {
					ldapUsers = append(ldapUsers, *user)
				} else {
					lo.G.Infof("UserDN %s not found in ldap", userDN)
				}
			}
		}
	}
	for _, userID := range usersInput.LdapUsers {
		userList := uaaUsers.GetByName(userID)
		if len(userList) > 0 {
			lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID)
			for _, uaaUser := range userList {
				if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) {
					ldapUsers = append(ldapUsers, ldap.User{
						UserID: userID,
						UserDN: uaaUser.ExternalID,
						Email:  uaaUser.Email,
					})
				} else {
					lo.G.Debugf("UserID [%s] with origin [%s] and externalID [%s] does not match ldap origin", uaaUser.Username, uaaUser.Origin, uaaUser.ExternalID)
				}
			}
		} else {
			lo.G.Debugf("User [%s] not found in UAA, executing ldap lookup", userID)
			user, err := m.LdapMgr.GetUserByID(userID)
			if err != nil {
				return nil, err
			}
			if user != nil {
				ldapUsers = append(ldapUsers, *user)
			} else {
				lo.G.Infof("User %s not found in ldap", userID)
			}
		}
	}

	uniqueLDAPUsers := make(map[string]ldap.User)
	for _, ldapUser := range ldapUsers {
		uniqueLDAPUsers[strings.ToUpper(ldapUser.UserDN)] = ldapUser
	}

	ldapUsersToReturn := []ldap.User{}

	for _, uniqueLDAPUser := range uniqueLDAPUsers {
		ldapUsersToReturn = append(ldapUsersToReturn, uniqueLDAPUser)
	}
	return ldapUsersToReturn, nil
}

func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User {
	userID := strings.ToLower(user.UserID)
	externalID := user.UserDN
	email := user.Email
	if email == "" {
		email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
	}

	return ldap.User{
		UserID: userID,
		UserDN: externalID,
		Email:  email,
	}
}
<|endoftext|>


<commit_before>package workflows

import (
	"testing"

	"github.com/stelligent/mu/common"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

func TestNewServiceDeployer(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	ctx.Config.Namespace = "mu"
	deploye := NewServiceDeployer(ctx, "dev", "foo")
	assert.NotNil(deploye)
}

type mockedElbManager struct {
	mock.Mock
}

func (m *mockedElbManager) ListRules(listenerArn string) ([]common.ElbRule, error) {
	args := m.Called(listenerArn)
	return args.Get(0).([]common.ElbRule), nil
}

func TestServiceApplyCommon_Create(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForUpsert)
	outputs := make(map[string]string)
	outputs["ElbHttpListenerArn"] = "foo"
	outputs["ElbHttpsListenerArn"] = "foo"

	stackManager.On("AwaitFinalStatus", "mu-service-myservice-dev").Return(nil).Once()
	stackManager.On("AwaitFinalStatus", "mu-database-myservice-dev").Return(nil).Once()

	paramManager := new(mockedParamManager)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	service := new(common.Service)
	params := make(map[string]string)
	workflow := new(serviceWorkflow)
	workflow.serviceName = "myservice"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	err := workflow.serviceApplyCommonParams("mu", service, params, "dev", stackManager, elbRuleLister, paramManager)()
	assert.Nil(err)

	assert.Equal("mu-environment-dev-VpcId", params["VpcId"])
	assert.Equal("mu-loadbalancer-dev-ElbHttpListenerArn", params["ElbHttpListenerArn"])
	assert.Equal("mu-loadbalancer-dev-ElbHttpsListenerArn", params["ElbHttpsListenerArn"])
	assert.Equal("16", params["PathListenerRulePriority"])

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)
}

func TestServiceApplyCommon_Update(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForUpsert)
	outputs := make(map[string]string)
	outputs["ElbHttpListenerArn"] = "foo"
	outputs["ElbHttpsListenerArn"] = "foo"
	stackManager.On("AwaitFinalStatus", "mu-service-myservice-dev").Return(&common.Stack{Status: common.StackStatusCreateComplete, Outputs: outputs}).Once()
	stackManager.On("AwaitFinalStatus", "mu-database-myservice-dev").Return(nil).Once()

	paramManager := new(mockedParamManager)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	service := new(common.Service)
	params := make(map[string]string)
	workflow := new(serviceWorkflow)
	workflow.serviceName = "myservice"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	err := workflow.serviceApplyCommonParams("mu", service, params, "dev", stackManager, elbRuleLister, paramManager)()
	assert.Nil(err)

	assert.Equal("", params["ListenerRulePriority"])

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)
}
func TestServiceApplyCommon_StaticPriority(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForUpsert)
	outputs := make(map[string]string)
	outputs["ElbHttpListenerArn"] = "foo"
	outputs["ElbHttpsListenerArn"] = "foo"
	stackManager.On("AwaitFinalStatus", "mu-service-myservice-dev").Return(&common.Stack{Status: common.StackStatusCreateComplete, Outputs: outputs}).Once()
	stackManager.On("AwaitFinalStatus", "mu-database-myservice-dev").Return(nil).Once()

	paramManager := new(mockedParamManager)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	service := new(common.Service)
	params := make(map[string]string)
	workflow := new(serviceWorkflow)
	workflow.serviceName = "myservice"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.priority = 77
	err := workflow.serviceApplyCommonParams("mu", service, params, "dev", stackManager, elbRuleLister, paramManager)()
	assert.Nil(err)

	assert.Equal("77", params["PathListenerRulePriority"])

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)
}

func TestServiceEnvLoader_NotFound(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForService)
	stackManager.On("AwaitFinalStatus", "mu-environment-dev").Return(nil).Once()
	stackManager.On("AwaitFinalStatus", "mu-loadbalancer-dev").Return(nil).Once()

	workflow := new(serviceWorkflow)
	err := workflow.serviceEnvironmentLoader("mu", "dev", stackManager)()

	assert.NotNil(err)

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
}

func TestServiceGetMaxPriority(t *testing.T) {
	assert := assert.New(t)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	max := getMaxPriority(elbRuleLister, "foo")

	assert.Equal(15, max)

	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)

}

func TestServiceEcsDeployer(t *testing.T) {
	assert := assert.New(t)

	stackManager := new(mockedStackManagerForService)
	stackManager.On("AwaitFinalStatus", "mu-service-foo-dev").Return(&common.Stack{Status: common.StackStatusCreateComplete})
	stackManager.On("UpsertStack", "mu-service-foo-dev").Return(nil)

	config := new(common.Config)
	config.Service.Name = "foo"

	params := make(map[string]string)

	workflow := new(serviceWorkflow)
	workflow.serviceName = "foo"
	outputs := make(map[string]string)
	outputs["provider"] = "ecs"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	err := workflow.serviceEcsDeployer("mu", &config.Service, params, "dev", stackManager, stackManager)()
	assert.Nil(err)

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 1)
	stackManager.AssertNumberOfCalls(t, "UpsertStack", 1)

}

func TestServiceEksDeployer(t *testing.T) {
	panic("TODO: implement this test")
}

func stringRef(v string) *string {
	return &v
}

func TestServiceDeployer_serviceRolesetUpserter(t *testing.T) {
	assert := assert.New(t)
	rolesetManager := new(mockedRolesetManagerForService)

	rolesetManager.On("UpsertCommonRoleset").Return(nil)
	rolesetManager.On("GetCommonRoleset").Return(common.Roleset{"CloudFormationRoleArn": "bar"}, nil)
	rolesetManager.On("UpsertServiceRoleset", "env1", "svc20", "").Return(nil)
	rolesetManager.On("GetServiceRoleset").Return(common.Roleset{"EcsEventsRoleArn": "bar"}, nil)

	workflow := new(serviceWorkflow)
	workflow.serviceName = "svc20"
	err := workflow.serviceRolesetUpserter(rolesetManager, rolesetManager, "env1")()
	assert.Nil(err)
	assert.Equal("bar", workflow.cloudFormationRoleArn)

	rolesetManager.AssertExpectations(t)
	rolesetManager.AssertNumberOfCalls(t, "UpsertCommonRoleset", 1)
	rolesetManager.AssertNumberOfCalls(t, "GetCommonRoleset", 1)
	rolesetManager.AssertNumberOfCalls(t, "UpsertServiceRoleset", 1)

}
<commit_msg>create a basic test for service_deploy:serviceEksDeployer to ensure UpsertResources is called<commit_after>package workflows

import (
	"testing"

	"github.com/stelligent/mu/common"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func TestNewServiceDeployer(t *testing.T) {
	assert := assert.New(t)
	ctx := common.NewContext()
	ctx.Config.Namespace = "mu"
	deployer := NewServiceDeployer(ctx, "dev", "foo")
	assert.NotNil(deployer)
}

type mockedElbManager struct {
	mock.Mock
}

func (m *mockedElbManager) ListRules(listenerArn string) ([]common.ElbRule, error) {
	args := m.Called(listenerArn)
	return args.Get(0).([]common.ElbRule), nil
}

func TestServiceApplyCommon_Create(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForUpsert)
	outputs := make(map[string]string)
	outputs["ElbHttpListenerArn"] = "foo"
	outputs["ElbHttpsListenerArn"] = "foo"

	stackManager.On("AwaitFinalStatus", "mu-service-myservice-dev").Return(nil).Once()
	stackManager.On("AwaitFinalStatus", "mu-database-myservice-dev").Return(nil).Once()

	paramManager := new(mockedParamManager)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	service := new(common.Service)
	params := make(map[string]string)
	workflow := new(serviceWorkflow)
	workflow.serviceName = "myservice"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	err := workflow.serviceApplyCommonParams("mu", service, params, "dev", stackManager, elbRuleLister, paramManager)()
	assert.Nil(err)

	assert.Equal("mu-environment-dev-VpcId", params["VpcId"])
	assert.Equal("mu-loadbalancer-dev-ElbHttpListenerArn", params["ElbHttpListenerArn"])
	assert.Equal("mu-loadbalancer-dev-ElbHttpsListenerArn", params["ElbHttpsListenerArn"])
	assert.Equal("16", params["PathListenerRulePriority"])

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)
}

func TestServiceApplyCommon_Update(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForUpsert)
	outputs := make(map[string]string)
	outputs["ElbHttpListenerArn"] = "foo"
	outputs["ElbHttpsListenerArn"] = "foo"
	stackManager.On("AwaitFinalStatus", "mu-service-myservice-dev").Return(&common.Stack{Status: common.StackStatusCreateComplete, Outputs: outputs}).Once()
	stackManager.On("AwaitFinalStatus", "mu-database-myservice-dev").Return(nil).Once()

	paramManager := new(mockedParamManager)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	service := new(common.Service)
	params := make(map[string]string)
	workflow := new(serviceWorkflow)
	workflow.serviceName = "myservice"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	err := workflow.serviceApplyCommonParams("mu", service, params, "dev", stackManager, elbRuleLister, paramManager)()
	assert.Nil(err)

	assert.Equal("", params["ListenerRulePriority"])

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)
}
func TestServiceApplyCommon_StaticPriority(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForUpsert)
	outputs := make(map[string]string)
	outputs["ElbHttpListenerArn"] = "foo"
	outputs["ElbHttpsListenerArn"] = "foo"
	stackManager.On("AwaitFinalStatus", "mu-service-myservice-dev").Return(&common.Stack{Status: common.StackStatusCreateComplete, Outputs: outputs}).Once()
	stackManager.On("AwaitFinalStatus", "mu-database-myservice-dev").Return(nil).Once()

	paramManager := new(mockedParamManager)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	service := new(common.Service)
	params := make(map[string]string)
	workflow := new(serviceWorkflow)
	workflow.serviceName = "myservice"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.priority = 77
	err := workflow.serviceApplyCommonParams("mu", service, params, "dev", stackManager, elbRuleLister, paramManager)()
	assert.Nil(err)

	assert.Equal("77", params["PathListenerRulePriority"])

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)
}

func TestServiceEnvLoader_NotFound(t *testing.T) {
	assert := assert.New(t)
	stackManager := new(mockedStackManagerForService)
	stackManager.On("AwaitFinalStatus", "mu-environment-dev").Return(nil).Once()
	stackManager.On("AwaitFinalStatus", "mu-loadbalancer-dev").Return(nil).Once()

	workflow := new(serviceWorkflow)
	err := workflow.serviceEnvironmentLoader("mu", "dev", stackManager)()

	assert.NotNil(err)

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 2)
}

func TestServiceGetMaxPriority(t *testing.T) {
	assert := assert.New(t)

	elbRuleLister := new(mockedElbManager)
	elbRuleLister.On("ListRules", "foo").Return([]common.ElbRule{
		{Priority: stringRef("15")},
		{Priority: stringRef("5")},
		{Priority: stringRef("10")},
	})

	max := getMaxPriority(elbRuleLister, "foo")

	assert.Equal(15, max)

	elbRuleLister.AssertExpectations(t)
	elbRuleLister.AssertNumberOfCalls(t, "ListRules", 1)

}

func TestServiceEcsDeployer(t *testing.T) {
	assert := assert.New(t)

	stackManager := new(mockedStackManagerForService)
	stackManager.On("AwaitFinalStatus", "mu-service-foo-dev").Return(&common.Stack{Status: common.StackStatusCreateComplete})
	stackManager.On("UpsertStack", "mu-service-foo-dev").Return(nil)

	config := new(common.Config)
	config.Service.Name = "foo"

	params := make(map[string]string)

	workflow := new(serviceWorkflow)
	workflow.serviceName = "foo"
	outputs := make(map[string]string)
	outputs["provider"] = "ecs"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	err := workflow.serviceEcsDeployer("mu", &config.Service, params, "dev", stackManager, stackManager)()
	assert.Nil(err)

	stackManager.AssertExpectations(t)
	stackManager.AssertNumberOfCalls(t, "AwaitFinalStatus", 1)
	stackManager.AssertNumberOfCalls(t, "UpsertStack", 1)

}

type mockKubernetesResourceManager struct {
	mock.Mock
}

func (m *mockKubernetesResourceManager) UpsertResources(templateName string, templateData interface{}) error {
	args := m.Called(templateName)
	return args.Error(0)
}

func (m *mockKubernetesResourceManager) ListResources(apiVersion string, kind string, namespace string) (*unstructured.UnstructuredList, error) {
	args := m.Called(apiVersion)
	stack := args.Get(0)
	if stack == nil {
		return nil, args.Error(0)
	}
	return stack.(*unstructured.UnstructuredList), args.Error(0)
}

func (m *mockKubernetesResourceManager) DeleteResource(apiVersion string, kind string, namespace string, name string) error {
	args := m.Called(apiVersion)
	return args.Error(0)
}

func TestServiceEksDeployer(t *testing.T) {
	assert := assert.New(t)

	// from workflows/service_common_test.go
	kubernetesResourceManager := new(mockKubernetesResourceManager)
	kubernetesResourceManager.On("UpsertResources", "kubernetes/deployment.yml").Return(nil)

	config := new(common.Config)
	config.Service.Name = "foo"

	params := make(map[string]string)

	workflow := new(serviceWorkflow)
	workflow.serviceName = "foo"
	outputs := make(map[string]string)
	outputs["provider"] = "eks"
	workflow.envStack = &common.Stack{Name: "mu-environment-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.lbStack = &common.Stack{Name: "mu-loadbalancer-dev", Status: common.StackStatusCreateComplete, Outputs: outputs}
	workflow.kubernetesResourceManager = kubernetesResourceManager
	// err := workflow.serviceEcsDeployer("mu", &config.Service, params, "dev", stackManager, stackManager)()
	err := workflow.serviceEksDeployer("mu", &config.Service, params, "dev")()
	assert.Nil(err)

	kubernetesResourceManager.AssertExpectations(t)
	kubernetesResourceManager.AssertNumberOfCalls(t, "UpsertResources", 1)
}

func stringRef(v string) *string {
	return &v
}

func TestServiceDeployer_serviceRolesetUpserter(t *testing.T) {
	assert := assert.New(t)
	rolesetManager := new(mockedRolesetManagerForService)

	rolesetManager.On("UpsertCommonRoleset").Return(nil)
	rolesetManager.On("GetCommonRoleset").Return(common.Roleset{"CloudFormationRoleArn": "bar"}, nil)
	rolesetManager.On("UpsertServiceRoleset", "env1", "svc20", "").Return(nil)
	rolesetManager.On("GetServiceRoleset").Return(common.Roleset{"EcsEventsRoleArn": "bar"}, nil)

	workflow := new(serviceWorkflow)
	workflow.serviceName = "svc20"
	err := workflow.serviceRolesetUpserter(rolesetManager, rolesetManager, "env1")()
	assert.Nil(err)
	assert.Equal("bar", workflow.cloudFormationRoleArn)

	rolesetManager.AssertExpectations(t)
	rolesetManager.AssertNumberOfCalls(t, "UpsertCommonRoleset", 1)
	rolesetManager.AssertNumberOfCalls(t, "GetCommonRoleset", 1)
	rolesetManager.AssertNumberOfCalls(t, "UpsertServiceRoleset", 1)

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/shenwei356/cnote/cnotedb"
)

var (
	funcs  map[string]func(c *cli.Context)
	DBFILE string
	notedb *cnotedb.NoteDB
)

func init() {
	// DBFILE
	usr, err := user.Current()
	if err != nil {
		os.Exit(1)
	}
	DBFILE = filepath.Join(usr.HomeDir, ".cnote")

	funcs = make(map[string]func(c *cli.Context))
	funcs["new"] = funNew
	funcs["del"] = funDel
	funcs["use"] = funUse
	funcs["list"] = funLs

	funcs["add"] = funAdd
	funcs["rm"] = funRm

	funcs["tag"] = funTag
	funcs["search"] = funSearch

	funcs["dump"] = funDump
	funcs["wipe"] = funWipe
	funcs["restore"] = funRestore
	funcs["import"] = funImport

}

func getFunc(funcs map[string]func(c *cli.Context), name string) func(c *cli.Context) {
	if f, ok := funcs[name]; ok {
		return f
	} else {
		return func(c *cli.Context) {
			fmt.Printf("command %s not implemented\n", name)
		}
	}
}

func funLs(c *cli.Context) {
	if len(c.Args()) > 0 {
		fmt.Println("no arguments should be given.")
		return
	}

	for _, notename := range notedb.NotesList {

		// read note
		note, err := notedb.ReadNote(notename)
		if err != nil {
			fmt.Println(err)
			os.Exit(0)
		}

		fmt.Printf("note: %s\t(#. of items: %d, last update: %s).",
			notename, note.Sum, note.LastUpdate)
		if notedb.CurrentNote != nil &&
			notename == notedb.CurrentNote.NoteID {

			fmt.Printf(" (current note)")
		}
		fmt.Println()
	}
}

func funNew(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("note name needed.")
		return
	}
	if len(c.Args()) > 1 {
		fmt.Println("only one note name allowed.")
		return
	}

	notename := c.Args().First()

	err := notedb.NewNote(notename)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("note \"%s\" created.\n", notename)
	fmt.Printf("current note: \"%s\".\n", notename)
}

func funDel(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("note name needed.")
		return
	}

	for _, notename := range c.Args() {
		err := notedb.DeleteNote(notename)
		if err != nil {
			fmt.Println(err)
			continue
		}

		// fmt.Printf("note \"%s\" deleted.\n", notename)
	}
}

func funUse(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("note name needed.")
		return
	}
	if len(c.Args()) > 1 {
		fmt.Println("only one note name allowed.")
		return
	}

	notename := c.Args().First()
	err := notedb.UseNote(notename)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("current note: \"%s\" (last update: %s).\n",
		notename, notedb.CurrentNote.LastUpdate)
}

func funAdd(c *cli.Context) {
	if len(c.Args()) != 2 {
		fmt.Println("tag and content needed.")
		return
	}

	item, err := notedb.AddNoteItem(c.Args()[0], c.Args()[1])
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(item)
}

func funRm(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("item ID needed.")
		return
	}

	for _, itemid := range c.Args() {

		itemid, err := strconv.Atoi(itemid)
		if err != nil {
			fmt.Println("item ID should be integer.")
			continue
		}

		err = notedb.RemoveNoteItem(notedb.CurrentNote, itemid)
		if err != nil {
			fmt.Println(err)
			continue
		}

		// fmt.Printf("note item \"%d\" deleted from note \"%s\".\n", itemid, notedb.Currentcnote.NoteID)
	}
}

func funTag(c *cli.Context) {
	// list all tags
	note, err := notedb.GetCurrentNote()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if len(c.Args()) == 0 {
		for tag, taginfo := range note.Tags {
			fmt.Printf("tag: %s\t(#. of items: %d).\n", tag, len(taginfo))
		}
		return
	}

	items, err := notedb.ItemByTag(c.Args())
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	for _, item := range items {
		fmt.Println(item)
	}
}

func funSearch(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("search keyword needed.")
		return
	}

	items, err := notedb.ItemByRegexp(c.Args())
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	for _, item := range items {
		fmt.Println(item)
	}
}

func funDump(c *cli.Context) {
	if len(c.Args()) > 0 {
		fmt.Println("no arguments should be given.")
		return
	}

	err := notedb.Dump()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func request_reply(reply string) (bool, error) {
	fmt.Printf("Attention, it will clear all the data."+
		" type \"%s\" to continue:", reply)

	reader := bufio.NewReader(os.Stdin)
	str, err := reader.ReadString('\n')
	if err != nil {
		return false, err
	}

	str = regexp.MustCompile(`[\r\n]`).ReplaceAllString(str, "")
	str = regexp.MustCompile(`^\s+|\s+$`).ReplaceAllString(str, "")
	if str != "yes" {
		fmt.Println("\ngiven up.")
		return false, nil
	}
	return true, nil
}

func funWipe(c *cli.Context) {
	if len(c.Args()) > 0 {
		fmt.Println("no arguments should be given.")
		return
	}

	reply, err := request_reply("yes")
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if reply == false {
		return
	}

	err = notedb.Wipe()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func funRestore(c *cli.Context) {
	if len(c.Args()) != 1 {
		fmt.Println("dumpped filename needed.")
		return
	}

	reply, err := request_reply("yes")
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if reply == false {
		return
	}

	err = notedb.Restore(c.Args().First())
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func funImport(c *cli.Context) {
	if len(c.Args()) != 3 {
		fmt.Println("three arguments needed: <notename in your cnote>" +
			" <notename in dumpped note> <dumpped filename>.")
		return
	}
	notename, othernotename, filename := c.Args()[0], c.Args()[1], c.Args()[2]
	n, err := notedb.Import(notename, othernotename, filename)
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	fmt.Printf("%d items imported into note \"%s\".\n", n, notename)
}

func main() {
	notedb = cnotedb.NewNoteDB(DBFILE)
	defer notedb.Close()

	app := cli.NewApp()
	app.Name = "cnote"
	app.Usage = "A command line note app. https://github.com/shenwei356/cnote"
	app.Version = "1.1 (2014-07-20)"
	app.Author = "Wei Shen"
	app.Email = "shenwei356@gmail.com"

	app.Commands = []cli.Command{
		{
			Name:   "new",
			Usage:  "Create a new note",
			Action: getFunc(funcs, "new"),
		},
		{
			Name:   "del",
			Usage:  "Delete a note",
			Action: getFunc(funcs, "del"),
		},
		{
			Name:   "use",
			Usage:  "Select a note",
			Action: getFunc(funcs, "use"),
		},
		{
			Name:      "list",
			ShortName: "ls",
			Usage:     "List all notes",
			Action:    getFunc(funcs, "list"),
		},
		{
			Name:   "add",
			Usage:  "Add a note item",
			Action: getFunc(funcs, "add"),
		},
		{
			Name:   "rm",
			Usage:  "Remove a note item",
			Action: getFunc(funcs, "rm"),
		},
		{
			Name:      "tag",
			ShortName: "t",
			Usage:     "List items by tags. List all tags if no arguments given",
			Action:    getFunc(funcs, "tag"),
		},
		{
			Name:      "search",
			ShortName: "s",
			Usage:     "Search items with regular expression",
			Action:    getFunc(funcs, "search"),
		},
		{
			Name:   "dump",
			Usage:  "Dump whole database, for backup or transfer",
			Action: getFunc(funcs, "dump"),
		},
		{
			Name:   "wipe",
			Usage:  "Attention! Wipe whole database",
			Action: getFunc(funcs, "wipe"),
		},
		{
			Name:   "restore",
			Usage:  "Wipe whole database, and restore from dumpped file",
			Action: getFunc(funcs, "restore"),
		},
		{
			Name:   "import",
			Usage:  "Import note items from dumpped data",
			Action: getFunc(funcs, "import"),
		},
	}

	app.Run(os.Args)
}
<commit_msg>format the confirmation for dangerous operation, deleting a note also need confirmation.<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/codegangsta/cli"
	"github.com/shenwei356/cnote/cnotedb"
)

var (
	funcs  map[string]func(c *cli.Context)
	DBFILE string
	notedb *cnotedb.NoteDB
)

func init() {
	// DBFILE
	usr, err := user.Current()
	if err != nil {
		os.Exit(1)
	}
	DBFILE = filepath.Join(usr.HomeDir, ".cnote")

	funcs = make(map[string]func(c *cli.Context))
	funcs["new"] = funNew
	funcs["del"] = funDel
	funcs["use"] = funUse
	funcs["list"] = funLs

	funcs["add"] = funAdd
	funcs["rm"] = funRm

	funcs["tag"] = funTag
	funcs["search"] = funSearch

	funcs["dump"] = funDump
	funcs["wipe"] = funWipe
	funcs["restore"] = funRestore
	funcs["import"] = funImport

}

func request_reply(message, reply string) (bool, error) {
	fmt.Printf(message, reply)

	reader := bufio.NewReader(os.Stdin)
	str, err := reader.ReadString('\n')
	if err != nil {
		return false, err
	}

	str = regexp.MustCompile(`[\r\n]`).ReplaceAllString(str, "")
	str = regexp.MustCompile(`^\s+|\s+$`).ReplaceAllString(str, "")
	if str != "yes" {
		fmt.Println("\ngiven up.")
		return false, nil
	}

	return true, nil
}

func getFunc(funcs map[string]func(c *cli.Context), name string) func(c *cli.Context) {
	if f, ok := funcs[name]; ok {
		return f
	} else {
		return func(c *cli.Context) {
			fmt.Printf("command %s not implemented\n", name)
		}
	}
}

func funLs(c *cli.Context) {
	if len(c.Args()) > 0 {
		fmt.Println("no arguments should be given.")
		return
	}

	for _, notename := range notedb.NotesList {

		// read note
		note, err := notedb.ReadNote(notename)
		if err != nil {
			fmt.Println(err)
			os.Exit(0)
		}

		fmt.Printf("note: %s\t(#. of items: %d, last update: %s).",
			notename, note.Sum, note.LastUpdate)
		if notedb.CurrentNote != nil &&
			notename == notedb.CurrentNote.NoteID {

			fmt.Printf(" (current note)")
		}
		fmt.Println()
	}
}

func funNew(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("note name needed.")
		return
	}
	if len(c.Args()) > 1 {
		fmt.Println("only one note name allowed.")
		return
	}

	notename := c.Args().First()

	err := notedb.NewNote(notename)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("note \"%s\" created.\n", notename)
	fmt.Printf("current note: \"%s\".\n", notename)
}

func funDel(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("note name needed.")
		return
	}
	notename := c.Args().First()

	note, err := notedb.ReadNote(notename)
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	reply, err := request_reply(
		fmt.Sprintf("==============================================================\n"+
			" Attention, it will delete all the %d items of note \"%s\".\n"+
			"==============================================================\n",
			note.Sum, notename)+
			" Type \"%s\" to continue:",
		"yes")
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if reply == false {
		return
	}

	err = notedb.DeleteNote(notename)
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func funUse(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("note name needed.")
		return
	}
	if len(c.Args()) > 1 {
		fmt.Println("only one note name allowed.")
		return
	}

	notename := c.Args().First()
	err := notedb.UseNote(notename)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("current note: \"%s\" (last update: %s).\n",
		notename, notedb.CurrentNote.LastUpdate)
}

func funAdd(c *cli.Context) {
	if len(c.Args()) != 2 {
		fmt.Println("tag and content needed.")
		return
	}

	item, err := notedb.AddNoteItem(c.Args()[0], c.Args()[1])
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(item)
}

func funRm(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("item ID needed.")
		return
	}

	for _, itemid := range c.Args() {

		itemid, err := strconv.Atoi(itemid)
		if err != nil {
			fmt.Println("item ID should be integer.")
			continue
		}

		err = notedb.RemoveNoteItem(notedb.CurrentNote, itemid)
		if err != nil {
			fmt.Println(err)
			continue
		}

		// fmt.Printf("note item \"%d\" deleted from note \"%s\".\n", itemid, notedb.Currentcnote.NoteID)
	}
}

func funTag(c *cli.Context) {
	// list all tags
	note, err := notedb.GetCurrentNote()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if len(c.Args()) == 0 {
		for tag, taginfo := range note.Tags {
			fmt.Printf("tag: %s\t(#. of items: %d).\n", tag, len(taginfo))
		}
		return
	}

	items, err := notedb.ItemByTag(c.Args())
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	for _, item := range items {
		fmt.Println(item)
	}
}

func funSearch(c *cli.Context) {
	if len(c.Args()) == 0 {
		fmt.Println("search keyword needed.")
		return
	}

	items, err := notedb.ItemByRegexp(c.Args())
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	for _, item := range items {
		fmt.Println(item)
	}
}

func funDump(c *cli.Context) {
	if len(c.Args()) > 0 {
		fmt.Println("no arguments should be given.")
		return
	}

	err := notedb.Dump()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func funWipe(c *cli.Context) {
	if len(c.Args()) > 0 {
		fmt.Println("no arguments should be given.")
		return
	}

	reply, err := request_reply(
		"========================================\n"+
			" Attention, it will clear all the data.\n"+
			"========================================\n"+
			" Type \"%s\" to continue:",
		"yes")
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if reply == false {
		return
	}

	err = notedb.Wipe()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func funRestore(c *cli.Context) {
	if len(c.Args()) != 1 {
		fmt.Println("dumpped filename needed.")
		return
	}

	reply, err := request_reply(
		"========================================\n"+
			" Attention, it will clear all the data.\n"+
			"========================================\n"+
			" Type \"%s\" to continue:",
		"yes")
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}

	if reply == false {
		return
	}

	err = notedb.Restore(c.Args().First())
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
}

func funImport(c *cli.Context) {
	if len(c.Args()) != 3 {
		fmt.Println("three arguments needed: <notename in your cnote>" +
			" <notename in dumpped note> <dumpped filename>.")
		return
	}
	notename, othernotename, filename := c.Args()[0], c.Args()[1], c.Args()[2]
	n, err := notedb.Import(notename, othernotename, filename)
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	fmt.Printf("%d items imported into note \"%s\".\n", n, notename)
}

func main() {
	notedb = cnotedb.NewNoteDB(DBFILE)
	defer notedb.Close()

	app := cli.NewApp()
	app.Name = "cnote"
	app.Usage = "A command line note app. https://github.com/shenwei356/cnote"
	app.Version = "1.1 (2014-07-20)"
	app.Author = "Wei Shen"
	app.Email = "shenwei356@gmail.com"

	app.Commands = []cli.Command{
		{
			Name:   "new",
			Usage:  "Create a new note",
			Action: getFunc(funcs, "new"),
		},
		{
			Name:   "del",
			Usage:  "Delete a note",
			Action: getFunc(funcs, "del"),
		},
		{
			Name:   "use",
			Usage:  "Select a note",
			Action: getFunc(funcs, "use"),
		},
		{
			Name:      "list",
			ShortName: "ls",
			Usage:     "List all notes",
			Action:    getFunc(funcs, "list"),
		},
		{
			Name:   "add",
			Usage:  "Add a note item",
			Action: getFunc(funcs, "add"),
		},
		{
			Name:   "rm",
			Usage:  "Remove a note item",
			Action: getFunc(funcs, "rm"),
		},
		{
			Name:      "tag",
			ShortName: "t",
			Usage:     "List items by tags. List all tags if no arguments given",
			Action:    getFunc(funcs, "tag"),
		},
		{
			Name:      "search",
			ShortName: "s",
			Usage:     "Search items with regular expression",
			Action:    getFunc(funcs, "search"),
		},
		{
			Name:   "dump",
			Usage:  "Dump whole database, for backup or transfer",
			Action: getFunc(funcs, "dump"),
		},
		{
			Name:   "wipe",
			Usage:  "Attention! Wipe whole database",
			Action: getFunc(funcs, "wipe"),
		},
		{
			Name:   "restore",
			Usage:  "Wipe whole database, and restore from dumpped file",
			Action: getFunc(funcs, "restore"),
		},
		{
			Name:   "import",
			Usage:  "Import note items from dumpped data",
			Action: getFunc(funcs, "import"),
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


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

// Package ltm provides a REST client for the /tm/ltm F5 BigIP API.
package ltm

import "e-xpert_solutions/f5-rest-client/f5"

// BasePath is the base path of the ltm API.
const BasePath = "mgmt/tm/ltm"

// LTM implement a REST client for the F5 BigIP LTM API.
type LTM struct {
	c f5.Client

	virtual     VirtualResource
	rule        RuleResource
	pool        PoolResource
	poolMembers PoolMembersResource
}

// New creates a new LTM client.
func New(c f5.Client) LTM {
	return LTM{
		c:           c,
		virtual:     VirtualResource{c: c},
		rule:        RuleResource{c: c},
		pool:        PoolResource{c: c},
		poolMembers: PoolMembersResource{c: c},
	}
}

// Virtual returns a VirtualResource configured to query tm/ltm/virtual API.
func (ltm LTM) Virtual() *VirtualResource {
	return &ltm.virtual
}

// Rule returns a RuleResource configured to query tm/ltm/rule API.
func (ltm LTM) Rule() *RuleResource {
	return &ltm.rule
}

// Pool returns a PoolResource configured to query /tm/ltm/pool API.
func (ltm LTM) Pool() *PoolResource {
	return &ltm.pool
}

// PoolMembers returns a PoolResource configured to query /tm/ltm/pool API.
func (ltm LTM) PoolMembers() *PoolMembersResource {
	return &ltm.poolMembers
}
<commit_msg>f5/ltm: add NodeResource to LTM client<commit_after>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ltm provides a REST client for the /tm/ltm F5 BigIP API.
package ltm

import "e-xpert_solutions/f5-rest-client/f5"

// BasePath is the base path of the ltm API.
const BasePath = "mgmt/tm/ltm"

// LTM implement a REST client for the F5 BigIP LTM API.
type LTM struct {
	c f5.Client

	virtual     VirtualResource
	rule        RuleResource
	pool        PoolResource
	poolMembers PoolMembersResource
	node        NodeResource
}

// New creates a new LTM client.
func New(c f5.Client) LTM {
	return LTM{
		c:           c,
		virtual:     VirtualResource{c: c},
		rule:        RuleResource{c: c},
		pool:        PoolResource{c: c},
		poolMembers: PoolMembersResource{c: c},
		node:        NodeResource{c: c},
	}
}

// Virtual returns a VirtualResource configured to query tm/ltm/virtual API.
func (ltm LTM) Virtual() *VirtualResource {
	return &ltm.virtual
}

// Rule returns a RuleResource configured to query tm/ltm/rule API.
func (ltm LTM) Rule() *RuleResource {
	return &ltm.rule
}

// Pool returns a PoolResource configured to query /tm/ltm/pool API.
func (ltm LTM) Pool() *PoolResource {
	return &ltm.pool
}

// PoolMembers returns a PoolResource configured to query /tm/ltm/pool API.
func (ltm LTM) PoolMembers() *PoolMembersResource {
	return &ltm.poolMembers
}

// Node returns a NodeResource configured to query /tm/ltm/node API.
func (ltm LTM) Node() *NodeResource {
	return &ltm.node
}
<|endoftext|>