<commit_before>package configstack

import (
	"fmt"
	"strings"
	"sync"

	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/shell"
)

// Represents the status of a module that we are trying to apply as part of the apply-all or destroy-all command
type ModuleStatus int

const (
	Waiting ModuleStatus = iota
	Running
	Finished
)

// Represents a module we are trying to "run" (i.e. apply or destroy) as part of the apply-all or destroy-all command
type runningModule struct {
	Module         *TerraformModule
	Status         ModuleStatus
	Err            error
	DependencyDone chan *runningModule
	Dependencies   map[string]*runningModule
	NotifyWhenDone []*runningModule
	FlagExcluded   bool
}

// This controls in what order dependencies should be enforced between modules
type DependencyOrder int

const (
	NormalOrder DependencyOrder = iota
	ReverseOrder
)

// Create a new RunningModule struct for the given module. This will initialize all fields to reasonable defaults,
// except for the Dependencies and NotifyWhenDone, both of which will be empty. You should fill these using a
// function such as crossLinkDependencies.
func newRunningModule(module *TerraformModule) *runningModule {
	return &runningModule{
		Module:         module,
		Status:         Waiting,
		DependencyDone: make(chan *runningModule, 1000), // Use a huge buffer to ensure senders are never blocked
		Dependencies:   map[string]*runningModule{},
		NotifyWhenDone: []*runningModule{},
		FlagExcluded:   module.FlagExcluded,
	}
}

// Run the given map of module path to runningModule. To "run" a module, execute the RunTerragrunt command in its
// TerragruntOptions object. The modules will be executed in an order determined by their inter-dependencies, using
// as much concurrency as possible.
func RunModules(modules []*TerraformModule, parallelism int) error {
	runningModules, err := toRunningModules(modules, NormalOrder)
	if err != nil {
		return err
	}
	return runModules(runningModules, parallelism)
}

// Run the given map of module path to runningModule. To "run" a module, execute the RunTerragrunt command in its
// TerragruntOptions object. The modules will be executed in the reverse order of their inter-dependencies, using
// as much concurrency as possible.
func RunModulesReverseOrder(modules []*TerraformModule, parallelism int) error {
	runningModules, err := toRunningModules(modules, ReverseOrder)
	if err != nil {
		return err
	}
	return runModules(runningModules, parallelism)
}

// Convert the list of modules to a map from module path to a runningModule struct. This struct contains information
// about executing the module, such as whether it has finished running or not and any errors that happened. Note that
// this does NOT actually run the module. For that, see the RunModules method.
func toRunningModules(modules []*TerraformModule, dependencyOrder DependencyOrder) (map[string]*runningModule, error) {
	runningModules := map[string]*runningModule{}
	for _, module := range modules {
		runningModules[module.Path] = newRunningModule(module)
	}

	crossLinkedModules, err := crossLinkDependencies(runningModules, dependencyOrder)
	if err != nil {
		return crossLinkedModules, err
	}

	return removeFlagExcluded(crossLinkedModules), nil
}

// Loop through the map of runningModules and for each module M:
//
// * If dependencyOrder is NormalOrder, plug in all the modules M depends on into the Dependencies field and all the
//   modules that depend on M into the NotifyWhenDone field.
// * If dependencyOrder is ReverseOrder, do the reverse.
func crossLinkDependencies(modules map[string]*runningModule, dependencyOrder DependencyOrder) (map[string]*runningModule, error) {
	for _, module := range modules {
		for _, dependency := range module.Module.Dependencies {
			runningDependency, hasDependency := modules[dependency.Path]
			if !hasDependency {
				return modules, errors.WithStackTrace(DependencyNotFoundWhileCrossLinking{module, dependency})
			}
			if dependencyOrder == NormalOrder {
				module.Dependencies[runningDependency.Module.Path] = runningDependency
				runningDependency.NotifyWhenDone = append(runningDependency.NotifyWhenDone, module)
			} else {
				runningDependency.Dependencies[module.Module.Path] = module
				module.NotifyWhenDone = append(module.NotifyWhenDone, runningDependency)
			}
		}
	}

	return modules, nil
}

// Return a cleaned-up map that only contains modules and dependencies that should not be excluded
func removeFlagExcluded(modules map[string]*runningModule) map[string]*runningModule {
	var finalModules = make(map[string]*runningModule)

	for key, module := range modules {

		// Only add modules that should not be excluded
		if !module.FlagExcluded {
			finalModules[key] = &runningModule{
				Module:         module.Module,
				Dependencies:   make(map[string]*runningModule),
				DependencyDone: module.DependencyDone,
				Err:            module.Err,
				NotifyWhenDone: module.NotifyWhenDone,
				Status:         module.Status,
			}

			// Only add dependencies that should not be excluded
			for path, dependency := range module.Dependencies {
				if !dependency.FlagExcluded {
					finalModules[key].Dependencies[path] = dependency
				}
			}
		}
	}

	return finalModules
}

// Run the given map of module path to runningModule. To "run" a module, execute the RunTerragrunt command in its
// TerragruntOptions object. The modules will be executed in an order determined by their inter-dependencies, using
// as much concurrency as possible.
func runModules(modules map[string]*runningModule, parallelism int) error {
	var waitGroup sync.WaitGroup
	var semaphore = make(chan struct{}, parallelism) // Make a semaphore from a buffered channel

	for _, module := range modules {
		waitGroup.Add(1)
		go func(module *runningModule) {
			defer waitGroup.Done()
			module.runModuleWhenReady(semaphore)
		}(module)
	}

	waitGroup.Wait()

	return collectErrors(modules)
}

// Collect the errors from the given modules and return a single error object to represent them, or nil if no errors
// occurred
func collectErrors(modules map[string]*runningModule) error {
	errs := []error{}
	for _, module := range modules {
		if module.Err != nil {
			errs = append(errs, module.Err)
		}
	}

	if len(errs) == 0 {
		return nil
	} else {
		return errors.WithStackTrace(MultiError{Errors: errs})
	}
}

// Run a module once all of its dependencies have finished executing.
func (module *runningModule) runModuleWhenReady(semaphore chan struct{}) {
	err := module.waitForDependencies()
	semaphore <- struct{}{} // Add one to the buffered channel. Will block in parallelism limit is met
	defer func() {
		<-semaphore // Remove one from the buffered channel
	}()
	if err == nil {
		err = module.runNow()
	}
	module.moduleFinished(err)
}

// Wait for all of this modules dependencies to finish executing. Return an error if any of those dependencies complete
// with an error. Return immediately if this module has no dependencies.
func (module *runningModule) waitForDependencies() error {
	module.Module.TerragruntOptions.Logger.Printf("Module %s must wait for %d dependencies to finish", module.Module.Path, len(module.Dependencies))
	for len(module.Dependencies) > 0 {
		doneDependency := <-module.DependencyDone
		delete(module.Dependencies, doneDependency.Module.Path)

		if doneDependency.Err != nil {
			if module.Module.TerragruntOptions.IgnoreDependencyErrors {
				module.Module.TerragruntOptions.Logger.Printf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too. However, because of --terragrunt-ignore-dependency-errors, module %s will run anyway.", doneDependency.Module.Path, module.Module.Path, module.Module.Path, module.Module.Path)
			} else {
				module.Module.TerragruntOptions.Logger.Printf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too.", doneDependency.Module.Path, module.Module.Path, module.Module.Path)
				return DependencyFinishedWithError{module.Module, doneDependency.Module, doneDependency.Err}
			}
		} else {
			module.Module.TerragruntOptions.Logger.Printf("Dependency %s of module %s just finished successfully. Module %s must wait on %d more dependencies.", doneDependency.Module.Path, module.Module.Path, module.Module.Path, len(module.Dependencies))
		}
	}

	return nil
}

// Run a module right now by executing the RunTerragrunt command of its TerragruntOptions field.
func (module *runningModule) runNow() error {
	module.Status = Running

	if module.Module.AssumeAlreadyApplied {
		module.Module.TerragruntOptions.Logger.Printf("Assuming module %s has already been applied and skipping it", module.Module.Path)
		return nil
	} else {
		module.Module.TerragruntOptions.Logger.Printf("Running module %s now", module.Module.Path)
		return module.Module.TerragruntOptions.RunTerragrunt(module.Module.TerragruntOptions)
	}
}

// Record that a module has finished executing and notify all of this module's dependencies
func (module *runningModule) moduleFinished(moduleErr error) {
	if moduleErr == nil {
		module.Module.TerragruntOptions.Logger.Printf("Module %s has finished successfully!", module.Module.Path)
	} else {
		module.Module.TerragruntOptions.Logger.Printf("Module %s has finished with an error: %v", module.Module.Path, moduleErr)
	}

	module.Status = Finished
	module.Err = moduleErr

	for _, toNotify := range module.NotifyWhenDone {
		toNotify.DependencyDone <- module
	}
}

// Custom error types

type DependencyFinishedWithError struct {
	Module     *TerraformModule
	Dependency *TerraformModule
	Err        error
}

func (err DependencyFinishedWithError) Error() string {
	return fmt.Sprintf("Cannot process module %s because one of its dependencies, %s, finished with an error: %s", err.Module, err.Dependency, err.Err)
}

func (this DependencyFinishedWithError) ExitStatus() (int, error) {
	if exitCode, err := shell.GetExitCode(this.Err); err == nil {
		return exitCode, nil
	}
	return -1, this
}

type MultiError struct {
	Errors []error
}

func (err MultiError) Error() string {
	errorStrings := []string{}
	for _, err := range err.Errors {
		errorStrings = append(errorStrings, err.Error())
	}
	return fmt.Sprintf("Encountered the following errors:\n%s", strings.Join(errorStrings, "\n"))
}

func (this MultiError) ExitStatus() (int, error) {
	exitCode := 0
	for i := range this.Errors {
		if code, err := shell.GetExitCode(this.Errors[i]); err != nil {
			return -1, this
		} else if code > exitCode {
			exitCode = code
		}
	}
	return exitCode, nil
}

type DependencyNotFoundWhileCrossLinking struct {
	Module     *runningModule
	Dependency *TerraformModule
}

func (err DependencyNotFoundWhileCrossLinking) Error() string {
	return fmt.Sprintf("Module %v specifies a dependency on module %v, but could not find that module while cross-linking dependencies. This is most likely a bug in Terragrunt. Please report it.", err.Module, err.Dependency)
}
<commit_msg>comment correction<commit_after>package configstack

import (
	"fmt"
	"strings"
	"sync"

	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/shell"
)

// Represents the status of a module that we are trying to apply as part of the apply-all or destroy-all command
type ModuleStatus int

const (
	Waiting ModuleStatus = iota
	Running
	Finished
)

// Represents a module we are trying to "run" (i.e. apply or destroy) as part of the apply-all or destroy-all command
type runningModule struct {
	Module         *TerraformModule
	Status         ModuleStatus
	Err            error
	DependencyDone chan *runningModule
	Dependencies   map[string]*runningModule
	NotifyWhenDone []*runningModule
	FlagExcluded   bool
}

// This controls in what order dependencies should be enforced between modules
type DependencyOrder int

const (
	NormalOrder DependencyOrder = iota
	ReverseOrder
)

// Create a new RunningModule struct for the given module. This will initialize all fields to reasonable defaults,
// except for the Dependencies and NotifyWhenDone, both of which will be empty. You should fill these using a
// function such as crossLinkDependencies.
func newRunningModule(module *TerraformModule) *runningModule {
	return &runningModule{
		Module:         module,
		Status:         Waiting,
		DependencyDone: make(chan *runningModule, 1000), // Use a huge buffer to ensure senders are never blocked
		Dependencies:   map[string]*runningModule{},
		NotifyWhenDone: []*runningModule{},
		FlagExcluded:   module.FlagExcluded,
	}
}

// Run the given map of module path to runningModule. To "run" a module, execute the RunTerragrunt command in its
// TerragruntOptions object. The modules will be executed in an order determined by their inter-dependencies, using
// as much concurrency as possible.
func RunModules(modules []*TerraformModule, parallelism int) error {
	runningModules, err := toRunningModules(modules, NormalOrder)
	if err != nil {
		return err
	}
	return runModules(runningModules, parallelism)
}

// Run the given map of module path to runningModule. To "run" a module, execute the RunTerragrunt command in its
// TerragruntOptions object. The modules will be executed in the reverse order of their inter-dependencies, using
// as much concurrency as possible.
func RunModulesReverseOrder(modules []*TerraformModule, parallelism int) error {
	runningModules, err := toRunningModules(modules, ReverseOrder)
	if err != nil {
		return err
	}
	return runModules(runningModules, parallelism)
}

// Convert the list of modules to a map from module path to a runningModule struct. This struct contains information
// about executing the module, such as whether it has finished running or not and any errors that happened. Note that
// this does NOT actually run the module. For that, see the RunModules method.
func toRunningModules(modules []*TerraformModule, dependencyOrder DependencyOrder) (map[string]*runningModule, error) {
	runningModules := map[string]*runningModule{}
	for _, module := range modules {
		runningModules[module.Path] = newRunningModule(module)
	}

	crossLinkedModules, err := crossLinkDependencies(runningModules, dependencyOrder)
	if err != nil {
		return crossLinkedModules, err
	}

	return removeFlagExcluded(crossLinkedModules), nil
}

// Loop through the map of runningModules and for each module M:
//
// * If dependencyOrder is NormalOrder, plug in all the modules M depends on into the Dependencies field and all the
//   modules that depend on M into the NotifyWhenDone field.
// * If dependencyOrder is ReverseOrder, do the reverse.
func crossLinkDependencies(modules map[string]*runningModule, dependencyOrder DependencyOrder) (map[string]*runningModule, error) {
	for _, module := range modules {
		for _, dependency := range module.Module.Dependencies {
			runningDependency, hasDependency := modules[dependency.Path]
			if !hasDependency {
				return modules, errors.WithStackTrace(DependencyNotFoundWhileCrossLinking{module, dependency})
			}
			if dependencyOrder == NormalOrder {
				module.Dependencies[runningDependency.Module.Path] = runningDependency
				runningDependency.NotifyWhenDone = append(runningDependency.NotifyWhenDone, module)
			} else {
				runningDependency.Dependencies[module.Module.Path] = module
				module.NotifyWhenDone = append(module.NotifyWhenDone, runningDependency)
			}
		}
	}

	return modules, nil
}

// Return a cleaned-up map that only contains modules and dependencies that should not be excluded
func removeFlagExcluded(modules map[string]*runningModule) map[string]*runningModule {
	var finalModules = make(map[string]*runningModule)

	for key, module := range modules {

		// Only add modules that should not be excluded
		if !module.FlagExcluded {
			finalModules[key] = &runningModule{
				Module:         module.Module,
				Dependencies:   make(map[string]*runningModule),
				DependencyDone: module.DependencyDone,
				Err:            module.Err,
				NotifyWhenDone: module.NotifyWhenDone,
				Status:         module.Status,
			}

			// Only add dependencies that should not be excluded
			for path, dependency := range module.Dependencies {
				if !dependency.FlagExcluded {
					finalModules[key].Dependencies[path] = dependency
				}
			}
		}
	}

	return finalModules
}

// Run the given map of module path to runningModule. To "run" a module, execute the RunTerragrunt command in its
// TerragruntOptions object. The modules will be executed in an order determined by their inter-dependencies, using
// as much concurrency as possible.
func runModules(modules map[string]*runningModule, parallelism int) error {
	var waitGroup sync.WaitGroup
	var semaphore = make(chan struct{}, parallelism) // Make a semaphore from a buffered channel

	for _, module := range modules {
		waitGroup.Add(1)
		go func(module *runningModule) {
			defer waitGroup.Done()
			module.runModuleWhenReady(semaphore)
		}(module)
	}

	waitGroup.Wait()

	return collectErrors(modules)
}

// Collect the errors from the given modules and return a single error object to represent them, or nil if no errors
// occurred
func collectErrors(modules map[string]*runningModule) error {
	errs := []error{}
	for _, module := range modules {
		if module.Err != nil {
			errs = append(errs, module.Err)
		}
	}

	if len(errs) == 0 {
		return nil
	} else {
		return errors.WithStackTrace(MultiError{Errors: errs})
	}
}

// Run a module once all of its dependencies have finished executing.
func (module *runningModule) runModuleWhenReady(semaphore chan struct{}) {
	err := module.waitForDependencies()
	semaphore <- struct{}{} // Add one to the buffered channel. Will block if parallelism limit is met
	defer func() {
		<-semaphore // Remove one from the buffered channel
	}()
	if err == nil {
		err = module.runNow()
	}
	module.moduleFinished(err)
}

// Wait for all of this modules dependencies to finish executing. Return an error if any of those dependencies complete
// with an error. Return immediately if this module has no dependencies.
func (module *runningModule) waitForDependencies() error {
	module.Module.TerragruntOptions.Logger.Printf("Module %s must wait for %d dependencies to finish", module.Module.Path, len(module.Dependencies))
	for len(module.Dependencies) > 0 {
		doneDependency := <-module.DependencyDone
		delete(module.Dependencies, doneDependency.Module.Path)

		if doneDependency.Err != nil {
			if module.Module.TerragruntOptions.IgnoreDependencyErrors {
				module.Module.TerragruntOptions.Logger.Printf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too. However, because of --terragrunt-ignore-dependency-errors, module %s will run anyway.", doneDependency.Module.Path, module.Module.Path, module.Module.Path, module.Module.Path)
			} else {
				module.Module.TerragruntOptions.Logger.Printf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too.", doneDependency.Module.Path, module.Module.Path, module.Module.Path)
				return DependencyFinishedWithError{module.Module, doneDependency.Module, doneDependency.Err}
			}
		} else {
			module.Module.TerragruntOptions.Logger.Printf("Dependency %s of module %s just finished successfully. Module %s must wait on %d more dependencies.", doneDependency.Module.Path, module.Module.Path, module.Module.Path, len(module.Dependencies))
		}
	}

	return nil
}

// Run a module right now by executing the RunTerragrunt command of its TerragruntOptions field.
func (module *runningModule) runNow() error {
	module.Status = Running

	if module.Module.AssumeAlreadyApplied {
		module.Module.TerragruntOptions.Logger.Printf("Assuming module %s has already been applied and skipping it", module.Module.Path)
		return nil
	} else {
		module.Module.TerragruntOptions.Logger.Printf("Running module %s now", module.Module.Path)
		return module.Module.TerragruntOptions.RunTerragrunt(module.Module.TerragruntOptions)
	}
}

// Record that a module has finished executing and notify all of this module's dependencies
func (module *runningModule) moduleFinished(moduleErr error) {
	if moduleErr == nil {
		module.Module.TerragruntOptions.Logger.Printf("Module %s has finished successfully!", module.Module.Path)
	} else {
		module.Module.TerragruntOptions.Logger.Printf("Module %s has finished with an error: %v", module.Module.Path, moduleErr)
	}

	module.Status = Finished
	module.Err = moduleErr

	for _, toNotify := range module.NotifyWhenDone {
		toNotify.DependencyDone <- module
	}
}

// Custom error types

type DependencyFinishedWithError struct {
	Module     *TerraformModule
	Dependency *TerraformModule
	Err        error
}

func (err DependencyFinishedWithError) Error() string {
	return fmt.Sprintf("Cannot process module %s because one of its dependencies, %s, finished with an error: %s", err.Module, err.Dependency, err.Err)
}

func (this DependencyFinishedWithError) ExitStatus() (int, error) {
	if exitCode, err := shell.GetExitCode(this.Err); err == nil {
		return exitCode, nil
	}
	return -1, this
}

type MultiError struct {
	Errors []error
}

func (err MultiError) Error() string {
	errorStrings := []string{}
	for _, err := range err.Errors {
		errorStrings = append(errorStrings, err.Error())
	}
	return fmt.Sprintf("Encountered the following errors:\n%s", strings.Join(errorStrings, "\n"))
}

func (this MultiError) ExitStatus() (int, error) {
	exitCode := 0
	for i := range this.Errors {
		if code, err := shell.GetExitCode(this.Errors[i]); err != nil {
			return -1, this
		} else if code > exitCode {
			exitCode = code
		}
	}
	return exitCode, nil
}

type DependencyNotFoundWhileCrossLinking struct {
	Module     *runningModule
	Dependency *TerraformModule
}

func (err DependencyNotFoundWhileCrossLinking) Error() string {
	return fmt.Sprintf("Module %v specifies a dependency on module %v, but could not find that module while cross-linking dependencies. This is most likely a bug in Terragrunt. Please report it.", err.Module, err.Dependency)
}
<|endoftext|>


<commit_before>// Copyright 2015 The multipart-related Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package related

import (
	"bytes"
	"encoding/base64"
	"errors"
	"io"
	"mime/multipart"
	"net/mail"
	"net/textproto"
)

var (
	ErrDupRoot = errors.New("Detect duplicate roots")
)

// Reader is an iterator over parts in a MIME multipart/related body.
type Reader struct {
	// SkipMatch controls whether a Reader matches the root body part's
	// content-type against compound object's type
	// SkipMatch bool

	// start is the content-ID of the compound object's "root"; optional
	start string

	// mediaType is the MIME media type of the compound object; required
	mediaType string

	// startInfo provides additional information to an application
	startInfo string

	r        *multipart.Reader
	rootRead bool
}

// NewReader returns a new multipart/related Reader reading from r using the
// given MIME boundary. It's a wrapper around multipart's Reader
func NewReader(
	r io.Reader,
	params map[string]string,
) *Reader {
	return &Reader{
		r:         multipart.NewReader(r, params["boundary"]),
		mediaType: params["type"],
		start:     parseContentId(params["start"]),
		startInfo: params["start-info"],
		rootRead:  false,
	}
}

// A Part represents a single part in a multipart/related body
type Part struct {
	Header textproto.MIMEHeader
	Root   bool

	// r is either a reader directly reading from p, or it's a wrapper
	// around such a reader, decoding the Content-Tranfer-Encoding
	r io.Reader
}

// A Object is parsed multipart/related compound object.
type Object struct {
	Values []*ObjectHeader
}

// A ObjectHeader describes a component of the aggregate whole of a
// multipart/related request.
type ObjectHeader struct {
	content []byte
	i       int64 // current reading index
}

// NextPart returns the next part in the multipart/related or and error.
// When there are no more parts, the error io.EOF is returned.
func (r *Reader) NextPart() (*Part, error) {
	wrap, err := r.r.NextPart()
	if err != nil {
		return nil, err
	}
	p := &Part{
		Header: wrap.Header,
		Root:   false,
	}

	contentId := parseContentId(p.Header.Get("Content-Id"))
	if r.start != "" && r.start == contentId {
		if r.rootRead {
			return nil, ErrDupRoot
		} else {
			p.Root = true
			r.rootRead = true
		}
	} else if !r.rootRead && r.start == "" {
		p.Root = true
		r.rootRead = true
	}
	p.r = wrap

	switch p.Header.Get("Content-Transfer-Encoding") {
	case "base64":
		p.Header.Del("Content-Transfer-Encoding")
		p.r = base64.NewDecoder(base64.StdEncoding, p.r)
		break
		// TODO binary reader
	}
	// TODO SkipMatch

	return p, nil
}

// Read reads the body of a part, after its headers and before the next
// part (if any) begins. It's a wrapper around multipart's Part.Read()
func (p *Part) Read(d []byte) (n int, err error) {
	return p.r.Read(d)
}

// ReadObject parses an entire multipart/related message.
func (r *Reader) ReadObject() (*Object, error) {
	object := &Object{[]*ObjectHeader{}}
	for {
		p, err := r.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		var b bytes.Buffer

		_, err = io.Copy(&b, p)
		if err != nil {
			return nil, err
		}

		oh := &ObjectHeader{
			content: b.Bytes(),
		}
		if p.Root {
			object.Values = append([]*ObjectHeader{oh}, object.Values...)
		} else {
			object.Values = append(object.Values, oh)
		}

	}

	return object, nil
}

// Read reads the content of a ObjectHeader.
func (oh *ObjectHeader) Read(b []byte) (n int, err error) {
	if len(b) == 0 {
		return 0, nil
	}
	if oh.i >= int64(len(oh.content)) {
		return 0, io.EOF
	}
	n = copy(b, oh.content[oh.i:])
	oh.i += int64(n)
	return
}

func parseContentId(contentId string) string {
	addr, err := mail.ParseAddress(contentId)
	if err != nil {
		return ""
	}
	return addr.Address
}
<commit_msg>Combine io.Copy and err checking;<commit_after>// Copyright 2015 The multipart-related Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package related

import (
	"bytes"
	"encoding/base64"
	"errors"
	"io"
	"mime/multipart"
	"net/mail"
	"net/textproto"
)

var (
	ErrDupRoot = errors.New("Detect duplicate roots")
)

// Reader is an iterator over parts in a MIME multipart/related body.
type Reader struct {
	// SkipMatch controls whether a Reader matches the root body part's
	// content-type against compound object's type
	// SkipMatch bool

	// start is the content-ID of the compound object's "root"; optional
	start string

	// mediaType is the MIME media type of the compound object; required
	mediaType string

	// startInfo provides additional information to an application
	startInfo string

	r        *multipart.Reader
	rootRead bool
}

// NewReader returns a new multipart/related Reader reading from r using the
// given MIME boundary. It's a wrapper around multipart's Reader
func NewReader(
	r io.Reader,
	params map[string]string,
) *Reader {
	return &Reader{
		r:         multipart.NewReader(r, params["boundary"]),
		mediaType: params["type"],
		start:     parseContentId(params["start"]),
		startInfo: params["start-info"],
		rootRead:  false,
	}
}

// A Part represents a single part in a multipart/related body
type Part struct {
	Header textproto.MIMEHeader
	Root   bool

	// r is either a reader directly reading from p, or it's a wrapper
	// around such a reader, decoding the Content-Tranfer-Encoding
	r io.Reader
}

// A Object is parsed multipart/related compound object.
type Object struct {
	Values []*ObjectHeader
}

// A ObjectHeader describes a component of the aggregate whole of a
// multipart/related request.
type ObjectHeader struct {
	content []byte
	i       int64 // current reading index
}

// NextPart returns the next part in the multipart/related or and error.
// When there are no more parts, the error io.EOF is returned.
func (r *Reader) NextPart() (*Part, error) {
	wrap, err := r.r.NextPart()
	if err != nil {
		return nil, err
	}
	p := &Part{
		Header: wrap.Header,
		Root:   false,
	}

	contentId := parseContentId(p.Header.Get("Content-Id"))
	if r.start != "" && r.start == contentId {
		if r.rootRead {
			return nil, ErrDupRoot
		} else {
			p.Root = true
			r.rootRead = true
		}
	} else if !r.rootRead && r.start == "" {
		p.Root = true
		r.rootRead = true
	}
	p.r = wrap

	switch p.Header.Get("Content-Transfer-Encoding") {
	case "base64":
		p.Header.Del("Content-Transfer-Encoding")
		p.r = base64.NewDecoder(base64.StdEncoding, p.r)
		break
		// TODO binary reader
	}
	// TODO SkipMatch

	return p, nil
}

// Read reads the body of a part, after its headers and before the next
// part (if any) begins. It's a wrapper around multipart's Part.Read()
func (p *Part) Read(d []byte) (n int, err error) {
	return p.r.Read(d)
}

// ReadObject parses an entire multipart/related message.
func (r *Reader) ReadObject() (*Object, error) {
	object := &Object{[]*ObjectHeader{}}
	for {
		p, err := r.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		var b bytes.Buffer

		if _, err := io.Copy(&b, p); err != nil {
			return nil, err
		}

		oh := &ObjectHeader{
			content: b.Bytes(),
		}
		if p.Root {
			object.Values = append([]*ObjectHeader{oh}, object.Values...)
		} else {
			object.Values = append(object.Values, oh)
		}

	}

	return object, nil
}

// Read reads the content of a ObjectHeader.
func (oh *ObjectHeader) Read(b []byte) (n int, err error) {
	if len(b) == 0 {
		return 0, nil
	}
	if oh.i >= int64(len(oh.content)) {
		return 0, io.EOF
	}
	n = copy(b, oh.content[oh.i:])
	oh.i += int64(n)
	return
}

func parseContentId(contentId string) string {
	addr, err := mail.ParseAddress(contentId)
	if err != nil {
		return ""
	}
	return addr.Address
}
<|endoftext|>


<commit_before>package sqlstore

import (
	"bytes"
	"strings"

	m "github.com/grafana/grafana/pkg/models"
)

type SqlBuilder struct {
	sql    bytes.Buffer
	params []interface{}
}

func (sb *SqlBuilder) writeDashboardPermissionFilter(user *m.SignedInUser, permission m.PermissionType) {

	if user.OrgRole == m.ROLE_ADMIN {
		return
	}

	okRoles := []interface{}{user.OrgRole}

	if user.OrgRole == m.ROLE_EDITOR {
		okRoles = append(okRoles, m.ROLE_VIEWER)
	}

	sb.sql.WriteString(` AND
	(
		dashboard.has_acl = ` + dialect.BooleanStr(false) + ` OR
		dashboard.id in (
			SELECT distinct d.id AS DashboardId
			FROM dashboard AS d
				LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
				LEFT JOIN team_member as ugm on ugm.team_id =  da.team_id
			WHERE
				d.has_acl = ` + dialect.BooleanStr(true) + ` AND
				d.org_id = ? AND
				da.permission >= ? AND
				(da.user_id = ? or ugm.user_id = ? or da.role IN (?` + strings.Repeat(",?", len(okRoles)-1) + `))
		)
	)`)

	sb.params = append(sb.params, user.OrgId, permission, user.UserId, user.UserId)
	sb.params = append(sb.params, okRoles...)
}
<commit_msg>permissions: might have a solution for search<commit_after>package sqlstore

import (
	"bytes"
	"strings"

	m "github.com/grafana/grafana/pkg/models"
)

type SqlBuilder struct {
	sql    bytes.Buffer
	params []interface{}
}

func (sb *SqlBuilder) writeDashboardPermissionFilter(user *m.SignedInUser, permission m.PermissionType) {

	if user.OrgRole == m.ROLE_ADMIN {
		return
	}

	okRoles := []interface{}{user.OrgRole}

	if user.OrgRole == m.ROLE_EDITOR {
		okRoles = append(okRoles, m.ROLE_VIEWER)
	}

	// SELECT dash.id, dash.title, dash.folder_id
	// FROM dashboard AS dash
	// 	LEFT JOIN dashboard folder on folder.id = dash.folder_id
	// 	LEFT JOIN dashboard_acl AS da ON
	// 			da.dashboard_id = dash.id OR
	// 			da.dashboard_id = dash.folder_id OR
	// 			(
	// 				-- include default permissions -->
	// 				da.org_id = -1 AND (folder.has_acl = 0 OR (dash.has_acl = 0 AND  dash.folder_id = 0))
	// 			)
	// 	LEFT JOIN team_member as ugm on ugm.team_id =  da.team_id
	// WHERE
	// 	 dash.org_id = 5 AND
	// 	 (
	// 		da.user_id = 8 or
	// 		ugm.user_id = 8 or
	// 		da.role in ('Viewer', 'Editor')
	// 	) AND
	// 	da.permission > 1
	//
	sb.sql.WriteString(` AND
	(
		dashboard.has_acl = ` + dialect.BooleanStr(false) + ` OR
		dashboard.id in (
			SELECT distinct d.id AS DashboardId
			FROM dashboard AS d
				LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
				LEFT JOIN team_member as ugm on ugm.team_id =  da.team_id
			WHERE
				d.has_acl = ` + dialect.BooleanStr(true) + ` AND
				d.org_id = ? AND
				da.permission >= ? AND
				(da.user_id = ? or ugm.user_id = ? or da.role IN (?` + strings.Repeat(",?", len(okRoles)-1) + `))
		)
	)`)

	sb.params = append(sb.params, user.OrgId, permission, user.UserId, user.UserId)
	sb.params = append(sb.params, okRoles...)
}
<|endoftext|>


<commit_before>package utils

import (
	"encoding/json"
	"os"
	"path/filepath"
)

// ConfigStructure is structure of main configuration
type ConfigStructure struct {
	RootDir                string                   `json:"rootDir"`
	DownloadConcurrency    int                      `json:"downloadConcurrency"`
	DownloadLimit          int64                    `json:"downloadSpeedLimit"`
	Architectures          []string                 `json:"architectures"`
	DepFollowSuggests      bool                     `json:"dependencyFollowSuggests"`
	DepFollowRecommends    bool                     `json:"dependencyFollowRecommends"`
	DepFollowAllVariants   bool                     `json:"dependencyFollowAllVariants"`
	DepFollowSource        bool                     `json:"dependencyFollowSource"`
	GpgDisableSign         bool                     `json:"gpgDisableSign"`
	GpgDisableVerify       bool                     `json:"gpgDisableVerify"`
	DownloadSourcePackages bool                     `json:"downloadSourcePackages"`
	PpaDistributorID       string                   `json:"ppaDistributorID"`
	PpaCodename            string                   `json:"ppaCodename"`
	S3PublishRoots         map[string]S3PublishRoot `json:"S3PublishRoots"`
}

type S3PublishRoot struct {
	Region          string `json:"region"`
	Bucket          string `json:"bucket"`
	AccessKeyID     string `json:"awsAccessKeyID"`
	SecretAccessKey string `json:"awsSecretAccessKey"`
	Prefix          string `json:"prefix"`
	ACL             string `json:"acl"`
}

// Config is configuration for aptly, shared by all modules
var Config = ConfigStructure{
	RootDir:                filepath.Join(os.Getenv("HOME"), ".aptly"),
	DownloadConcurrency:    4,
	DownloadLimit:          0,
	Architectures:          []string{},
	DepFollowSuggests:      false,
	DepFollowRecommends:    false,
	DepFollowAllVariants:   false,
	DepFollowSource:        false,
	GpgDisableSign:         false,
	GpgDisableVerify:       false,
	DownloadSourcePackages: false,
	PpaDistributorID:       "ubuntu",
	PpaCodename:            "",
	S3PublishRoots:         map[string]S3PublishRoot{},
}

// LoadConfig loads configuration from json file
func LoadConfig(filename string, config *ConfigStructure) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	dec := json.NewDecoder(f)
	return dec.Decode(&config)
}

// SaveConfig write configuration to json file
func SaveConfig(filename string, config *ConfigStructure) error {
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	encoded, err := json.MarshalIndent(&config, "", "  ")
	if err != nil {
		return err
	}

	_, err = f.Write(encoded)
	return err
}
<commit_msg>Style fix.<commit_after>package utils

import (
	"encoding/json"
	"os"
	"path/filepath"
)

// ConfigStructure is structure of main configuration
type ConfigStructure struct {
	RootDir                string                   `json:"rootDir"`
	DownloadConcurrency    int                      `json:"downloadConcurrency"`
	DownloadLimit          int64                    `json:"downloadSpeedLimit"`
	Architectures          []string                 `json:"architectures"`
	DepFollowSuggests      bool                     `json:"dependencyFollowSuggests"`
	DepFollowRecommends    bool                     `json:"dependencyFollowRecommends"`
	DepFollowAllVariants   bool                     `json:"dependencyFollowAllVariants"`
	DepFollowSource        bool                     `json:"dependencyFollowSource"`
	GpgDisableSign         bool                     `json:"gpgDisableSign"`
	GpgDisableVerify       bool                     `json:"gpgDisableVerify"`
	DownloadSourcePackages bool                     `json:"downloadSourcePackages"`
	PpaDistributorID       string                   `json:"ppaDistributorID"`
	PpaCodename            string                   `json:"ppaCodename"`
	S3PublishRoots         map[string]S3PublishRoot `json:"S3PublishRoots"`
}

// S3PublishRoot describes single S3 publishing entry point
type S3PublishRoot struct {
	Region          string `json:"region"`
	Bucket          string `json:"bucket"`
	AccessKeyID     string `json:"awsAccessKeyID"`
	SecretAccessKey string `json:"awsSecretAccessKey"`
	Prefix          string `json:"prefix"`
	ACL             string `json:"acl"`
}

// Config is configuration for aptly, shared by all modules
var Config = ConfigStructure{
	RootDir:                filepath.Join(os.Getenv("HOME"), ".aptly"),
	DownloadConcurrency:    4,
	DownloadLimit:          0,
	Architectures:          []string{},
	DepFollowSuggests:      false,
	DepFollowRecommends:    false,
	DepFollowAllVariants:   false,
	DepFollowSource:        false,
	GpgDisableSign:         false,
	GpgDisableVerify:       false,
	DownloadSourcePackages: false,
	PpaDistributorID:       "ubuntu",
	PpaCodename:            "",
	S3PublishRoots:         map[string]S3PublishRoot{},
}

// LoadConfig loads configuration from json file
func LoadConfig(filename string, config *ConfigStructure) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	dec := json.NewDecoder(f)
	return dec.Decode(&config)
}

// SaveConfig write configuration to json file
func SaveConfig(filename string, config *ConfigStructure) error {
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	encoded, err := json.MarshalIndent(&config, "", "  ")
	if err != nil {
		return err
	}

	_, err = f.Write(encoded)
	return err
}
<|endoftext|>


<commit_before>package utils

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"hash"
	"io"
	"sort"
	"strconv"
)

type TarSum struct {
	io.Reader
	tarR     *tar.Reader
	tarW     *tar.Writer
	gz       *gzip.Writer
	bufTar   *bytes.Buffer
	bufGz    *bytes.Buffer
	h        hash.Hash
	sums     []string
	finished bool
	first    bool
}

func (ts *TarSum) encodeHeader(h *tar.Header) error {
	for _, elem := range [][2]string{
		{"name", h.Name},
		{"mode", strconv.Itoa(int(h.Mode))},
		{"uid", strconv.Itoa(h.Uid)},
		{"gid", strconv.Itoa(h.Gid)},
		{"size", strconv.Itoa(int(h.Size))},
		{"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
		{"typeflag", string([]byte{h.Typeflag})},
		{"linkname", h.Linkname},
		{"uname", h.Uname},
		{"gname", h.Gname},
		{"devmajor", strconv.Itoa(int(h.Devmajor))},
		{"devminor", strconv.Itoa(int(h.Devminor))},
		// {"atime", strconv.Itoa(int(h.AccessTime.UTC().Unix()))},
		// {"ctime", strconv.Itoa(int(h.ChangeTime.UTC().Unix()))},
	} {
		if _, err := ts.h.Write([]byte(elem[0] + elem[1])); err != nil {
			return err
		}
	}
	return nil
}

func (ts *TarSum) Read(buf []byte) (int, error) {
	if ts.gz == nil {
		ts.bufTar = bytes.NewBuffer([]byte{})
		ts.bufGz = bytes.NewBuffer([]byte{})
		ts.tarR = tar.NewReader(ts.Reader)
		ts.tarW = tar.NewWriter(ts.bufTar)
		ts.gz = gzip.NewWriter(ts.bufGz)
		ts.h = sha256.New()
		ts.h.Reset()
		ts.first = true
	}

	if ts.finished {
		return ts.bufGz.Read(buf)
	}
	buf2 := make([]byte, len(buf), cap(buf))

	n, err := ts.tarR.Read(buf2)
	if err != nil {
		if err == io.EOF {
			if _, err := ts.h.Write(buf2[:n]); err != nil {
				return 0, err
			}
			if !ts.first {
				ts.sums = append(ts.sums, hex.EncodeToString(ts.h.Sum(nil)))
				ts.h.Reset()
			} else {
				ts.first = false
			}

			currentHeader, err := ts.tarR.Next()
			if err != nil {
				if err == io.EOF {
					if err := ts.gz.Close(); err != nil {
						return 0, err
					}
					ts.finished = true
					return n, nil
				}
				return n, err
			}
			if err := ts.encodeHeader(currentHeader); err != nil {
				return 0, err
			}
			if err := ts.tarW.WriteHeader(currentHeader); err != nil {
				return 0, err
			}
			if _, err := ts.tarW.Write(buf2[:n]); err != nil {
				return 0, err
			}
			ts.tarW.Flush()
			if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
				return 0, err
			}
			ts.gz.Flush()

			return ts.bufGz.Read(buf)
		}
		return n, err
	}

	// Filling the hash buffer
	if _, err = ts.h.Write(buf2[:n]); err != nil {
		return 0, err
	}

	// Filling the tar writter
	if _, err = ts.tarW.Write(buf2[:n]); err != nil {
		return 0, err
	}
	ts.tarW.Flush()

	// Filling the gz writter
	if _, err = io.Copy(ts.gz, ts.bufTar); err != nil {
		return 0, err
	}
	ts.gz.Flush()

	return ts.bufGz.Read(buf)
}

func (ts *TarSum) Sum(extra []byte) string {
	sort.Strings(ts.sums)
	h := sha256.New()
	if extra != nil {
		h.Write(extra)
	}
	for _, sum := range ts.sums {
		Debugf("-->%s<--", sum)
		h.Write([]byte(sum))
	}
	checksum := "tarsum+sha256:" + hex.EncodeToString(h.Sum(nil))
	Debugf("checksum processed: %s", checksum)
	return checksum
}
<commit_msg>Log files name along with their checksum in TarSum + add a Method to retrieve the checksum map<commit_after>package utils

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"hash"
	"io"
	"sort"
	"strconv"
)

type TarSum struct {
	io.Reader
	tarR        *tar.Reader
	tarW        *tar.Writer
	gz          *gzip.Writer
	bufTar      *bytes.Buffer
	bufGz       *bytes.Buffer
	h           hash.Hash
	sums        map[string]string
	currentFile string
	finished    bool
	first       bool
}

func (ts *TarSum) encodeHeader(h *tar.Header) error {
	for _, elem := range [][2]string{
		{"name", h.Name},
		{"mode", strconv.Itoa(int(h.Mode))},
		{"uid", strconv.Itoa(h.Uid)},
		{"gid", strconv.Itoa(h.Gid)},
		{"size", strconv.Itoa(int(h.Size))},
		{"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
		{"typeflag", string([]byte{h.Typeflag})},
		{"linkname", h.Linkname},
		{"uname", h.Uname},
		{"gname", h.Gname},
		{"devmajor", strconv.Itoa(int(h.Devmajor))},
		{"devminor", strconv.Itoa(int(h.Devminor))},
		// {"atime", strconv.Itoa(int(h.AccessTime.UTC().Unix()))},
		// {"ctime", strconv.Itoa(int(h.ChangeTime.UTC().Unix()))},
	} {
		if _, err := ts.h.Write([]byte(elem[0] + elem[1])); err != nil {
			return err
		}
	}
	return nil
}

func (ts *TarSum) Read(buf []byte) (int, error) {
	if ts.gz == nil {
		ts.bufTar = bytes.NewBuffer([]byte{})
		ts.bufGz = bytes.NewBuffer([]byte{})
		ts.tarR = tar.NewReader(ts.Reader)
		ts.tarW = tar.NewWriter(ts.bufTar)
		ts.gz = gzip.NewWriter(ts.bufGz)
		ts.h = sha256.New()
		ts.h.Reset()
		ts.first = true
		ts.sums = make(map[string]string)
	}

	if ts.finished {
		return ts.bufGz.Read(buf)
	}
	buf2 := make([]byte, len(buf), cap(buf))

	n, err := ts.tarR.Read(buf2)
	if err != nil {
		if err == io.EOF {
			if _, err := ts.h.Write(buf2[:n]); err != nil {
				return 0, err
			}
			if !ts.first {
				ts.sums[ts.currentFile] = hex.EncodeToString(ts.h.Sum(nil))
				ts.h.Reset()
			} else {
				ts.first = false
			}

			currentHeader, err := ts.tarR.Next()
			if err != nil {
				if err == io.EOF {
					if err := ts.gz.Close(); err != nil {
						return 0, err
					}
					ts.finished = true
					return n, nil
				}
				return n, err
			}
			if err := ts.encodeHeader(currentHeader); err != nil {
				return 0, err
			}
			if err := ts.tarW.WriteHeader(currentHeader); err != nil {
				return 0, err
			}
			if _, err := ts.tarW.Write(buf2[:n]); err != nil {
				return 0, err
			}
			ts.tarW.Flush()
			if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
				return 0, err
			}
			ts.gz.Flush()

			return ts.bufGz.Read(buf)
		}
		return n, err
	}

	// Filling the hash buffer
	if _, err = ts.h.Write(buf2[:n]); err != nil {
		return 0, err
	}

	// Filling the tar writter
	if _, err = ts.tarW.Write(buf2[:n]); err != nil {
		return 0, err
	}
	ts.tarW.Flush()

	// Filling the gz writter
	if _, err = io.Copy(ts.gz, ts.bufTar); err != nil {
		return 0, err
	}
	ts.gz.Flush()

	return ts.bufGz.Read(buf)
}

func (ts *TarSum) Sum(extra []byte) string {
	var sums []string

	for _, sum := range ts.sums {
		sums = append(sums, sum)
	}
	sort.Strings(sums)
	h := sha256.New()
	if extra != nil {
		h.Write(extra)
	}
	for _, sum := range sums {
		Debugf("-->%s<--", sum)
		h.Write([]byte(sum))
	}
	checksum := "tarsum+sha256:" + hex.EncodeToString(h.Sum(nil))
	Debugf("checksum processed: %s", checksum)
	return checksum
}

func (ts *TarSum) GetSums() map[string]string {
	return ts.sums
}
<|endoftext|>


<commit_before>package connections

type OutputMessageType uint8

const (
	OutputMessageTypeGame OutputMessageType = iota
	OutputMessageTypePlayer
	OutputMessageTypeBroadcast
)

var outputMessageTypeLabels = map[OutputMessageType]string{
	OutputMessageTypeGame:      "game",
	OutputMessageTypePlayer:    "player",
	OutputMessageTypeBroadcast: "broadcast",
}

func (t OutputMessageType) String() string {
	if label, ok := outputMessageTypeLabels[t]; ok {
		return label
	}
	return "unknown"
}

type OutputMessage struct {
	Type    OutputMessageType
	Payload interface{}
}
<commit_msg>Implement JSON Marshaler interface for OutputMessage<commit_after>package connections

type OutputMessageType uint8

const (
	OutputMessageTypeGame OutputMessageType = iota
	OutputMessageTypePlayer
	OutputMessageTypeBroadcast
)

var outputMessageTypeLabels = map[OutputMessageType]string{
	OutputMessageTypeGame:      "game",
	OutputMessageTypePlayer:    "player",
	OutputMessageTypeBroadcast: "broadcast",
}

func (t OutputMessageType) String() string {
	if label, ok := outputMessageTypeLabels[t]; ok {
		return label
	}
	return "unknown"
}

var outputMessageTypeJSONs = map[OutputMessageType][]byte{
	OutputMessageTypeGame:      []byte(`"game"`),
	OutputMessageTypePlayer:    []byte(`"player"`),
	OutputMessageTypeBroadcast: []byte(`"broadcast"`),
}

func (t OutputMessageType) MarshalJSON() ([]byte, error) {
	if json, ok := outputMessageTypeJSONs[t]; ok {
		return json, nil
	}
	return []byte(`"unknown"`), nil
}

type OutputMessage struct {
	Type    OutputMessageType `json:"type"`
	Payload interface{}       `json:"payload"`
}
<|endoftext|>


<commit_before>package daemon

import (
	"fmt"
	"os"
	"syscall"

	"github.com/ondevice/ondevice/logg"
)

type lockFile struct {
	Path   string
	fd     int
	closed bool
}

// TryLock -- Try to acquire the daemon's lock file (and write to PID file)
//
// We don't want `ondevice daemon` to be run more than once per user and pc
// since both instances would request the same devID from the server.
// The API server would then believe there was a conflict and after ~5min
// assign a new devID to one of the daemons.
//
// This issue would be repeated (e.g. the next time the system is restarted)
// and therefore cause a lot of garbage data.
//
func (l *lockFile) TryLock() error {
	var err error
	if l.fd, err = syscall.Open(l.Path, os.O_CREATE|os.O_WRONLY, 0644); err != nil {
		return fmt.Errorf("Couldn't open '%s' for locking: %s", l.Path, err)
	}
	l.closed = false

	if err = syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		return fmt.Errorf("ondevice daemon seems to be running already (%s)", err)
	}
	logg.Debug("acquired daemon lock file")

	// only do this if we've got the lock
	logg.Debug("Writing to PID file: ", os.Getpid())
	pidstr := fmt.Sprintf("%d\n", os.Getpid())
	syscall.Write(l.fd, []byte(pidstr))

	return nil
}

func (l *lockFile) Unlock() error {
	var err error

	if l.closed {
		return nil
	}

	if err = syscall.Flock(l.fd, syscall.LOCK_UN); err != nil {
		return fmt.Errorf("Failed to unlock lock file: %s", err)
	}
	if err = syscall.Close(l.fd); err != nil {
		return fmt.Errorf("Failed to close lock file: %s", err)
	}

	// remove PID file
	if err = os.Remove(l.Path); err != nil {
		return fmt.Errorf("Failed to remove PID file: %s", err)
	}

	l.closed = true
	return nil
}
<commit_msg>daemon.fileLock: some minor improvements (should fix certain race conditions)<commit_after>package daemon

import (
	"fmt"
	"os"
	"syscall"

	"github.com/ondevice/ondevice/logg"
)

type lockFile struct {
	Path   string
	fd     int
	closed bool
}

// TryLock -- Try to acquire the daemon's lock file (and write to PID file)
//
// We don't want `ondevice daemon` to be run more than once per user and pc
// since both instances would request the same devID from the server.
// The API server would then believe there was a conflict and after ~5min
// assign a new devID to one of the daemons.
//
// This issue would be repeated (e.g. the next time the system is restarted)
// and therefore cause a lot of garbage data.
//
func (l *lockFile) TryLock() error {
	var err error
	if l.fd, err = syscall.Open(l.Path, os.O_CREATE|os.O_WRONLY, 0644); err != nil {
		return fmt.Errorf("Couldn't open '%s' for locking: %s", l.Path, err)
	}

	if err = syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		syscall.Close(l.fd)
		return fmt.Errorf("ondevice daemon seems to be running already (%s)", err)
	}

	l.closed = false
	logg.Debug("acquired daemon lock file")

	// only do this once we've got the lock
	logg.Debug("Writing to PID file: ", os.Getpid())
	pidstr := fmt.Sprintf("%d\n", os.Getpid())
	syscall.Write(l.fd, []byte(pidstr))

	return nil
}

func (l *lockFile) Unlock() error {
	var err error

	if l.closed {
		return nil
	}

	// Note: we're not doing close(), then delete() because that could lead to
	// race conditions (we close, another process acquires lock, we unlink the
	// file, rendering their lock ineffective)

	// remove PID file (rendering the (still active) lock inactive)
	if err = os.Remove(l.Path); err != nil {
		return fmt.Errorf("Failed to remove PID file: %s", err)
	}

	if err = syscall.Close(l.fd); err != nil {
		return fmt.Errorf("Failed to close lock file: %s", err)
	}

	l.closed = true
	return nil
}
<|endoftext|>


<commit_before>package tmpl

import (
	"html/template"
	"strings"
	"time"

	"bitbucket.org/mundipagg/boletoapi/models"
)

var funcMap = template.FuncMap{
	"today":   today,
	"brdate":  brDate,
	"replace": replace,
	"docType": docType,
	"trim":    trim,
}

func today() time.Time {
	return time.Now()
}

func brDate(d time.Time) string {
	return d.Format("02/01/2006")
}

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func docType(s models.DocumentType) int {
	if s.IsCpf() {
		return 1
	}
	return 2
}

func trim(s string) string {
	return strings.TrimSpace(s)
}
<commit_msg>:heavy_plus_sign: Adiciona padLeft funcmap<commit_after>package tmpl

import (
	"html/template"
	"strings"
	"time"

	"fmt"

	"strconv"

	"bitbucket.org/mundipagg/boletoapi/models"
)

var funcMap = template.FuncMap{
	"today":   today,
	"brdate":  brDate,
	"replace": replace,
	"docType": docType,
	"trim":    trim,
	"padLeft": padLeft,
}

func padLeft(value, char string, total uint) string {
	s := "%" + char + strconv.Itoa(int(total)) + "s"
	return fmt.Sprintf(s, value)
}

func today() time.Time {
	return time.Now()
}

func brDate(d time.Time) string {
	return d.Format("02/01/2006")
}

func replace(str, old, new string) string {
	return strings.Replace(str, old, new, -1)
}

func docType(s models.DocumentType) int {
	if s.IsCpf() {
		return 1
	}
	return 2
}

func trim(s string) string {
	return strings.TrimSpace(s)
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/db"
	//e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/user"
)

func TestEmailController(t *testing.T) {

	var err error

	user.Secret = "secret"

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(true))

	router.POST("/email", EmailController)

	user := user.DefaultUser()
	user.SetId(2)
	user.SetAuthenticated()

	user.hash, err = user.HashPassword("testpassword")
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotNil(t, user.hash, "password should be returned")
	}

	assert.True(t, user.ComparePassword("testpassword"), "Password should validate")

	token, err := user.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	rows := sqlmock.NewRows([]string{"name", "email"}).AddRow("test", "old@test.com")
	mock.ExpectQuery(`SELECT user_name,user_email FROM users WHERE user_id`).WillReturnRows(rows)

	mock.ExpectExec("UPDATE users SET user_email").
		WithArgs("cool@test.com", 2).
		WillReturnResult(sqlmock.NewResult(1, 1))

	request := []byte(`{"ib": 1, "email": "test@test.com"}`)

	first := performJwtJsonRequest(router, "POST", "/email", token, request)

	assert.Equal(t, first.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), successMessage(audit.AuditEmailUpdate), "HTTP response should match")

	assert.NoError(t, mock.ExpectationsWereMet(), "An error was not expected")

}
<commit_msg>add controller tests<commit_after>package controllers

import (
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/db"
	//e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/user"
)

func TestEmailController(t *testing.T) {

	var err error

	user.Secret = "secret"

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(true))

	router.POST("/email", EmailController)

	user := user.DefaultUser()
	user.SetId(2)
	user.SetAuthenticated()

	token, err := user.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	rows := sqlmock.NewRows([]string{"name", "email"}).AddRow("test", "old@test.com")
	mock.ExpectQuery(`SELECT user_name,user_email FROM users WHERE user_id`).WillReturnRows(rows)

	mock.ExpectExec("UPDATE users SET user_email").
		WithArgs("cool@test.com", 2).
		WillReturnResult(sqlmock.NewResult(1, 1))

	request := []byte(`{"ib": 1, "email": "test@test.com"}`)

	first := performJwtJsonRequest(router, "POST", "/email", token, request)

	assert.Equal(t, first.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), successMessage(audit.AuditEmailUpdate), "HTTP response should match")

	assert.NoError(t, mock.ExpectationsWereMet(), "An error was not expected")

}
<|endoftext|>


<commit_before>package ov

import (
	"encoding/json"
	"fmt"
	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
	"strconv"
)

type VirtualPort struct {
	Capabilities []string `json:"capabilities,omitempty"` // "capabilities": {}
	PortFunction string   `json:"portFunction,omitempty"` // "portFunction": "a"
	PortNumber   int      `json:"portNumber,omitempty"`   // "portNumber": 2
}
type SHTPort struct {
	Mapping               int           `json:"mapping,omitempty"`               // "mapping": 3
	MaxSpeedMbps          int           `json:"maxSpeedMbps,omitempty"`          // "maxSpeedMbps":3553
	MaxVFsSupported       int           `json:"maxVFsSupported,omitempty"`       // "maxVFsSupported": 23
	Number                int           `json:"number,omitempty"`                // "number": 1
	PhysicalFunctionCount int           `json:"physicalFunctionCount,omitempty"` // "physicalFunctionCount": 4
	SupportedFcGbps       []string      `json:"supportedFcGbps,omitempty"`       // "supportedFcGbps": ""
	Type                  string        `json:"type,omitempty"`                  // "type": "Ethernet"
	VirtualPort           []VirtualPort `json:"virtualPorts,omitempty"`          // "virtualPorts": {}
}
type Adapter struct {
	Capabilities    []string  `json:"capabilities,omitempty"`    // "capabilities": {}
	DeviceNumber    int       `json:"deviceNumber,omitempty"`    // "deviceNumber": 123
	DeviceType      string    `json:"deviceType,omitempty"`      // "deviceType":"Ethernet"
	Location        string    `json:"location,omitempty"`        // "location": "Mezz"
	MaxVFsSupported int       `json:"maxVFsSupported,omitempty"` // "maxVFsSupported": 256
	MinVFsIncrement int       `json:"minVFsIncrement,omitempty"` // "minVFsIncrement": 8
	Model           string    `json:"model,omitempty"`           // "model": ""
	Ports           []SHTPort `json:"ports,omitempty"`           // "ports": {}
	Slot            int       `json:"slot,omitempty"`            // "slot": 3
	StorageCapabilities *StorageCapability `json:"storageCapabilities,omitempty"` // "storageCapabilities":{..,},
}

type Dependency struct {
	Type string `json:"type,omitempty"` // "type": "Map"
}

type OptionLink struct {
	Action    string `json:"action,omitempty"`
	OptionID  string `json:"optionId,omitempty"`
	SettingID string `json:"settingId,omitempty"`
}

type Option struct {
	ID          string       `json:"id,omitempty"`          //"id":"sc3d"
	Name        string       `json:"name,omitempty"`        // "name":"COM"
	OptionLinks []OptionLink `json:"optionLinks,omitempty"` // "optionLinks":{}
}
type BiosSetting struct {
	Category        string       `json:"category,omitempty"`        // "category":""
	DefaultValue    string       `json:"defaultValue,omitempty"`    // "defaultValue":"Cpm21Trq"
	DependencyList  []Dependency `json:"dependencyList,omitempty"`  //"dependencyList:" {}
	HelpText        string       `json:"helpText,omitempty"`        // "helpText": ""
	ID              string       `json:"id,omitempty"`              // "id":""
	LowerBound      int          `json:"lowerBound,omitempty"`      // "lowerBound": 3
	Name            string       `json:"name,omitempty"`            // "name": ""
	Options         []Option     `json:"options,omitempty"`         // "options":{}
	ScalarIncrement int          `json:"scalarIncrement,omitempty"` // "scalarIncrement":4
	StringMaxLength int          `json:"stringMaxLength,omitempty"` // "stringMaxLength":24
	StringMinLength int          `json:"stringMinLength,omitempty"` // "stringMinLength":2
	Type            string       `json:"type,omitempty"`            // "type":""
	UpperBound      int          `json:"upperBound,omitempty"`      // "upperBound": 78
	ValueExpression string       `json:"valueExpression,omitempty"` // "valueExpression":""
	WarningText     string       `json:"warningText,omitempty"`     // "warningText":""
}

type StorageCapability struct {
	ControllerModes            []string `json:"controllerModes,omitempty"`            // "controllerModes":{}
	DriveTechnologies          []string `json:"driveTechnologies,omitempty"`          // "driveTechnologies":{}
	DriveWriteCacheSupported   bool     `json:"driveWriteCacheSupported,omitempty"`   // "driveWriteCacheSupported":false
	MaximumDrives              int      `json:"maximumDrives,omitempty"`              // "maximumDrives":5
	NvmeBackplaneCapable       bool     `json:"nvmeBackplaneCapable,omitempty"`       // "nvmeBackplaneCapable":true
	RaidLevels                 []string `json:"raidLevels,omitempty"`                 // "raidLevels":{}
	StandupControllerSupported bool     `json:"standupControllerSupported,omitempty"` // "standupControllerSupported,":false
}
type ServerHardwareType struct {
	Adapters            []Adapter          `json:"adapters,omitempty"`            // "adapters": {},
	BiosSettings        []BiosSetting      `json:"biosSettings,omitempty"`        //"biosSettings": {},
	BootCapabilities    []string           `json:"bootCapabilities,omitempty"`    // "bootCapabilities":{},
	BootModes           []string           `json:"bootModes,omitempty"`           // "bootModes":{},
	Capabilities        []string           `json:"capabilities,omitempty"`        // "capabilities": {},
	Category            string             `json:"category,omitempty"`            // "category": "server-hardware",
	Created             string             `json:"created,omitempty"`             // "created": "20150831T154835.250Z",
	Description         utils.Nstring      `json:"description,omitempty"`         // "description": "ServerHardware",
	ETAG                string             `json:"eTag,omitempty"`                // "eTag": "1441036118675/8",
	Family              string             `json:"family,omitempty"`              // "family":"135",
	FormFactor          string             `json:"formFactor,omitempty"`          // "formFactor":"HalfHeight",
	Model               string             `json:"model,omitempty"`               // "model":"",
	Modified            string             `json:"modified,omitempty"`            // "modified": "20150831T154835.250Z",
	Name                string             `json:"name,omitempty"`                // "name": "ServerHardware 1",
	PxeBootPolicies     []string           `json:"pxeBootPolicies,omitempty"`     // "pxeBootPolicies":{},
	StorageCapabilities *StorageCapability `json:"storageCapabilities,omitempty"` // "storageCapabilities":{..,},
	Type                string             `json:"type,omitempty"`                // "type": "server-hardware-type-4",
	URI                 utils.Nstring      `json:"uri,omitempty"`                 // "uri": "/rest/server-hardware-types/e2f0031b-52bd-4223-9ac1-d91cb519d548"
	UefiClass           string             `json:"uefiClass,omitempty"`           // "uefiClass":"2"
}

type ServerHardwareTypeList struct {
	Total       int                  `json:"total,omitempty"`       // "total": 1,
	Count       int                  `json:"count,omitempty"`       // "count": 1,
	Start       int                  `json:"start,omitempty"`       // "start": 0,
	PrevPageURI utils.Nstring        `json:"prevPageUri,omitempty"` // "prevPageUri": null,
	NextPageURI utils.Nstring        `json:"nextPageUri,omitempty"` // "nextPageUri": null,
	URI         utils.Nstring        `json:"uri,omitempty"`         // "uri": "/rest/server-hardware-types?filter=connectionTemplateUri%20matches%7769cae0-b680-435b-9b87-9b864c81657fsort=name:asc"
	Members     []ServerHardwareType `json:"members,omitempty"`     // "members":[]
}

func (c *OVClient) GetServerHardwareTypeByName(name string) (ServerHardwareType, error) {
	var (
		serverHardwareType ServerHardwareType
	)
	serverHardwareTypes, err := c.GetServerHardwareTypes(0, 0, fmt.Sprintf("name matches '%s'", name), "name:asc")
	if serverHardwareTypes.Total > 0 {
		return serverHardwareTypes.Members[0], err
	} else {
		return serverHardwareType, fmt.Errorf("Could not find Server Hardware Type: %s", name)
	}
}

func (c *OVClient) GetServerHardwareTypeByUri(uri utils.Nstring) (ServerHardwareType, error) {
	var (
		serverHardwareType ServerHardwareType
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	data, err := c.RestAPICall(rest.GET, uri.String(), nil)
	if err != nil {
		return serverHardwareType, err
	}
	log.Debugf("GetServerHardwareType %s", data)
	if err := json.Unmarshal([]byte(data), &serverHardwareType); err != nil {
		return serverHardwareType, err
	}
	return serverHardwareType, nil
}

func (c *OVClient) GetServerHardwareTypes(start int, count int, filter string, sort string) (ServerHardwareTypeList, error) {
	var (
		uri                 = "/rest/server-hardware-types"
		q                   map[string]interface{}
		serverHardwareTypes ServerHardwareTypeList
	)
	q = make(map[string]interface{})
	if start > 0 {
		q["start"] = strconv.Itoa(start)
	}

	if count > 0 {
		q["count"] = strconv.Itoa(count)
	}

	if len(filter) > 0 {
		q["filter"] = filter
	}

	if sort != "" {
		q["sort"] = sort
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}

	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return serverHardwareTypes, err
	}

	log.Debugf("GetServerHardwareTypes %s", data)
	if err := json.Unmarshal([]byte(data), &serverHardwareTypes); err != nil {
		return serverHardwareTypes, err
	}
	return serverHardwareTypes, nil
}
<commit_msg>Format change<commit_after>package ov

import (
	"encoding/json"
	"fmt"
	"github.com/HewlettPackard/oneview-golang/rest"
	"github.com/HewlettPackard/oneview-golang/utils"
	"github.com/docker/machine/libmachine/log"
	"strconv"
)

type VirtualPort struct {
	Capabilities []string `json:"capabilities,omitempty"` // "capabilities": {}
	PortFunction string   `json:"portFunction,omitempty"` // "portFunction": "a"
	PortNumber   int      `json:"portNumber,omitempty"`   // "portNumber": 2
}
type SHTPort struct {
	Mapping               int           `json:"mapping,omitempty"`               // "mapping": 3
	MaxSpeedMbps          int           `json:"maxSpeedMbps,omitempty"`          // "maxSpeedMbps":3553
	MaxVFsSupported       int           `json:"maxVFsSupported,omitempty"`       // "maxVFsSupported": 23
	Number                int           `json:"number,omitempty"`                // "number": 1
	PhysicalFunctionCount int           `json:"physicalFunctionCount,omitempty"` // "physicalFunctionCount": 4
	SupportedFcGbps       []string      `json:"supportedFcGbps,omitempty"`       // "supportedFcGbps": ""
	Type                  string        `json:"type,omitempty"`                  // "type": "Ethernet"
	VirtualPort           []VirtualPort `json:"virtualPorts,omitempty"`          // "virtualPorts": {}
}
type Adapter struct {
	Capabilities        []string           `json:"capabilities,omitempty"`        // "capabilities": {}
	DeviceNumber        int                `json:"deviceNumber,omitempty"`        // "deviceNumber": 123
	DeviceType          string             `json:"deviceType,omitempty"`          // "deviceType":"Ethernet"
	Location            string             `json:"location,omitempty"`            // "location": "Mezz"
	MaxVFsSupported     int                `json:"maxVFsSupported,omitempty"`     // "maxVFsSupported": 256
	MinVFsIncrement     int                `json:"minVFsIncrement,omitempty"`     // "minVFsIncrement": 8
	Model               string             `json:"model,omitempty"`               // "model": ""
	Ports               []SHTPort          `json:"ports,omitempty"`               // "ports": {}
	Slot                int                `json:"slot,omitempty"`                // "slot": 3
	StorageCapabilities *StorageCapability `json:"storageCapabilities,omitempty"` // "storageCapabilities":{..,},
}

type Dependency struct {
	Type string `json:"type,omitempty"` // "type": "Map"
}

type OptionLink struct {
	Action    string `json:"action,omitempty"`
	OptionID  string `json:"optionId,omitempty"`
	SettingID string `json:"settingId,omitempty"`
}

type Option struct {
	ID          string       `json:"id,omitempty"`          //"id":"sc3d"
	Name        string       `json:"name,omitempty"`        // "name":"COM"
	OptionLinks []OptionLink `json:"optionLinks,omitempty"` // "optionLinks":{}
}
type BiosSetting struct {
	Category        string       `json:"category,omitempty"`        // "category":""
	DefaultValue    string       `json:"defaultValue,omitempty"`    // "defaultValue":"Cpm21Trq"
	DependencyList  []Dependency `json:"dependencyList,omitempty"`  //"dependencyList:" {}
	HelpText        string       `json:"helpText,omitempty"`        // "helpText": ""
	ID              string       `json:"id,omitempty"`              // "id":""
	LowerBound      int          `json:"lowerBound,omitempty"`      // "lowerBound": 3
	Name            string       `json:"name,omitempty"`            // "name": ""
	Options         []Option     `json:"options,omitempty"`         // "options":{}
	ScalarIncrement int          `json:"scalarIncrement,omitempty"` // "scalarIncrement":4
	StringMaxLength int          `json:"stringMaxLength,omitempty"` // "stringMaxLength":24
	StringMinLength int          `json:"stringMinLength,omitempty"` // "stringMinLength":2
	Type            string       `json:"type,omitempty"`            // "type":""
	UpperBound      int          `json:"upperBound,omitempty"`      // "upperBound": 78
	ValueExpression string       `json:"valueExpression,omitempty"` // "valueExpression":""
	WarningText     string       `json:"warningText,omitempty"`     // "warningText":""
}

type StorageCapability struct {
	ControllerModes            []string `json:"controllerModes,omitempty"`            // "controllerModes":{}
	DriveTechnologies          []string `json:"driveTechnologies,omitempty"`          // "driveTechnologies":{}
	DriveWriteCacheSupported   bool     `json:"driveWriteCacheSupported,omitempty"`   // "driveWriteCacheSupported":false
	MaximumDrives              int      `json:"maximumDrives,omitempty"`              // "maximumDrives":5
	NvmeBackplaneCapable       bool     `json:"nvmeBackplaneCapable,omitempty"`       // "nvmeBackplaneCapable":true
	RaidLevels                 []string `json:"raidLevels,omitempty"`                 // "raidLevels":{}
	StandupControllerSupported bool     `json:"standupControllerSupported,omitempty"` // "standupControllerSupported,":false
}
type ServerHardwareType struct {
	Adapters            []Adapter          `json:"adapters,omitempty"`            // "adapters": {},
	BiosSettings        []BiosSetting      `json:"biosSettings,omitempty"`        //"biosSettings": {},
	BootCapabilities    []string           `json:"bootCapabilities,omitempty"`    // "bootCapabilities":{},
	BootModes           []string           `json:"bootModes,omitempty"`           // "bootModes":{},
	Capabilities        []string           `json:"capabilities,omitempty"`        // "capabilities": {},
	Category            string             `json:"category,omitempty"`            // "category": "server-hardware",
	Created             string             `json:"created,omitempty"`             // "created": "20150831T154835.250Z",
	Description         utils.Nstring      `json:"description,omitempty"`         // "description": "ServerHardware",
	ETAG                string             `json:"eTag,omitempty"`                // "eTag": "1441036118675/8",
	Family              string             `json:"family,omitempty"`              // "family":"135",
	FormFactor          string             `json:"formFactor,omitempty"`          // "formFactor":"HalfHeight",
	Model               string             `json:"model,omitempty"`               // "model":"",
	Modified            string             `json:"modified,omitempty"`            // "modified": "20150831T154835.250Z",
	Name                string             `json:"name,omitempty"`                // "name": "ServerHardware 1",
	PxeBootPolicies     []string           `json:"pxeBootPolicies,omitempty"`     // "pxeBootPolicies":{},
	StorageCapabilities *StorageCapability `json:"storageCapabilities,omitempty"` // "storageCapabilities":{..,},
	Type                string             `json:"type,omitempty"`                // "type": "server-hardware-type-4",
	URI                 utils.Nstring      `json:"uri,omitempty"`                 // "uri": "/rest/server-hardware-types/e2f0031b-52bd-4223-9ac1-d91cb519d548"
	UefiClass           string             `json:"uefiClass,omitempty"`           // "uefiClass":"2"
}

type ServerHardwareTypeList struct {
	Total       int                  `json:"total,omitempty"`       // "total": 1,
	Count       int                  `json:"count,omitempty"`       // "count": 1,
	Start       int                  `json:"start,omitempty"`       // "start": 0,
	PrevPageURI utils.Nstring        `json:"prevPageUri,omitempty"` // "prevPageUri": null,
	NextPageURI utils.Nstring        `json:"nextPageUri,omitempty"` // "nextPageUri": null,
	URI         utils.Nstring        `json:"uri,omitempty"`         // "uri": "/rest/server-hardware-types?filter=connectionTemplateUri%20matches%7769cae0-b680-435b-9b87-9b864c81657fsort=name:asc"
	Members     []ServerHardwareType `json:"members,omitempty"`     // "members":[]
}

func (c *OVClient) GetServerHardwareTypeByName(name string) (ServerHardwareType, error) {
	var (
		serverHardwareType ServerHardwareType
	)
	serverHardwareTypes, err := c.GetServerHardwareTypes(0, 0, fmt.Sprintf("name matches '%s'", name), "name:asc")
	if serverHardwareTypes.Total > 0 {
		return serverHardwareTypes.Members[0], err
	} else {
		return serverHardwareType, fmt.Errorf("Could not find Server Hardware Type: %s", name)
	}
}

func (c *OVClient) GetServerHardwareTypeByUri(uri utils.Nstring) (ServerHardwareType, error) {
	var (
		serverHardwareType ServerHardwareType
	)
	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	data, err := c.RestAPICall(rest.GET, uri.String(), nil)
	if err != nil {
		return serverHardwareType, err
	}
	log.Debugf("GetServerHardwareType %s", data)
	if err := json.Unmarshal([]byte(data), &serverHardwareType); err != nil {
		return serverHardwareType, err
	}
	return serverHardwareType, nil
}

func (c *OVClient) GetServerHardwareTypes(start int, count int, filter string, sort string) (ServerHardwareTypeList, error) {
	var (
		uri                 = "/rest/server-hardware-types"
		q                   map[string]interface{}
		serverHardwareTypes ServerHardwareTypeList
	)
	q = make(map[string]interface{})
	if start > 0 {
		q["start"] = strconv.Itoa(start)
	}

	if count > 0 {
		q["count"] = strconv.Itoa(count)
	}

	if len(filter) > 0 {
		q["filter"] = filter
	}

	if sort != "" {
		q["sort"] = sort
	}

	// refresh login
	c.RefreshLogin()
	c.SetAuthHeaderOptions(c.GetAuthHeaderMap())
	// Setup query
	if len(q) > 0 {
		c.SetQueryString(q)
	}

	data, err := c.RestAPICall(rest.GET, uri, nil)
	if err != nil {
		return serverHardwareTypes, err
	}

	log.Debugf("GetServerHardwareTypes %s", data)
	if err := json.Unmarshal([]byte(data), &serverHardwareTypes); err != nil {
		return serverHardwareTypes, err
	}
	return serverHardwareTypes, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"github.com/martini-contrib/sessions"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

const CLEAR string = "\\33[H\\33[2J"

const (
	WSTerm  = 1
	WSClick = 2
)

type Config struct {
	Secret  string
	Address string
}

type LockingWebsockets struct {
	sync.RWMutex
	byId        map[int64]*websocket.Conn
	consoleToId map[int64][]int64
	currentId   int64
}

var websockets *LockingWebsockets

func (c *LockingWebsockets) deleteWebsocket(id int64) {
	c.Lock()
	defer c.Unlock()
	delete(c.byId, id)
}

func (c *LockingWebsockets) addWebsocket(ws *websocket.Conn) int64 {
	c.Lock()
	defer c.Unlock()
	c.currentId += 1
	retid := c.currentId
	c.byId[retid] = ws
	return retid
}

func main() {
	websockets = &LockingWebsockets{
		byId:        make(map[int64]*websocket.Conn),
		consoleToId: make(map[int64][]int64),
		currentId:   0,
	}
	consoleReadChannel = make(chan ConsoleChunk)
	go consoleDispatch()
	vzcontrol := ConnectVZControl()
	defer vzcontrol.Close()

	file, _ := os.Open("game_server.cfg")
	decoder := json.NewDecoder(file)
	config := Config{}
	decoder.Decode(&config)

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(config.Secret))
	m.Use(sessions.Sessions("session", store))

	generating := false
	gr := NewGraph()
	m.Get("/reset/:secret", func(w http.ResponseWriter, r *http.Request, params martini.Params, session sessions.Session) string {
		if params["secret"] != config.Secret {
			return ""
		}
		err := vzcontrol.Reset()
		if err != nil {
			return err.Error()
		}
		generating = false
		gr = NewGraph()
		return "Done"
	})

	m.Get("/gen", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		if generating {
			return "Already generating"
		}
		generating = true
		maxNodes := 5
		maxEdges := 5
		startNodeId := 100

		startNode := Node{Id: NodeId(startNodeId)}
		gr.AddNode(startNode)
		err := vzcontrol.ContainerCreate(int64(startNode.Id))
		err = vzcontrol.ConsoleStart(int64(startNode.Id))

		nodes := make([]Node, 0)
		nodes = append(nodes, startNode)

		steps := 1
		for len(nodes) != 0 && steps < maxNodes {
			node, nodes := nodes[len(nodes)-1], nodes[:len(nodes)-1]

			numEdges := random(1, maxEdges)
			for i := 1; i <= numEdges; i++ {
				targetNode := Node{Id: NodeId(i*steps + startNodeId)}
				if gr.AddNode(targetNode) {
					err = vzcontrol.ContainerCreate(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Container Create: %d, %d, %d\n%s", targetNode.Id, i*steps, numEdges, err.Error())
					}
					err = vzcontrol.ConsoleStart(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Console Start: %d\n%s", targetNode.Id, err.Error())
					}
					nodes = append(nodes, targetNode)
					edgeid := int64(i * steps)
					if gr.AddEdge(Edge{Id: EdgeId(edgeid), Head: node.Id, Tail: targetNode.Id}) {
						err = vzcontrol.NetworkCreate(edgeid)
						if err != nil {
							return fmt.Sprintf("Network Create: %d\n%s", edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(node.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Node: %d, %d\n%s", node.Id, edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(targetNode.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Target: %d, %d\n%s", targetNode.Id, edgeid, err.Error())
						}
					}
				}

			}
			steps += 1
		}
		return gr.String()
	})

	m.Get("/graph", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		output, err := json.Marshal(gr)
		if err != nil {
			return err.Error()
		}
		return string(output)
	})

	m.Get("/ws", func(w http.ResponseWriter, r *http.Request, session sessions.Session) {
		var currentVm int64 = -1
		ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		if _, ok := err.(websocket.HandshakeError); ok {
			http.Error(w, "Not a websocket handshake", 400)
			log.Println(err)
			return
		} else if err != nil {
			log.Println(err)
			return
		}
		defer ws.Close()
		websocketId := websockets.addWebsocket(ws)
		defer websockets.deleteWebsocket(websocketId)
		ws.WriteMessage(websocket.TextMessage, []byte("Welcome to ginux!\r\n"))
		for {
			_, message, err := ws.ReadMessage()
			if err != nil {
				log.Println(err)
				return
			} else {
				msgType := message[0]
				msgData := message[1:len(message)]
				switch msgType {
				case WSTerm:
					if currentVm != -1 {
						vzcontrol.ConsoleWrite(currentVm, msgData)
					}
				case WSClick:
					prevVm := currentVm
					tmp, _ := strconv.Atoi(string(msgData))
					currentVm = int64(tmp)
					websockets.Lock()
					if prevVm != -1 {
						for index, wsId := range websockets.consoleToId[prevVm] {
							if wsId == websocketId {
								websockets.consoleToId[prevVm] = append(websockets.consoleToId[prevVm][:index], websockets.consoleToId[prevVm][index+1:]...)
							}
						}
					}
					websockets.consoleToId[currentVm] = append(websockets.consoleToId[currentVm], websocketId)
					websockets.Unlock()
					ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("r\nSelected Container %d\r\n", currentVm)))
				}
			}
		}
	})
	log.Println("Game Server started on", config.Address)
	log.Fatal(http.ListenAndServe(config.Address, m))
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func consoleDispatch() {
	for chunk := range consoleReadChannel {
		websockets.RLock()
		for _, wsId := range websockets.consoleToId[chunk.Id] {
			if socket, ok := websockets.byId[wsId]; ok {
				socket.WriteMessage(websocket.TextMessage, chunk.Data)
			}
		}
		websockets.RUnlock()
	}
}
<commit_msg>Fixed message<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"github.com/martini-contrib/sessions"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
)

const CLEAR string = "\\33[H\\33[2J"

const (
	WSTerm  = 1
	WSClick = 2
)

type Config struct {
	Secret  string
	Address string
}

type LockingWebsockets struct {
	sync.RWMutex
	byId        map[int64]*websocket.Conn
	consoleToId map[int64][]int64
	currentId   int64
}

var websockets *LockingWebsockets

func (c *LockingWebsockets) deleteWebsocket(id int64) {
	c.Lock()
	defer c.Unlock()
	delete(c.byId, id)
}

func (c *LockingWebsockets) addWebsocket(ws *websocket.Conn) int64 {
	c.Lock()
	defer c.Unlock()
	c.currentId += 1
	retid := c.currentId
	c.byId[retid] = ws
	return retid
}

func main() {
	websockets = &LockingWebsockets{
		byId:        make(map[int64]*websocket.Conn),
		consoleToId: make(map[int64][]int64),
		currentId:   0,
	}
	consoleReadChannel = make(chan ConsoleChunk)
	go consoleDispatch()
	vzcontrol := ConnectVZControl()
	defer vzcontrol.Close()

	file, _ := os.Open("game_server.cfg")
	decoder := json.NewDecoder(file)
	config := Config{}
	decoder.Decode(&config)

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(config.Secret))
	m.Use(sessions.Sessions("session", store))

	generating := false
	gr := NewGraph()
	m.Get("/reset/:secret", func(w http.ResponseWriter, r *http.Request, params martini.Params, session sessions.Session) string {
		if params["secret"] != config.Secret {
			return ""
		}
		err := vzcontrol.Reset()
		if err != nil {
			return err.Error()
		}
		generating = false
		gr = NewGraph()
		return "Done"
	})

	m.Get("/gen", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		if generating {
			return "Already generating"
		}
		generating = true
		maxNodes := 5
		maxEdges := 5
		startNodeId := 100

		startNode := Node{Id: NodeId(startNodeId)}
		gr.AddNode(startNode)
		err := vzcontrol.ContainerCreate(int64(startNode.Id))
		err = vzcontrol.ConsoleStart(int64(startNode.Id))

		nodes := make([]Node, 0)
		nodes = append(nodes, startNode)

		steps := 1
		for len(nodes) != 0 && steps < maxNodes {
			node, nodes := nodes[len(nodes)-1], nodes[:len(nodes)-1]

			numEdges := random(1, maxEdges)
			for i := 1; i <= numEdges; i++ {
				targetNode := Node{Id: NodeId(i*steps + startNodeId)}
				if gr.AddNode(targetNode) {
					err = vzcontrol.ContainerCreate(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Container Create: %d, %d, %d\n%s", targetNode.Id, i*steps, numEdges, err.Error())
					}
					err = vzcontrol.ConsoleStart(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Console Start: %d\n%s", targetNode.Id, err.Error())
					}
					nodes = append(nodes, targetNode)
					edgeid := int64(i * steps)
					if gr.AddEdge(Edge{Id: EdgeId(edgeid), Head: node.Id, Tail: targetNode.Id}) {
						err = vzcontrol.NetworkCreate(edgeid)
						if err != nil {
							return fmt.Sprintf("Network Create: %d\n%s", edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(node.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Node: %d, %d\n%s", node.Id, edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(targetNode.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Target: %d, %d\n%s", targetNode.Id, edgeid, err.Error())
						}
					}
				}

			}
			steps += 1
		}
		return gr.String()
	})

	m.Get("/graph", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		output, err := json.Marshal(gr)
		if err != nil {
			return err.Error()
		}
		return string(output)
	})

	m.Get("/ws", func(w http.ResponseWriter, r *http.Request, session sessions.Session) {
		var currentVm int64 = -1
		ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		if _, ok := err.(websocket.HandshakeError); ok {
			http.Error(w, "Not a websocket handshake", 400)
			log.Println(err)
			return
		} else if err != nil {
			log.Println(err)
			return
		}
		defer ws.Close()
		websocketId := websockets.addWebsocket(ws)
		defer websockets.deleteWebsocket(websocketId)
		ws.WriteMessage(websocket.TextMessage, []byte("Welcome to ginux!\r\n"))
		for {
			_, message, err := ws.ReadMessage()
			if err != nil {
				log.Println(err)
				return
			} else {
				msgType := message[0]
				msgData := message[1:len(message)]
				switch msgType {
				case WSTerm:
					if currentVm != -1 {
						vzcontrol.ConsoleWrite(currentVm, msgData)
					}
				case WSClick:
					prevVm := currentVm
					tmp, _ := strconv.Atoi(string(msgData))
					currentVm = int64(tmp)
					websockets.Lock()
					if prevVm != -1 {
						for index, wsId := range websockets.consoleToId[prevVm] {
							if wsId == websocketId {
								websockets.consoleToId[prevVm] = append(websockets.consoleToId[prevVm][:index], websockets.consoleToId[prevVm][index+1:]...)
							}
						}
					}
					websockets.consoleToId[currentVm] = append(websockets.consoleToId[currentVm], websocketId)
					websockets.Unlock()
					ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("\r\nSelected Container %d\r\n", currentVm)))
				}
			}
		}
	})
	log.Println("Game Server started on", config.Address)
	log.Fatal(http.ListenAndServe(config.Address, m))
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func consoleDispatch() {
	for chunk := range consoleReadChannel {
		websockets.RLock()
		for _, wsId := range websockets.consoleToId[chunk.Id] {
			if socket, ok := websockets.byId[wsId]; ok {
				socket.WriteMessage(websocket.TextMessage, chunk.Data)
			}
		}
		websockets.RUnlock()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
)

type (
	// Config holds input parameters for the plugin
	Config struct {
		Plan        bool
		Vars        map[string]string
		Secrets     map[string]string
		InitOptions InitOptions
		Cacert      string
		Sensitive   bool
		RoleARN     string
		RootDir     string
		Parallelism int
		Targets     []string
		VarFiles    []string
		Destroy     bool
	}

	// InitOptions include options for the Terraform's init command
	InitOptions struct {
		BackendConfig []string `json:"backend-config"`
		Lock          *bool    `json:"lock"`
		LockTimeout   string   `json:"lock-timeout"`
	}

	// Plugin represents the plugin instance to be executed
	Plugin struct {
		Config    Config
		Terraform Terraform
	}
)

// Exec executes the plugin
func (p Plugin) Exec() error {
	// Install specified version of terraform
	if p.Terraform.Version != "" {
		err := installTerraform(p.Terraform.Version)

		if err != nil {
			return err
		}
	}

	if p.Config.RoleARN != "" {
		assumeRole(p.Config.RoleARN)
	}

	var commands []*exec.Cmd

	commands = append(commands, exec.Command("terraform", "version"))

	CopyTfEnv()

	if p.Config.Cacert != "" {
		commands = append(commands, installCaCert(p.Config.Cacert))
	}

	commands = append(commands, deleteCache())

	commands = append(commands, initCommand(p.Config.InitOptions))

	commands = append(commands, getModules())
	commands = append(commands, validateCommand())
	commands = append(commands, planCommand(p.Config))
	if !p.Config.Plan {
		commands = append(commands, terraformCommand(p.Config))
	}
	commands = append(commands, deleteCache())

	for _, c := range commands {
		if c.Dir == "" {
			wd, err := os.Getwd()
			if err == nil {
				c.Dir = wd
			}
		}
		if p.Config.RootDir != "" {
			c.Dir = c.Dir + "/" + p.Config.RootDir
		}
		c.Stdout = os.Stdout
		c.Stderr = os.Stderr
		if !p.Config.Sensitive {
			trace(c)
		}

		err := c.Run()
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"error": err,
			}).Fatal("Failed to execute a command")
		}
		logrus.Debug("Command completed successfully")
	}

	return nil
}

func installCaCert(cacert string) *exec.Cmd {
	ioutil.WriteFile("/usr/local/share/ca-certificates/ca_cert.crt", []byte(cacert), 0644)
	return exec.Command(
		"update-ca-certificates",
	)
}

func CopyTfEnv() {
	tfVar := regexp.MustCompile(`^TF_VAR_.*$`)
	for _, e := range os.Environ() {
		pair := strings.SplitN(e, "=", 2)
		if tfVar.MatchString(pair[0]) {
			name := strings.Split(pair[0], "TF_VAR_")
			os.Setenv(fmt.Sprintf("TF_VAR_%s", strings.ToLower(name[1])), pair[1])
		}
	}
}

func deleteCache() *exec.Cmd {
	return exec.Command(
		"rm",
		"-rf",
		".terraform",
	)
}

func initCommand(config InitOptions) *exec.Cmd {
	args := []string{
		"init",
	}

	for _, v := range config.BackendConfig {
		args = append(args, fmt.Sprintf("-backend-config=%s", v))
	}

	// True is default in TF
	if config.Lock != nil {
		args = append(args, fmt.Sprintf("-lock=%t", *config.Lock))
	}

	// "0s" is default in TF
	if config.LockTimeout != "" {
		args = append(args, fmt.Sprintf("-lock-timeout=%s", config.LockTimeout))
	}

	// Fail Terraform execution on prompt
	args = append(args, "-input=false")

	return exec.Command(
		"terraform",
		args...,
	)
}

func getModules() *exec.Cmd {
	return exec.Command(
		"terraform",
		"get",
	)
}

func validateCommand() *exec.Cmd {
	args := []string{
		"validate",
	}
	return exec.Command(
		"terraform",
		args...,
	)
}

func planCommand(config Config) *exec.Cmd {
	args := []string{
		"plan",
	}
	if config.Destroy {
		args = append(args, "-destroy")
	} else {
		args = append(args, "-out=plan.tfout")
	}

	for _, v := range config.Targets {
		args = append(args, "--target", fmt.Sprintf("%s", v))
	}
	for _, v := range config.VarFiles {
		args = append(args, "-var-file", fmt.Sprintf("%s", v))
	}
	for k, v := range config.Vars {
		args = append(args, "-var")
		args = append(args, fmt.Sprintf("%s=%s", k, v))
	}
	if config.Parallelism > 0 {
		args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism))
	}
	return exec.Command(
		"terraform",
		args...,
	)
}

func terraformCommand(config Config) *exec.Cmd {
	if config.Destroy {
		return destroyCommand(config)
	}

	return applyCommand(config)
}

func applyCommand(config Config) *exec.Cmd {
	args := []string{
		"apply",
	}
	for _, v := range config.Targets {
		args = append(args, "--target", fmt.Sprintf("%s", v))
	}
	if config.Parallelism > 0 {
		args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism))
	}
	args = append(args, "plan.tfout")
	return exec.Command(
		"terraform",
		args...,
	)
}

func destroyCommand(config Config) *exec.Cmd {
	args := []string{
		"destroy",
	}
	for _, v := range config.Targets {
		args = append(args, fmt.Sprintf("-target=%s", v))
	}
	if config.Parallelism > 0 {
		args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism))
	}
	args = append(args, "-force")
	return exec.Command(
		"terraform",
		args...,
	)
}

func assumeRole(roleArn string) {
	client := sts.New(session.New())
	duration := time.Hour * 1
	stsProvider := &stscreds.AssumeRoleProvider{
		Client:          client,
		Duration:        duration,
		RoleARN:         roleArn,
		RoleSessionName: "drone",
	}

	value, err := credentials.NewCredentials(stsProvider).Get()
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"error": err,
		}).Fatal("Error assuming role!")
	}
	os.Setenv("AWS_ACCESS_KEY_ID", value.AccessKeyID)
	os.Setenv("AWS_SECRET_ACCESS_KEY", value.SecretAccessKey)
	os.Setenv("AWS_SESSION_TOKEN", value.SessionToken)
}

func trace(cmd *exec.Cmd) {
	fmt.Println("$", strings.Join(cmd.Args, " "))
}
<commit_msg>Append vars to validate command<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
)

type (
	// Config holds input parameters for the plugin
	Config struct {
		Plan        bool
		Vars        map[string]string
		Secrets     map[string]string
		InitOptions InitOptions
		Cacert      string
		Sensitive   bool
		RoleARN     string
		RootDir     string
		Parallelism int
		Targets     []string
		VarFiles    []string
		Destroy     bool
	}

	// InitOptions include options for the Terraform's init command
	InitOptions struct {
		BackendConfig []string `json:"backend-config"`
		Lock          *bool    `json:"lock"`
		LockTimeout   string   `json:"lock-timeout"`
	}

	// Plugin represents the plugin instance to be executed
	Plugin struct {
		Config    Config
		Terraform Terraform
	}
)

// Exec executes the plugin
func (p Plugin) Exec() error {
	// Install specified version of terraform
	if p.Terraform.Version != "" {
		err := installTerraform(p.Terraform.Version)

		if err != nil {
			return err
		}
	}

	if p.Config.RoleARN != "" {
		assumeRole(p.Config.RoleARN)
	}

	var commands []*exec.Cmd

	commands = append(commands, exec.Command("terraform", "version"))

	CopyTfEnv()

	if p.Config.Cacert != "" {
		commands = append(commands, installCaCert(p.Config.Cacert))
	}

	commands = append(commands, deleteCache())

	commands = append(commands, initCommand(p.Config.InitOptions))

	commands = append(commands, getModules())
	commands = append(commands, validateCommand(p.Config))
	commands = append(commands, planCommand(p.Config))
	if !p.Config.Plan {
		commands = append(commands, terraformCommand(p.Config))
	}
	commands = append(commands, deleteCache())

	for _, c := range commands {
		if c.Dir == "" {
			wd, err := os.Getwd()
			if err == nil {
				c.Dir = wd
			}
		}
		if p.Config.RootDir != "" {
			c.Dir = c.Dir + "/" + p.Config.RootDir
		}
		c.Stdout = os.Stdout
		c.Stderr = os.Stderr
		if !p.Config.Sensitive {
			trace(c)
		}

		err := c.Run()
		if err != nil {
			logrus.WithFields(logrus.Fields{
				"error": err,
			}).Fatal("Failed to execute a command")
		}
		logrus.Debug("Command completed successfully")
	}

	return nil
}

func installCaCert(cacert string) *exec.Cmd {
	ioutil.WriteFile("/usr/local/share/ca-certificates/ca_cert.crt", []byte(cacert), 0644)
	return exec.Command(
		"update-ca-certificates",
	)
}

func CopyTfEnv() {
	tfVar := regexp.MustCompile(`^TF_VAR_.*$`)
	for _, e := range os.Environ() {
		pair := strings.SplitN(e, "=", 2)
		if tfVar.MatchString(pair[0]) {
			name := strings.Split(pair[0], "TF_VAR_")
			os.Setenv(fmt.Sprintf("TF_VAR_%s", strings.ToLower(name[1])), pair[1])
		}
	}
}

func deleteCache() *exec.Cmd {
	return exec.Command(
		"rm",
		"-rf",
		".terraform",
	)
}

func initCommand(config InitOptions) *exec.Cmd {
	args := []string{
		"init",
	}

	for _, v := range config.BackendConfig {
		args = append(args, fmt.Sprintf("-backend-config=%s", v))
	}

	// True is default in TF
	if config.Lock != nil {
		args = append(args, fmt.Sprintf("-lock=%t", *config.Lock))
	}

	// "0s" is default in TF
	if config.LockTimeout != "" {
		args = append(args, fmt.Sprintf("-lock-timeout=%s", config.LockTimeout))
	}

	// Fail Terraform execution on prompt
	args = append(args, "-input=false")

	return exec.Command(
		"terraform",
		args...,
	)
}

func getModules() *exec.Cmd {
	return exec.Command(
		"terraform",
		"get",
	)
}

func validateCommand(config Config) *exec.Cmd {
	args := []string{
		"validate",
	}
	for k, v := range config.Vars {
		args = append(args, "-var")
		args = append(args, fmt.Sprintf("%s=%s", k, v))
	}
	return exec.Command(
		"terraform",
		args...,
	)
}

func planCommand(config Config) *exec.Cmd {
	args := []string{
		"plan",
	}
	if config.Destroy {
		args = append(args, "-destroy")
	} else {
		args = append(args, "-out=plan.tfout")
	}

	for _, v := range config.Targets {
		args = append(args, "--target", fmt.Sprintf("%s", v))
	}
	for _, v := range config.VarFiles {
		args = append(args, "-var-file", fmt.Sprintf("%s", v))
	}
	for k, v := range config.Vars {
		args = append(args, "-var")
		args = append(args, fmt.Sprintf("%s=%s", k, v))
	}
	if config.Parallelism > 0 {
		args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism))
	}
	return exec.Command(
		"terraform",
		args...,
	)
}

func terraformCommand(config Config) *exec.Cmd {
	if config.Destroy {
		return destroyCommand(config)
	}

	return applyCommand(config)
}

func applyCommand(config Config) *exec.Cmd {
	args := []string{
		"apply",
	}
	for _, v := range config.Targets {
		args = append(args, "--target", fmt.Sprintf("%s", v))
	}
	if config.Parallelism > 0 {
		args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism))
	}
	args = append(args, "plan.tfout")
	return exec.Command(
		"terraform",
		args...,
	)
}

func destroyCommand(config Config) *exec.Cmd {
	args := []string{
		"destroy",
	}
	for _, v := range config.Targets {
		args = append(args, fmt.Sprintf("-target=%s", v))
	}
	if config.Parallelism > 0 {
		args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism))
	}
	args = append(args, "-force")
	return exec.Command(
		"terraform",
		args...,
	)
}

func assumeRole(roleArn string) {
	client := sts.New(session.New())
	duration := time.Hour * 1
	stsProvider := &stscreds.AssumeRoleProvider{
		Client:          client,
		Duration:        duration,
		RoleARN:         roleArn,
		RoleSessionName: "drone",
	}

	value, err := credentials.NewCredentials(stsProvider).Get()
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"error": err,
		}).Fatal("Error assuming role!")
	}
	os.Setenv("AWS_ACCESS_KEY_ID", value.AccessKeyID)
	os.Setenv("AWS_SECRET_ACCESS_KEY", value.SecretAccessKey)
	os.Setenv("AWS_SESSION_TOKEN", value.SessionToken)
}

func trace(cmd *exec.Cmd) {
	fmt.Println("$", strings.Join(cmd.Args, " "))
}
<|endoftext|>