<commit_before>package amalgam

import (
	"fmt"
	"log/syslog"
	"os"
	"sync"

	"github.com/inconshreveable/log15"
)

type LoggerStatsType struct {
	Warns  int `json:"warns"`
	Errors int `json:"errors"`
	Crits  int `json:"crits"`
}

var (
	LOGGER            log15.Logger
	LOGGER_STATS_Lock sync.Mutex
	LOGGER_STATS      = &LoggerStatsType{}
)

func LoggerStatter(r *log15.Record) error {
	LOGGER_STATS_Lock.Lock()
	defer LOGGER_STATS_Lock.Unlock()

	switch r.Lvl {
	case log15.LvlWarn:
		LOGGER_STATS.Warns += 1
	case log15.LvlError:
		LOGGER_STATS.Errors += 1
	case log15.LvlCrit:
		LOGGER_STATS.Crits += 1
	default:
	}

	return nil
}

func LvlFilterHandler(exactLvl log15.Lvl, h log15.Handler) log15.Handler {
	return log15.FilterHandler(func(r *log15.Record) (pass bool) {
		return r.Lvl == exactLvl
	}, h)
}

// Logging verbosity level, from 0 (nothing) upwards.
func SetLoggingVerbosity(level int) {
	// TODO: make this guy pick the binary name instead of hardcoding
	logfile_error := fmt.Sprintf("/tmp/claimator__errorLogger.log")
	logfile_info := fmt.Sprintf("/tmp/claimator__infoLogger.log")
	logfile_general := fmt.Sprintf("/tmp/claimator__generalLogger.log")

	LOGGER = log15.Root()

	LOGGER.SetHandler(
		log15.MultiHandler(
			log15.LvlFilterHandler(
				log15.Lvl(level),
				log15.CallerFuncHandler(
					log15.CallerStackHandler(
						"%+v",
						log15.MultiHandler(
							log15.Must.FileHandler(logfile_general, log15.LogfmtFormat()),
							log15.Must.SyslogNetHandler("udp",
								"logs5.papertrailapp.com:53174",
								syslog.LOG_DEBUG|syslog.LOG_USER, "claimator",
								log15.LogfmtFormat()),
							log15.StreamHandler(os.Stderr, log15.TerminalFormat()),
							log15.FuncHandler(LoggerStatter),
						),
					),
				),
			),
			log15.LvlFilterHandler(
				log15.Lvl(log15.LvlError),
				log15.CallerFuncHandler(
					log15.CallerStackHandler(
						"%+v",
						log15.Must.FileHandler(logfile_error, log15.LogfmtFormat()),
					),
				),
			),
			LvlFilterHandler(
				log15.Lvl(log15.LvlInfo),
				log15.Must.FileHandler(logfile_info, log15.LogfmtFormat()),
			)),
	)

	LOGGER.Debug(
		"logger_initialized", log15.Ctx{
			"logfile": logfile_general,
			"pid":     os.Getpid(),
			"level":   log15.Lvl(level).String(),
		},
	)
}
<commit_msg>changing papertrail logging destination<commit_after>package amalgam

import (
	"fmt"
	"log/syslog"
	"os"
	"sync"

	"github.com/inconshreveable/log15"
)

type LoggerStatsType struct {
	Warns  int `json:"warns"`
	Errors int `json:"errors"`
	Crits  int `json:"crits"`
}

var (
	LOGGER            log15.Logger
	LOGGER_STATS_Lock sync.Mutex
	LOGGER_STATS      = &LoggerStatsType{}
)

func LoggerStatter(r *log15.Record) error {
	LOGGER_STATS_Lock.Lock()
	defer LOGGER_STATS_Lock.Unlock()

	switch r.Lvl {
	case log15.LvlWarn:
		LOGGER_STATS.Warns += 1
	case log15.LvlError:
		LOGGER_STATS.Errors += 1
	case log15.LvlCrit:
		LOGGER_STATS.Crits += 1
	default:
	}

	return nil
}

func LvlFilterHandler(exactLvl log15.Lvl, h log15.Handler) log15.Handler {
	return log15.FilterHandler(func(r *log15.Record) (pass bool) {
		return r.Lvl == exactLvl
	}, h)
}

// Logging verbosity level, from 0 (nothing) upwards.
func SetLoggingVerbosity(level int) {
	// TODO: make this guy pick the binary name instead of hardcoding
	logfile_error := fmt.Sprintf("/tmp/claimator__errorLogger.log")
	logfile_info := fmt.Sprintf("/tmp/claimator__infoLogger.log")
	logfile_general := fmt.Sprintf("/tmp/claimator__generalLogger.log")

	LOGGER = log15.Root()

	LOGGER.SetHandler(
		log15.MultiHandler(
			log15.LvlFilterHandler(
				log15.Lvl(level),
				log15.CallerFuncHandler(
					log15.CallerStackHandler(
						"%+v",
						log15.MultiHandler(
							log15.Must.FileHandler(logfile_general, log15.LogfmtFormat()),
							log15.Must.SyslogNetHandler("udp",
								"logs5.papertrailapp.com:32438",
								syslog.LOG_DEBUG|syslog.LOG_USER, "claimator",
								log15.LogfmtFormat()),
							log15.StreamHandler(os.Stderr, log15.TerminalFormat()),
							log15.FuncHandler(LoggerStatter),
						),
					),
				),
			),
			log15.LvlFilterHandler(
				log15.Lvl(log15.LvlError),
				log15.CallerFuncHandler(
					log15.CallerStackHandler(
						"%+v",
						log15.Must.FileHandler(logfile_error, log15.LogfmtFormat()),
					),
				),
			),
			LvlFilterHandler(
				log15.Lvl(log15.LvlInfo),
				log15.Must.FileHandler(logfile_info, log15.LogfmtFormat()),
			)),
	)

	LOGGER.Debug(
		"logger_initialized", log15.Ctx{
			"logfile": logfile_general,
			"pid":     os.Getpid(),
			"level":   log15.Lvl(level).String(),
		},
	)
}
<|endoftext|>


<commit_before>package tutum

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

/*
func ListActions
Returns : Array of Action objects
*/
func ListActions() (ActionListResponse, error) {
	url := "action/"
	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)
	var response ActionListResponse
	var finalResponse ActionListResponse
	data, err := TutumCall(url, request, body)
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return response, err
	}

	finalResponse = response

Loop:
	for {
		if response.Meta.Next != "" {
			var nextResponse ActionListResponse
			data, err := TutumCall(response.Meta.Next[8:], request, body)
			if err != nil {
				return nextResponse, err
			}
			err = json.Unmarshal(data, &nextResponse)
			if err != nil {
				return nextResponse, err
			}
			finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...)
			response = nextResponse

		} else {
			break Loop
		}
	}

	return finalResponse, nil
}

/*
func GetAction
Argument : uuid
Returns : Action JSON object
*/
func GetAction(uuid string) (Action, error) {

	url := ""
	if string(uuid[0]) == "/" {
		url = uuid[8:]
	} else {
		url = "action/" + uuid + "/"
	}

	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)
	var response Action

	data, err := TutumCall(url, request, body)
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return response, err
	}

	return response, nil
}

/*
func GetLogs
Argument : a channel of type string for the output
*/

func (self *Action) GetLogs(c chan Logs) {
	endpoint := "action/" + self.Uuid + "/logs/?user=" + User + "&token=" + ApiKey
	url := StreamUrl + endpoint

	header := http.Header{}
	header.Add("User-Agent", customUserAgent)

	var Dialer websocket.Dialer
	ws, _, err := Dialer.Dial(url, header)
	if err != nil {
		log.Println(err)
	}

	var msg Logs
	for {
		if err = ws.ReadJSON(&msg); err != nil {
			if err != nil && err.Error() != "EOF" {
				log.Println(err)
			} else {
				break
			}
		}
		c <- msg
	}
}
<commit_msg>add retry and cancel endpoint for actions<commit_after>package tutum

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

/*
func ListActions
Returns : Array of Action objects
*/
func ListActions() (ActionListResponse, error) {
	url := "action/"
	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)
	var response ActionListResponse
	var finalResponse ActionListResponse
	data, err := TutumCall(url, request, body)
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return response, err
	}

	finalResponse = response

Loop:
	for {
		if response.Meta.Next != "" {
			var nextResponse ActionListResponse
			data, err := TutumCall(response.Meta.Next[8:], request, body)
			if err != nil {
				return nextResponse, err
			}
			err = json.Unmarshal(data, &nextResponse)
			if err != nil {
				return nextResponse, err
			}
			finalResponse.Objects = append(finalResponse.Objects, nextResponse.Objects...)
			response = nextResponse

		} else {
			break Loop
		}
	}

	return finalResponse, nil
}

/*
func GetAction
Argument : uuid
Returns : Action JSON object
*/
func GetAction(uuid string) (Action, error) {

	url := ""
	if string(uuid[0]) == "/" {
		url = uuid[8:]
	} else {
		url = "action/" + uuid + "/"
	}

	request := "GET"
	//Empty Body Request
	body := []byte(`{}`)
	var response Action

	data, err := TutumCall(url, request, body)
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return response, err
	}

	return response, nil
}

/*
func GetLogs
Argument : a channel of type string for the output
*/

func (self *Action) GetLogs(c chan Logs) {
	endpoint := "action/" + self.Uuid + "/logs/?user=" + User + "&token=" + ApiKey
	url := StreamUrl + endpoint

	header := http.Header{}
	header.Add("User-Agent", customUserAgent)

	var Dialer websocket.Dialer
	ws, _, err := Dialer.Dial(url, header)
	if err != nil {
		log.Println(err)
	}

	var msg Logs
	for {
		if err = ws.ReadJSON(&msg); err != nil {
			if err != nil && err.Error() != "EOF" {
				log.Println(err)
			} else {
				break
			}
		}
		c <- msg
	}
}

func (self *Action) Cancel() (Action, error) {
	url := ""
	if string(self.Uuid[0]) == "/" {
		url = self.Uuid[8:]
	} else {
		url = "action/" + self.Uuid + "/cancel/"
	}

	request := "POST"
	//Empty Body Request
	body := []byte(`{}`)
	var response Action

	data, err := TutumCall(url, request, body)
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return response, err
	}

	return response, nil
}

func (self *Action) Retry() (Action, error) {
	url := ""
	if string(self.Uuid[0]) == "/" {
		url = self.Uuid[8:]
	} else {
		url = "action/" + self.Uuid + "/retry/"
	}

	request := "POST"
	//Empty Body Request
	body := []byte(`{}`)
	var response Action

	data, err := TutumCall(url, request, body)
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		return response, err
	}

	return response, nil
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/json"
	"fmt"
	"github.com/alanctgardner/gogen-avro/generator"
	"strings"
)

const UTIL_FILE = "primitive.go"

/*
  An Avro qualified name, which includes an optional namespace and the type name.
*/
type QualifiedName struct {
	Namespace string
	Name      string
}

func (q QualifiedName) String() string {
	if q.Namespace == "" {
		return q.Name
	}
	return q.Namespace + "." + q.Name
}

type Schema struct {
	Root       AvroType
	JSONSchema []byte
}

/*
  Namespace is a mapping of QualifiedNames to their Definitions, used to resolve
  type lookups within a schema.
*/
type Namespace struct {
	Definitions map[QualifiedName]Definition
	Schemas     []Schema
}

func NewNamespace() *Namespace {
	return &Namespace{
		Definitions: make(map[QualifiedName]Definition),
		Schemas:     make([]Schema, 0),
	}
}

func (namespace *Namespace) AddToPackage(p *generator.Package, headerComment string, containers bool) error {
	for _, schema := range namespace.Schemas {
		err := schema.Root.ResolveReferences(namespace)
		if err != nil {
			return err
		}

		schema.Root.AddStruct(p, containers)
		schema.Root.AddSerializer(p)
		schema.Root.AddDeserializer(p)
	}

	for _, f := range p.Files() {
		p.AddHeader(f, headerComment)
	}
	return nil
}

/*
  Add a new type definition to the namespace. Returns an error if the type is already defined.
*/
func (n *Namespace) RegisterDefinition(d Definition) error {
	if _, ok := n.Definitions[d.AvroName()]; ok {
		return fmt.Errorf("Conflicting definitions for %v", d.AvroName())
	}
	n.Definitions[d.AvroName()] = d

	for _, alias := range d.Aliases() {
		if _, ok := n.Definitions[alias]; ok {
			return fmt.Errorf("Conflicting alias for %v - %v", d.AvroName(), alias)
		}
		n.Definitions[alias] = d
	}
	return nil
}

/*
  Parse a name according to the Avro spec:
  - If the name contains a dot ('.'), the last part is the name and the rest is the namespace
  - Otherwise, the enclosing namespace is used
*/
func ParseAvroName(enclosing, name string) QualifiedName {
	lastIndex := strings.LastIndex(name, ".")
	if lastIndex != -1 {
		return QualifiedName{name[:lastIndex], name[lastIndex+1:]}
	}
	return QualifiedName{enclosing, name}
}

/*
  Given an Avro schema as a JSON string, decode it and return the AvroType defined at the top level:
    - a single record definition (JSON map)
    - a union of multiple types (JSON array)
    - an already-defined type (JSON string)

The Avro type defined at the top level and all the type definitions beneath it will also be added to this Namespace.
*/
func (n *Namespace) TypeForSchema(schemaJson []byte) (AvroType, error) {
	var schema interface{}
	if err := json.Unmarshal(schemaJson, &schema); err != nil {
		return nil, err
	}

	field, err := n.decodeTypeDefinition("", schema)
	if err != nil {
		return nil, err
	}

	n.Schemas = append(n.Schemas, Schema{field, schemaJson})
	return field, nil
}

func (n *Namespace) decodeTypeDefinition(namespace string, schema interface{}) (AvroType, error) {
	switch schema.(type) {
	case string:
		typeStr := schema.(string)
		return n.getTypeByName(namespace, typeStr, schema), nil

	case []interface{}:
		return n.decodeUnionDefinition(namespace, schema.([]interface{}))

	case map[string]interface{}:
		return n.decodeComplexDefinition(namespace, schema.(map[string]interface{}))

	}

	return nil, NewWrongMapValueTypeError("type", "array, string, map", schema)
}

/*
   Given a map representing a record definition, validate the definition and build the RecordDefinition struct.
*/
func (n *Namespace) decodeRecordDefinition(namespace string, schemaMap map[string]interface{}) (Definition, error) {
	typeStr, err := getMapString(schemaMap, "type")
	if err != nil {
		return nil, err
	}

	if typeStr != "record" {
		return nil, fmt.Errorf("Type of record must be 'record'")
	}

	name, err := getMapString(schemaMap, "name")
	if err != nil {
		return nil, err
	}

	if _, ok := schemaMap["namespace"]; ok {
		namespace, err = getMapString(schemaMap, "namespace")
		if err != nil {
			return nil, err
		}
	}

	fieldList, err := getMapArray(schemaMap, "fields")
	if err != nil {
		return nil, err
	}

	decodedFields := make([]*Field, 0)
	for _, f := range fieldList {
		field, ok := f.(map[string]interface{})
		if !ok {
			return nil, NewWrongMapValueTypeError("fields", "map[]", field)
		}

		fieldName, err := getMapString(field, "name")
		if err != nil {
			return nil, err
		}

		t, ok := field["type"]
		if !ok {
			return nil, NewRequiredMapKeyError("type")
		}

		fieldType, err := n.decodeTypeDefinition(namespace, t)
		if err != nil {
			return nil, err
		}

		def, hasDef := field["default"]
		fieldStruct := NewField(fieldName, fieldType, def, hasDef, field)

		decodedFields = append(decodedFields, fieldStruct)
	}

	aliases, err := parseAliases(schemaMap, namespace)
	if err != nil {
		return nil, err
	}

	return NewRecordDefinition(ParseAvroName(namespace, name), aliases, decodedFields, schemaMap), nil
}

/* Given a map representing an enum definition, validate the definition and build the EnumDefinition struct.
 */
func (n *Namespace) decodeEnumDefinition(namespace string, schemaMap map[string]interface{}) (Definition, error) {
	typeStr, err := getMapString(schemaMap, "type")
	if err != nil {
		return nil, err
	}

	if typeStr != "enum" {
		return nil, fmt.Errorf("Type of enum must be 'enum'")
	}

	if _, ok := schemaMap["namespace"]; ok {
		namespace, err = getMapString(schemaMap, "namespace")
		if err != nil {
			return nil, err
		}
	}

	name, err := getMapString(schemaMap, "name")
	if err != nil {
		return nil, err
	}

	symbolSlice, err := getMapArray(schemaMap, "symbols")
	if err != nil {
		return nil, err
	}

	symbolStr, ok := interfaceSliceToStringSlice(symbolSlice)
	if !ok {
		return nil, fmt.Errorf("'symbols' must be an array of strings")
	}

	aliases, err := parseAliases(schemaMap, namespace)
	if err != nil {
		return nil, err
	}

	return NewEnumDefinition(ParseAvroName(namespace, name), aliases, symbolStr, schemaMap), nil
}

/* Given a map representing a fixed definition, validate the definition and build the FixedDefinition struct. */
func (n *Namespace) decodeFixedDefinition(namespace string, schemaMap map[string]interface{}) (Definition, error) {
	typeStr, err := getMapString(schemaMap, "type")
	if err != nil {
		return nil, err
	}

	if typeStr != "fixed" {
		return nil, fmt.Errorf("Type of fixed must be 'fixed'")
	}

	if _, ok := schemaMap["namespace"]; ok {
		namespace, err = getMapString(schemaMap, "namespace")
		if err != nil {
			return nil, err
		}
	}

	name, err := getMapString(schemaMap, "name")
	if err != nil {
		return nil, err
	}

	sizeBytes, err := getMapFloat(schemaMap, "size")
	if err != nil {
		return nil, err
	}

	aliases, err := parseAliases(schemaMap, namespace)
	if err != nil {
		return nil, err
	}

	return NewFixedDefinition(ParseAvroName(namespace, name), aliases, int(sizeBytes), schemaMap), nil
}

func (n *Namespace) decodeUnionDefinition(namespace string, fieldList []interface{}) (AvroType, error) {
	unionFields := make([]AvroType, 0)
	for _, f := range fieldList {
		fieldDef, err := n.decodeTypeDefinition(namespace, f)
		if err != nil {
			return nil, err
		}

		unionFields = append(unionFields, fieldDef)
	}
	return NewUnionField(unionFields, fieldList), nil
}

func (n *Namespace) decodeComplexDefinition(namespace string, typeMap map[string]interface{}) (AvroType, error) {
	typeStr, err := getMapString(typeMap, "type")
	if err != nil {
		return nil, err
	}
	switch typeStr {
	case "array":
		items, ok := typeMap["items"]
		if !ok {
			return nil, NewRequiredMapKeyError("items")
		}

		fieldType, err := n.decodeTypeDefinition(namespace, items)
		if err != nil {
			return nil, err
		}

		return NewArrayField(fieldType, typeMap), nil

	case "map":
		values, ok := typeMap["values"]
		if !ok {
			return nil, NewRequiredMapKeyError("values")
		}

		fieldType, err := n.decodeTypeDefinition(namespace, values)
		if err != nil {
			return nil, err
		}

		return NewMapField(fieldType, typeMap), nil

	case "enum":
		definition, err := n.decodeEnumDefinition(namespace, typeMap)
		if err != nil {
			return nil, err
		}

		err = n.RegisterDefinition(definition)
		if err != nil {
			return nil, err
		}
		return NewReference(definition.AvroName()), nil

	case "fixed":
		definition, err := n.decodeFixedDefinition(namespace, typeMap)
		if err != nil {
			return nil, err
		}

		err = n.RegisterDefinition(definition)
		if err != nil {
			return nil, err
		}

		return NewReference(definition.AvroName()), nil

	case "record":
		definition, err := n.decodeRecordDefinition(namespace, typeMap)
		if err != nil {
			return nil, err
		}

		err = n.RegisterDefinition(definition)
		if err != nil {
			return nil, err
		}

		return NewReference(definition.AvroName()), nil

	default:
		// If the type isn't a special case, it's a primitive or a reference to an existing type
		return n.getTypeByName(namespace, typeStr, typeMap), nil
	}
}

func (n *Namespace) getTypeByName(namespace string, typeStr string, definition interface{}) AvroType {
	switch typeStr {
	case "int":
		return NewIntField(definition)

	case "long":
		return NewLongField(definition)

	case "float":
		return NewFloatField(definition)

	case "double":
		return NewDoubleField(definition)

	case "boolean":
		return NewBoolField(definition)

	case "bytes":
		return NewBytesField(definition)

	case "string":
		return NewStringField(definition)

	case "null":
		return NewNullField(definition)
	}

	return NewReference(ParseAvroName(namespace, typeStr))
}

/*
  Parse out all the aliases from a definition map - returns an empty slice if no aliases exist.
  Returns an error if the aliases key exists but the value isn't a list of strings.
*/
func parseAliases(objectMap map[string]interface{}, namespace string) ([]QualifiedName, error) {
	aliases, ok := objectMap["aliases"]
	if !ok {
		return make([]QualifiedName, 0), nil
	}

	aliasList, ok := aliases.([]interface{})
	if !ok {
		return nil, fmt.Errorf("Field aliases expected to be array, got %v", aliases)
	}

	qualifiedAliases := make([]QualifiedName, 0, len(aliasList))

	for _, alias := range aliasList {
		aliasString, ok := alias.(string)
		if !ok {
			return nil, fmt.Errorf("Field aliases expected to be array of strings, got %v", aliases)
		}
		qualifiedAliases = append(qualifiedAliases, ParseAvroName(namespace, aliasString))
	}
	return qualifiedAliases, nil
}
<commit_msg>Inspect duplicate definitions for equality.<commit_after>package types

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

	"github.com/alanctgardner/gogen-avro/generator"
)

const UTIL_FILE = "primitive.go"

/*
  An Avro qualified name, which includes an optional namespace and the type name.
*/
type QualifiedName struct {
	Namespace string
	Name      string
}

func (q QualifiedName) String() string {
	if q.Namespace == "" {
		return q.Name
	}
	return q.Namespace + "." + q.Name
}

type Schema struct {
	Root       AvroType
	JSONSchema []byte
}

/*
  Namespace is a mapping of QualifiedNames to their Definitions, used to resolve
  type lookups within a schema.
*/
type Namespace struct {
	Definitions map[QualifiedName]Definition
	Schemas     []Schema
}

func NewNamespace() *Namespace {
	return &Namespace{
		Definitions: make(map[QualifiedName]Definition),
		Schemas:     make([]Schema, 0),
	}
}

func (namespace *Namespace) AddToPackage(p *generator.Package, headerComment string, containers bool) error {
	for _, schema := range namespace.Schemas {
		err := schema.Root.ResolveReferences(namespace)
		if err != nil {
			return err
		}

		schema.Root.AddStruct(p, containers)
		schema.Root.AddSerializer(p)
		schema.Root.AddDeserializer(p)
	}

	for _, f := range p.Files() {
		p.AddHeader(f, headerComment)
	}
	return nil
}

/*
  Add a new type definition to the namespace. Returns an error if the type is already defined.
*/
func (n *Namespace) RegisterDefinition(d Definition) error {
	if curDef, ok := n.Definitions[d.AvroName()]; ok {
		if !reflect.DeepEqual(curDef, d) {
			return fmt.Errorf("Conflicting definitions for %v", d.AvroName())
		}
	}
	n.Definitions[d.AvroName()] = d

	for _, alias := range d.Aliases() {
		if _, ok := n.Definitions[alias]; ok {
			return fmt.Errorf("Conflicting alias for %v - %v", d.AvroName(), alias)
		}
		n.Definitions[alias] = d
	}
	return nil
}

/*
  Parse a name according to the Avro spec:
  - If the name contains a dot ('.'), the last part is the name and the rest is the namespace
  - Otherwise, the enclosing namespace is used
*/
func ParseAvroName(enclosing, name string) QualifiedName {
	lastIndex := strings.LastIndex(name, ".")
	if lastIndex != -1 {
		return QualifiedName{name[:lastIndex], name[lastIndex+1:]}
	}
	return QualifiedName{enclosing, name}
}

/*
  Given an Avro schema as a JSON string, decode it and return the AvroType defined at the top level:
    - a single record definition (JSON map)
    - a union of multiple types (JSON array)
    - an already-defined type (JSON string)

The Avro type defined at the top level and all the type definitions beneath it will also be added to this Namespace.
*/
func (n *Namespace) TypeForSchema(schemaJson []byte) (AvroType, error) {
	var schema interface{}
	if err := json.Unmarshal(schemaJson, &schema); err != nil {
		return nil, err
	}

	field, err := n.decodeTypeDefinition("", schema)
	if err != nil {
		return nil, err
	}

	n.Schemas = append(n.Schemas, Schema{field, schemaJson})
	return field, nil
}

func (n *Namespace) decodeTypeDefinition(namespace string, schema interface{}) (AvroType, error) {
	switch schema.(type) {
	case string:
		typeStr := schema.(string)
		return n.getTypeByName(namespace, typeStr, schema), nil

	case []interface{}:
		return n.decodeUnionDefinition(namespace, schema.([]interface{}))

	case map[string]interface{}:
		return n.decodeComplexDefinition(namespace, schema.(map[string]interface{}))

	}

	return nil, NewWrongMapValueTypeError("type", "array, string, map", schema)
}

/*
   Given a map representing a record definition, validate the definition and build the RecordDefinition struct.
*/
func (n *Namespace) decodeRecordDefinition(namespace string, schemaMap map[string]interface{}) (Definition, error) {
	typeStr, err := getMapString(schemaMap, "type")
	if err != nil {
		return nil, err
	}

	if typeStr != "record" {
		return nil, fmt.Errorf("Type of record must be 'record'")
	}

	name, err := getMapString(schemaMap, "name")
	if err != nil {
		return nil, err
	}

	if _, ok := schemaMap["namespace"]; ok {
		namespace, err = getMapString(schemaMap, "namespace")
		if err != nil {
			return nil, err
		}
	}

	fieldList, err := getMapArray(schemaMap, "fields")
	if err != nil {
		return nil, err
	}

	decodedFields := make([]*Field, 0)
	for _, f := range fieldList {
		field, ok := f.(map[string]interface{})
		if !ok {
			return nil, NewWrongMapValueTypeError("fields", "map[]", field)
		}

		fieldName, err := getMapString(field, "name")
		if err != nil {
			return nil, err
		}

		t, ok := field["type"]
		if !ok {
			return nil, NewRequiredMapKeyError("type")
		}

		fieldType, err := n.decodeTypeDefinition(namespace, t)
		if err != nil {
			return nil, err
		}

		def, hasDef := field["default"]
		fieldStruct := NewField(fieldName, fieldType, def, hasDef, field)

		decodedFields = append(decodedFields, fieldStruct)
	}

	aliases, err := parseAliases(schemaMap, namespace)
	if err != nil {
		return nil, err
	}

	return NewRecordDefinition(ParseAvroName(namespace, name), aliases, decodedFields, schemaMap), nil
}

/* Given a map representing an enum definition, validate the definition and build the EnumDefinition struct.
 */
func (n *Namespace) decodeEnumDefinition(namespace string, schemaMap map[string]interface{}) (Definition, error) {
	typeStr, err := getMapString(schemaMap, "type")
	if err != nil {
		return nil, err
	}

	if typeStr != "enum" {
		return nil, fmt.Errorf("Type of enum must be 'enum'")
	}

	if _, ok := schemaMap["namespace"]; ok {
		namespace, err = getMapString(schemaMap, "namespace")
		if err != nil {
			return nil, err
		}
	}

	name, err := getMapString(schemaMap, "name")
	if err != nil {
		return nil, err
	}

	symbolSlice, err := getMapArray(schemaMap, "symbols")
	if err != nil {
		return nil, err
	}

	symbolStr, ok := interfaceSliceToStringSlice(symbolSlice)
	if !ok {
		return nil, fmt.Errorf("'symbols' must be an array of strings")
	}

	aliases, err := parseAliases(schemaMap, namespace)
	if err != nil {
		return nil, err
	}

	return NewEnumDefinition(ParseAvroName(namespace, name), aliases, symbolStr, schemaMap), nil
}

/* Given a map representing a fixed definition, validate the definition and build the FixedDefinition struct. */
func (n *Namespace) decodeFixedDefinition(namespace string, schemaMap map[string]interface{}) (Definition, error) {
	typeStr, err := getMapString(schemaMap, "type")
	if err != nil {
		return nil, err
	}

	if typeStr != "fixed" {
		return nil, fmt.Errorf("Type of fixed must be 'fixed'")
	}

	if _, ok := schemaMap["namespace"]; ok {
		namespace, err = getMapString(schemaMap, "namespace")
		if err != nil {
			return nil, err
		}
	}

	name, err := getMapString(schemaMap, "name")
	if err != nil {
		return nil, err
	}

	sizeBytes, err := getMapFloat(schemaMap, "size")
	if err != nil {
		return nil, err
	}

	aliases, err := parseAliases(schemaMap, namespace)
	if err != nil {
		return nil, err
	}

	return NewFixedDefinition(ParseAvroName(namespace, name), aliases, int(sizeBytes), schemaMap), nil
}

func (n *Namespace) decodeUnionDefinition(namespace string, fieldList []interface{}) (AvroType, error) {
	unionFields := make([]AvroType, 0)
	for _, f := range fieldList {
		fieldDef, err := n.decodeTypeDefinition(namespace, f)
		if err != nil {
			return nil, err
		}

		unionFields = append(unionFields, fieldDef)
	}
	return NewUnionField(unionFields, fieldList), nil
}

func (n *Namespace) decodeComplexDefinition(namespace string, typeMap map[string]interface{}) (AvroType, error) {
	typeStr, err := getMapString(typeMap, "type")
	if err != nil {
		return nil, err
	}
	switch typeStr {
	case "array":
		items, ok := typeMap["items"]
		if !ok {
			return nil, NewRequiredMapKeyError("items")
		}

		fieldType, err := n.decodeTypeDefinition(namespace, items)
		if err != nil {
			return nil, err
		}

		return NewArrayField(fieldType, typeMap), nil

	case "map":
		values, ok := typeMap["values"]
		if !ok {
			return nil, NewRequiredMapKeyError("values")
		}

		fieldType, err := n.decodeTypeDefinition(namespace, values)
		if err != nil {
			return nil, err
		}

		return NewMapField(fieldType, typeMap), nil

	case "enum":
		definition, err := n.decodeEnumDefinition(namespace, typeMap)
		if err != nil {
			return nil, err
		}

		err = n.RegisterDefinition(definition)
		if err != nil {
			return nil, err
		}
		return NewReference(definition.AvroName()), nil

	case "fixed":
		definition, err := n.decodeFixedDefinition(namespace, typeMap)
		if err != nil {
			return nil, err
		}

		err = n.RegisterDefinition(definition)
		if err != nil {
			return nil, err
		}

		return NewReference(definition.AvroName()), nil

	case "record":
		definition, err := n.decodeRecordDefinition(namespace, typeMap)
		if err != nil {
			return nil, err
		}

		err = n.RegisterDefinition(definition)
		if err != nil {
			return nil, err
		}

		return NewReference(definition.AvroName()), nil

	default:
		// If the type isn't a special case, it's a primitive or a reference to an existing type
		return n.getTypeByName(namespace, typeStr, typeMap), nil
	}
}

func (n *Namespace) getTypeByName(namespace string, typeStr string, definition interface{}) AvroType {
	switch typeStr {
	case "int":
		return NewIntField(definition)

	case "long":
		return NewLongField(definition)

	case "float":
		return NewFloatField(definition)

	case "double":
		return NewDoubleField(definition)

	case "boolean":
		return NewBoolField(definition)

	case "bytes":
		return NewBytesField(definition)

	case "string":
		return NewStringField(definition)

	case "null":
		return NewNullField(definition)
	}

	return NewReference(ParseAvroName(namespace, typeStr))
}

/*
  Parse out all the aliases from a definition map - returns an empty slice if no aliases exist.
  Returns an error if the aliases key exists but the value isn't a list of strings.
*/
func parseAliases(objectMap map[string]interface{}, namespace string) ([]QualifiedName, error) {
	aliases, ok := objectMap["aliases"]
	if !ok {
		return make([]QualifiedName, 0), nil
	}

	aliasList, ok := aliases.([]interface{})
	if !ok {
		return nil, fmt.Errorf("Field aliases expected to be array, got %v", aliases)
	}

	qualifiedAliases := make([]QualifiedName, 0, len(aliasList))

	for _, alias := range aliasList {
		aliasString, ok := alias.(string)
		if !ok {
			return nil, fmt.Errorf("Field aliases expected to be array of strings, got %v", aliases)
		}
		qualifiedAliases = append(qualifiedAliases, ParseAvroName(namespace, aliasString))
	}
	return qualifiedAliases, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	noTime     time.Time
	timeFormat = time.RFC3339
)

func init() {
	flag.Parse()
}

func main() {
	existingTime := readExistingTime()
	now := time.Now()
	args := flag.Args()

	newTime, output := parseCommand(existingTime, now, args)

	if newTime != noTime {
		writeTime(newTime)
	}

	fmt.Println(output)
}

func parseCommand(existingTime time.Time, now time.Time, args []string) (newTime time.Time, output string) {
	if len(args) > 0 && args[0] == "status" {
		output = formatRemainingTime(existingTime, now)
	} else {
		duration, _ := time.ParseDuration("25m")
		newTime = now.Add(duration)
		output = "Timer started, 25 minutes remaining"
	}

	return
}

func formatRemainingTime(existingTime time.Time, now time.Time) string {
	remaining := existingTime.Sub(now)
	remainingMinutes := remaining.Minutes()
	return strconv.FormatFloat(remainingMinutes, 'f', 0, 64)
}

func writeTime(t time.Time) {
	bytes := []byte(t.Format(timeFormat))
	err := ioutil.WriteFile(filePath(), bytes, 0644)
	if err != nil {
		panic(err)
	}
}

func readExistingTime() time.Time {
	var noTime time.Time

	bytes, err := ioutil.ReadFile(filePath())
	if err != nil {
		return noTime
	}

	contents := string(bytes[:])
	contents = strings.TrimSpace(contents)

	result, err := time.Parse(timeFormat, contents)
	if err != nil {
		return noTime
	}

	return result
}

func filePath() string {
	return homeDir() + "/.pomodoro"
}

func homeDir() string {
	return os.Getenv("HOME")
}
<commit_msg>Remote duplicate noTime var<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"
)

var (
	noTime     time.Time
	timeFormat = time.RFC3339
)

func init() {
	flag.Parse()
}

func main() {
	existingTime := readExistingTime()
	now := time.Now()
	args := flag.Args()

	newTime, output := parseCommand(existingTime, now, args)

	if newTime != noTime {
		writeTime(newTime)
	}

	fmt.Println(output)
}

func parseCommand(existingTime time.Time, now time.Time, args []string) (newTime time.Time, output string) {
	if len(args) > 0 && args[0] == "status" {
		output = formatRemainingTime(existingTime, now)
	} else {
		duration, _ := time.ParseDuration("25m")
		newTime = now.Add(duration)
		output = "Timer started, 25 minutes remaining"
	}

	return
}

func formatRemainingTime(existingTime time.Time, now time.Time) string {
	remaining := existingTime.Sub(now)
	remainingMinutes := remaining.Minutes()
	return strconv.FormatFloat(remainingMinutes, 'f', 0, 64)
}

func writeTime(t time.Time) {
	bytes := []byte(t.Format(timeFormat))
	err := ioutil.WriteFile(filePath(), bytes, 0644)
	if err != nil {
		panic(err)
	}
}

func readExistingTime() time.Time {
	bytes, err := ioutil.ReadFile(filePath())
	if err != nil {
		return noTime
	}

	contents := string(bytes[:])
	contents = strings.TrimSpace(contents)

	result, err := time.Parse(timeFormat, contents)
	if err != nil {
		return noTime
	}

	return result
}

func filePath() string {
	return homeDir() + "/.pomodoro"
}

func homeDir() string {
	return os.Getenv("HOME")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime/pprof"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/exp"
	"github.com/prometheus/node_exporter/collector"
)

var (
	configFile        = flag.String("config", "node_exporter.conf", "config file.")
	memProfile        = flag.String("memprofile", "", "write memory profile to this file")
	listeningAddress  = flag.String("listen", ":8080", "address to listen on")
	enabledCollectors = flag.String("enabledCollectors", "attributes,diskstats,loadavg,meminfo,netdev", "comma seperated list of collectors to use")
	interval          = flag.Duration("interval", 60*time.Second, "refresh interval")
	scrapeDurations   = prometheus.NewDefaultHistogram()
	metricsUpdated    = prometheus.NewGauge()
)

func main() {
	flag.Parse()
	registry := prometheus.NewRegistry()
	collectors, err := loadCollectors(*configFile, registry)
	if err != nil {
		log.Fatalf("Couldn't load config and collectors: %s", err)
	}

	registry.Register("node_exporter_scrape_duration_seconds", "node_exporter: Duration of a scrape job.", prometheus.NilLabels, scrapeDurations)
	registry.Register("node_exporter_metrics_updated", "node_exporter: Number of metrics updated.", prometheus.NilLabels, metricsUpdated)

	glog.Infof("Enabled collectors:")
	for n, _ := range collectors {
		glog.Infof(" - %s", n)
	}

	sigHup := make(chan os.Signal)
	sigUsr1 := make(chan os.Signal)
	signal.Notify(sigHup, syscall.SIGHUP)
	signal.Notify(sigUsr1, syscall.SIGUSR1)

	go serveStatus(registry)

	glog.Infof("Starting initial collection")
	collect(collectors)

	tick := time.Tick(*interval)
	for {
		select {
		case <-sigHup:
			collectors, err = loadCollectors(*configFile, registry)
			if err != nil {
				log.Fatalf("Couldn't load config and collectors: %s", err)
			}
			glog.Infof("Reloaded collectors and config")
			tick = time.Tick(*interval)

		case <-tick:
			glog.Infof("Starting new interval")
			collect(collectors)

		case <-sigUsr1:
			glog.Infof("got signal")
			if *memProfile != "" {
				glog.Infof("Writing memory profile to %s", *memProfile)
				f, err := os.Create(*memProfile)
				if err != nil {
					log.Fatal(err)
				}
				pprof.WriteHeapProfile(f)
				f.Close()
			}
		}
	}

}

func loadCollectors(file string, registry prometheus.Registry) (map[string]collector.Collector, error) {
	collectors := map[string]collector.Collector{}
	config, err := getConfig(file)
	if err != nil {
		log.Fatalf("Couldn't read config %s: %s", file, err)
	}
	for _, name := range strings.Split(*enabledCollectors, ",") {
		fn, ok := collector.Factories[name]
		if !ok {
			log.Fatalf("Collector '%s' not available", name)
		}
		c, err := fn(*config, registry)
		if err != nil {
			return nil, err
		}
		collectors[name] = c
	}
	return collectors, nil
}

func getConfig(file string) (*collector.Config, error) {
	config := &collector.Config{}
	glog.Infof("Reading config %s", *configFile)
	bytes, err := ioutil.ReadFile(*configFile)
	if err != nil {
		return nil, err
	}
	return config, json.Unmarshal(bytes, &config)
}

func serveStatus(registry prometheus.Registry) {
	exp.Handle(prometheus.ExpositionResource, registry.Handler())
	http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux)
}

func collect(collectors map[string]collector.Collector) {
	wg := sync.WaitGroup{}
	wg.Add(len(collectors))
	for n, c := range collectors {
		go func(n string, c collector.Collector) {
			Execute(n, c)
			wg.Done()
		}(n, c)
	}
	wg.Wait()
}

func Execute(name string, c collector.Collector) {
	begin := time.Now()
	updates, err := c.Update()
	duration := time.Since(begin)

	label := map[string]string{
		"collector": name,
	}
	if err != nil {
		glog.Infof("ERROR: %s failed after %fs: %s", name, duration.Seconds(), err)
		label["result"] = "error"
	} else {
		glog.Infof("OK: %s success after %fs.", name, duration.Seconds())
		label["result"] = "success"
	}
	scrapeDurations.Add(label, duration.Seconds())
	metricsUpdated.Set(label, float64(updates))
}
<commit_msg>Add option to print out available collectors.<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"runtime/pprof"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/golang/glog"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/exp"
	"github.com/prometheus/node_exporter/collector"
)

var (
	configFile        = flag.String("config", "node_exporter.conf", "config file.")
	memProfile        = flag.String("memprofile", "", "write memory profile to this file")
	listeningAddress  = flag.String("listen", ":8080", "address to listen on")
	enabledCollectors = flag.String("enabledCollectors", "attributes,diskstats,loadavg,meminfo,netdev", "comma-seperated list of collectors to use")
	printCollectors   = flag.Bool("printCollectors", false, "If true, print available collectors and exit")
	interval          = flag.Duration("interval", 60*time.Second, "refresh interval")
	scrapeDurations   = prometheus.NewDefaultHistogram()
	metricsUpdated    = prometheus.NewGauge()
)

func main() {
	flag.Parse()
	if *printCollectors {
		fmt.Printf("Available collectors:\n")
		for n, _ := range collector.Factories {
			fmt.Printf(" - %s\n", n)
		}
		return
	}
	registry := prometheus.NewRegistry()
	collectors, err := loadCollectors(*configFile, registry)
	if err != nil {
		log.Fatalf("Couldn't load config and collectors: %s", err)
	}

	registry.Register("node_exporter_scrape_duration_seconds", "node_exporter: Duration of a scrape job.", prometheus.NilLabels, scrapeDurations)
	registry.Register("node_exporter_metrics_updated", "node_exporter: Number of metrics updated.", prometheus.NilLabels, metricsUpdated)

	glog.Infof("Enabled collectors:")
	for n, _ := range collectors {
		glog.Infof(" - %s", n)
	}

	sigHup := make(chan os.Signal)
	sigUsr1 := make(chan os.Signal)
	signal.Notify(sigHup, syscall.SIGHUP)
	signal.Notify(sigUsr1, syscall.SIGUSR1)

	go serveStatus(registry)

	glog.Infof("Starting initial collection")
	collect(collectors)

	tick := time.Tick(*interval)
	for {
		select {
		case <-sigHup:
			collectors, err = loadCollectors(*configFile, registry)
			if err != nil {
				log.Fatalf("Couldn't load config and collectors: %s", err)
			}
			glog.Infof("Reloaded collectors and config")
			tick = time.Tick(*interval)

		case <-tick:
			glog.Infof("Starting new interval")
			collect(collectors)

		case <-sigUsr1:
			glog.Infof("got signal")
			if *memProfile != "" {
				glog.Infof("Writing memory profile to %s", *memProfile)
				f, err := os.Create(*memProfile)
				if err != nil {
					log.Fatal(err)
				}
				pprof.WriteHeapProfile(f)
				f.Close()
			}
		}
	}

}

func loadCollectors(file string, registry prometheus.Registry) (map[string]collector.Collector, error) {
	collectors := map[string]collector.Collector{}
	config, err := getConfig(file)
	if err != nil {
		log.Fatalf("Couldn't read config %s: %s", file, err)
	}
	for _, name := range strings.Split(*enabledCollectors, ",") {
		fn, ok := collector.Factories[name]
		if !ok {
			log.Fatalf("Collector '%s' not available", name)
		}
		c, err := fn(*config, registry)
		if err != nil {
			return nil, err
		}
		collectors[name] = c
	}
	return collectors, nil
}

func getConfig(file string) (*collector.Config, error) {
	config := &collector.Config{}
	glog.Infof("Reading config %s", *configFile)
	bytes, err := ioutil.ReadFile(*configFile)
	if err != nil {
		return nil, err
	}
	return config, json.Unmarshal(bytes, &config)
}

func serveStatus(registry prometheus.Registry) {
	exp.Handle(prometheus.ExpositionResource, registry.Handler())
	http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux)
}

func collect(collectors map[string]collector.Collector) {
	wg := sync.WaitGroup{}
	wg.Add(len(collectors))
	for n, c := range collectors {
		go func(n string, c collector.Collector) {
			Execute(n, c)
			wg.Done()
		}(n, c)
	}
	wg.Wait()
}

func Execute(name string, c collector.Collector) {
	begin := time.Now()
	updates, err := c.Update()
	duration := time.Since(begin)

	label := map[string]string{
		"collector": name,
	}
	if err != nil {
		glog.Infof("ERROR: %s failed after %fs: %s", name, duration.Seconds(), err)
		label["result"] = "error"
	} else {
		glog.Infof("OK: %s success after %fs.", name, duration.Seconds())
		label["result"] = "success"
	}
	scrapeDurations.Add(label, duration.Seconds())
	metricsUpdated.Set(label, float64(updates))
}
<|endoftext|>


<commit_before>package oci

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/go-ini/ini"
)

func testConfig(accessConfFile *os.File) map[string]interface{} {
	return map[string]interface{}{

		"availability_domain": "aaaa:PHX-AD-3",
		"access_cfg_file":     accessConfFile.Name(),

		// Image
		"base_image_ocid": "ocd1...",
		"image_name":      "HelloWorld",

		// Networking
		"subnet_ocid": "ocd1...",

		// Comm
		"ssh_username":   "opc",
		"use_private_ip": false,
		"metadata": map[string]string{
			"key": "value",
		},
		"defined_tags": map[string]map[string]interface{}{
			"namespace": {"key": "value"},
		},

		// Instance Details
		"instance_name": "hello-world",
		"instance_tags": map[string]string{
			"key": "value",
		},
		"create_vnic_details": map[string]interface{}{
			"nsg_ids": []string{"ocd1..."},
		},
		"shape":     "VM.Standard1.1",
		"disk_size": 60,
	}
}

func TestConfig(t *testing.T) {
	// Shared set-up and deferred deletion

	cfg, keyFile, err := baseTestConfigWithTmpKeyFile()
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(keyFile.Name())

	cfgFile, err := writeTestConfig(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(cfgFile.Name())

	// Temporarily set $HOME to temp directory to bypass default
	// access config loading.

	tmpHome, err := ioutil.TempDir("", "packer_config_test")
	if err != nil {
		t.Fatalf("Unexpected error when creating temporary directory: %+v", err)
	}
	defer os.Remove(tmpHome)

	home := os.Getenv("HOME")
	os.Setenv("HOME", tmpHome)
	defer os.Setenv("HOME", home)

	// Config tests
	t.Run("BaseConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("NoAccessConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")

		var c Config
		errs := c.Prepare(raw)

		expectedErrors := []string{
			"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
		}

		s := errs.Error()
		for _, expected := range expectedErrors {
			if !strings.Contains(s, expected) {
				t.Errorf("Expected %q to contain '%s'", s, expected)
			}
		}
	})

	t.Run("AccessConfigTemplateOnly", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")
		raw["user_ocid"] = "ocid1..."
		raw["tenancy_ocid"] = "ocid1..."
		raw["fingerprint"] = "00:00..."
		raw["key_file"] = keyFile.Name()

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("err: %+v", errs)
		}

	})

	t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, err := c.configProvider.TenancyOCID()
		if err != nil {
			t.Fatalf("Unexpected error getting tenancy ocid: %v", err)
		}

		expected := "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
		if tenancy != expected {
			t.Errorf("Expected tenancy: %s, got %s.", expected, tenancy)
		}

	})

	t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, err := c.configProvider.Region()
		if err != nil {
			t.Fatalf("Unexpected error getting region: %v", err)
		}

		expected := "us-ashburn-1"
		if region != expected {
			t.Errorf("Expected region: %s, got %s.", expected, region)
		}

	})

	// Test the correct errors are produced when required template keys are
	// omitted.
	requiredKeys := []string{"availability_domain", "base_image_ocid", "shape", "subnet_ocid"}
	for _, k := range requiredKeys {
		t.Run(k+"_required", func(t *testing.T) {
			raw := testConfig(cfgFile)
			delete(raw, k)

			var c Config
			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}

	t.Run("ImageNameDefaultedIfEmpty", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "image_name")

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if !strings.Contains(c.ImageName, "packer-") {
			t.Errorf("got default ImageName %q, want image name 'packer-{{timestamp}}'", c.ImageName)
		}
	})

	t.Run("user_ocid_overridden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["user_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		user, _ := c.configProvider.UserOCID()
		if user != expected {
			t.Errorf("Expected ConfigProvider.UserOCID: %s, got %s", expected, user)
		}
	})

	t.Run("tenancy_ocid_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["tenancy_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, _ := c.configProvider.TenancyOCID()
		if tenancy != expected {
			t.Errorf("Expected ConfigProvider.TenancyOCID: %s, got %s", expected, tenancy)
		}
	})

	t.Run("region_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["region"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, _ := c.configProvider.Region()
		if region != expected {
			t.Errorf("Expected ConfigProvider.Region: %s, got %s", expected, region)
		}
	})

	t.Run("fingerprint_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["fingerprint"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration: %+v", errs)
		}

		fingerprint, _ := c.configProvider.KeyFingerprint()
		if fingerprint != expected {
			t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
		}
	})

	// Test the correct errors are produced when certain template keys
	// are present alongside use_instance_principals key.
	invalidKeys := []string{
		"access_cfg_file",
		"access_cfg_file_account",
		"user_ocid",
		"tenancy_ocid",
		"region",
		"fingerprint",
		"key_file",
		"pass_phrase",
	}
	for _, k := range invalidKeys {
		t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
			raw := testConfig(cfgFile)
			raw["use_instance_principals"] = "true"
			raw[k] = "some_random_value"

			var c Config

			c.configProvider = instancePrincipalConfigurationProviderMock{}

			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}
}

// BaseTestConfig creates the base (DEFAULT) config including a temporary key
// file.
// NOTE: Caller is responsible for removing temporary key file.
func baseTestConfigWithTmpKeyFile() (*ini.File, *os.File, error) {
	keyFile, err := generateRSAKeyFile()
	if err != nil {
		return nil, keyFile, err
	}
	// Build ini
	cfg := ini.Empty()
	section, _ := cfg.NewSection("DEFAULT")
	section.NewKey("region", "us-ashburn-1")
	section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("fingerprint", "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20")
	section.NewKey("key_file", keyFile.Name())

	return cfg, keyFile, nil
}

// WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
// NOTE: Caller is responsible for removing temporary file.
func writeTestConfig(cfg *ini.File) (*os.File, error) {
	confFile, err := ioutil.TempFile("", "config_file")
	if err != nil {
		return nil, err
	}

	if _, err := confFile.Write([]byte("[DEFAULT]\n")); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}

	if _, err := cfg.WriteTo(confFile); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}
	return confFile, nil
}

// generateRSAKeyFile generates an RSA key file for use in unit tests.
// NOTE: The caller is responsible for deleting the temporary file.
func generateRSAKeyFile() (*os.File, error) {
	// Create temporary file for the key
	f, err := ioutil.TempFile("", "key")
	if err != nil {
		return nil, err
	}

	// Generate key
	priv, err := rsa.GenerateKey(rand.Reader, 2014)
	if err != nil {
		return nil, err
	}

	// ASN.1 DER encoded form
	privDer := x509.MarshalPKCS1PrivateKey(priv)
	privBlk := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privDer,
	}

	// Write the key out
	if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
		return nil, err
	}

	return f, nil
}
<commit_msg>tests (builder/oracle-oci): test base image filter in config<commit_after>package oci

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"os"
	"strings"
	"testing"

	"github.com/go-ini/ini"
)

func testConfig(accessConfFile *os.File) map[string]interface{} {
	return map[string]interface{}{

		"availability_domain": "aaaa:PHX-AD-3",
		"access_cfg_file":     accessConfFile.Name(),

		// Image
		"base_image_ocid": "ocd1...",
		"image_name":      "HelloWorld",

		// Networking
		"subnet_ocid": "ocd1...",

		// Comm
		"ssh_username":   "opc",
		"use_private_ip": false,
		"metadata": map[string]string{
			"key": "value",
		},
		"defined_tags": map[string]map[string]interface{}{
			"namespace": {"key": "value"},
		},

		// Instance Details
		"instance_name": "hello-world",
		"instance_tags": map[string]string{
			"key": "value",
		},
		"create_vnic_details": map[string]interface{}{
			"nsg_ids": []string{"ocd1..."},
		},
		"shape":     "VM.Standard1.1",
		"disk_size": 60,
	}
}

func TestConfig(t *testing.T) {
	// Shared set-up and deferred deletion

	cfg, keyFile, err := baseTestConfigWithTmpKeyFile()
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(keyFile.Name())

	cfgFile, err := writeTestConfig(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(cfgFile.Name())

	// Temporarily set $HOME to temp directory to bypass default
	// access config loading.

	tmpHome, err := ioutil.TempDir("", "packer_config_test")
	if err != nil {
		t.Fatalf("Unexpected error when creating temporary directory: %+v", err)
	}
	defer os.Remove(tmpHome)

	home := os.Getenv("HOME")
	os.Setenv("HOME", tmpHome)
	defer os.Setenv("HOME", home)

	// Config tests
	t.Run("BaseConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("BaseImageFilterWithoutOCID", func(t *testing.T) {
		raw := testConfig(cfgFile)
		raw["base_image_ocid"] = ""
		raw["base_image_filter"] = map[string]interface{}{
			"display_name": "hello_world",
		}

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}
	})

	t.Run("BaseImageFilterDefault", func(t *testing.T) {
		raw := testConfig(cfgFile)

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if *c.BaseImageFilter.Shape != raw["shape"] {
			t.Fatalf("Default base_image_filter shape %v does not equal config shape %v",
				*c.BaseImageFilter.Shape, raw["shape"])
		}
	})

	t.Run("NoAccessConfig", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")

		var c Config
		errs := c.Prepare(raw)

		expectedErrors := []string{
			"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
		}

		s := errs.Error()
		for _, expected := range expectedErrors {
			if !strings.Contains(s, expected) {
				t.Errorf("Expected %q to contain '%s'", s, expected)
			}
		}
	})

	t.Run("AccessConfigTemplateOnly", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "access_cfg_file")
		raw["user_ocid"] = "ocid1..."
		raw["tenancy_ocid"] = "ocid1..."
		raw["fingerprint"] = "00:00..."
		raw["key_file"] = keyFile.Name()

		var c Config
		errs := c.Prepare(raw)

		if errs != nil {
			t.Fatalf("err: %+v", errs)
		}

	})

	t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, err := c.configProvider.TenancyOCID()
		if err != nil {
			t.Fatalf("Unexpected error getting tenancy ocid: %v", err)
		}

		expected := "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
		if tenancy != expected {
			t.Errorf("Expected tenancy: %s, got %s.", expected, tenancy)
		}

	})

	t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) {
		raw := testConfig(cfgFile)
		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, err := c.configProvider.Region()
		if err != nil {
			t.Fatalf("Unexpected error getting region: %v", err)
		}

		expected := "us-ashburn-1"
		if region != expected {
			t.Errorf("Expected region: %s, got %s.", expected, region)
		}

	})

	// Test the correct errors are produced when required template keys are
	// omitted.
	requiredKeys := []string{"availability_domain", "base_image_ocid", "shape", "subnet_ocid"}
	for _, k := range requiredKeys {
		t.Run(k+"_required", func(t *testing.T) {
			raw := testConfig(cfgFile)
			delete(raw, k)

			var c Config
			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}

	t.Run("ImageNameDefaultedIfEmpty", func(t *testing.T) {
		raw := testConfig(cfgFile)
		delete(raw, "image_name")

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		if !strings.Contains(c.ImageName, "packer-") {
			t.Errorf("got default ImageName %q, want image name 'packer-{{timestamp}}'", c.ImageName)
		}
	})

	t.Run("user_ocid_overridden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["user_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		user, _ := c.configProvider.UserOCID()
		if user != expected {
			t.Errorf("Expected ConfigProvider.UserOCID: %s, got %s", expected, user)
		}
	})

	t.Run("tenancy_ocid_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["tenancy_ocid"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		tenancy, _ := c.configProvider.TenancyOCID()
		if tenancy != expected {
			t.Errorf("Expected ConfigProvider.TenancyOCID: %s, got %s", expected, tenancy)
		}
	})

	t.Run("region_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["region"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration %+v", errs)
		}

		region, _ := c.configProvider.Region()
		if region != expected {
			t.Errorf("Expected ConfigProvider.Region: %s, got %s", expected, region)
		}
	})

	t.Run("fingerprint_overidden", func(t *testing.T) {
		expected := "override"
		raw := testConfig(cfgFile)
		raw["fingerprint"] = expected

		var c Config
		errs := c.Prepare(raw)
		if errs != nil {
			t.Fatalf("Unexpected error in configuration: %+v", errs)
		}

		fingerprint, _ := c.configProvider.KeyFingerprint()
		if fingerprint != expected {
			t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
		}
	})

	// Test the correct errors are produced when certain template keys
	// are present alongside use_instance_principals key.
	invalidKeys := []string{
		"access_cfg_file",
		"access_cfg_file_account",
		"user_ocid",
		"tenancy_ocid",
		"region",
		"fingerprint",
		"key_file",
		"pass_phrase",
	}
	for _, k := range invalidKeys {
		t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
			raw := testConfig(cfgFile)
			raw["use_instance_principals"] = "true"
			raw[k] = "some_random_value"

			var c Config

			c.configProvider = instancePrincipalConfigurationProviderMock{}

			errs := c.Prepare(raw)

			if !strings.Contains(errs.Error(), k) {
				t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
			}
		})
	}
}

// BaseTestConfig creates the base (DEFAULT) config including a temporary key
// file.
// NOTE: Caller is responsible for removing temporary key file.
func baseTestConfigWithTmpKeyFile() (*ini.File, *os.File, error) {
	keyFile, err := generateRSAKeyFile()
	if err != nil {
		return nil, keyFile, err
	}
	// Build ini
	cfg := ini.Empty()
	section, _ := cfg.NewSection("DEFAULT")
	section.NewKey("region", "us-ashburn-1")
	section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
	section.NewKey("fingerprint", "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20")
	section.NewKey("key_file", keyFile.Name())

	return cfg, keyFile, nil
}

// WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
// NOTE: Caller is responsible for removing temporary file.
func writeTestConfig(cfg *ini.File) (*os.File, error) {
	confFile, err := ioutil.TempFile("", "config_file")
	if err != nil {
		return nil, err
	}

	if _, err := confFile.Write([]byte("[DEFAULT]\n")); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}

	if _, err := cfg.WriteTo(confFile); err != nil {
		os.Remove(confFile.Name())
		return nil, err
	}
	return confFile, nil
}

// generateRSAKeyFile generates an RSA key file for use in unit tests.
// NOTE: The caller is responsible for deleting the temporary file.
func generateRSAKeyFile() (*os.File, error) {
	// Create temporary file for the key
	f, err := ioutil.TempFile("", "key")
	if err != nil {
		return nil, err
	}

	// Generate key
	priv, err := rsa.GenerateKey(rand.Reader, 2014)
	if err != nil {
		return nil, err
	}

	// ASN.1 DER encoded form
	privDer := x509.MarshalPKCS1PrivateKey(priv)
	privBlk := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   privDer,
	}

	// Write the key out
	if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
		return nil, err
	}

	return f, nil
}
<|endoftext|>


<commit_before>package vm

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

var kvss = [][][2]*Thunk{
	{{True, False}},
	{{Nil, NewNumber(42)}},
	{{False, NewNumber(42)}, {True, NewNumber(13)}},
	{
		{False, NewNumber(42)},
		{True, False},
		{NewNumber(2), NewString("Mr. Value")},
	},
	{
		{NewString("go"), NewList(NewList(), Nil, NewNumber(123))},
		{False, NewNumber(42)},
		{True, False},
		{NewNumber(2), NewString("Mr. Value")},
	},
}

func TestDictionarySet(t *testing.T) {
	for _, k := range []*Thunk{
		True, False, Nil, NewNumber(42), NewString("risp"),
	} {
		_, ok := App(Set, EmptyDictionary, k, Nil).Eval().(dictionaryType)
		assert.True(t, ok)
	}
}

func TestDictionaryGet(t *testing.T) {
	for _, kvs := range kvss {
		d := EmptyDictionary

		for i, kv := range kvs {
			t.Logf("Setting a %vth key...\n", i)
			d = App(Set, d, kv[0], kv[1])
		}

		assert.Equal(t, len(kvs), dictionarySize(d))

		for i, kv := range kvs {
			t.Logf("Getting a %vth value...\n", i)

			k, v := kv[0], kv[1]

			t.Log(k.Eval())

			if e, ok := App(Get, d, k).Eval().(errorType); ok {
				t.Log(e.message)
			}

			assert.True(t, testEqual(App(Get, d, k), v))
		}
	}
}

func TestDictionaryToList(t *testing.T) {
	for i, kvs := range kvss {
		t.Log("TestDictionaryToList START", i)
		d := EmptyDictionary

		for i, kv := range kvs {
			t.Logf("Setting a %vth key...\n", i)
			d = App(Set, d, kv[0], kv[1])
		}

		assert.Equal(t, len(kvs), dictionarySize(d))

		l := App(ToList, d)

		for i := 0; i < len(kvs); i, l = i+1, App(Rest, l) {
			kv := App(First, l)
			k := App(First, kv)
			lv := App(First, App(Rest, kv))
			dv := App(Get, d, k)

			t.Log("Key:", k.Eval())
			t.Log("LIST Value:", lv.Eval())
			t.Log("DICT Value:", dv.Eval())

			assert.True(t, testEqual(lv, dv))
		}

		assert.Equal(t, l.Eval().(listType), emptyList)
	}
}

func TestDictionaryWithDuplicateKeys(t *testing.T) {
	ks := []*Thunk{
		True, False, Nil, NewNumber(0), NewNumber(1), NewNumber(42),
		NewNumber(2049), NewString("runner"), NewString("lisp"),
	}

	dups := []*Thunk{
		ks[0], ks[1], ks[2], ks[2], ks[7], ks[3], ks[0], ks[4], ks[6], ks[1],
		ks[1], ks[4], ks[5], ks[6], ks[0], ks[2], ks[8], ks[8],
	}

	d := EmptyDictionary

	for _, k := range dups {
		d = App(Set, d, k, k)
	}

	assert.Equal(t, len(ks), dictionarySize(d))

	for _, k := range ks {
		assert.True(t, testEqual(App(Get, d, k), k))
	}
}

func dictionarySize(d *Thunk) int {
	return int(d.Eval().(dictionaryType).Size())
}
<commit_msg>Refactor dictionary test<commit_after>package vm

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

var kvss = [][][2]*Thunk{
	{{True, False}},
	{{Nil, NewNumber(42)}},
	{{False, NewNumber(42)}, {True, NewNumber(13)}},
	{
		{False, NewNumber(42)},
		{True, False},
		{NewNumber(2), NewString("Mr. Value")},
	},
	{
		{NewString("go"), NewList(NewList(), Nil, NewNumber(123))},
		{False, NewNumber(42)},
		{True, False},
		{NewNumber(2), NewString("Mr. Value")},
	},
}

func TestDictionarySet(t *testing.T) {
	for _, k := range []*Thunk{
		True, False, Nil, NewNumber(42), NewString("risp"),
	} {
		_, ok := App(Set, EmptyDictionary, k, Nil).Eval().(dictionaryType)
		assert.True(t, ok)
	}
}

func TestDictionaryGet(t *testing.T) {
	for _, kvs := range kvss {
		d := EmptyDictionary

		for i, kv := range kvs {
			t.Logf("Setting a %vth key...\n", i)
			d = App(Set, d, kv[0], kv[1])
		}

		assert.Equal(t, len(kvs), dictionarySize(d))

		for i, kv := range kvs {
			t.Logf("Getting a %vth value...\n", i)

			k, v := kv[0], kv[1]

			t.Log(k.Eval())

			if e, ok := App(Get, d, k).Eval().(errorType); ok {
				t.Log(e.message)
			}

			assert.True(t, testEqual(App(Get, d, k), v))
		}
	}
}

func TestDictionaryToList(t *testing.T) {
	for i, kvs := range kvss {
		t.Log("TestDictionaryToList START", i)
		d := EmptyDictionary

		for i, kv := range kvs {
			t.Logf("Setting a %vth key...\n", i)
			d = App(Set, d, kv[0], kv[1])
		}

		assert.Equal(t, len(kvs), dictionarySize(d))

		l := App(ToList, d)

		for i := 0; i < len(kvs); i, l = i+1, App(Rest, l) {
			kv := App(First, l)
			k := App(First, kv)
			lv := App(First, App(Rest, kv))
			dv := App(Get, d, k)

			t.Log("Key:", k.Eval())
			t.Log("LIST Value:", lv.Eval())
			t.Log("DICT Value:", dv.Eval())

			assert.True(t, testEqual(lv, dv))
		}

		assert.Equal(t, l.Eval().(listType), emptyList)
	}
}

func TestDictionaryWithDuplicateKeys(t *testing.T) {
	ks := []*Thunk{
		True, False, Nil, NewNumber(0), NewNumber(1), NewNumber(42),
		NewNumber(2049), NewString("runner"), NewString("lisp"),
	}

	d := EmptyDictionary

	for _, i := range []int{0, 1, 2, 2, 7, 3, 0, 4, 6, 1, 1, 4, 5, 6, 0, 2, 8, 8} {
		d = App(Set, d, ks[i], ks[i])
	}

	assert.Equal(t, len(ks), dictionarySize(d))

	for _, k := range ks {
		assert.True(t, testEqual(App(Get, d, k), k))
	}
}

func dictionarySize(d *Thunk) int {
	return int(d.Eval().(dictionaryType).Size())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/DimShadoWWW/power-pg/proxy"
	"github.com/parnurzeal/gorequest"
)

var (
	localHost     = flag.String("l", ":9876", "Endereço e porta do listener local")
	remoteHost    = flag.String("r", "localhost:5432", "Endereço e porta do servidor PostgreSQL")
	remoteService = flag.String("s", "", "http://localhost:8080/query")
	messages      = []string{}
)

func main() {
	flag.Parse()
	msgs := make(chan string)
	msgCh := make(chan proxy.Pkg)
	if *remoteService != "" {
		go func() {
			time.Sleep(time.Second * 3)
			_, _, errs := gorequest.New().Get(*remoteService).End()
			if errs != nil {
				log.Fatalf("log failed: %v", errs)
			}
			log.Println("done")
			os.Exit(0)
		}()
	}

	go func() {
		for msg := range msgs {
			fmt.Println(msg)
		}
	}()

	go func() {
		temp := ""
		for msg := range msgCh {
			if msg.Type == 'P' {
				if strings.Contains(string(msg.Content), "$1") {
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx = len(msg.Content) - 4
					}
					temp = string(msg.Content[selectIdx:sepIdx])
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
				} else {
					temp = ""
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
						if sepIdx == -1 {
							sepIdx = len(msg.Content) - 4
						}
					}
					messages = append(messages, string(msg.Content[selectIdx:sepIdx]))
				}
			} else {
				if msg.Type == 'B' && len(msg.Content) > 28 && temp != "" {
					messages = append(messages, strings.Replace(temp, "$1", fmt.Sprintf("'%s'", string(msg.Content[29:len(msg.Content)-4])), -1))
				}
				temp = ""
			}
			fmt.Printf("---------->%v\n", messages)
			fmt.Printf("---------->%#v\n", messages)
		}
	}()

	proxy.Start(localHost, remoteHost, getQueryModificada, msgs, msgCh)
}

func getQueryModificada(queryOriginal string) string {
	// log.Println("aa")
	// if queryOriginal[:5] != "power" {
	// 	return queryOriginal
	// }

	// log.Println(queryOriginal)
	fmt.Println(queryOriginal)
	return queryOriginal
}
<commit_msg>Update<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/DimShadoWWW/power-pg/proxy"
	"github.com/parnurzeal/gorequest"
)

var (
	localHost     = flag.String("l", ":9876", "Endereço e porta do listener local")
	remoteHost    = flag.String("r", "localhost:5432", "Endereço e porta do servidor PostgreSQL")
	remoteService = flag.String("s", "", "http://localhost:8080/query")
	messages      = []string{}
)

func main() {
	flag.Parse()
	msgs := make(chan string)
	msgCh := make(chan proxy.Pkg)
	if *remoteService != "" {
		go func() {
			time.Sleep(time.Second * 3)
			_, _, errs := gorequest.New().Get(*remoteService).End()
			if errs != nil {
				log.Fatalf("log failed: %v", errs)
			}
			log.Println("done")
			os.Exit(0)
		}()
	}

	go func() {
		for msg := range msgs {
			fmt.Println(msg)
		}
	}()

	go func() {
		temp := ""
		for msg := range msgCh {
			if msg.Type == 'P' {
				if strings.Contains(string(msg.Content), "$1") {
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx = len(msg.Content) - 4
					}
					temp = string(msg.Content[selectIdx:sepIdx])
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
				} else {
					temp = ""
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 0, 1, 0, 0}))
					if sepIdx == -1 || sepIdx+5 > len(msg.Content) {
						sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
						if sepIdx == -1 {
							sepIdx = len(msg.Content) - 4
						}
					}
					messages = append(messages, string(msg.Content[selectIdx:sepIdx]))
				}
			} else {
				if msg.Type == 'B' && len(msg.Content) > 28 && temp != "" {
					messages = append(messages, strings.Replace(temp, "$1", fmt.Sprintf("'%s'", string(msg.Content[29:len(msg.Content)-4])), -1))
				}
				temp = ""
			}
			fmt.Printf("---------->%v\n", messages)
			fmt.Printf("---------->%#v\n", messages)
		}
	}()

	proxy.Start(localHost, remoteHost, getQueryModificada, msgs, msgCh)
}

func getQueryModificada(queryOriginal string) string {
	// log.Println("aa")
	// if queryOriginal[:5] != "power" {
	// 	return queryOriginal
	// }

	// log.Println(queryOriginal)
	fmt.Println(queryOriginal)
	return queryOriginal
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/DimShadoWWW/power-pg/proxy"
	"github.com/parnurzeal/gorequest"
)

var (
	localHost     = flag.String("l", ":9876", "Endereço e porta do listener local")
	remoteHost    = flag.String("r", "localhost:5432", "Endereço e porta do servidor PostgreSQL")
	remoteService = flag.String("s", "", "http://localhost:8080/query")
	messages      = []string{}
)

func main() {
	flag.Parse()
	msgs := make(chan string)
	msgCh := make(chan proxy.Pkg)
	msgOut := make(chan string)
	if *remoteService != "" {
		go func() {
			time.Sleep(time.Second * 3)
			inFile, _ := os.Open("canales_list.txt")
			defer inFile.Close()
			scanner := bufio.NewScanner(inFile)
			scanner.Split(bufio.ScanLines)

			for scanner.Scan() {
				time.Sleep(time.Second * 1)
				messages = []string{}
				fmt.Println(scanner.Text())
				msgOut <- fmt.Sprintf("# %s\n", scanner.Text())
				_, _, errs := gorequest.New().Get(fmt.Sprintf("%s%s", *remoteService, scanner.Text())).End()
				if errs != nil {
					log.Fatalf("log failed: %v", errs)
				}
			}
			log.Println("done")
			os.Exit(0)
		}()
	}

	go func() {
		f, err := os.OpenFile("/all.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
		if err != nil {
			panic(err)
		}
		defer f.Close()
		for msg := range msgs {
			// fmt.Println(msg)
			_, err := f.WriteString(fmt.Sprintf("%s\n", msg))
			if err != nil {
				log.Fatalf("log failed: %v", err)
			}
		}
	}()

	go func() {
		f, err := os.OpenFile("/report.md", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
		if err != nil {
			panic(err)
		}
		defer f.Close()
		for msg := range msgOut {
			fmt.Println(msg)
			_, err := f.WriteString(fmt.Sprintf("%s\n", msg))
			if err != nil {
				log.Fatalf("log failed: %v", err)
			}
		}
	}()

	go func() {
		temp := ""
		for msg := range msgCh {
			if msg.Type == 'P' {
				if strings.Contains(string(msg.Content), "$1") {
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx = len(msg.Content) - 4
					}

					temp = string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00"))
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
				} else {
					temp = ""
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 0, 1, 0, 0}))
					if sepIdx == -1 || sepIdx+5 > len(msg.Content) {
						sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
						if sepIdx == -1 || sepIdx+4 > len(msg.Content) {
							sepIdx = len(msg.Content) - 4
						} else {
							sepIdx = len(msg.Content) - 1
						}
					}
					if sepIdx == -1 {
						sepIdx = len(msg.Content)
					}
					if selectIdx == -1 {
						selectIdx = 0
					}

					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
					messages = append(messages, string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00")))
				}
			} else {
				if msg.Type == 'B' && len(msg.Content) > 28 && temp != "" {
					idxPdo := strings.Index(string(msg.Content), "pdo_stmt_")

					if idxPdo != -1 {
						var newMsg proxy.ReadBuf
						// B type allways ends with 0100
						fmt.Printf("msg.Content   ----->%#v\n", msg.Content)
						newMsg = msg.Content[idxPdo+22 : len(msg.Content)-4]
						fmt.Printf("1 newMsg   ----->%#v\n", newMsg)
						totalVar := newMsg.Int16()

						vars := make(map[int]string)
						var varsIdx []int
						for i := 0; i < totalVar; i++ {
							fmt.Printf("2 newMsg   ----->%#v\n", newMsg)
							varLen := newMsg.Int32()
							fmt.Println("aa   -----> ", string(varLen))
							// varLen := int(binary.BigEndian.Uint32(aa))
							fmt.Printf("varLen ----->%v\n", varLen)
							fmt.Printf("newMsg   ----->%#v\n", newMsg)
							vars[i] = string(newMsg.Next(varLen))
							fmt.Printf("vars   ----->%#v\n", vars)
							varsIdx = append(varsIdx, i)
							fmt.Printf("varIdx  ----->%#v\n", varsIdx)
						}
						sort.Sort(sort.Reverse(sort.IntSlice(varsIdx)))
						for _, k := range varsIdx {
							messages = append(messages, strings.Replace(temp, fmt.Sprintf("$%d", k+1), fmt.Sprintf("'%s'", string(newMsg[k+1])), -1))
						}
					} else {
						messages = append(messages, string(msg.Content[29:len(msg.Content)-4]))
					}

				}
				temp = ""
			}
			fmt.Printf("---------->%v\n", messages)
			fmt.Printf("---------->%#v\n", messages)
			for k, v := range messages {
				msgOut <- fmt.Sprintf("%d. %s\n", k+1, v)
			}
		}
	}()

	proxy.Start(localHost, remoteHost, getQueryModificada, msgs, msgCh)
}

func getQueryModificada(queryOriginal string) string {
	// log.Println("aa")
	// if queryOriginal[:5] != "power" {
	// 	return queryOriginal
	// }

	// log.Println(queryOriginal)
	fmt.Println(queryOriginal)
	return queryOriginal
}
<commit_msg>Update<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/DimShadoWWW/power-pg/proxy"
	"github.com/parnurzeal/gorequest"
)

var (
	localHost     = flag.String("l", ":9876", "Endereço e porta do listener local")
	remoteHost    = flag.String("r", "localhost:5432", "Endereço e porta do servidor PostgreSQL")
	remoteService = flag.String("s", "", "http://localhost:8080/query")
	messages      = []string{}
)

func main() {
	flag.Parse()
	msgs := make(chan string)
	msgCh := make(chan proxy.Pkg)
	msgOut := make(chan string)
	if *remoteService != "" {
		go func() {
			time.Sleep(time.Second * 3)
			inFile, _ := os.Open("canales_list.txt")
			defer inFile.Close()
			scanner := bufio.NewScanner(inFile)
			scanner.Split(bufio.ScanLines)

			for scanner.Scan() {
				time.Sleep(time.Second * 1)
				messages = []string{}
				fmt.Println(scanner.Text())
				msgOut <- fmt.Sprintf("# %s\n", scanner.Text())
				_, _, errs := gorequest.New().Get(fmt.Sprintf("%s%s", *remoteService, scanner.Text())).End()
				if errs != nil {
					log.Fatalf("log failed: %v", errs)
				}
			}
			log.Println("done")
			os.Exit(0)
		}()
	}

	go func() {
		f, err := os.OpenFile("/all.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
		if err != nil {
			panic(err)
		}
		defer f.Close()
		for msg := range msgs {
			// fmt.Println(msg)
			_, err := f.WriteString(fmt.Sprintf("%s\n", msg))
			if err != nil {
				log.Fatalf("log failed: %v", err)
			}
		}
	}()

	go func() {
		f, err := os.OpenFile("/report.md", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
		if err != nil {
			panic(err)
		}
		defer f.Close()
		for msg := range msgOut {
			fmt.Println(msg)
			_, err := f.WriteString(fmt.Sprintf("%s\n", msg))
			if err != nil {
				log.Fatalf("log failed: %v", err)
			}
		}
	}()

	go func() {
		temp := ""
		for msg := range msgCh {
			if msg.Type == 'P' {
				if strings.Contains(string(msg.Content), "$1") {
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx = len(msg.Content) - 4
					}

					temp = string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00"))
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
				} else {
					temp = ""
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 0, 1, 0, 0}))
					if sepIdx == -1 || sepIdx+5 > len(msg.Content) {
						sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
						if sepIdx == -1 || sepIdx+4 > len(msg.Content) {
							sepIdx = len(msg.Content) - 4
						} else {
							sepIdx = len(msg.Content) - 1
						}
					}
					if sepIdx == -1 {
						sepIdx = len(msg.Content)
					}
					if selectIdx == -1 {
						selectIdx = 0
					}

					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
					messages = append(messages, string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00")))
				}
			} else {
				if msg.Type == 'B' && len(msg.Content) > 28 && temp != "" {
					idxPdo := strings.Index(string(msg.Content), "pdo_stmt_")

					if idxPdo != -1 {
						var newMsg proxy.ReadBuf
						// B type allways ends with 0100
						fmt.Printf("msg.Content   ----->%#v\n", msg.Content)
						newMsg = msg.Content[idxPdo+22 : len(msg.Content)-4]
						fmt.Printf("1 newMsg   ----->%#v\n", newMsg)
						totalVar := newMsg.Int16()

						vars := make(map[int]string)
						var varsIdx []int
						for i := 0; i < totalVar; i++ {
							fmt.Printf("2 newMsg   ----->%#v\n", newMsg)
							aa := newMsg.Next(4)
							fmt.Printf("aa   -----> %#v\n", aa)
							varLen := int(binary.LittleEndian.Uint32(aa))
							fmt.Printf("varLen ----->%v\n", varLen)
							fmt.Printf("newMsg   ----->%#v\n", newMsg)
							vars[i] = string(newMsg.Next(varLen))
							fmt.Printf("vars   ----->%#v\n", vars)
							varsIdx = append(varsIdx, i)
							fmt.Printf("varIdx  ----->%#v\n", varsIdx)
						}
						sort.Sort(sort.Reverse(sort.IntSlice(varsIdx)))
						for _, k := range varsIdx {
							messages = append(messages, strings.Replace(temp, fmt.Sprintf("$%d", k+1), fmt.Sprintf("'%s'", string(newMsg[k+1])), -1))
						}
					} else {
						messages = append(messages, string(msg.Content[29:len(msg.Content)-4]))
					}

				}
				temp = ""
			}
			fmt.Printf("---------->%v\n", messages)
			fmt.Printf("---------->%#v\n", messages)
			for k, v := range messages {
				msgOut <- fmt.Sprintf("%d. %s\n", k+1, v)
			}
		}
	}()

	proxy.Start(localHost, remoteHost, getQueryModificada, msgs, msgCh)
}

func getQueryModificada(queryOriginal string) string {
	// log.Println("aa")
	// if queryOriginal[:5] != "power" {
	// 	return queryOriginal
	// }

	// log.Println(queryOriginal)
	fmt.Println(queryOriginal)
	return queryOriginal
}
<|endoftext|>


<commit_before>package lzmadec

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	timeLayout = "2006-01-02 15:04:05"
)

var (
	// Err7zNotAvailable is returned if 7z executable is not available
	Err7zNotAvailable  = errors.New("7z executable not available")
	ErrNoEntries       = errors.New("no entries in 7z file")
	ErrUnexpectedLines = errors.New("unexpected number of lines")
	detectionStateOf7z int // 0 - not checked, 1 - checked and present, 2 - checked and not present
	mu                 sync.Mutex
)

// Archive describes a single .7z archive
type Archive struct {
	Path    string
	Entries []Entry
}

// Entry describes a single file inside .7z archive
type Entry struct {
	Path string
	Size int
	// -1 means "size unknown"
	PackedSize int
	Modified   time.Time
	Attributes string
	CRC        string
	Encrypted  string
	Method     string
	Block      int
}

func detect7zCached() error {
	mu.Lock()
	defer mu.Unlock()
	if detectionStateOf7z == 0 {
		cmd := exec.Command("7z")
		_, err := cmd.CombinedOutput()
		if err != nil {
			detectionStateOf7z = 2
		} else {
			detectionStateOf7z = 1
		}
	}
	if detectionStateOf7z == 1 {
		// checked and present
		return nil
	}
	// checked and not present
	return Err7zNotAvailable
}

/*
----------
Path = Badges.xml
Size = 4065633
Packed Size = 18990516
Modified = 2015-03-09 14:30:49
Attributes = ....A
CRC = 2C468F32
Encrypted = -
Method = BZip2
Block = 0
*/
func advanceToFirstEntry(scanner *bufio.Scanner) error {
	for scanner.Scan() {
		s := scanner.Text()
		if s == "----------" {
			return nil
		}
	}
	err := scanner.Err()
	if err == nil {
		err = ErrNoEntries
	}
	return err
}

func getEntryLines(scanner *bufio.Scanner) ([]string, error) {
	var res []string
	for scanner.Scan() {
		s := scanner.Text()
		s = strings.TrimSpace(s)
		if s == "" {
			break
		}
		res = append(res, s)
	}
	err := scanner.Err()
	if err != nil {
		return nil, err
	}
	if len(res) == 9 || len(res) == 0 {
		return res, nil
	}
	return nil, ErrUnexpectedLines
}

func parseEntryLines(lines []string) (Entry, error) {
	var e Entry
	var err error
	for _, s := range lines {
		parts := strings.SplitN(s, " =", 2)
		if len(parts) != 2 {
			return e, fmt.Errorf("unexpected line: '%s'", s)
		}
		name := strings.ToLower(parts[0])
		v := strings.TrimSpace(parts[1])
		switch name {
		case "path":
			e.Path = v
		case "size":
			e.Size, err = strconv.Atoi(v)
		case "packed size":
			e.PackedSize = -1
			if v != "" {
				e.PackedSize, err = strconv.Atoi(v)
			}
		case "modified":
			e.Modified, err = time.Parse(timeLayout, v)
		case "attributes":
			e.Attributes = v
		case "crc":
			e.CRC = v
		case "encrypted":
			e.Encrypted = v
		case "method":
			e.Method = v
		case "block":
			e.Block, err = strconv.Atoi(v)
		default:
			err = fmt.Errorf("unexpected entry line '%s'", name)
		}
		if err != nil {
			return e, err
		}
	}
	return e, nil
}

func parse7zListOutput(d []byte) ([]Entry, error) {
	var res []Entry
	r := bytes.NewBuffer(d)
	scanner := bufio.NewScanner(r)
	err := advanceToFirstEntry(scanner)
	if err != nil {
		return nil, err
	}
	for {
		lines, err := getEntryLines(scanner)
		if err != nil {
			return nil, err
		}
		if len(lines) == 0 {
			// last entry
			break
		}
		e, err := parseEntryLines(lines)
		if err != nil {
			return nil, err
		}
		res = append(res, e)
	}
	return res, nil
}

// NewArchive uses 7z to extract a list of files in .7z archive
func NewArchive(path string) (*Archive, error) {
	err := detect7zCached()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("7z", "l", "-slt", path)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	entries, err := parse7zListOutput(out)
	if err != nil {
		return nil, err
	}
	return &Archive{
		Path:    path,
		Entries: entries,
	}, nil
}

type readCloser struct {
	rc  io.ReadCloser
	cmd *exec.Cmd
}

func (rc *readCloser) Read(p []byte) (int, error) {
	return rc.rc.Read(p)
}

func (rc *readCloser) Close() error {
	// note: trying to Close() rc.rc returns an error 'invalid argument'
	return rc.cmd.Wait()
}

func (a *Archive) ExtractReader(name string) (io.ReadCloser, error) {
	found := false
	for _, e := range a.Entries {
		if e.Path == name {
			found = true
			break
		}
	}
	if !found {
		return nil, errors.New("File not in the archive")
	}
	cmd := exec.Command("7z", "x", "-so", a.Path, name)
	stdout, err := cmd.StdoutPipe()
	rc := &readCloser{
		rc:  stdout,
		cmd: cmd,
	}
	err = cmd.Start()
	if err != nil {
		stdout.Close()
		return nil, err
	}
	return rc, nil
}

func (a *Archive) ExtractToWriter(dst io.Writer, name string) error {
	r, err := a.ExtractReader(name)
	if err != nil {
		return err
	}
	_, err = io.Copy(dst, r)
	err2 := r.Close()
	if err != nil {
		return err
	}
	return err2
}

func (a *Archive) ExtractToFile(dstPath string, name string) error {
	f, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer f.Close()
	return a.ExtractToWriter(f, name)
}
<commit_msg>make Close() work even if we haven't consumed all the data<commit_after>package lzmadec

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	timeLayout = "2006-01-02 15:04:05"
)

var (
	// Err7zNotAvailable is returned if 7z executable is not available
	Err7zNotAvailable  = errors.New("7z executable not available")
	ErrNoEntries       = errors.New("no entries in 7z file")
	ErrUnexpectedLines = errors.New("unexpected number of lines")
	detectionStateOf7z int // 0 - not checked, 1 - checked and present, 2 - checked and not present
	mu                 sync.Mutex
)

// Archive describes a single .7z archive
type Archive struct {
	Path    string
	Entries []Entry
}

// Entry describes a single file inside .7z archive
type Entry struct {
	Path string
	Size int
	// -1 means "size unknown"
	PackedSize int
	Modified   time.Time
	Attributes string
	CRC        string
	Encrypted  string
	Method     string
	Block      int
}

func detect7zCached() error {
	mu.Lock()
	defer mu.Unlock()
	if detectionStateOf7z == 0 {
		cmd := exec.Command("7z")
		_, err := cmd.CombinedOutput()
		if err != nil {
			detectionStateOf7z = 2
		} else {
			detectionStateOf7z = 1
		}
	}
	if detectionStateOf7z == 1 {
		// checked and present
		return nil
	}
	// checked and not present
	return Err7zNotAvailable
}

/*
----------
Path = Badges.xml
Size = 4065633
Packed Size = 18990516
Modified = 2015-03-09 14:30:49
Attributes = ....A
CRC = 2C468F32
Encrypted = -
Method = BZip2
Block = 0
*/
func advanceToFirstEntry(scanner *bufio.Scanner) error {
	for scanner.Scan() {
		s := scanner.Text()
		if s == "----------" {
			return nil
		}
	}
	err := scanner.Err()
	if err == nil {
		err = ErrNoEntries
	}
	return err
}

func getEntryLines(scanner *bufio.Scanner) ([]string, error) {
	var res []string
	for scanner.Scan() {
		s := scanner.Text()
		s = strings.TrimSpace(s)
		if s == "" {
			break
		}
		res = append(res, s)
	}
	err := scanner.Err()
	if err != nil {
		return nil, err
	}
	if len(res) == 9 || len(res) == 0 {
		return res, nil
	}
	return nil, ErrUnexpectedLines
}

func parseEntryLines(lines []string) (Entry, error) {
	var e Entry
	var err error
	for _, s := range lines {
		parts := strings.SplitN(s, " =", 2)
		if len(parts) != 2 {
			return e, fmt.Errorf("unexpected line: '%s'", s)
		}
		name := strings.ToLower(parts[0])
		v := strings.TrimSpace(parts[1])
		switch name {
		case "path":
			e.Path = v
		case "size":
			e.Size, err = strconv.Atoi(v)
		case "packed size":
			e.PackedSize = -1
			if v != "" {
				e.PackedSize, err = strconv.Atoi(v)
			}
		case "modified":
			e.Modified, err = time.Parse(timeLayout, v)
		case "attributes":
			e.Attributes = v
		case "crc":
			e.CRC = v
		case "encrypted":
			e.Encrypted = v
		case "method":
			e.Method = v
		case "block":
			e.Block, err = strconv.Atoi(v)
		default:
			err = fmt.Errorf("unexpected entry line '%s'", name)
		}
		if err != nil {
			return e, err
		}
	}
	return e, nil
}

func parse7zListOutput(d []byte) ([]Entry, error) {
	var res []Entry
	r := bytes.NewBuffer(d)
	scanner := bufio.NewScanner(r)
	err := advanceToFirstEntry(scanner)
	if err != nil {
		return nil, err
	}
	for {
		lines, err := getEntryLines(scanner)
		if err != nil {
			return nil, err
		}
		if len(lines) == 0 {
			// last entry
			break
		}
		e, err := parseEntryLines(lines)
		if err != nil {
			return nil, err
		}
		res = append(res, e)
	}
	return res, nil
}

// NewArchive uses 7z to extract a list of files in .7z archive
func NewArchive(path string) (*Archive, error) {
	err := detect7zCached()
	if err != nil {
		return nil, err
	}
	cmd := exec.Command("7z", "l", "-slt", path)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	entries, err := parse7zListOutput(out)
	if err != nil {
		return nil, err
	}
	return &Archive{
		Path:    path,
		Entries: entries,
	}, nil
}

type readCloser struct {
	rc  io.ReadCloser
	cmd *exec.Cmd
}

func (rc *readCloser) Read(p []byte) (int, error) {
	return rc.rc.Read(p)
}

func (rc *readCloser) Close() error {
	// if we want to finish before reading all the data, we need to close
	// it all the data has already been read, or else rc.cmd.Wait() will hang
	// if it's already closed then Close() will return 'invalid argument',
	// which we can ignore
	rc.rc.Close()
	return rc.cmd.Wait()
}

func (a *Archive) ExtractReader(name string) (io.ReadCloser, error) {
	found := false
	for _, e := range a.Entries {
		if e.Path == name {
			found = true
			break
		}
	}
	if !found {
		return nil, errors.New("File not in the archive")
	}
	cmd := exec.Command("7z", "x", "-so", a.Path, name)
	stdout, err := cmd.StdoutPipe()
	rc := &readCloser{
		rc:  stdout,
		cmd: cmd,
	}
	err = cmd.Start()
	if err != nil {
		stdout.Close()
		return nil, err
	}
	return rc, nil
}

func (a *Archive) ExtractToWriter(dst io.Writer, name string) error {
	r, err := a.ExtractReader(name)
	if err != nil {
		return err
	}
	_, err = io.Copy(dst, r)
	err2 := r.Close()
	if err != nil {
		return err
	}
	return err2
}

func (a *Archive) ExtractToFile(dstPath string, name string) error {
	f, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer f.Close()
	return a.ExtractToWriter(f, name)
}
<|endoftext|>