<commit_before>package reform

import (
	"fmt"
	"strings"
)

func filteredColumnsAndValues(record Record, columnsIn []string, isUpdate bool) (columns []string, values []interface{}, err error) {
	columnsSet := make(map[string]struct{}, len(columnsIn))
	for _, c := range columnsIn {
		columnsSet[c] = struct{}{}
	}

	// select columns from set and collect values
	table := record.Table()
	pk := int(table.PKColumnIndex())
	allColumns := table.Columns()
	allValues := record.Values()
	columns = make([]string, 0, len(columnsSet))
	values = make([]interface{}, 0, len(columns))
	for i, c := range allColumns {
		if _, ok := columnsSet[c]; ok {
			if isUpdate && i == pk {
				err = fmt.Errorf("reform: will not update PK column: %s", c)
				return
			}
			delete(columnsSet, c)
			columns = append(columns, c)
			values = append(values, allValues[i])
		}
	}

	// make error for extra columns
	if len(columnsSet) > 0 {
		columns = make([]string, 0, len(columnsSet))
		for c := range columnsSet {
			columns = append(columns, c)
		}
		// TODO make exported type for that error
		err = fmt.Errorf("reform: unexpected columns: %v", columns)
		return
	}

	return
}

func (q *Querier) insert(str Struct, columns []string, values []interface{}) error {
	for i, c := range columns {
		columns[i] = q.QuoteIdentifier(c)
	}
	placeholders := q.Placeholders(1, len(columns))

	view := str.View()
	record, _ := str.(Record)
	lastInsertIdMethod := q.LastInsertIdMethod()

	var pk uint
	if record != nil {
		pk = view.(Table).PKColumnIndex()
	}

	query := fmt.Sprintf("INSERT INTO %s (%s)",
		q.QualifiedView(view),
		strings.Join(columns, ", "),
	)

	if record != nil && lastInsertIdMethod == OutputInserted {
		query += fmt.Sprintf(" OUTPUT INSERTED.%s", q.QuoteIdentifier(view.Columns()[pk]))
	}

	query += fmt.Sprintf(" VALUES (%s)", strings.Join(placeholders, ", "))

	if record != nil && lastInsertIdMethod == Returning {
		query += fmt.Sprintf(" RETURNING %s", q.QuoteIdentifier(view.Columns()[pk]))
	}

	switch lastInsertIdMethod {
	case LastInsertId:
		res, err := q.Exec(query, values...)
		if err != nil {
			return err
		}
		if record != nil {
			id, err := res.LastInsertId()
			if err != nil {
				return err
			}
			record.SetPK(id)
		}
		return nil

	case Returning, OutputInserted:
		var err error
		if record != nil {
			err = q.QueryRow(query, values...).Scan(record.PKPointer())
		} else {
			_, err = q.Exec(query, values...)
		}
		return err

	default:
		panic("reform: Unhandled LastInsertIdMethod. Please report this bug.")
	}
}

func (q *Querier) beforeInsert(str Struct) error {
	if bi, ok := str.(BeforeInserter); ok {
		err := bi.BeforeInsert()
		if err != nil {
			return err
		}
	}

	return nil
}

// Insert inserts a struct into SQL database table.
// If str implements BeforeInserter, it calls BeforeInsert() before doing so.
//
// It fills record's primary key field.
func (q *Querier) Insert(str Struct) error {
	err := q.beforeInsert(str)
	if err != nil {
		return err
	}

	view := str.View()
	values := str.Values()
	columns := view.Columns()
	record, _ := str.(Record)
	var pk uint

	if record != nil {
		pk = view.(Table).PKColumnIndex()

		// cut primary key
		if !record.HasPK() {
			values = append(values[:pk], values[pk+1:]...)
			columns = append(columns[:pk], columns[pk+1:]...)
		}
	}

	return q.insert(str, columns, values)
}

// InsertColumns inserts a struct into SQL database table with specified columns.
// Other columns are omitted from generated INSERT statement.
// If str implements BeforeInserter, it calls BeforeInsert() before doing so.
//
// It fills record's primary key field.
func (q *Querier) InsertColumns(str Struct, columns ...string) error {
	record, _ := str.(Record)

	err := q.beforeInsert(record)
	if err != nil {
		return err
	}

	columns, values, err := filteredColumnsAndValues(record, columns, false)
	if err != nil {
		return err
	}

	return q.insert(record, columns, values)
}

// InsertMulti inserts several structs into SQL database table with single query.
// If they implement BeforeInserter, it calls BeforeInsert() before doing so.
//
// All structs should belong to the same view/table.
// All records should either have or not have primary key set.
// It doesn't fill primary key fields.
// Given all these limitations, most users should use Querier.Insert in a loop, not this method.
func (q *Querier) InsertMulti(structs ...Struct) error {
	if len(structs) == 0 {
		return nil
	}

	// check that view is the same
	view := structs[0].View()
	for _, str := range structs {
		if str.View() != view {
			return fmt.Errorf("reform: different tables in InsertMulti: %s and %s", view.Name(), str.View().Name())
		}
	}

	var err error
	for _, str := range structs {
		if bi, ok := str.(BeforeInserter); ok {
			e := bi.BeforeInsert()
			if err == nil {
				err = e
			}
		}
	}
	if err != nil {
		return err
	}

	// check if all PK are present or all are absent
	record, _ := structs[0].(Record)
	if record != nil {
		for _, str := range structs {
			rec, _ := str.(Record)
			if record.HasPK() != rec.HasPK() {
				return fmt.Errorf("reform: PK in present in one struct and absent in other: first: %s, second: %s",
					record, rec)
			}
		}
	}

	columns := view.Columns()
	for i, c := range columns {
		columns[i] = q.QuoteIdentifier(c)
	}

	var pk uint
	if record != nil && !record.HasPK() {
		pk = view.(Table).PKColumnIndex()
		columns = append(columns[:pk], columns[pk+1:]...)
	}

	placeholders := q.Placeholders(1, len(columns)*len(structs))
	query := fmt.Sprintf("INSERT INTO %s (%s) VALUES ",
		q.QualifiedView(view),
		strings.Join(columns, ", "),
	)
	for i := 0; i < len(structs); i++ {
		query += fmt.Sprintf("(%s), ", strings.Join(placeholders[len(columns)*i:len(columns)*(i+1)], ", "))
	}
	query = query[:len(query)-2] // cut last ", "

	values := make([]interface{}, 0, len(placeholders))
	for _, str := range structs {
		v := str.Values()
		if record != nil && !record.HasPK() {
			v = append(v[:pk], v[pk+1:]...)
		}
		values = append(values, v...)
	}

	_, err = q.Exec(query, values...)
	return err
}

func (q *Querier) update(record Record, columns []string, values []interface{}) error {
	for i, c := range columns {
		columns[i] = q.QuoteIdentifier(c)
	}
	placeholders := q.Placeholders(1, len(columns))

	p := make([]string, len(columns))
	for i, c := range columns {
		p[i] = c + " = " + placeholders[i]
	}
	table := record.Table()
	query := fmt.Sprintf("UPDATE %s SET %s WHERE %s = %s",
		q.QualifiedView(table),
		strings.Join(p, ", "),
		q.QuoteIdentifier(table.Columns()[table.PKColumnIndex()]),
		q.Placeholder(len(columns)+1),
	)

	args := append(values, record.PKValue())
	res, err := q.Exec(query, args...)
	if err != nil {
		return err
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNoRows
	}
	if ra > 1 {
		panic(fmt.Errorf("reform: %d rows by UPDATE by primary key. Please report this bug.", ra))
	}
	return nil
}

func (q *Querier) beforeUpdate(record Record) error {
	if !record.HasPK() {
		return ErrNoPK
	}

	if bu, ok := record.(BeforeUpdater); ok {
		err := bu.BeforeUpdate()
		if err != nil {
			return err
		}
	}

	return nil
}

// Update updates all columns of row specified by primary key in SQL database table with given record.
// If record implements BeforeUpdater, it calls BeforeUpdate() before doing so.
//
// Method returns ErrNoRows if no rows were updated.
// Method returns ErrNoPK if primary key is not set.
func (q *Querier) Update(record Record) error {
	err := q.beforeUpdate(record)
	if err != nil {
		return err
	}

	table := record.Table()
	values := record.Values()
	columns := table.Columns()

	// cut primary key
	pk := table.PKColumnIndex()
	values = append(values[:pk], values[pk+1:]...)
	columns = append(columns[:pk], columns[pk+1:]...)

	return q.update(record, columns, values)
}

// UpdateColumns updates specified columns of row specified by primary key in SQL database table with given record.
// Other columns are omitted from generated UPDATE statement.
// If record implements BeforeUpdater, it calls BeforeUpdate() before doing so.
//
// Method returns ErrNoRows if no rows were updated.
// Method returns ErrNoPK if primary key is not set.
func (q *Querier) UpdateColumns(record Record, columns ...string) error {
	err := q.beforeUpdate(record)
	if err != nil {
		return err
	}

	columns, values, err := filteredColumnsAndValues(record, columns, true)
	if err != nil {
		return err
	}

	if len(values) == 0 {
		// TODO make exported type for that error
		return fmt.Errorf("reform: nothing to update")
	}

	return q.update(record, columns, values)
}

// Save saves record in SQL database table.
// If primary key is set, it first calls Update and checks if row was updated.
// If primary key is absent or no row was updated, it calls Insert.
func (q *Querier) Save(record Record) error {
	if record.HasPK() {
		err := q.Update(record)
		if err != ErrNoRows {
			return err
		}
	}

	return q.Insert(record)
}

// Delete deletes record from SQL database table by primary key.
//
// Method returns ErrNoRows if no rows were deleted.
// Method returns ErrNoPK if primary key is not set.
func (q *Querier) Delete(record Record) error {
	if !record.HasPK() {
		return ErrNoPK
	}

	table := record.Table()
	pk := table.PKColumnIndex()
	query := fmt.Sprintf("DELETE FROM %s WHERE %s = %s",
		q.QualifiedView(table),
		q.QuoteIdentifier(table.Columns()[pk]),
		q.Placeholder(1),
	)

	res, err := q.Exec(query, record.PKValue())
	if err != nil {
		return err
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNoRows
	}
	if ra > 1 {
		panic(fmt.Errorf("reform: %d rows by DELETE by primary key. Please report this bug.", ra))
	}
	return nil
}

// DeleteFrom deletes rows from view with tail and args and returns a number of deleted rows.
//
// Method never returns ErrNoRows.
func (q *Querier) DeleteFrom(view View, tail string, args ...interface{}) (uint, error) {
	query := fmt.Sprintf("DELETE FROM %s %s",
		q.QualifiedView(view),
		tail,
	)

	res, err := q.Exec(query, args...)
	if err != nil {
		return 0, err
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}
	return uint(ra), nil
}
<commit_msg>Minor style tweak.<commit_after>package reform

import (
	"fmt"
	"strings"
)

func filteredColumnsAndValues(record Record, columnsIn []string, isUpdate bool) (columns []string, values []interface{}, err error) {
	columnsSet := make(map[string]struct{}, len(columnsIn))
	for _, c := range columnsIn {
		columnsSet[c] = struct{}{}
	}

	// select columns from set and collect values
	table := record.Table()
	pk := int(table.PKColumnIndex())
	allColumns := table.Columns()
	allValues := record.Values()
	columns = make([]string, 0, len(columnsSet))
	values = make([]interface{}, 0, len(columns))
	for i, c := range allColumns {
		if _, ok := columnsSet[c]; ok {
			if isUpdate && i == pk {
				err = fmt.Errorf("reform: will not update PK column: %s", c)
				return
			}
			delete(columnsSet, c)
			columns = append(columns, c)
			values = append(values, allValues[i])
		}
	}

	// make error for extra columns
	if len(columnsSet) > 0 {
		columns = make([]string, 0, len(columnsSet))
		for c := range columnsSet {
			columns = append(columns, c)
		}
		// TODO make exported type for that error
		err = fmt.Errorf("reform: unexpected columns: %v", columns)
		return
	}

	return
}

func (q *Querier) insert(str Struct, columns []string, values []interface{}) error {
	for i, c := range columns {
		columns[i] = q.QuoteIdentifier(c)
	}
	placeholders := q.Placeholders(1, len(columns))

	view := str.View()
	record, _ := str.(Record)
	lastInsertIdMethod := q.LastInsertIdMethod()

	var pk uint
	if record != nil {
		pk = view.(Table).PKColumnIndex()
	}

	// make query
	query := fmt.Sprintf("INSERT INTO %s (%s)", q.QualifiedView(view), strings.Join(columns, ", "))
	if record != nil && lastInsertIdMethod == OutputInserted {
		query += fmt.Sprintf(" OUTPUT INSERTED.%s", q.QuoteIdentifier(view.Columns()[pk]))
	}
	query += fmt.Sprintf(" VALUES (%s)", strings.Join(placeholders, ", "))
	if record != nil && lastInsertIdMethod == Returning {
		query += fmt.Sprintf(" RETURNING %s", q.QuoteIdentifier(view.Columns()[pk]))
	}

	switch lastInsertIdMethod {
	case LastInsertId:
		res, err := q.Exec(query, values...)
		if err != nil {
			return err
		}
		if record != nil {
			id, err := res.LastInsertId()
			if err != nil {
				return err
			}
			record.SetPK(id)
		}
		return nil

	case Returning, OutputInserted:
		var err error
		if record != nil {
			err = q.QueryRow(query, values...).Scan(record.PKPointer())
		} else {
			_, err = q.Exec(query, values...)
		}
		return err

	default:
		panic("reform: Unhandled LastInsertIdMethod. Please report this bug.")
	}
}

func (q *Querier) beforeInsert(str Struct) error {
	if bi, ok := str.(BeforeInserter); ok {
		err := bi.BeforeInsert()
		if err != nil {
			return err
		}
	}

	return nil
}

// Insert inserts a struct into SQL database table.
// If str implements BeforeInserter, it calls BeforeInsert() before doing so.
//
// It fills record's primary key field.
func (q *Querier) Insert(str Struct) error {
	err := q.beforeInsert(str)
	if err != nil {
		return err
	}

	view := str.View()
	values := str.Values()
	columns := view.Columns()
	record, _ := str.(Record)
	var pk uint

	if record != nil {
		pk = view.(Table).PKColumnIndex()

		// cut primary key
		if !record.HasPK() {
			values = append(values[:pk], values[pk+1:]...)
			columns = append(columns[:pk], columns[pk+1:]...)
		}
	}

	return q.insert(str, columns, values)
}

// InsertColumns inserts a struct into SQL database table with specified columns.
// Other columns are omitted from generated INSERT statement.
// If str implements BeforeInserter, it calls BeforeInsert() before doing so.
//
// It fills record's primary key field.
func (q *Querier) InsertColumns(str Struct, columns ...string) error {
	record, _ := str.(Record)

	err := q.beforeInsert(record)
	if err != nil {
		return err
	}

	columns, values, err := filteredColumnsAndValues(record, columns, false)
	if err != nil {
		return err
	}

	return q.insert(record, columns, values)
}

// InsertMulti inserts several structs into SQL database table with single query.
// If they implement BeforeInserter, it calls BeforeInsert() before doing so.
//
// All structs should belong to the same view/table.
// All records should either have or not have primary key set.
// It doesn't fill primary key fields.
// Given all these limitations, most users should use Querier.Insert in a loop, not this method.
func (q *Querier) InsertMulti(structs ...Struct) error {
	if len(structs) == 0 {
		return nil
	}

	// check that view is the same
	view := structs[0].View()
	for _, str := range structs {
		if str.View() != view {
			return fmt.Errorf("reform: different tables in InsertMulti: %s and %s", view.Name(), str.View().Name())
		}
	}

	var err error
	for _, str := range structs {
		if bi, ok := str.(BeforeInserter); ok {
			e := bi.BeforeInsert()
			if err == nil {
				err = e
			}
		}
	}
	if err != nil {
		return err
	}

	// check if all PK are present or all are absent
	record, _ := structs[0].(Record)
	if record != nil {
		for _, str := range structs {
			rec, _ := str.(Record)
			if record.HasPK() != rec.HasPK() {
				return fmt.Errorf("reform: PK in present in one struct and absent in other: first: %s, second: %s",
					record, rec)
			}
		}
	}

	columns := view.Columns()
	for i, c := range columns {
		columns[i] = q.QuoteIdentifier(c)
	}

	var pk uint
	if record != nil && !record.HasPK() {
		pk = view.(Table).PKColumnIndex()
		columns = append(columns[:pk], columns[pk+1:]...)
	}

	placeholders := q.Placeholders(1, len(columns)*len(structs))
	query := fmt.Sprintf("INSERT INTO %s (%s) VALUES ",
		q.QualifiedView(view),
		strings.Join(columns, ", "),
	)
	for i := 0; i < len(structs); i++ {
		query += fmt.Sprintf("(%s), ", strings.Join(placeholders[len(columns)*i:len(columns)*(i+1)], ", "))
	}
	query = query[:len(query)-2] // cut last ", "

	values := make([]interface{}, 0, len(placeholders))
	for _, str := range structs {
		v := str.Values()
		if record != nil && !record.HasPK() {
			v = append(v[:pk], v[pk+1:]...)
		}
		values = append(values, v...)
	}

	_, err = q.Exec(query, values...)
	return err
}

func (q *Querier) update(record Record, columns []string, values []interface{}) error {
	for i, c := range columns {
		columns[i] = q.QuoteIdentifier(c)
	}
	placeholders := q.Placeholders(1, len(columns))

	p := make([]string, len(columns))
	for i, c := range columns {
		p[i] = c + " = " + placeholders[i]
	}
	table := record.Table()
	query := fmt.Sprintf("UPDATE %s SET %s WHERE %s = %s",
		q.QualifiedView(table),
		strings.Join(p, ", "),
		q.QuoteIdentifier(table.Columns()[table.PKColumnIndex()]),
		q.Placeholder(len(columns)+1),
	)

	args := append(values, record.PKValue())
	res, err := q.Exec(query, args...)
	if err != nil {
		return err
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNoRows
	}
	if ra > 1 {
		panic(fmt.Errorf("reform: %d rows by UPDATE by primary key. Please report this bug.", ra))
	}
	return nil
}

func (q *Querier) beforeUpdate(record Record) error {
	if !record.HasPK() {
		return ErrNoPK
	}

	if bu, ok := record.(BeforeUpdater); ok {
		err := bu.BeforeUpdate()
		if err != nil {
			return err
		}
	}

	return nil
}

// Update updates all columns of row specified by primary key in SQL database table with given record.
// If record implements BeforeUpdater, it calls BeforeUpdate() before doing so.
//
// Method returns ErrNoRows if no rows were updated.
// Method returns ErrNoPK if primary key is not set.
func (q *Querier) Update(record Record) error {
	err := q.beforeUpdate(record)
	if err != nil {
		return err
	}

	table := record.Table()
	values := record.Values()
	columns := table.Columns()

	// cut primary key
	pk := table.PKColumnIndex()
	values = append(values[:pk], values[pk+1:]...)
	columns = append(columns[:pk], columns[pk+1:]...)

	return q.update(record, columns, values)
}

// UpdateColumns updates specified columns of row specified by primary key in SQL database table with given record.
// Other columns are omitted from generated UPDATE statement.
// If record implements BeforeUpdater, it calls BeforeUpdate() before doing so.
//
// Method returns ErrNoRows if no rows were updated.
// Method returns ErrNoPK if primary key is not set.
func (q *Querier) UpdateColumns(record Record, columns ...string) error {
	err := q.beforeUpdate(record)
	if err != nil {
		return err
	}

	columns, values, err := filteredColumnsAndValues(record, columns, true)
	if err != nil {
		return err
	}

	if len(values) == 0 {
		// TODO make exported type for that error
		return fmt.Errorf("reform: nothing to update")
	}

	return q.update(record, columns, values)
}

// Save saves record in SQL database table.
// If primary key is set, it first calls Update and checks if row was updated.
// If primary key is absent or no row was updated, it calls Insert.
func (q *Querier) Save(record Record) error {
	if record.HasPK() {
		err := q.Update(record)
		if err != ErrNoRows {
			return err
		}
	}

	return q.Insert(record)
}

// Delete deletes record from SQL database table by primary key.
//
// Method returns ErrNoRows if no rows were deleted.
// Method returns ErrNoPK if primary key is not set.
func (q *Querier) Delete(record Record) error {
	if !record.HasPK() {
		return ErrNoPK
	}

	table := record.Table()
	pk := table.PKColumnIndex()
	query := fmt.Sprintf("DELETE FROM %s WHERE %s = %s",
		q.QualifiedView(table),
		q.QuoteIdentifier(table.Columns()[pk]),
		q.Placeholder(1),
	)

	res, err := q.Exec(query, record.PKValue())
	if err != nil {
		return err
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNoRows
	}
	if ra > 1 {
		panic(fmt.Errorf("reform: %d rows by DELETE by primary key. Please report this bug.", ra))
	}
	return nil
}

// DeleteFrom deletes rows from view with tail and args and returns a number of deleted rows.
//
// Method never returns ErrNoRows.
func (q *Querier) DeleteFrom(view View, tail string, args ...interface{}) (uint, error) {
	query := fmt.Sprintf("DELETE FROM %s %s",
		q.QualifiedView(view),
		tail,
	)

	res, err := q.Exec(query, args...)
	if err != nil {
		return 0, err
	}
	ra, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}
	return uint(ra), nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"net/http"
	"os"
	"strconv"

	log "github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/infinityworksltd/prometheus-rancher-exporter/measure"
)

const (
	namespace = "rancher" // Used to prepand Prometheus metrics created by this exporter.
)

// Runtime variables, user controllable for targeting, authentication and filtering.
var (
	metricsPath   = getEnv("METRICS_PATH", "/metrics") // Path under which to expose metrics
	listenAddress = getEnv("LISTEN_ADDRESS", ":9173")  // Address on which to expose metrics
	rancherURL    = os.Getenv("CATTLE_URL")            // URL of Rancher Server API e.g. http://192.168.0.1:8080/v2-beta
	accessKey     = os.Getenv("CATTLE_ACCESS_KEY")     // Optional - Access Key for Rancher API
	secretKey     = os.Getenv("CATTLE_SECRET_KEY")     // Optional - Secret Key for Rancher API
	logLevel      = getEnv("LOG_LEVEL", "info")        // Optional - Set the logging level
	//hideSys, _    = strconv.ParseBool(os.Getenv("HIDE_SYS")) // hideSys - Optional - Flag that indicates if the environment variable `HIDE_SYS` is set to a boolean true value
	hideSys, _ = strconv.ParseBool(getEnv("HIDE_SYS", "true")) // hideSys - Optional - Flag that indicates if the environment variable `HIDE_SYS` is set to a boolean true value
)

// Predefined variables that are used throughout the exporter
var (
	agentStates   = []string{"activating", "active", "reconnecting", "disconnected", "disconnecting", "finishing-reconnect", "reconnected"}
	hostStates    = []string{"activating", "active", "deactivating", "error", "erroring", "inactive", "provisioned", "purged", "purging", "registering", "removed", "removing", "requested", "restoring", "updating_active", "updating_inactive"}
	stackStates   = []string{"activating", "active", "canceled_upgrade", "canceling_upgrade", "error", "erroring", "finishing_upgrade", "removed", "removing", "requested", "restarting", "rolling_back", "updating_active", "upgraded", "upgrading"}
	serviceStates = []string{"activating", "active", "canceled_upgrade", "canceling_upgrade", "deactivating", "finishing_upgrade", "inactive", "registering", "removed", "removing", "requested", "restarting", "rolling_back", "updating_active", "updating_inactive", "upgraded", "upgrading"}
	healthStates  = []string{"healthy", "unhealthy"}
	endpoints     = []string{"stacks", "services", "hosts"} // EndPoints the exporter will trawl
	stackRef      = make(map[string]string)                 // Stores the StackID and StackName as a map, used to provide label dimensions to service metrics

)

// getEnv - Allows us to supply a fallback option if nothing specified
func getEnv(key, fallback string) string {
	value := os.Getenv(key)
	if len(value) == 0 {
		return fallback
	}
	return value
}

func main() {
	flag.Parse()

	// Sets the logging value for the exporter, defaults to info
	setLogLevel(logLevel)

	// check the rancherURL ($CATTLE_URL) has been provided correctly
	if rancherURL == "" {
		log.Fatal("CATTLE_URL must be set and non-empty")
	}

	log.Info("Starting Prometheus Exporter for Rancher")
	log.Info("Runtime Configuration in-use: URL of Rancher Server: ", rancherURL, " AccessKey: ", accessKey, "System Services hidden: ", hideSys)

	// Register internal metrics used for tracking the exporter performance
	measure.Init()

	// Register a new Exporter
	Exporter := newExporter(rancherURL, accessKey, secretKey, hideSys)

	// Register Metrics from each of the endpoints
	// This invokes the Collect method through the prometheus client libraries.
	prometheus.MustRegister(Exporter)

	// Setup HTTP handler
	http.Handle(metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
		                <title>Rancher exporter</title>

rancher exporter

<a href='` + metricsPath + `'>Metrics

		              `))
	})
	log.Printf("Starting Server on port %s and path %s", listenAddress, metricsPath)
	log.Fatal(http.ListenAndServe(listenAddress, nil))
}
<commit_msg>add degraded as a possible health state<commit_after>package main

import (
	"flag"
	"net/http"
	"os"
	"strconv"

	log "github.com/Sirupsen/logrus"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/infinityworksltd/prometheus-rancher-exporter/measure"
)

const (
	namespace = "rancher" // Used to prepand Prometheus metrics created by this exporter.
)

// Runtime variables, user controllable for targeting, authentication and filtering.
var (
	metricsPath   = getEnv("METRICS_PATH", "/metrics") // Path under which to expose metrics
	listenAddress = getEnv("LISTEN_ADDRESS", ":9173")  // Address on which to expose metrics
	rancherURL    = os.Getenv("CATTLE_URL")            // URL of Rancher Server API e.g. http://192.168.0.1:8080/v2-beta
	accessKey     = os.Getenv("CATTLE_ACCESS_KEY")     // Optional - Access Key for Rancher API
	secretKey     = os.Getenv("CATTLE_SECRET_KEY")     // Optional - Secret Key for Rancher API
	logLevel      = getEnv("LOG_LEVEL", "info")        // Optional - Set the logging level
	//hideSys, _    = strconv.ParseBool(os.Getenv("HIDE_SYS")) // hideSys - Optional - Flag that indicates if the environment variable `HIDE_SYS` is set to a boolean true value
	hideSys, _ = strconv.ParseBool(getEnv("HIDE_SYS", "true")) // hideSys - Optional - Flag that indicates if the environment variable `HIDE_SYS` is set to a boolean true value
)

// Predefined variables that are used throughout the exporter
var (
	agentStates   = []string{"activating", "active", "reconnecting", "disconnected", "disconnecting", "finishing-reconnect", "reconnected"}
	hostStates    = []string{"activating", "active", "deactivating", "error", "erroring", "inactive", "provisioned", "purged", "purging", "registering", "removed", "removing", "requested", "restoring", "updating_active", "updating_inactive"}
	stackStates   = []string{"activating", "active", "canceled_upgrade", "canceling_upgrade", "error", "erroring", "finishing_upgrade", "removed", "removing", "requested", "restarting", "rolling_back", "updating_active", "upgraded", "upgrading"}
	serviceStates = []string{"activating", "active", "canceled_upgrade", "canceling_upgrade", "deactivating", "finishing_upgrade", "inactive", "registering", "removed", "removing", "requested", "restarting", "rolling_back", "updating_active", "updating_inactive", "upgraded", "upgrading"}
	healthStates  = []string{"healthy", "unhealthy", "degraded"}
	endpoints     = []string{"stacks", "services", "hosts"} // EndPoints the exporter will trawl
	stackRef      = make(map[string]string)                 // Stores the StackID and StackName as a map, used to provide label dimensions to service metrics

)

// getEnv - Allows us to supply a fallback option if nothing specified
func getEnv(key, fallback string) string {
	value := os.Getenv(key)
	if len(value) == 0 {
		return fallback
	}
	return value
}

func main() {
	flag.Parse()

	// Sets the logging value for the exporter, defaults to info
	setLogLevel(logLevel)

	// check the rancherURL ($CATTLE_URL) has been provided correctly
	if rancherURL == "" {
		log.Fatal("CATTLE_URL must be set and non-empty")
	}

	log.Info("Starting Prometheus Exporter for Rancher")
	log.Info("Runtime Configuration in-use: URL of Rancher Server: ", rancherURL, " AccessKey: ", accessKey, "System Services hidden: ", hideSys)

	// Register internal metrics used for tracking the exporter performance
	measure.Init()

	// Register a new Exporter
	Exporter := newExporter(rancherURL, accessKey, secretKey, hideSys)

	// Register Metrics from each of the endpoints
	// This invokes the Collect method through the prometheus client libraries.
	prometheus.MustRegister(Exporter)

	// Setup HTTP handler
	http.Handle(metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
		                <title>Rancher exporter</title>

rancher exporter

<a href='` + metricsPath + `'>Metrics

		              `))
	})
	log.Printf("Starting Server on port %s and path %s", listenAddress, metricsPath)
	log.Fatal(http.ListenAndServe(listenAddress, nil))
}
<|endoftext|>


<commit_before>package job

import "github.com/ferrariframework/ferrariserver/models"
import "fmt"

var _ Service = (*Job)(nil)

//Service defines a job service
type Service interface {
	Save(models.Job) (string, error)
	RecordLog(models.Log) error
}

//New retuns a new job service
func New() *Job {
	return &Job{}
}

//Job a job service implementation
type Job struct {
}

//Save saves a new job
func (j *Job) Save(job models.Job) (string, error) {
	return "", nil
}

//RecordLog records a job log
func (j *Job) RecordLog(log models.Log) error {
	fmt.Println("workerID", log.WorkerID, "jobID", log.JobID)
	fmt.Println(log.Message)
	return nil
}
<commit_msg>Small change to RecordLog in jobService<commit_after>package job

import "github.com/ferrariframework/ferrariserver/models"
import "fmt"

var _ Service = (*Job)(nil)

//Service defines a job service
type Service interface {
	Save(models.Job) (string, error)
	RecordLog(models.Log) error
}

//New retuns a new job service
func New() *Job {
	return &Job{}
}

//Job a job service implementation
type Job struct {
}

//Save saves a new job
func (j *Job) Save(job models.Job) (string, error) {
	return "", nil
}

//RecordLog records a job log
func (j *Job) RecordLog(log models.Log) error {
	fmt.Println("workerID", log.WorkerID, "jobID", log.JobID)
	fmt.Println(string(log.Message))
	return nil
}
<|endoftext|>


<commit_before>package core

import (
	"github.com/scrapli/scrapligo/driver/base"
	"github.com/scrapli/scrapligo/driver/network"
)

// NewSROSDriver returns a driver setup for operation with Nokia SR OS devices.
func NewSROSDriver(
	host string,
	options ...base.Option,
) (*network.Driver, error) {
	defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
		"exec": {
			Pattern:        `(?im)^\[.*\]\n[abcd]:\S+@\S+#\s?$`,
			Name:           execPrivLevel,
			PreviousPriv:   "",
			Deescalate:     "",
			Escalate:       "",
			EscalateAuth:   false,
			EscalatePrompt: ``,
		},
		// configuration privilege level maps to the exclusive config mode on SR OS
		"configuration": {
			Pattern:        `(?im)^\*?\(ex\)\[/?\]\n[abcd]:\S+@\S+#\s?$`,
			Name:           configPrivLevel,
			PreviousPriv:   execPrivLevel,
			Deescalate:     "quit-config",
			Escalate:       "edit-config exclusive",
			EscalateAuth:   false,
			EscalatePrompt: ``,
		},
		"configuration-with-path": {
			Pattern:        `(?im)^\*?\(ex\)\[\S{2,}.+\]\n[abcd]:\S+@\S+#\s?$`,
			Name:           "configuration-with-path",
			PreviousPriv:   configPrivLevel,
			Deescalate:     "exit all",
			Escalate:       "",
			EscalateAuth:   false,
			EscalatePrompt: ``,
		},
	}

	defaultFailedWhenContains := []string{
		"CRITICAL:",
		"MAJOR:",
		"MINOR:",
	}

	const defaultDefaultDesiredPriv = execPrivLevel

	d, err := network.NewNetworkDriver(
		host,
		defaultPrivilegeLevels,
		defaultDefaultDesiredPriv,
		defaultFailedWhenContains,
		SROSOnOpen,
		SROSOnClose,
		options...)

	if err != nil {
		return nil, err
	}

	d.Augments["abortConfig"] = SROSAbortConfig

	return d, nil
}

// SROSOnOpen is a default on open callable for SR OS.
func SROSOnOpen(d *network.Driver) error {
	err := d.AcquirePriv(d.DefaultDesiredPriv)
	if err != nil {
		return err
	}

	if _, err := d.SendCommand("environment console width 512", nil); err != nil {
		return err
	}

	if _, err := d.SendCommand("environment more false", nil); err != nil {
		return err
	}

	_, err = d.SendCommand("environment command-completion space false", nil)

	return err
}

// SROSOnClose is a default on close callable for SR OS.
func SROSOnClose(d *network.Driver) error {
	err := d.AcquirePriv(d.DefaultDesiredPriv)
	if err != nil {
		return err
	}

	err = d.Channel.Write([]byte("logout"), false)
	if err != nil {
		return err
	}

	err = d.Channel.SendReturn()
	if err != nil {
		return err
	}

	return nil
}

// SROSAbortConfig aborts SR OS configuration session.
func SROSAbortConfig(d *network.Driver) (*base.Response, error) {
	if _, err := d.Channel.SendInput("discard /", false, false, -1); err != nil {
		return nil, err
	}
	if _, err := d.Channel.SendInput("exit", false, false, -1); err != nil {
		return nil, err
	}
	_, err := d.Channel.SendInput("quit-config", false, false, -1)

	d.CurrentPriv = privExecPrivLevel

	return nil, err
}
<commit_msg>linter happy<commit_after>package core

import (
	"github.com/scrapli/scrapligo/driver/base"
	"github.com/scrapli/scrapligo/driver/network"
)

// NewSROSDriver returns a driver setup for operation with Nokia SR OS devices.
func NewSROSDriver(
	host string,
	options ...base.Option,
) (*network.Driver, error) {
	defaultPrivilegeLevels := map[string]*base.PrivilegeLevel{
		"exec": {
			Pattern:        `(?im)^\[.*\]\n[abcd]:\S+@\S+#\s?$`,
			Name:           execPrivLevel,
			PreviousPriv:   "",
			Deescalate:     "",
			Escalate:       "",
			EscalateAuth:   false,
			EscalatePrompt: ``,
		},
		// configuration privilege level maps to the exclusive config mode on SR OS
		"configuration": {
			Pattern:        `(?im)^\*?\(ex\)\[/?\]\n[abcd]:\S+@\S+#\s?$`,
			Name:           configPrivLevel,
			PreviousPriv:   execPrivLevel,
			Deescalate:     "quit-config",
			Escalate:       "edit-config exclusive",
			EscalateAuth:   false,
			EscalatePrompt: ``,
		},
		"configuration-with-path": {
			Pattern:        `(?im)^\*?\(ex\)\[\S{2,}.+\]\n[abcd]:\S+@\S+#\s?$`,
			Name:           "configuration-with-path",
			PreviousPriv:   configPrivLevel,
			Deescalate:     "exit all",
			Escalate:       "",
			EscalateAuth:   false,
			EscalatePrompt: ``,
		},
	}

	defaultFailedWhenContains := []string{
		"CRITICAL:",
		"MAJOR:",
		"MINOR:",
	}

	const defaultDefaultDesiredPriv = execPrivLevel

	d, err := network.NewNetworkDriver(
		host,
		defaultPrivilegeLevels,
		defaultDefaultDesiredPriv,
		defaultFailedWhenContains,
		SROSOnOpen,
		SROSOnClose,
		options...)

	if err != nil {
		return nil, err
	}

	d.Augments["abortConfig"] = SROSAbortConfig

	return d, nil
}

// SROSOnOpen is a default on open callable for SR OS.
func SROSOnOpen(d *network.Driver) error {
	err := d.AcquirePriv(d.DefaultDesiredPriv)
	if err != nil {
		return err
	}

	if _, err = d.SendCommand("environment console width 512", nil); err != nil {
		return err
	}

	if _, err = d.SendCommand("environment more false", nil); err != nil {
		return err
	}

	_, err = d.SendCommand("environment command-completion space false", nil)

	return err
}

// SROSOnClose is a default on close callable for SR OS.
func SROSOnClose(d *network.Driver) error {
	err := d.AcquirePriv(d.DefaultDesiredPriv)
	if err != nil {
		return err
	}

	err = d.Channel.Write([]byte("logout"), false)
	if err != nil {
		return err
	}

	err = d.Channel.SendReturn()
	if err != nil {
		return err
	}

	return nil
}

// SROSAbortConfig aborts SR OS configuration session.
func SROSAbortConfig(d *network.Driver) (*base.Response, error) {
	if _, err := d.Channel.SendInput("discard /", false, false, -1); err != nil {
		return nil, err
	}

	if _, err := d.Channel.SendInput("exit", false, false, -1); err != nil {
		return nil, err
	}

	_, err := d.Channel.SendInput("quit-config", false, false, -1)

	d.CurrentPriv = privExecPrivLevel

	return nil, err
}
<|endoftext|>


<commit_before>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-G",
			"docker",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}

		// Add to the Docker group
		/*
			le.WithField("path", shellPath).Debug("Adding to Docker group")
			if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
				return err
			}
		*/
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\n%s\n", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.Mkdir(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	authConf := cs.config.Auth
	if authConf != nil {
		if authConf.CopyRootKeys {
			rkf, err := os.Open("/root/.ssh/authorized_keys")
			if err != nil {
				return err
			}
			_, err = io.Copy(f, rkf)
			rkf.Close()
			if err != nil {
				return err
			}
			f.WriteString("\n")
		}
		for _, key := range authConf.SSHKeys {
			f.WriteString(key)
			f.WriteString("\n")
		}
	}

	f.Sync()
	if err := os.Chown(authorizedKeysPath, uid, gid); err != nil {
		return err
	}

	containerId, err := cs.waiter.WaitForContainer(cs.config.Container)
	if err != nil {
		return err
	}

	userConfPath := path.Join(euser.HomeDir, config.UserConfigFile)
	le.WithField("path", userConfPath).Debug("Writing user config...")
	userConf := cs.config.ToConfigUserShell(containerId)
	userConfData, err := userConf.Marshal()
	if err != nil {
		return err
	}

	userConfFile, err := os.OpenFile(userConfPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	if _, err := userConfFile.Write(userConfData); err != nil {
		return err
	}
	userConfFile.Close()

	return os.Chown(userConfPath, uid, gid)
}

// Wait waits for Execute() to finish.
func (i *UserSetup) Wait() error {
	i.wg.Wait()
	return i.err
}
<commit_msg>fix: create home dir if not exist<commit_after>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-G",
			"docker",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}

		// Add to the Docker group
		/*
			le.WithField("path", shellPath).Debug("Adding to Docker group")
			if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
				return err
			}
		*/
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\n%s\n", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.MkdirAll(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	authConf := cs.config.Auth
	if authConf != nil {
		if authConf.CopyRootKeys {
			rkf, err := os.Open("/root/.ssh/authorized_keys")
			if err != nil {
				return err
			}
			_, err = io.Copy(f, rkf)
			rkf.Close()
			if err != nil {
				return err
			}
			f.WriteString("\n")
		}
		for _, key := range authConf.SSHKeys {
			f.WriteString(key)
			f.WriteString("\n")
		}
	}

	f.Sync()
	if err := os.Chown(authorizedKeysPath, uid, gid); err != nil {
		return err
	}

	containerId, err := cs.waiter.WaitForContainer(cs.config.Container)
	if err != nil {
		return err
	}

	userConfPath := path.Join(euser.HomeDir, config.UserConfigFile)
	le.WithField("path", userConfPath).Debug("Writing user config...")
	userConf := cs.config.ToConfigUserShell(containerId)
	userConfData, err := userConf.Marshal()
	if err != nil {
		return err
	}

	userConfFile, err := os.OpenFile(userConfPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	if _, err := userConfFile.Write(userConfData); err != nil {
		return err
	}
	userConfFile.Close()

	return os.Chown(userConfPath, uid, gid)
}

// Wait waits for Execute() to finish.
func (i *UserSetup) Wait() error {
	i.wg.Wait()
	return i.err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/NebulousLabs/Andromeda/network"
	"github.com/NebulousLabs/Andromeda/siacore"
)

// Environment is the struct that serves as the state for siad. It contains a
// pointer to the state, as things like a wallet, a friend list, etc. Each
// environment should have its own state.
type Environment struct {
	state *siacore.State

	server       *network.TCPServer
	host         *Host
	hostDatabase *HostDatabase
	renter       *Renter
	wallet       *Wallet

	friends map[string]siacore.CoinAddress

	// Channels for incoming blocks and transactions to be processed
	blockChan       chan siacore.Block
	transactionChan chan siacore.Transaction

	// Mining variables. The mining variables are protected by the miningLock.
	// Any time that you read from or write to any of the mining variables, you
	// need to be under a lock.
	mining        bool         // true when mining
	miningThreads int          // number of processes mining at once
	miningLock    sync.RWMutex // prevents benign race conditions
}

// createEnvironment creates a server, host, miner, renter and wallet and
// puts it all in a single environment struct that's used as the state for the
// main package.
func CreateEnvironment(rpcPort uint16, apiPort uint16, nobootstrap bool) (e *Environment, err error) {
	e = &Environment{
		state:           siacore.CreateGenesisState(),
		friends:         make(map[string]siacore.CoinAddress),
		blockChan:       make(chan siacore.Block, 100),
		transactionChan: make(chan siacore.Transaction, 100),
	}

	// Initialize all internal structures.
	e.hostDatabase = CreateHostDatabase()
	e.host = CreateHost()
	e.renter = CreateRenter()
	e.wallet = CreateWallet(e.state)

	// Bootstrap to the network.
	err = e.initializeNetwork(rpcPort, nobootstrap)
	if err != nil {
		return
	}
	e.host.Settings.IPAddress = e.server.NetAddress()

	// Begin listening for requests on the api.
	e.setUpHandlers(apiPort)

	return
}

// Close does any finishing maintenence before the environment can be garbage
// collected. Right now that just means closing the server.
func (e *Environment) Close() {
	e.server.Close()
}

// initializeNetwork registers the rpcs and bootstraps to the network,
// downlading all of the blocks and establishing a peer list.
func (e *Environment) initializeNetwork(rpcPort uint16, nobootstrap bool) (err error) {
	e.server, err = network.NewTCPServer(rpcPort)
	if err != nil {
		return
	}

	e.server.Register("AcceptBlock", e.AcceptBlock)
	e.server.Register("AcceptTransaction", e.AcceptTransaction)
	e.server.Register("SendBlocks", e.SendBlocks)
	e.server.Register("NegotiateContract", e.NegotiateContract)
	e.server.Register("RetrieveFile", e.RetrieveFile)

	if nobootstrap {
		go e.listen()
		return
	}

	// establish an initial peer list
	if err = e.server.Bootstrap(); err != nil {
		return
	}

	// Download the blockchain, getting blocks one batch at a time until an
	// empty batch is sent.
	go func() {
		// Catch up the first time.
		go e.CatchUp(e.server.RandomPeer())

		// Every 2 minutes call CatchUp() on a random peer. This will help to
		// resolve synchronization issues and keep everybody on the same page
		// with regards to the longest chain. It's a bit of a hack but will
		// make the network substantially more robust.
		for {
			time.Sleep(time.Minute * 2)
			go e.CatchUp(e.RandomPeer())
		}
	}()

	go e.listen()

	return nil
}

// AcceptBlock sends the input block down a channel, where it will be dealt
// with by the Environment's listener.
func (e *Environment) AcceptBlock(b siacore.Block) error {
	e.blockChan <- b
	return nil
}

// AcceptTransaction sends the input transaction down a channel, where it will
// be dealt with by the Environment's listener.
func (e *Environment) AcceptTransaction(t siacore.Transaction) error {
	e.transactionChan <- t
	return nil
}

// processBlock is called by the environment's listener.
func (e *Environment) processBlock(b siacore.Block) {
	e.state.Lock()
	e.hostDatabase.Lock()
	e.host.Lock()
	defer e.state.Unlock()
	defer e.hostDatabase.Unlock()
	defer e.host.Unlock()

	initialStateHeight := e.state.Height()
	rewoundBlocks, appliedBlocks, err := e.state.AcceptBlock(b)

	// Perform error handling.
	if err == siacore.BlockKnownErr || err == siacore.KnownOrphanErr {
		return
	} else if err != nil {
		// Call CatchUp() if an unknown orphan is sent.
		if err == siacore.UnknownOrphanErr {
			go e.CatchUp(e.server.RandomPeer())
		}
		return
	}

	e.updateHostDB(rewoundBlocks, appliedBlocks)
	e.storageProofMaintenance(initialStateHeight, rewoundBlocks, appliedBlocks)

	// Broadcast all valid blocks.
	go e.server.Broadcast("AcceptBlock", b, nil)
}

// processTransaction sends a transaction to the state.
func (e *Environment) processTransaction(t siacore.Transaction) {
	e.state.Lock()
	defer e.state.Unlock()

	err := e.state.AcceptTransaction(t)
	if err != nil {
		if err != siacore.ConflictingTransactionErr {
			// TODO: Change this println to a logging statement.
			fmt.Println("AcceptTransaction Error:", err)
		}
		return
	}

	go e.server.Broadcast("AcceptTransaction", t, nil)
}

// listen waits until a new block or transaction arrives, then attempts to
// process and rebroadcast it.
func (e *Environment) listen() {
	for {
		select {
		case b := <-e.blockChan:
			e.processBlock(b)

		case t := <-e.transactionChan:
			e.processTransaction(t)
		}
	}
}
<commit_msg>create downloads directory<commit_after>package main

import (
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/NebulousLabs/Andromeda/network"
	"github.com/NebulousLabs/Andromeda/siacore"
)

// Environment is the struct that serves as the state for siad. It contains a
// pointer to the state, as things like a wallet, a friend list, etc. Each
// environment should have its own state.
type Environment struct {
	state *siacore.State

	server       *network.TCPServer
	host         *Host
	hostDatabase *HostDatabase
	renter       *Renter
	wallet       *Wallet

	friends map[string]siacore.CoinAddress

	// Channels for incoming blocks and transactions to be processed
	blockChan       chan siacore.Block
	transactionChan chan siacore.Transaction

	// Mining variables. The mining variables are protected by the miningLock.
	// Any time that you read from or write to any of the mining variables, you
	// need to be under a lock.
	mining        bool         // true when mining
	miningThreads int          // number of processes mining at once
	miningLock    sync.RWMutex // prevents benign race conditions
}

// createEnvironment creates a server, host, miner, renter and wallet and
// puts it all in a single environment struct that's used as the state for the
// main package.
func CreateEnvironment(rpcPort uint16, apiPort uint16, nobootstrap bool) (e *Environment, err error) {
	e = &Environment{
		state:           siacore.CreateGenesisState(),
		friends:         make(map[string]siacore.CoinAddress),
		blockChan:       make(chan siacore.Block, 100),
		transactionChan: make(chan siacore.Transaction, 100),
	}

	// Initialize all internal structures.
	e.hostDatabase = CreateHostDatabase()
	e.host = CreateHost()
	e.renter = CreateRenter()
	e.wallet = CreateWallet(e.state)

	// Bootstrap to the network.
	err = e.initializeNetwork(rpcPort, nobootstrap)
	if err != nil {
		return
	}
	e.host.Settings.IPAddress = e.server.NetAddress()

	// create downloads directory
	err = os.MkdirAll("webpages/downloads", os.ModeDir|os.ModePerm)
	if err != nil {
		return
	}

	// Begin listening for requests on the api.
	e.setUpHandlers(apiPort)

	return
}

// Close does any finishing maintenence before the environment can be garbage
// collected. Right now that just means closing the server.
func (e *Environment) Close() {
	e.server.Close()
}

// initializeNetwork registers the rpcs and bootstraps to the network,
// downlading all of the blocks and establishing a peer list.
func (e *Environment) initializeNetwork(rpcPort uint16, nobootstrap bool) (err error) {
	e.server, err = network.NewTCPServer(rpcPort)
	if err != nil {
		return
	}

	e.server.Register("AcceptBlock", e.AcceptBlock)
	e.server.Register("AcceptTransaction", e.AcceptTransaction)
	e.server.Register("SendBlocks", e.SendBlocks)
	e.server.Register("NegotiateContract", e.NegotiateContract)
	e.server.Register("RetrieveFile", e.RetrieveFile)

	if nobootstrap {
		go e.listen()
		return
	}

	// establish an initial peer list
	if err = e.server.Bootstrap(); err != nil {
		return
	}

	// Download the blockchain, getting blocks one batch at a time until an
	// empty batch is sent.
	go func() {
		// Catch up the first time.
		go e.CatchUp(e.server.RandomPeer())

		// Every 2 minutes call CatchUp() on a random peer. This will help to
		// resolve synchronization issues and keep everybody on the same page
		// with regards to the longest chain. It's a bit of a hack but will
		// make the network substantially more robust.
		for {
			time.Sleep(time.Minute * 2)
			go e.CatchUp(e.RandomPeer())
		}
	}()

	go e.listen()

	return nil
}

// AcceptBlock sends the input block down a channel, where it will be dealt
// with by the Environment's listener.
func (e *Environment) AcceptBlock(b siacore.Block) error {
	e.blockChan <- b
	return nil
}

// AcceptTransaction sends the input transaction down a channel, where it will
// be dealt with by the Environment's listener.
func (e *Environment) AcceptTransaction(t siacore.Transaction) error {
	e.transactionChan <- t
	return nil
}

// processBlock is called by the environment's listener.
func (e *Environment) processBlock(b siacore.Block) {
	e.state.Lock()
	e.hostDatabase.Lock()
	e.host.Lock()
	defer e.state.Unlock()
	defer e.hostDatabase.Unlock()
	defer e.host.Unlock()

	initialStateHeight := e.state.Height()
	rewoundBlocks, appliedBlocks, err := e.state.AcceptBlock(b)

	// Perform error handling.
	if err == siacore.BlockKnownErr || err == siacore.KnownOrphanErr {
		return
	} else if err != nil {
		// Call CatchUp() if an unknown orphan is sent.
		if err == siacore.UnknownOrphanErr {
			go e.CatchUp(e.server.RandomPeer())
		}
		return
	}

	e.updateHostDB(rewoundBlocks, appliedBlocks)
	e.storageProofMaintenance(initialStateHeight, rewoundBlocks, appliedBlocks)

	// Broadcast all valid blocks.
	go e.server.Broadcast("AcceptBlock", b, nil)
}

// processTransaction sends a transaction to the state.
func (e *Environment) processTransaction(t siacore.Transaction) {
	e.state.Lock()
	defer e.state.Unlock()

	err := e.state.AcceptTransaction(t)
	if err != nil {
		if err != siacore.ConflictingTransactionErr {
			// TODO: Change this println to a logging statement.
			fmt.Println("AcceptTransaction Error:", err)
		}
		return
	}

	go e.server.Broadcast("AcceptTransaction", t, nil)
}

// listen waits until a new block or transaction arrives, then attempts to
// process and rebroadcast it.
func (e *Environment) listen() {
	for {
		select {
		case b := <-e.blockChan:
			e.processBlock(b)

		case t := <-e.transactionChan:
			e.processTransaction(t)
		}
	}
}
<|endoftext|>


<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/roles"
	"github.com/qor/qor/utils"
)

type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetValuer() func(interface{}, *qor.Context) interface{}
	HasPermission(roles.PermissionMode, *qor.Context) bool
	GetMetas() []Metaor
	GetResource() Resourcer
}

type Meta struct {
	Name          string
	FieldName     string
	Setter        func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer        func(interface{}, *qor.Context) interface{}
	Permission    *roles.Permission
	ResourceValue interface{}
}

func (meta Meta) GetName() string {
	return meta.Name
}

func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

func (meta *Meta) UpdateMeta() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.ValueOf(meta).Type())
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	var (
		scope       = &gorm.Scope{Value: meta.ResourceValue}
		nestedField = strings.Contains(meta.FieldName, ".")
		field       *gorm.StructField
		hasColumn   bool
	)

	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.ResourceValue), meta.FieldName)
		subScope := &gorm.Scope{Value: subModel.Interface()}
		field, hasColumn = getField(subScope.GetStructFields(), name)
	} else {
		field, hasColumn = getField(scope.GetStructFields(), meta.FieldName)
	}

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if field.Relationship != nil {
						if f.Field.CanAddr() && !scope.PrimaryKeyZero() {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						}
					}
					if f.Field.CanAddr() {
						return f.Field.Addr().Interface()
					} else {
						return f.Field.Interface()
					}
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Unsupported meta name %v for resource %v", meta.FieldName, reflect.TypeOf(meta.ResourceValue))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				value := metaValue.Value
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := now.Parse(str); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.ValueOf(value).Type(), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getField(fields []*gorm.StructField, name string) (*gorm.StructField, bool) {
	for _, field := range fields {
		if field.Name == name || field.DBName == name {
			return field, true
		}
	}
	return nil, false
}

// Profile.Name
func parseNestedField(value reflect.Value, name string) (reflect.Value, string) {
	fields := strings.Split(name, ".")
	value = reflect.Indirect(value)
	for _, field := range fields[:len(fields)-1] {
		value = value.FieldByName(field)
	}

	return value, fields[len(fields)-1]
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>Add SetSetter, SetValuer to resource.Meta<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/qor/qor"
	"github.com/qor/qor/roles"
	"github.com/qor/qor/utils"
)

type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetValuer() func(interface{}, *qor.Context) interface{}
	HasPermission(roles.PermissionMode, *qor.Context) bool
	GetMetas() []Metaor
	GetResource() Resourcer
}

type Meta struct {
	Name          string
	FieldName     string
	Setter        func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer        func(interface{}, *qor.Context) interface{}
	Permission    *roles.Permission
	ResourceValue interface{}
}

func (meta Meta) GetName() string {
	return meta.Name
}

func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

func (meta Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

func (meta Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

func (meta *Meta) UpdateMeta() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.ValueOf(meta).Type())
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	var (
		scope       = &gorm.Scope{Value: meta.ResourceValue}
		nestedField = strings.Contains(meta.FieldName, ".")
		field       *gorm.StructField
		hasColumn   bool
	)

	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.ResourceValue), meta.FieldName)
		subScope := &gorm.Scope{Value: subModel.Interface()}
		field, hasColumn = getField(subScope.GetStructFields(), name)
	} else {
		field, hasColumn = getField(scope.GetStructFields(), meta.FieldName)
	}

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if field.Relationship != nil {
						if f.Field.CanAddr() && !scope.PrimaryKeyZero() {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						}
					}
					if f.Field.CanAddr() {
						return f.Field.Addr().Interface()
					} else {
						return f.Field.Interface()
					}
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Unsupported meta name %v for resource %v", meta.FieldName, reflect.TypeOf(meta.ResourceValue))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				value := metaValue.Value
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := now.Parse(str); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.ValueOf(value).Type(), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getField(fields []*gorm.StructField, name string) (*gorm.StructField, bool) {
	for _, field := range fields {
		if field.Name == name || field.DBName == name {
			return field, true
		}
	}
	return nil, false
}

// Profile.Name
func parseNestedField(value reflect.Value, name string) (reflect.Value, string) {
	fields := strings.Split(name, ".")
	value = reflect.Indirect(value)
	for _, field := range fields[:len(fields)-1] {
		value = value.FieldByName(field)
	}

	return value, fields[len(fields)-1]
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>


<commit_before>package siri

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/af83/edwig/version"
	"github.com/jbowtie/gokogiri/xml"
	"golang.org/x/text/encoding/charmap"
)

type requestType int

const (
	DEFAULT requestType = iota
	SUBSCRIPTION
	NOTIFICATION
)

type Request interface {
	BuildXML() (string, error)
}

type SOAPClient struct {
	SOAPClientUrls
}

type SOAPClientUrls struct {
	Url              string
	SubscriptionsUrl string
	NotificationsUrl string
}

type soapClientArguments struct {
	request          Request
	requestType      requestType
	expectedResponse string
	acceptGzip       bool
}

func NewSOAPClient(urls SOAPClientUrls) *SOAPClient {
	return &SOAPClient{SOAPClientUrls: urls}
}

func (client *SOAPClient) responseFromFormat(body io.Reader, contentType string) io.Reader {
	r, _ := regexp.Compile("^text/xml;charset=([ -~]+)")
	s := r.FindStringSubmatch(contentType)
	if len(s) == 0 {
		return body
	}
	if s[1] == "ISO-8859-1" {
		return charmap.ISO8859_1.NewDecoder().Reader(body)
	}
	return body
}

func (client *SOAPClient) prepareAndSendRequest(args soapClientArguments) (xml.Node, error) {
	// Wrap the request XML
	soapEnvelope := NewSOAPEnvelopeBuffer()
	xml, err := args.request.BuildXML()
	if err != nil {
		return nil, err
	}

	soapEnvelope.WriteXML(xml)

	// For tests
	// logger.Log.Debugf("%v", soapEnvelope.String())

	// Create http request
	httpRequest, err := http.NewRequest("POST", client.getURL(args.requestType), soapEnvelope)
	if err != nil {
		return nil, err
	}
	if args.acceptGzip {
		httpRequest.Header.Set("Accept-Encoding", "gzip, deflate")
	}
	httpRequest.Header.Set("Content-Type", "text/xml; charset=utf-8")
	httpRequest.Header.Set("User-Agent", version.ApplicationName())
	httpRequest.ContentLength = soapEnvelope.Length()

	// Send http request
	httpClient := &http.Client{Timeout: time.Second * 5}
	response, err := httpClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	// Do nothing if request is a notification
	if args.requestType == NOTIFICATION {
		return nil, nil
	}

	// Check response status
	if response.StatusCode != http.StatusOK {
		return nil, NewSiriError(strings.Join([]string{"SIRI CRITICAL: HTTP status ", strconv.Itoa(response.StatusCode)}, ""))
	}

	if !strings.Contains(response.Header.Get("Content-Type"), "text/xml") {
		return nil, NewSiriError(fmt.Sprintf("SIRI CRITICAL: HTTP Content-Type %v", response.Header.Get("Content-Type")))
	}

	// Check if response is gzip
	var responseReader io.Reader
	if args.acceptGzip && response.Header.Get("Content-Encoding") == "gzip" {
		gzipReader, err := gzip.NewReader(response.Body)
		if err != nil {
			return nil, err
		}
		defer gzipReader.Close()
		responseReader = gzipReader
	} else {
		responseReader = client.responseFromFormat(response.Body, response.Header.Get("Content-Type"))
	}

	// Create SOAPEnvelope and check body type
	envelope, err := NewSOAPEnvelope(responseReader)
	if err != nil {
		return nil, err
	}
	if envelope.BodyType() != args.expectedResponse {
		return nil, NewSiriError(fmt.Sprintf("SIRI CRITICAL: Wrong Soap from server: %v", envelope.BodyType()))
	}
	return envelope.Body(), nil
}

func (client *SOAPClient) getURL(requestType requestType) string {
	switch requestType {
	case SUBSCRIPTION:
		if client.SubscriptionsUrl != "" {
			return client.SubscriptionsUrl
		}
	case NOTIFICATION:
		if client.NotificationsUrl != "" {
			return client.NotificationsUrl
		}
	}
	return client.Url
}

func (client *SOAPClient) CheckStatus(request *SIRICheckStatusRequest) (*XMLCheckStatusResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		expectedResponse: "CheckStatusResponse",
		acceptGzip:       true,
	})
	if err != nil {
		return nil, err
	}

	checkStatus := NewXMLCheckStatusResponse(node)
	return checkStatus, nil
}

func (client *SOAPClient) StopMonitoring(request *SIRIGetStopMonitoringRequest) (*XMLStopMonitoringResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		expectedResponse: "GetStopMonitoringResponse",
	})
	if err != nil {
		return nil, err
	}

	stopMonitoring := NewXMLStopMonitoringResponse(node)
	return stopMonitoring, nil
}

func (client *SOAPClient) SituationMonitoring(request *SIRIGetGeneralMessageRequest) (*XMLGeneralMessageResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		expectedResponse: "GetGeneralMessageResponse",
	})
	if err != nil {
		return nil, err
	}

	generalMessage := NewXMLGeneralMessageResponse(node)
	return generalMessage, nil
}

func (client *SOAPClient) StopMonitoringSubscription(request *SIRIStopMonitoringSubscriptionRequest) (*XMLSubscriptionResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		requestType:      SUBSCRIPTION,
		expectedResponse: "SubscribeResponse",
	})
	if err != nil {
		return nil, err
	}
	response := NewXMLSubscriptionResponse(node)
	return response, nil
}

func (client *SOAPClient) GeneralMessageSubscription(request *SIRIGeneralMessageSubscriptionRequest) (*XMLSubscriptionResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		requestType:      SUBSCRIPTION,
		expectedResponse: "SubscribeResponse",
	})
	if err != nil {
		return nil, err
	}
	response := NewXMLSubscriptionResponse(node)
	return response, nil
}

func (client *SOAPClient) DeleteSubscription(request *SIRIDeleteSubscriptionRequest) (*XMLDeleteSubscriptionResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		requestType:      SUBSCRIPTION,
		expectedResponse: "DeleteSubscriptionResponse",
	})
	if err != nil {
		return nil, err
	}

	terminatedSub := NewXMLDeleteSubscriptionResponse(node)
	return terminatedSub, nil
}

func (client *SOAPClient) NotifyStopMonitoring(request *SIRINotifyStopMonitoring) error {
	_, err := client.prepareAndSendRequest(soapClientArguments{
		request:     request,
		requestType: NOTIFICATION,
	})
	if err != nil {
		return err
	}
	return nil
}

func (client *SOAPClient) NotifyGeneralMessage(request *SIRINotifyGeneralMessage) error {
	_, err := client.prepareAndSendRequest(soapClientArguments{
		request:     request,
		requestType: NOTIFICATION,
	})
	if err != nil {
		return err
	}
	return nil
}

func (client *SOAPClient) NotifyEstimatedTimeTable(request *SIRINotifyEstimatedTimeTable) error {
	_, err := client.prepareAndSendRequest(soapClientArguments{
		request:     request,
		requestType: NOTIFICATION,
	})
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Change timeout for subscription requests<commit_after>package siri

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/af83/edwig/version"
	"github.com/jbowtie/gokogiri/xml"
	"golang.org/x/text/encoding/charmap"
)

type requestType int

const (
	DEFAULT requestType = iota
	SUBSCRIPTION
	NOTIFICATION
)

type Request interface {
	BuildXML() (string, error)
}

type SOAPClient struct {
	SOAPClientUrls
}

type SOAPClientUrls struct {
	Url              string
	SubscriptionsUrl string
	NotificationsUrl string
}

type soapClientArguments struct {
	request          Request
	requestType      requestType
	expectedResponse string
	acceptGzip       bool
}

func NewSOAPClient(urls SOAPClientUrls) *SOAPClient {
	return &SOAPClient{SOAPClientUrls: urls}
}

func (client *SOAPClient) responseFromFormat(body io.Reader, contentType string) io.Reader {
	r, _ := regexp.Compile("^text/xml;charset=([ -~]+)")
	s := r.FindStringSubmatch(contentType)
	if len(s) == 0 {
		return body
	}
	if s[1] == "ISO-8859-1" {
		return charmap.ISO8859_1.NewDecoder().Reader(body)
	}
	return body
}

func (client *SOAPClient) prepareAndSendRequest(args soapClientArguments) (xml.Node, error) {
	// Wrap the request XML
	soapEnvelope := NewSOAPEnvelopeBuffer()
	xml, err := args.request.BuildXML()
	if err != nil {
		return nil, err
	}

	soapEnvelope.WriteXML(xml)

	// For tests
	// logger.Log.Debugf("%v", soapEnvelope.String())

	// Create http request
	httpRequest, err := http.NewRequest("POST", client.getURL(args.requestType), soapEnvelope)
	if err != nil {
		return nil, err
	}
	if args.acceptGzip {
		httpRequest.Header.Set("Accept-Encoding", "gzip, deflate")
	}
	httpRequest.Header.Set("Content-Type", "text/xml; charset=utf-8")
	httpRequest.Header.Set("User-Agent", version.ApplicationName())
	httpRequest.ContentLength = soapEnvelope.Length()

	// Send http request
	httpClient := &http.Client{Timeout: getTimeOut(args.requestType)}
	response, err := httpClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()

	// Do nothing if request is a notification
	if args.requestType == NOTIFICATION {
		return nil, nil
	}

	// Check response status
	if response.StatusCode != http.StatusOK {
		return nil, NewSiriError(strings.Join([]string{"SIRI CRITICAL: HTTP status ", strconv.Itoa(response.StatusCode)}, ""))
	}

	if !strings.Contains(response.Header.Get("Content-Type"), "text/xml") {
		return nil, NewSiriError(fmt.Sprintf("SIRI CRITICAL: HTTP Content-Type %v", response.Header.Get("Content-Type")))
	}

	// Check if response is gzip
	var responseReader io.Reader
	if args.acceptGzip && response.Header.Get("Content-Encoding") == "gzip" {
		gzipReader, err := gzip.NewReader(response.Body)
		if err != nil {
			return nil, err
		}
		defer gzipReader.Close()
		responseReader = gzipReader
	} else {
		responseReader = client.responseFromFormat(response.Body, response.Header.Get("Content-Type"))
	}

	// Create SOAPEnvelope and check body type
	envelope, err := NewSOAPEnvelope(responseReader)
	if err != nil {
		return nil, err
	}
	if envelope.BodyType() != args.expectedResponse {
		return nil, NewSiriError(fmt.Sprintf("SIRI CRITICAL: Wrong Soap from server: %v", envelope.BodyType()))
	}
	return envelope.Body(), nil
}

func (client *SOAPClient) getURL(requestType requestType) string {
	switch requestType {
	case SUBSCRIPTION:
		if client.SubscriptionsUrl != "" {
			return client.SubscriptionsUrl
		}
	case NOTIFICATION:
		if client.NotificationsUrl != "" {
			return client.NotificationsUrl
		}
	}
	return client.Url
}

func getTimeOut(rt requestType) time.Duration {
	if rt == SUBSCRIPTION {
		return 30 * time.Second
	}
	return 5 * time.Second
}

func (client *SOAPClient) CheckStatus(request *SIRICheckStatusRequest) (*XMLCheckStatusResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		expectedResponse: "CheckStatusResponse",
		acceptGzip:       true,
	})
	if err != nil {
		return nil, err
	}

	checkStatus := NewXMLCheckStatusResponse(node)
	return checkStatus, nil
}

func (client *SOAPClient) StopMonitoring(request *SIRIGetStopMonitoringRequest) (*XMLStopMonitoringResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		expectedResponse: "GetStopMonitoringResponse",
	})
	if err != nil {
		return nil, err
	}

	stopMonitoring := NewXMLStopMonitoringResponse(node)
	return stopMonitoring, nil
}

func (client *SOAPClient) SituationMonitoring(request *SIRIGetGeneralMessageRequest) (*XMLGeneralMessageResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		expectedResponse: "GetGeneralMessageResponse",
	})
	if err != nil {
		return nil, err
	}

	generalMessage := NewXMLGeneralMessageResponse(node)
	return generalMessage, nil
}

func (client *SOAPClient) StopMonitoringSubscription(request *SIRIStopMonitoringSubscriptionRequest) (*XMLSubscriptionResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		requestType:      SUBSCRIPTION,
		expectedResponse: "SubscribeResponse",
	})
	if err != nil {
		return nil, err
	}
	response := NewXMLSubscriptionResponse(node)
	return response, nil
}

func (client *SOAPClient) GeneralMessageSubscription(request *SIRIGeneralMessageSubscriptionRequest) (*XMLSubscriptionResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		requestType:      SUBSCRIPTION,
		expectedResponse: "SubscribeResponse",
	})
	if err != nil {
		return nil, err
	}
	response := NewXMLSubscriptionResponse(node)
	return response, nil
}

func (client *SOAPClient) DeleteSubscription(request *SIRIDeleteSubscriptionRequest) (*XMLDeleteSubscriptionResponse, error) {
	node, err := client.prepareAndSendRequest(soapClientArguments{
		request:          request,
		requestType:      SUBSCRIPTION,
		expectedResponse: "DeleteSubscriptionResponse",
	})
	if err != nil {
		return nil, err
	}

	terminatedSub := NewXMLDeleteSubscriptionResponse(node)
	return terminatedSub, nil
}

func (client *SOAPClient) NotifyStopMonitoring(request *SIRINotifyStopMonitoring) error {
	_, err := client.prepareAndSendRequest(soapClientArguments{
		request:     request,
		requestType: NOTIFICATION,
	})
	if err != nil {
		return err
	}
	return nil
}

func (client *SOAPClient) NotifyGeneralMessage(request *SIRINotifyGeneralMessage) error {
	_, err := client.prepareAndSendRequest(soapClientArguments{
		request:     request,
		requestType: NOTIFICATION,
	})
	if err != nil {
		return err
	}
	return nil
}

func (client *SOAPClient) NotifyEstimatedTimeTable(request *SIRINotifyEstimatedTimeTable) error {
	_, err := client.prepareAndSendRequest(soapClientArguments{
		request:     request,
		requestType: NOTIFICATION,
	})
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>