<commit_before>/*Package configurationFile represents Encodable/Decodable Golang structures from/to TOML structures.
 *
 *The global structure is ConfigurationFile, which is the simple way to store accurtely your local informations.
 *
 */
package configurationFile

import (
	"bytes"
	"errors"
	"io/ioutil"
	"path/filepath"

	"fmt"

	"os/user"

	"os"

	"strings"

	"github.com/BurntSushi/toml"
	"github.com/k0pernicus/goyave/consts"
	"github.com/k0pernicus/goyave/gitManip"
	"github.com/k0pernicus/goyave/traces"
	"github.com/k0pernicus/goyave/utils"
)

/*GetConfigurationFileContent get the content of the local configuration file.
 *If no configuration file has been found, create a default one and set the bytes array.
 */
func GetConfigurationFileContent(filePointer *os.File, bytesArray *[]byte) {
	fileState, err := filePointer.Stat()
	// If the file is empty, get the default structure and save it
	if err != nil || fileState.Size() == 0 {
		traces.WarningTracer.Println("No (or empty) configuration file - creating default one...")
		var fileBuffer bytes.Buffer
		defaultStructure := Default()
		defaultStructure.Encode(&fileBuffer)
		*bytesArray = fileBuffer.Bytes()
	} else {
		b, _ := ioutil.ReadAll(filePointer)
		*bytesArray = b
	}
}

/*ConfigurationFile represents the TOML structure of the Goyave configuration file.
 *
 *The structure of the configuration file is:
 *	Author:
 *		The name of the user.
 *	VisibleRepositories:
 *		A list of local visible git repositories.
 *	HiddenRepositories:
 *		A list of local ignored git repositories.
 *	Groups:
 *		A list of groups.
 */
type ConfigurationFile struct {
	Author              string
	Local               LocalInformations `toml:"local"`
	VisibleRepositories []GitRepository   `toml:"visible"`
	HiddenRepositories  []GitRepository   `toml:"hidden"`
	Groups              []Group           `toml:"group"`
}

/*Default returns a default ConfigurationFile structure.
 */
func Default() *ConfigurationFile {
	usr, _ := user.Current()
	return &ConfigurationFile{
		Author: usr.Username,
		Local: LocalInformations{
			DefaultTarget: consts.VisibleFlag,
			Group:         utils.GetLocalhost(),
		},
	}
}

/*GetDefaultEntry returns the default entry to store a new git repository.
 *This methods returns HiddenFlag, or VisibleFlag
 */
func (c *ConfigurationFile) GetDefaultEntry() (string, error) {
	defaultTarget := c.Local.DefaultTarget
	if defaultTarget != consts.VisibleFlag && defaultTarget != consts.HiddenFlag {
		return consts.VisibleFlag, fmt.Errorf("the default target is not set to %s or %s. Please check your configuration file", consts.HiddenFlag, consts.VisibleFlag)
	}
	return defaultTarget, nil
}

/*AddRepository will add a single path in the TOML's target if the path does not exists.
 *This method uses both methods addVisibleRepository and addHiddenRepository.
 */
func (c *ConfigurationFile) AddRepository(path string, target string) error {
	if target == consts.VisibleFlag {
		return c.addVisibleRepository(path)
	}
	if target == consts.HiddenFlag {
		return c.addHiddenRepository(path)
	}
	return errors.New("the target does not exists")
}

/*addVisibleRepository adds a given git repo path as a visible repository.
 *If the repository already exists in the VisibleRepository field, the method throws an error: RepositoryAlreadyExists.
 *Else, the repository is append to the VisibleRepository field, and the method returns nil.
 */
func (c *ConfigurationFile) addVisibleRepository(path string) error {
	for _, registeredRepository := range c.VisibleRepositories {
		if registeredRepository.Path == path {
			return errors.New(consts.RepositoryAlreadyExists)
		}
	}
	var newVisibleRepository = NewGitRepository(filepath.Base(path), path)
	c.VisibleRepositories = append(c.VisibleRepositories, newVisibleRepository)
	return nil
}

/*addHiddenRepository adds a given git repo path as an hidden repository.
 *If the repository already exists in the HiddenRepository field, the method throws an error: RepositoryAlreadyExists.
 *Else, the repository is append to the HiddenRepository field, and the method returns nil.
 */
func (c *ConfigurationFile) addHiddenRepository(path string) error {
	for _, registeredRepository := range c.HiddenRepositories {
		if registeredRepository.Path == path {
			return errors.New(consts.RepositoryAlreadyExists)
		}
	}
	var newHiddenRepository = NewGitRepository(filepath.Base(path), path)
	c.HiddenRepositories = append(c.HiddenRepositories, newHiddenRepository)
	return nil
}

/*GetPathFromRepository returns the path of a given git repository name.
 *If the repository does not exists, it returns an empty string.
 */
func (c *ConfigurationFile) GetPathFromRepository(target string) string {
	for _, registeredRepository := range c.VisibleRepositories {
		if registeredRepository.Name == target {
			return registeredRepository.Path
		}
	}
	return ""
}

/*RemoveRepositoryFromSlice returns a new slice without the corresponding element (here, a string).
 *If the element is not found, this method returns an error.
 */
func (c *ConfigurationFile) RemoveRepositoryFromSlice(path string, slice string) error {
	// The code below is the same for visible and hidden repositories - need to refactor the code later
	if slice == consts.VisibleFlag {
		sliceIndex := utils.SliceIndex(len(c.VisibleRepositories), func(i int) bool { return c.VisibleRepositories[i].Path == path })
		if sliceIndex != -1 {
			c.VisibleRepositories = append(c.VisibleRepositories[:sliceIndex], c.VisibleRepositories[sliceIndex+1:]...)
			return nil
		}
		return errors.New(consts.ItemIsNotInSlice)
	}
	sliceIndex := utils.SliceIndex(len(c.HiddenRepositories), func(i int) bool { return c.HiddenRepositories[i].Path == path })
	if sliceIndex != -1 {
		c.HiddenRepositories = append(c.HiddenRepositories[:sliceIndex], c.HiddenRepositories[sliceIndex+1:]...)
		return nil
	}
	return errors.New(consts.ItemIsNotInSlice)
}

/*GitRepository represents the structure of a local git repository.
 *
 *Properties of this structure are:
 *	GitObject:
 *		A reference to a git structure that represents the repository - ignored in the TOML file.
 *	Name:
 * 		The custom name of the repository.
 *	Path:
 *		The path of the repository.
 *	URL:
 *		The remote URL of the repository (from origin).
 */
type GitRepository struct {
	GitObject *gitManip.GitObject `toml:"-"`
	Name      string
	Path      string
	URL       string
}

/*ByName implements sort.Interface for []GitRepository based on the Name field.
 */
type ByName []GitRepository

/*Len returns the length of the ByName type object.
 */
func (g ByName) Len() int { return len(g) }

/*Swap swaps two objects in the same array.
 */
func (g ByName) Swap(i, j int) { g[i], g[j] = g[j], g[i] }

/*Less returns True if the first element is lower than the second one (alphabetic order).
 */
func (g ByName) Less(i, j int) bool { return strings.Compare(g[i].Name, g[j].Name) == -1 }

/*NewGitRepository instantiates the GitRepository struct, based on the path information.
 */
func NewGitRepository(name, path string) GitRepository {
	gitObject := gitManip.New(path)
	return GitRepository{
		GitObject: gitObject,
		Name:      name,
		Path:      path,
		URL:       gitObject.GetRemoteURL(),
	}
}

/*Init (re)initializes the GitObject structure
 */
func (g *GitRepository) Init() {
	g.GitObject = gitManip.New(g.Path)
}

/*isExists check if the current path of the git repository is correct or not,
 *and if the current repository exists again or not.
 *This methods returns a boolean value.
 */
func (g *GitRepository) isExists() bool {
	_, err := os.Stat(g.Path)
	return os.IsNotExist(err)
}

/*Group represents a group of git repositories.
 *
 *The structure of a Group type is:
 *
 *	Name:
 *		The group name.
 *	Repositories:
 *		A list of git repositories id, tagged in the group.
 */
type Group struct {
	Name         string
	Repositories []string
}

/*LocalInformations represents your local configuration of Goyave.
 *
 *The structure contains:
 *  DefaultEntry:
 *		The default entry to store a git repository (hidden or visible).
 *	Group:
 *		The current group name.
 */
type LocalInformations struct {
	DefaultTarget string
	Group         string
}

/*DecodeString is a function to decode an entire string (which is the content of a given TOML file) to a ConfigurationFile structure.
 */
func DecodeString(c *ConfigurationFile, data string) error {
	_, err := toml.Decode(data, *c)
	return err
}

/*DecodeBytesArray is a function to decode an entire string (which is the content of a given TOML file) to a ConfigurationFile structure.
 */
func DecodeBytesArray(c *ConfigurationFile, data []byte) error {
	_, err := toml.Decode(string(data[:]), *c)
	return err
}

/*Encode is a function to encode a ConfigurationFile structure to a byffer of bytes.
 */
func (c *ConfigurationFile) Encode(buffer *bytes.Buffer) error {
	return toml.NewEncoder(buffer).Encode(c)
}
<commit_msg>First step to the new version of Goyave<commit_after>/*Package configurationFile represents Encodable/Decodable Golang structures from/to TOML structures.
 *
 *The global structure is ConfigurationFile, which is the simple way to store accurtely your local informations.
 *
 */
package configurationFile

import (
	"bytes"
	"errors"
	"io/ioutil"
	"path/filepath"

	"fmt"

	"os/user"

	"os"

	"strings"

	"sort"

	"github.com/BurntSushi/toml"
	"github.com/k0pernicus/goyave/consts"
	"github.com/k0pernicus/goyave/gitManip"
	"github.com/k0pernicus/goyave/traces"
	"github.com/k0pernicus/goyave/utils"
)

/*GetConfigurationFileContent get the content of the local configuration file.
 *If no configuration file has been found, create a default one and set the bytes array.
 */
func GetConfigurationFileContent(filePointer *os.File, bytesArray *[]byte) {
	fileState, err := filePointer.Stat()
	// If the file is empty, get the default structure and save it
	if err != nil || fileState.Size() == 0 {
		traces.WarningTracer.Println("No (or empty) configuration file - creating default one...")
		var fileBuffer bytes.Buffer
		defaultStructure := Default()
		defaultStructure.Encode(&fileBuffer)
		*bytesArray = fileBuffer.Bytes()
	} else {
		b, _ := ioutil.ReadAll(filePointer)
		*bytesArray = b
	}
}

/*ConfigurationFile represents the TOML structure of the Goyave configuration file.
 *
 *The structure of the configuration file is:
 *	Author:
 *		The name of the user.
 *	VisibleRepositories:
 *		A list of local visible git repositories.
 *	HiddenRepositories:
 *		A list of local ignored git repositories.
 *	Groups:
 *		A list of groups.
 */
type ConfigurationFile struct {
	Author              string
	Local               LocalInformations `toml:"local"`
	Repositories        []GitRepository   `toml:"repositories"`
	VisibleRepositories []GitRepository   `toml:"-"`
	Groups              []Group           `toml:"group"`
}

/*Default returns a default ConfigurationFile structure.
 */
func Default() *ConfigurationFile {
	usr, _ := user.Current()
	localhost := utils.GetLocalhost()
	return &ConfigurationFile{
		Author: usr.Username,
		Local: LocalInformations{
			DefaultTarget: consts.VisibleFlag,
			Group:         localhost,
		},
		Groups: []Group{
			Group{
				Name: localhost,
			},
		},
	}
}

/*GetDefaultEntry returns the default entry to store a new git repository.
 *This methods returns HiddenFlag, or VisibleFlag
 */
func (c *ConfigurationFile) GetDefaultEntry() (string, error) {
	defaultTarget := c.Local.DefaultTarget
	if defaultTarget != consts.VisibleFlag && defaultTarget != consts.HiddenFlag {
		return consts.VisibleFlag, fmt.Errorf("the default target is not set to %s or %s. Please check your configuration file", consts.HiddenFlag, consts.VisibleFlag)
	}
	return defaultTarget, nil
}

/*AddRepository will add a single path in the TOML's target if the path does not exists.
 *This method uses both methods addVisibleRepository and addHiddenRepository.
 */
func (c *ConfigurationFile) AddRepository(path string, target string) error {
	if utils.SliceIndex(len(c.Repositories), func(i int) bool { return c.Repositories[i].Path == path }) != -1 {
		return errors.New(consts.RepositoryAlreadyExists)
	}
	var newRepository = NewGitRepository(filepath.Base(path), path)
	c.Repositories = append(c.Repositories, newRepository)
	if target == consts.VisibleFlag {
		c.VisibleRepositories = append(c.VisibleRepositories, newRepository)
	}
	return nil
}

/*addVisibleRepository adds a given git repo path as a visible repository.
 *If the repository already exists in the VisibleRepository field, the method throws an error: RepositoryAlreadyExists.
 *Else, the repository is append to the VisibleRepository field, and the method returns nil.
 */
func (c *ConfigurationFile) addVisibleRepository(path string) error {
	for _, registeredRepository := range c.VisibleRepositories {
		if registeredRepository.Path == path {
			return errors.New(consts.RepositoryAlreadyExists)
		}
	}
	var newVisibleRepository = NewGitRepository(filepath.Base(path), path)
	c.VisibleRepositories = append(c.VisibleRepositories, newVisibleRepository)
	return nil
}

/*GetPathFromRepository returns the path of a given git repository name.
 *If the repository does not exists, it returns an empty string.
 */
func (c *ConfigurationFile) GetPathFromRepository(target string) string {
	for _, registeredRepository := range c.VisibleRepositories {
		if registeredRepository.Name == target {
			return registeredRepository.Path
		}
	}
	return ""
}

/*Process manages the current configuration file
 */
func (c *ConfigurationFile) Process() error {
	currentGroup := c.Local.Group
	var visibleRepositories []string
	for _, group := range c.Groups {
		groupName := group.Name
		groupRepositories := group.VisibleRepositories
		if groupName == currentGroup {
			visibleRepositories = groupRepositories
		}
	}
	if len(visibleRepositories) == 0 {
		return fmt.Errorf("no group %s in your configuration file", currentGroup)
	}
	// Sort repositories
	sort.Sort(ByName(c.Repositories))
	for _, visibleRepository := range visibleRepositories {
		index := utils.SliceIndex(len(c.Repositories), func(i int) bool { return c.Repositories[i].Name == visibleRepository })
		if index == -1 {
			traces.WarningTracer.Printf("Repository %s has not been found!", visibleRepository)
			continue
		}
		c.addVisibleRepository(c.Repositories[index].Path)
	}
	return nil
}

/*RemoveRepositoryFromSlice returns a new slice without the corresponding element (here, a string).
 *If the element is not found, this method returns an error.
 */
func (c *ConfigurationFile) RemoveRepositoryFromSlice(path string, slice string) error {
	// The code below is the same for visible and hidden repositories - need to refactor the code later
	if slice == consts.VisibleFlag {
		sliceIndex := utils.SliceIndex(len(c.VisibleRepositories), func(i int) bool { return c.VisibleRepositories[i].Path == path })
		if sliceIndex != -1 {
			c.VisibleRepositories = append(c.VisibleRepositories[:sliceIndex], c.VisibleRepositories[sliceIndex+1:]...)
			return nil
		}
		return errors.New(consts.ItemIsNotInSlice)
	}
	return errors.New(consts.ItemIsNotInSlice)
}

/*GitRepository represents the structure of a local git repository.
 *
 *Properties of this structure are:
 *	GitObject:
 *		A reference to a git structure that represents the repository - ignored in the TOML file.
 *	Name:
 * 		The custom name of the repository.
 *	Path:
 *		The path of the repository.
 *	URL:
 *		The remote URL of the repository (from origin).
 */
type GitRepository struct {
	GitObject *gitManip.GitObject `toml:"-"`
	Name      string
	Path      string
	URL       string
}

/*ByName implements sort.Interface for []GitRepository based on the Name field.
 */
type ByName []GitRepository

/*Len returns the length of the ByName type object.
 */
func (g ByName) Len() int { return len(g) }

/*Swap swaps two objects in the same array.
 */
func (g ByName) Swap(i, j int) { g[i], g[j] = g[j], g[i] }

/*Less returns True if the first element is lower than the second one (alphabetic order).
 */
func (g ByName) Less(i, j int) bool { return strings.Compare(g[i].Name, g[j].Name) == -1 }

/*NewGitRepository instantiates the GitRepository struct, based on the path information.
 */
func NewGitRepository(name, path string) GitRepository {
	gitObject := gitManip.New(path)
	return GitRepository{
		GitObject: gitObject,
		Name:      name,
		Path:      path,
		URL:       gitObject.GetRemoteURL(),
	}
}

/*Init (re)initializes the GitObject structure
 */
func (g *GitRepository) Init() {
	g.GitObject = gitManip.New(g.Path)
}

/*isExists check if the current path of the git repository is correct or not,
 *and if the current repository exists again or not.
 *This methods returns a boolean value.
 */
func (g *GitRepository) isExists() bool {
	_, err := os.Stat(g.Path)
	return os.IsNotExist(err)
}

/*Group represents a group of git repositories.
 *
 *The structure of a Group type is:
 *
 *	Name:
 *		The group name.
 *	Repositories:
 *		A list of git repositories id, tagged in the group.
 */
type Group struct {
	Name                string
	VisibleRepositories []string
}

/*LocalInformations represents your local configuration of Goyave.
 *
 *The structure contains:
 *  DefaultEntry:
 *		The default entry to store a git repository (hidden or visible).
 *	Group:
 *		The current group name.
 */
type LocalInformations struct {
	DefaultTarget string
	Group         string
}

/*DecodeString is a function to decode an entire string (which is the content of a given TOML file) to a ConfigurationFile structure.
 */
func DecodeString(c *ConfigurationFile, data string) error {
	_, err := toml.Decode(data, *c)
	return err
}

/*DecodeBytesArray is a function to decode an entire string (which is the content of a given TOML file) to a ConfigurationFile structure.
 */
func DecodeBytesArray(c *ConfigurationFile, data []byte) error {
	_, err := toml.Decode(string(data[:]), *c)
	return err
}

/*Encode is a function to encode a ConfigurationFile structure to a byffer of bytes.
 */
func (c *ConfigurationFile) Encode(buffer *bytes.Buffer) error {
	return toml.NewEncoder(buffer).Encode(c)
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package modelmanager provides the business logic for
// model management operations in the controller.
package modelmanager

import (
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/tools"
	"github.com/juju/juju/version"
)

var (
	logger = loggo.GetLogger("juju.controller.modelmanager")

	configValuesFromController = []string{
		"type",
		"ca-cert",
		"state-port",
		"api-port",
		config.ControllerUUIDKey,
	}
)

// ModelConfigCreator provides a method of creating a new model config.
//
// The zero value of ModelConfigCreator is usable with the limitations
// noted on each struct field.
type ModelConfigCreator struct {
	// FindTools, if non-nil, will be used to validate the agent-version
	// value in NewModelConfig if it differs from the base configuration.
	//
	// If FindTools is nil, agent-version may not be different to the
	// base configuration.
	FindTools func(version.Number) (tools.List, error)
}

// NewModelConfig returns a new model config given a base (controller) config
// and a set of attributes that will be specific to the new model, overriding
// and non-restricted attributes in the base configuration. The resulting
// config will be suitable for creating a new model in state.
//
// If "attrs" does not include a UUID, a new, random one will be generated
// and added to the config.
//
// The config will be validated with the provider before being returned.
func (c ModelConfigCreator) NewModelConfig(
	base *config.Config,
	attrs map[string]interface{},
) (*config.Config, error) {

	if err := c.checkVersion(base, attrs); err != nil {
		return nil, errors.Trace(err)
	}

	// Before comparing any values, we need to push the config through
	// the provider validation code. One of the reasons for this is that
	// numbers being serialized through JSON get turned into float64. The
	// schema code used in config will convert these back into integers.
	// However, before we can create a valid config, we need to make sure
	// we copy across fields from the main config that aren't there.
	baseAttrs := base.AllAttrs()
	restrictedFields, err := RestrictedProviderFields(base.Type())
	if err != nil {
		return nil, errors.Trace(err)
	}
	for _, field := range restrictedFields {
		if _, ok := attrs[field]; !ok {
			if baseValue, ok := baseAttrs[field]; ok {
				attrs[field] = baseValue
			}
		}
	}

	// Generate a new UUID for the model as necessary,
	// and finalize the new config.
	if _, ok := attrs[config.UUIDKey]; !ok {
		uuid, err := utils.NewUUID()
		if err != nil {
			return nil, errors.Trace(err)
		}
		attrs[config.UUIDKey] = uuid.String()
	}
	cfg, err := finalizeConfig(attrs)
	if err != nil {
		return nil, errors.Trace(err)
	}
	attrs = cfg.AllAttrs()

	// Any values that would normally be copied from the controller
	// config can also be defined, but if they differ from the controller
	// values, an error is returned.
	for _, field := range restrictedFields {
		if value, ok := attrs[field]; ok {
			if serverValue := baseAttrs[field]; value != serverValue {
				return nil, errors.Errorf(
					"specified %s \"%v\" does not match controller \"%v\"",
					field, value, serverValue)
			}
		}
	}
	return cfg, nil
}

func (c *ModelConfigCreator) checkVersion(base *config.Config, attrs map[string]interface{}) error {
	baseVersion, ok := base.AgentVersion()
	if !ok {
		return errors.Errorf("agent-version not found in base config")
	}

	// If there is no agent-version specified, use the current version.
	// otherwise we need to check for tools
	value, ok := attrs["agent-version"]
	if !ok {
		attrs["agent-version"] = baseVersion.String()
		return nil
	}
	versionStr, ok := value.(string)
	if !ok {
		return errors.Errorf("agent-version must be a string but has type '%T'", value)
	}
	versionNumber, err := version.Parse(versionStr)
	if err != nil {
		return errors.Trace(err)
	}

	n := versionNumber.Compare(baseVersion)
	switch {
	case n > 0:
		return errors.Errorf(
			"agent-version (%s) cannot be greater than the controller (%s)",
			versionNumber, baseVersion,
		)
	case n == 0:
		// If the version is the same as the base config,
		// then assume tools are available.
		return nil
	case n < 0:
		if c.FindTools == nil {
			return errors.New(
				"agent-version does not match base config, " +
					"and no tools-finder is supplied",
			)
		}
	}

	// Look to see if we have tools available for that version.
	list, err := c.FindTools(versionNumber)
	if err != nil {
		return errors.Trace(err)
	}
	if len(list) == 0 {
		return errors.Errorf("no tools found for version %s", versionNumber)
	}
	logger.Tracef("found tools: %#v", list)
	return nil
}

// RestrictedProviderFields returns the set of config fields that may not be
// overridden.
func RestrictedProviderFields(providerType string) ([]string, error) {
	provider, err := environs.Provider(providerType)
	if err != nil {
		return nil, errors.Trace(err)
	}
	var fields []string
	fields = append(fields, configValuesFromController...)
	fields = append(fields, provider.RestrictedConfigAttributes()...)
	return fields, nil
}

// finalizeConfig creates the config object from attributes, calls
// PrepareForCreateEnvironment, and then finally validates the config
// before returning it.
func finalizeConfig(attrs map[string]interface{}) (*config.Config, error) {
	cfg, err := config.New(config.UseDefaults, attrs)
	if err != nil {
		return nil, errors.Annotate(err, "creating config from values failed")
	}
	provider, err := environs.Provider(cfg.Type())
	if err != nil {
		return nil, errors.Trace(err)
	}
	cfg, err = provider.PrepareForCreateEnvironment(cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}
	cfg, err = provider.Validate(cfg, nil)
	if err != nil {
		return nil, errors.Annotate(err, "provider validation failed")
	}
	return cfg, nil
}
<commit_msg>Fix typo<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package modelmanager provides the business logic for
// model management operations in the controller.
package modelmanager

import (
	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/environs"
	"github.com/juju/juju/environs/config"
	"github.com/juju/juju/tools"
	"github.com/juju/juju/version"
)

var (
	logger = loggo.GetLogger("juju.controller.modelmanager")

	configValuesFromController = []string{
		"type",
		"ca-cert",
		"state-port",
		"api-port",
		config.ControllerUUIDKey,
	}
)

// ModelConfigCreator provides a method of creating a new model config.
//
// The zero value of ModelConfigCreator is usable with the limitations
// noted on each struct field.
type ModelConfigCreator struct {
	// FindTools, if non-nil, will be used to validate the agent-version
	// value in NewModelConfig if it differs from the base configuration.
	//
	// If FindTools is nil, agent-version may not be different to the
	// base configuration.
	FindTools func(version.Number) (tools.List, error)
}

// NewModelConfig returns a new model config given a base (controller) config
// and a set of attributes that will be specific to the new model, overriding
// any non-restricted attributes in the base configuration. The resulting
// config will be suitable for creating a new model in state.
//
// If "attrs" does not include a UUID, a new, random one will be generated
// and added to the config.
//
// The config will be validated with the provider before being returned.
func (c ModelConfigCreator) NewModelConfig(
	base *config.Config,
	attrs map[string]interface{},
) (*config.Config, error) {

	if err := c.checkVersion(base, attrs); err != nil {
		return nil, errors.Trace(err)
	}

	// Before comparing any values, we need to push the config through
	// the provider validation code. One of the reasons for this is that
	// numbers being serialized through JSON get turned into float64. The
	// schema code used in config will convert these back into integers.
	// However, before we can create a valid config, we need to make sure
	// we copy across fields from the main config that aren't there.
	baseAttrs := base.AllAttrs()
	restrictedFields, err := RestrictedProviderFields(base.Type())
	if err != nil {
		return nil, errors.Trace(err)
	}
	for _, field := range restrictedFields {
		if _, ok := attrs[field]; !ok {
			if baseValue, ok := baseAttrs[field]; ok {
				attrs[field] = baseValue
			}
		}
	}

	// Generate a new UUID for the model as necessary,
	// and finalize the new config.
	if _, ok := attrs[config.UUIDKey]; !ok {
		uuid, err := utils.NewUUID()
		if err != nil {
			return nil, errors.Trace(err)
		}
		attrs[config.UUIDKey] = uuid.String()
	}
	cfg, err := finalizeConfig(attrs)
	if err != nil {
		return nil, errors.Trace(err)
	}
	attrs = cfg.AllAttrs()

	// Any values that would normally be copied from the controller
	// config can also be defined, but if they differ from the controller
	// values, an error is returned.
	for _, field := range restrictedFields {
		if value, ok := attrs[field]; ok {
			if serverValue := baseAttrs[field]; value != serverValue {
				return nil, errors.Errorf(
					"specified %s \"%v\" does not match controller \"%v\"",
					field, value, serverValue)
			}
		}
	}
	return cfg, nil
}

func (c *ModelConfigCreator) checkVersion(base *config.Config, attrs map[string]interface{}) error {
	baseVersion, ok := base.AgentVersion()
	if !ok {
		return errors.Errorf("agent-version not found in base config")
	}

	// If there is no agent-version specified, use the current version.
	// otherwise we need to check for tools
	value, ok := attrs["agent-version"]
	if !ok {
		attrs["agent-version"] = baseVersion.String()
		return nil
	}
	versionStr, ok := value.(string)
	if !ok {
		return errors.Errorf("agent-version must be a string but has type '%T'", value)
	}
	versionNumber, err := version.Parse(versionStr)
	if err != nil {
		return errors.Trace(err)
	}

	n := versionNumber.Compare(baseVersion)
	switch {
	case n > 0:
		return errors.Errorf(
			"agent-version (%s) cannot be greater than the controller (%s)",
			versionNumber, baseVersion,
		)
	case n == 0:
		// If the version is the same as the base config,
		// then assume tools are available.
		return nil
	case n < 0:
		if c.FindTools == nil {
			return errors.New(
				"agent-version does not match base config, " +
					"and no tools-finder is supplied",
			)
		}
	}

	// Look to see if we have tools available for that version.
	list, err := c.FindTools(versionNumber)
	if err != nil {
		return errors.Trace(err)
	}
	if len(list) == 0 {
		return errors.Errorf("no tools found for version %s", versionNumber)
	}
	logger.Tracef("found tools: %#v", list)
	return nil
}

// RestrictedProviderFields returns the set of config fields that may not be
// overridden.
func RestrictedProviderFields(providerType string) ([]string, error) {
	provider, err := environs.Provider(providerType)
	if err != nil {
		return nil, errors.Trace(err)
	}
	var fields []string
	fields = append(fields, configValuesFromController...)
	fields = append(fields, provider.RestrictedConfigAttributes()...)
	return fields, nil
}

// finalizeConfig creates the config object from attributes, calls
// PrepareForCreateEnvironment, and then finally validates the config
// before returning it.
func finalizeConfig(attrs map[string]interface{}) (*config.Config, error) {
	cfg, err := config.New(config.UseDefaults, attrs)
	if err != nil {
		return nil, errors.Annotate(err, "creating config from values failed")
	}
	provider, err := environs.Provider(cfg.Type())
	if err != nil {
		return nil, errors.Trace(err)
	}
	cfg, err = provider.PrepareForCreateEnvironment(cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}
	cfg, err = provider.Validate(cfg, nil)
	if err != nil {
		return nil, errors.Annotate(err, "provider validation failed")
	}
	return cfg, nil
}
<|endoftext|>


<commit_before>package generate

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/blue-jay/jay/command"
)

var (
	LoopLimit = 100
)

var Cmd = &command.Info{
	Run:       run,
	UsageLine: "generate [folder/file] key:value...",
	Short:     "code generation",
	Long: `
Generate will parse and create files from template pairs.

A template pair is a set of template files:
  * default.json - json file
  * default.gen - any type of text file

Both files are template files and are parsed using the Go text/template
package. The 'jay generate' tool loops through the first level of key pairs
for empty strings. For every empty string, an argument is required to be
passed (whether empty or not) to the 'jay generate' command.

Let's look at generate/model/default.json:
{
	"info": {
		"output": "model/{{.package}}/{{.package}}.go"
	},
	"package": "",
	"table": ""
}

Let's break down this command into pieces:
jay generate model/default package:automobile table:car

Argument: 'model/default'
Specifies generate/model/default.json and generate/model/default.gen are the
template pair.

Argument: 'package:automobile'
The key, 'package', from default.json will be filled with the value:
'automobile'

Argument: 'table:car'
The key, 'table', from default.json will be filled with the value: 'car'

The .json file is actually parsed up to 100 times (LoopLimit of 100 can be
changed at the package level) to ensure all variables like '{{.package}}' are
set to the correct value.

In the first iteration of parsing, the 'package' key is set to 'car'.
In the second iteration of parsing, the '{{.package}}' variables
are set to 'car' also since the 'package' key becomes a variable.

All first level keys (info, package, table) become variables after the first
iteration of parsing so they can be used without the file. If a variable is
misspelled and is never filled, a helpful error will be displayed.

The 'output' key under 'info' is required. It should be the relative output
file path to the project root for the generated file.

The folder structure of the templates (model, controller, etc) has no effect
on the generation, it's purely to air with organization of the template pairs.

You must store the path to the env.json file in the environment
variable: JAYCONFIG. The file is at project root that is prepended to all
relative file paths.

Examples:
  jay generate model/default package:car table:car
	Generate a new model from variables in model/default.json and applies
	those variables to model/default.gen.
  jay generate controller/default package:car url:car model:car view:car
	Generate a new controller from variables in controller/default.json
	and applies those variables to controller/default.gen.

Flags:
  Argument 1 - model/default or controller/default
	Relative path without an extension to the template pair. Any combination
	of folders and files can be used.
  Argument 2,3,etc - package:car
	Key pair to set in the .json file. Required for every empty key in the
	.json file.
`,
}

func run(cmd *command.Info, args []string) {
	rootFolder := projectFolder()

	if len(args) >= 2 {
		// Ensure the template pair files exist
		jsonFilePath := filepath.Join(rootFolder, "generate", args[0]+".json")
		if !exists(jsonFilePath) {
			log.Fatalf("File doesn't exist: %v", jsonFilePath)
		}

		argMap := argsToMap(args)

		// Get the json file as a map - not parsed
		mapFile, err := jsonFileToMap(jsonFilePath)
		if err != nil {
			log.Fatal(err)
		}

		// Generate variable map
		variableMap := generateVariableMap(mapFile, argMap)

		// Check for config type
		configType, ok := variableMap["config.type"]
		if !ok {
			log.Fatal("Key, 'config.type', is missing from the .json file")
		}

		// Handle based on config.type
		switch configType {
		case "single":
			// Template File
			genFilePath := filepath.Join(rootFolder, "generate", args[0]+".gen")

			// Generate the template
			generateSingle(rootFolder, genFilePath, variableMap)

			return
		case "collection":
			generateCollection(rootFolder, variableMap)
			return
		default:
			log.Fatalf("Value of '%v' for key 'config.type' is not supported", configType)
		}
	}

	fmt.Println("Flags are missing.")
}

func generateCollection(folderPath string, variableMap map[string]interface{}) {
	// Check for required key
	collectionRaw, ok := variableMap["config.collection"]
	if !ok {
		log.Fatal("Key, 'config.collection', is missing from the .json file")
	}

	collection, ok := collectionRaw.([]interface{})
	if !ok {
		log.Fatal("Key, 'config.collection', is not in the correct format")
	}

	// Loop through the collections
	for i, v := range collection {
		vMap, ok := v.(map[string]interface{})
		if !ok {
			log.Fatal("Values for key, 'config.collection', are not in the correct format")
		}

		for name, varArray := range vMap {

			argMap, ok := varArray.(map[string]interface{})
			if !ok {
				log.Fatalf("Item at index '%v' for key, 'config.collection', is not in the correct format", i)
			}

			// Template File
			genFilePath := filepath.Join(folderPath, "generate", name+".gen")
			jsonFilePath := filepath.Join(folderPath, "generate", name+".json")

			// Get the json file as a map - not parsed
			mapFile, err := jsonFileToMap(jsonFilePath)
			if err != nil {
				log.Fatal(err)
			}

			// Generate variable map
			variableMap := generateVariableMap(mapFile, argMap)

			generateSingle(folderPath, genFilePath, variableMap)
		}
	}
}

func generateSingle(folderPath string, genFilePath string, variableMap map[string]interface{}) {
	// Check for required key
	if _, ok := variableMap["config.output"]; !ok {
		log.Fatal("Key, 'config.output', is missing from the .json file")
	}

	// Output file
	outputRelativeFile := fmt.Sprintf("%v", variableMap["config.output"])
	outputFile := filepath.Join(folderPath, outputRelativeFile)

	// Check if the file exists
	if exists(outputFile) {
		log.Fatalf("Cannot generate because file already exists: %v", outputFile)
	}

	// Check if the folder exists
	dir := filepath.Dir(outputFile)
	if !exists(dir) {
		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil {
			log.Fatalln(err)
		}
	}

	err := fromMapToFile(genFilePath, variableMap, outputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Code generated:", outputFile)
}

// projectFolder returns the project folder path
func projectFolder() string {
	jc := os.Getenv("JAYCONFIG")
	if len(jc) == 0 {
		log.Fatalln("Environment variable JAYCONFIG needs to be set to the env.json file location.")
	}

	info := make(map[string]interface{})

	// Read the config file
	jsonBytes, err := ioutil.ReadFile(jc)
	if err != nil {
		log.Fatalln("The configuration file cannot be found so this command will not work.")
	}

	// Parse the config
	err = json.Unmarshal(jsonBytes, &info)
	if err != nil {
		log.Fatalln("The configuration file cannot be parsed so this command will not work.")
	}

	return filepath.Dir(jc)
}

func cloneMap(originalMap map[string]interface{}) map[string]interface{} {
	copyMap := make(map[string]interface{})

	for k, v := range originalMap {
		copyMap[k] = v
	}

	return copyMap
}

// exists will exit if the file or folder doesn't exist
func exists(f string) bool {
	if _, err := os.Stat(f); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// jsonFileToMap converts json file to an interface map
func jsonFileToMap(file string) (map[string]interface{}, error) {
	// Read the config file
	b, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	// Convert json to interface
	var d map[string]interface{}
	err = json.Unmarshal(b, &d)
	if err != nil {
		return nil, err
	}

	return d, nil
}

// fromMapToFile will output a file by parsing a template and applying
// variables from an interface map
func fromMapToFile(templateFile string, d map[string]interface{}, outputFile string) error {
	// Parse the template
	t, err := template.ParseFiles(templateFile)
	if err != nil {
		return err
	}

	// Create the output file
	f, err := os.Create(outputFile)
	if err != nil {
		return err
	}
	defer f.Close()

	// Fills template with variables and writes to file
	err = t.Execute(f, d)
	if err != nil {
		return err
	}

	return nil
}

func argsToMap(args []string) map[string]interface{} {
	// Fill a new map with variables
	argMap := make(map[string]interface{})
	for _, a := range args[1:] {
		arr := strings.Split(a, ":")

		if len(arr) < 2 {
			log.Fatalf("Arg is in wrong format: %v", a)
		}

		argMap[arr[0]] = strings.Join(arr[1:], ":")
	}

	return argMap
}

// generateVariableMap returns the relative file output path and the map of
// variables
func generateVariableMap(mapFile map[string]interface{}, argMap map[string]interface{}) map[string]interface{} {
	m := cloneMap(mapFile)

	// Loop through the map to find empty variables
	for s, v := range m {
		switch t := v.(type) {
		case string:
			if len(t) == 0 {
				if val, ok := argMap[s]; ok {
					m[s] = val
				} else {
					log.Fatalf("Variable missing: %v", s)
				}
			} else { // Else delete any values that are not empty
				delete(m, s)
			}
		default:
			delete(m, s)
		}
	}

	// Look through the map of the file and update it with the variables
	for s, _ := range mapFile {
		if passedVal, ok := m[s]; ok {
			mapFile[s] = passedVal
		}
	}

	// Look through the map of the variables and overwrite it with any left over arguments
	// This allows you to overwrite config.output
	for s, v := range argMap {
		if _, ok := m[s]; !ok {
			mapFile[s] = v
		}
	}

	// Counter to prevent infinite loops
	counter := 0

	for true {
		// Convert the mapFile to bytes
		mapFileBytes, err := json.Marshal(mapFile)
		if err != nil {
			log.Fatal(err)
		}

		// Create the buffer
		buf := new(bytes.Buffer)

		// Parse the template
		t, err := template.New("").Parse(string(mapFileBytes))
		if err != nil {
			log.Fatal(err)
		}

		// Fills template with variables
		err = t.Execute(buf, m)
		if err != nil {
			log.Fatal(err)
		}

		parsedTemplate := buf.Bytes()

		// Convert the json text back to a map
		err = json.Unmarshal(parsedTemplate, &m)
		if err != nil {
			log.Fatal(err)
		}

		// If the parsed template is completely filled, then stop the run, else
		// keep running
		if !strings.Contains(string(parsedTemplate), "<no value>") {
			break
		}

		var invalidKeys []string

		// Loop through the map to find empty variables
		for s, v := range m {
			switch t := v.(type) {
			case string:
				if strings.Contains(t, "<no value>") {
					invalidKeys = append(invalidKeys, s)
					delete(m, s)
				}
			default:
				// This if statement outputs a helpful error if in a nested
				// map
				if strings.Contains(fmt.Sprintf("%v", v), "<no value>") {
					invalidKeys = append(invalidKeys, fmt.Sprintf("%v %v", s, v))
				}
				delete(m, s)
			}
		}

		counter += 1

		if counter > LoopLimit {
			log.Fatalf("Check these keys for variable mistakes: %v", invalidKeys)
			break
		}
	}

	return m
}
<commit_msg>Add ability to generate collections from collections<commit_after>package generate

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"

	"github.com/blue-jay/jay/command"
)

var (
	LoopLimit = 100
)

var Cmd = &command.Info{
	Run:       run,
	UsageLine: "generate [folder/file] key:value...",
	Short:     "code generation",
	Long: `
Generate will parse and create files from template pairs.

A template pair is a set of template files:
  * default.json - json file
  * default.gen - any type of text file

Both files are template files and are parsed using the Go text/template
package. The 'jay generate' tool loops through the first level of key pairs
for empty strings. For every empty string, an argument is required to be
passed (whether empty or not) to the 'jay generate' command.

Let's look at generate/model/default.json:
{
	"info": {
		"output": "model/{{.package}}/{{.package}}.go"
	},
	"package": "",
	"table": ""
}

Let's break down this command into pieces:
jay generate model/default package:automobile table:car

Argument: 'model/default'
Specifies generate/model/default.json and generate/model/default.gen are the
template pair.

Argument: 'package:automobile'
The key, 'package', from default.json will be filled with the value:
'automobile'

Argument: 'table:car'
The key, 'table', from default.json will be filled with the value: 'car'

The .json file is actually parsed up to 100 times (LoopLimit of 100 can be
changed at the package level) to ensure all variables like '{{.package}}' are
set to the correct value.

In the first iteration of parsing, the 'package' key is set to 'car'.
In the second iteration of parsing, the '{{.package}}' variables
are set to 'car' also since the 'package' key becomes a variable.

All first level keys (info, package, table) become variables after the first
iteration of parsing so they can be used without the file. If a variable is
misspelled and is never filled, a helpful error will be displayed.

The 'output' key under 'info' is required. It should be the relative output
file path to the project root for the generated file.

The folder structure of the templates (model, controller, etc) has no effect
on the generation, it's purely to air with organization of the template pairs.

You must store the path to the env.json file in the environment
variable: JAYCONFIG. The file is at project root that is prepended to all
relative file paths.

Examples:
  jay generate model/default package:car table:car
	Generate a new model from variables in model/default.json and applies
	those variables to model/default.gen.
  jay generate controller/default package:car url:car model:car view:car
	Generate a new controller from variables in controller/default.json
	and applies those variables to controller/default.gen.

Flags:
  Argument 1 - model/default or controller/default
	Relative path without an extension to the template pair. Any combination
	of folders and files can be used.
  Argument 2,3,etc - package:car
	Key pair to set in the .json file. Required for every empty key in the
	.json file.
`,
}

func run(cmd *command.Info, args []string) {
	rootFolder := projectFolder()

	if len(args) >= 2 {
		// Ensure the template pair files exist
		jsonFilePath := filepath.Join(rootFolder, "generate", args[0]+".json")
		if !exists(jsonFilePath) {
			log.Fatalf("File doesn't exist: %v", jsonFilePath)
		}

		argMap := argsToMap(args)

		// Get the json file as a map - not parsed
		mapFile, err := jsonFileToMap(jsonFilePath)
		if err != nil {
			log.Fatal(err)
		}

		// Generate variable map
		variableMap := generateVariableMap(mapFile, argMap)

		// Check for config type
		configType, ok := variableMap["config.type"]
		if !ok {
			log.Fatal("Key, 'config.type', is missing from the .json file")
		}

		// Handle based on config.type
		switch configType {
		case "single":
			// Template File
			genFilePath := filepath.Join(rootFolder, "generate", args[0]+".gen")

			// Generate the template
			generateSingle(rootFolder, genFilePath, variableMap)

			return
		case "collection":
			generateCollection(rootFolder, variableMap)
			return
		default:
			log.Fatalf("Value of '%v' for key 'config.type' is not supported", configType)
		}
	}

	fmt.Println("Flags are missing.")
}

func generateCollection(folderPath string, variableMap map[string]interface{}) {
	// Check for required key
	collectionRaw, ok := variableMap["config.collection"]
	if !ok {
		log.Fatal("Key, 'config.collection', is missing from the .json file")
	}

	collection, ok := collectionRaw.([]interface{})
	if !ok {
		log.Fatal("Key, 'config.collection', is not in the correct format")
	}

	// Loop through the collections
	for i, v := range collection {
		vMap, ok := v.(map[string]interface{})
		if !ok {
			log.Fatal("Values for key, 'config.collection', are not in the correct format")
		}

		for name, varArray := range vMap {
			argMap, ok := varArray.(map[string]interface{})
			if !ok {
				log.Fatalf("Item at index '%v' for key, 'config.collection', is not in the correct format", i)
			}

			// Template File
			genFilePath := filepath.Join(folderPath, "generate", name+".gen")
			jsonFilePath := filepath.Join(folderPath, "generate", name+".json")

			// Get the json file as a map - not parsed
			mapFile, err := jsonFileToMap(jsonFilePath)
			if err != nil {
				log.Fatal(err)
			}

			// Generate variable map
			variableMap := generateVariableMap(mapFile, argMap)

			// Check for config type
			configType, ok := variableMap["config.type"]
			if !ok {
				log.Fatal("Key, 'config.type', is missing from the .json file")
			}

			// Handle based on config.type
			switch configType {
			case "single":
				generateSingle(folderPath, genFilePath, variableMap)
			case "collection":
				generateCollection(folderPath, variableMap)
			default:
				log.Fatalf("Value of '%v' for key 'config.type' is not supported", configType)
			}
		}
	}
}

func generateSingle(folderPath string, genFilePath string, variableMap map[string]interface{}) {
	// Check for required key
	if _, ok := variableMap["config.output"]; !ok {
		log.Fatal("Key, 'config.output', is missing from the .json file")
	}

	// Output file
	outputRelativeFile := fmt.Sprintf("%v", variableMap["config.output"])
	outputFile := filepath.Join(folderPath, outputRelativeFile)

	// Check if the file exists
	if exists(outputFile) {
		log.Fatalf("Cannot generate because file already exists: %v", outputFile)
	}

	// Check if the folder exists
	dir := filepath.Dir(outputFile)
	if !exists(dir) {
		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil {
			log.Fatalln(err)
		}
	}

	// If config.parse = false
	if val, ok := variableMap["config.parse"]; ok {
		if b, _ := strconv.ParseBool(fmt.Sprintf("%v", val)); !b {
			// Don't parse template, just copy to new file
			err := toFile(genFilePath, outputFile)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Println("Code generated:", outputFile)
			return
		}
	}

	// Parse template and write to file
	err := fromMapToFile(genFilePath, variableMap, outputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Code generated:", outputFile)
}

// projectFolder returns the project folder path
func projectFolder() string {
	jc := os.Getenv("JAYCONFIG")
	if len(jc) == 0 {
		log.Fatalln("Environment variable JAYCONFIG needs to be set to the env.json file location.")
	}

	info := make(map[string]interface{})

	// Read the config file
	jsonBytes, err := ioutil.ReadFile(jc)
	if err != nil {
		log.Fatalln("The configuration file cannot be found so this command will not work.")
	}

	// Parse the config
	err = json.Unmarshal(jsonBytes, &info)
	if err != nil {
		log.Fatalln("The configuration file cannot be parsed so this command will not work.")
	}

	return filepath.Dir(jc)
}

func cloneMap(originalMap map[string]interface{}) map[string]interface{} {
	copyMap := make(map[string]interface{})

	for k, v := range originalMap {
		copyMap[k] = v
	}

	return copyMap
}

// exists will exit if the file or folder doesn't exist
func exists(f string) bool {
	if _, err := os.Stat(f); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// jsonFileToMap converts json file to an interface map
func jsonFileToMap(file string) (map[string]interface{}, error) {
	// Read the config file
	b, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	// Convert json to interface
	var d map[string]interface{}
	err = json.Unmarshal(b, &d)
	if err != nil {
		return nil, err
	}

	return d, nil
}

// fromMapToFile will output a file by parsing a template and applying
// variables from an interface map
func fromMapToFile(templateFile string, d map[string]interface{}, outputFile string) error {
	// Parse the template
	t, err := template.ParseFiles(templateFile)
	if err != nil {
		return err
	}

	// Create the output file
	f, err := os.Create(outputFile)
	if err != nil {
		return err
	}
	defer f.Close()

	// Fills template with variables and writes to file
	err = t.Execute(f, d)
	if err != nil {
		return err
	}

	return nil
}

// toFile will output a file by without parsing
func toFile(templateFile string, outputFile string) error {

	data, err := ioutil.ReadFile(templateFile)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(outputFile, data, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

func argsToMap(args []string) map[string]interface{} {
	// Fill a new map with variables
	argMap := make(map[string]interface{})
	for _, a := range args[1:] {
		arr := strings.Split(a, ":")

		if len(arr) < 2 {
			log.Fatalf("Arg is in wrong format: %v", a)
		}

		argMap[arr[0]] = strings.Join(arr[1:], ":")
	}

	return argMap
}

// generateVariableMap returns the relative file output path and the map of
// variables
func generateVariableMap(mapFile map[string]interface{}, argMap map[string]interface{}) map[string]interface{} {
	m := cloneMap(mapFile)

	// Loop through the map to find empty variables
	for s, v := range m {
		switch t := v.(type) {
		case string:
			if len(t) == 0 {
				if val, ok := argMap[s]; ok {
					m[s] = val
				} else {
					log.Fatalf("Variable missing: %v", s)
				}
			} else { // Else delete any values that are not empty
				delete(m, s)
			}
		default:
			delete(m, s)
		}
	}

	// Look through the map of the file and update it with the variables
	for s, _ := range mapFile {
		if passedVal, ok := m[s]; ok {
			mapFile[s] = passedVal
		}
	}

	// Look through the map of the variables and overwrite it with any left over arguments
	// This allows you to overwrite config.output
	for s, v := range argMap {
		if _, ok := m[s]; !ok {
			mapFile[s] = v
		}
	}

	// Counter to prevent infinite loops
	counter := 0

	for true {
		// Convert the mapFile to bytes
		mapFileBytes, err := json.Marshal(mapFile)
		if err != nil {
			log.Fatal(err)
		}

		// Create the buffer
		buf := new(bytes.Buffer)

		// Parse the template
		t, err := template.New("").Parse(string(mapFileBytes))
		if err != nil {
			log.Fatal(err)
		}

		// Fills template with variables
		err = t.Execute(buf, m)
		if err != nil {
			log.Fatal(err)
		}

		parsedTemplate := buf.Bytes()

		// Convert the json text back to a map
		err = json.Unmarshal(parsedTemplate, &m)
		if err != nil {
			log.Fatal(err)
		}

		// If the parsed template is completely filled, then stop the run, else
		// keep running
		if !strings.Contains(string(parsedTemplate), "<no value>") {
			break
		}

		var invalidKeys []string

		// Loop through the map to find empty variables
		for s, v := range m {
			switch t := v.(type) {
			case string:
				if strings.Contains(t, "<no value>") {
					invalidKeys = append(invalidKeys, s)
					delete(m, s)
				}
			default:
				// This if statement outputs a helpful error if in a nested
				// map
				if strings.Contains(fmt.Sprintf("%v", v), "<no value>") {
					invalidKeys = append(invalidKeys, fmt.Sprintf("%v %v", s, v))
				}
				delete(m, s)
			}
		}

		counter += 1

		if counter > LoopLimit {
			log.Fatalf("Check these keys for variable mistakes: %v", invalidKeys)
			break
		}
	}

	return m
}
<|endoftext|>


<commit_before>package generate

import (
	"log"

	"github.com/ejcx/passgo/pc"
)

var (
	defaultPwLen = 24
)

// Generate will return a securely generated password.
// A default password length of 24 with be used if
//     1. No pwlen is supplied
//     2. pwlen is less than 1
//     3. pwlen is greater than 2^17(131,072)
// NOTE: passwords are only guaranteed to be *at least* pwlen in length
//       they may in fact be longer
func Generate(pwlen int) string {
	if pwlen < 1 || pwlen > 1<<17 {
		pwlen = defaultPwLen
	}

	// By default, we should generate a strog password that needs everything
	specs := &pc.PasswordSpecs{
		NeedsUpper:  true,
		NeedsLower:  true,
		NeedsSymbol: true,
		NeedsDigit:  true,
	}
	pass, err := pc.GeneratePassword(specs, pwlen)
	if err != nil {
		log.Fatalf("Could not generate password: %s", err.Error())
	}
	return pass
}
<commit_msg>Fixed two typos in comments<commit_after>package generate

import (
	"log"

	"github.com/ejcx/passgo/pc"
)

var (
	defaultPwLen = 24
)

// Generate will return a securely generated password.
// A default password length of 24 will be used if
//     1. No pwlen is supplied
//     2. pwlen is less than 1
//     3. pwlen is greater than 2^17(131,072)
// NOTE: passwords are only guaranteed to be *at least* pwlen in length
//       they may in fact be longer
func Generate(pwlen int) string {
	if pwlen < 1 || pwlen > 1<<17 {
		pwlen = defaultPwLen
	}

	// By default, we should generate a strong password that needs everything
	specs := &pc.PasswordSpecs{
		NeedsUpper:  true,
		NeedsLower:  true,
		NeedsSymbol: true,
		NeedsDigit:  true,
	}
	pass, err := pc.GeneratePassword(specs, pwlen)
	if err != nil {
		log.Fatalf("Could not generate password: %s", err.Error())
	}
	return pass
}
<|endoftext|>


<commit_before>package exec

import (
	"bufio"
	"encoding/base64"
	"io"
	"net/url"

	log "github.com/Sirupsen/logrus"
	dockerClient "github.com/fsouza/go-dockerclient"

	"github.com/rancherio/websocket-proxy/backend"
	"github.com/rancherio/websocket-proxy/common"

	"github.com/rancherio/host-api/auth"
	"github.com/rancherio/host-api/events"
)

type ExecHandler struct {
}

func (h *ExecHandler) Handle(key string, initialMessage string, incomingMessages <-chan string, response chan<- common.Message) {
	defer backend.SignalHandlerClosed(key, response)

	requestUrl, err := url.Parse(initialMessage)
	if err != nil {
		log.WithFields(log.Fields{"error": err, "url": initialMessage}).Error("Couldn't parse url.")
		return
	}
	tokenString := requestUrl.Query().Get("token")
	token, valid := auth.GetAndCheckToken(tokenString)
	if !valid {
		return
	}

	execMap := token.Claims["exec"].(map[string]interface{})
	execConfig := convert(execMap)

	client, err := events.NewDockerClient()
	if err != nil {
		log.WithFields(log.Fields{"error": err}).Error("Couldn't get docker client.")
		return
	}

	outputReader, outputWriter := io.Pipe()
	inputReader, inputWriter := io.Pipe()

	execObj, err := client.CreateExec(execConfig)
	if err != nil {
		return
	}

	go func(w *io.PipeWriter) {
		for {
			msg, ok := <-incomingMessages
			if !ok {
				w.Close()
				return
			}
			data, err := base64.StdEncoding.DecodeString(msg)
			if err != nil {
				log.WithFields(log.Fields{"error": err}).Error("Error decoding message.")
				continue
			}
			inputWriter.Write([]byte(data))
		}
	}(outputWriter)

	go func(r *io.PipeReader) {
		scanner := bufio.NewScanner(r)
		scanner.Split(bufio.ScanRunes)
		for scanner.Scan() {
			data := scanner.Bytes()
			text := base64.StdEncoding.EncodeToString(data)
			message := common.Message{
				Key:  key,
				Type: common.Body,
				Body: text,
			}
			response <- message
		}
		if err := scanner.Err(); err != nil {
			log.WithFields(log.Fields{"error": err}).Error("Error with the exec scanner.")
		}
	}(outputReader)

	startConfig := dockerClient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		RawTerminal:  true,
		InputStream:  inputReader,
		OutputStream: outputWriter,
	}

	client.StartExec(execObj.ID, startConfig)
}

func convert(execMap map[string]interface{}) dockerClient.CreateExecOptions {
	// Not fancy at all
	config := dockerClient.CreateExecOptions{}

	if param, ok := execMap["AttachStdin"]; ok {
		if val, ok := param.(bool); ok {
			config.AttachStdin = val
		}
	}

	if param, ok := execMap["AttachStdout"]; ok {
		if val, ok := param.(bool); ok {
			config.AttachStdout = val
		}
	}

	if param, ok := execMap["AttachStderr"]; ok {
		if val, ok := param.(bool); ok {
			config.AttachStderr = val
		}
	}

	if param, ok := execMap["Tty"]; ok {
		if val, ok := param.(bool); ok {
			config.Tty = val
		}
	}

	if param, ok := execMap["Container"]; ok {
		if val, ok := param.(string); ok {
			config.Container = val
		}
	}

	if param, ok := execMap["Cmd"]; ok {
		cmd := []string{}
		if list, ok := param.([]interface{}); ok {
			for _, item := range list {
				if val, ok := item.(string); ok {
					cmd = append(cmd, val)
				}
			}
		}
		config.Cmd = cmd
	}

	return config
}
<commit_msg>Send End of transmission to kill bash processes<commit_after>package exec

import (
	"bufio"
	"encoding/base64"
	"io"
	"net/url"

	log "github.com/Sirupsen/logrus"
	dockerClient "github.com/fsouza/go-dockerclient"

	"github.com/rancherio/websocket-proxy/backend"
	"github.com/rancherio/websocket-proxy/common"

	"github.com/rancherio/host-api/auth"
	"github.com/rancherio/host-api/events"
)

type ExecHandler struct {
}

func (h *ExecHandler) Handle(key string, initialMessage string, incomingMessages <-chan string, response chan<- common.Message) {
	defer backend.SignalHandlerClosed(key, response)

	requestUrl, err := url.Parse(initialMessage)
	if err != nil {
		log.WithFields(log.Fields{"error": err, "url": initialMessage}).Error("Couldn't parse url.")
		return
	}
	tokenString := requestUrl.Query().Get("token")
	token, valid := auth.GetAndCheckToken(tokenString)
	if !valid {
		return
	}

	execMap := token.Claims["exec"].(map[string]interface{})
	execConfig := convert(execMap)

	client, err := events.NewDockerClient()
	if err != nil {
		log.WithFields(log.Fields{"error": err}).Error("Couldn't get docker client.")
		return
	}

	outputReader, outputWriter := io.Pipe()
	inputReader, inputWriter := io.Pipe()

	execObj, err := client.CreateExec(execConfig)
	if err != nil {
		return
	}

	go func(w *io.PipeWriter) {
		for {
			msg, ok := <-incomingMessages
			if !ok {
				if _, err := inputWriter.Write([]byte("\x04")); err != nil {
					log.WithFields(log.Fields{"error": err}).Error("Error writing EOT message.")
				}
				w.Close()
				return
			}
			data, err := base64.StdEncoding.DecodeString(msg)
			if err != nil {
				log.WithFields(log.Fields{"error": err}).Error("Error decoding message.")
				continue
			}
			inputWriter.Write([]byte(data))
		}
	}(outputWriter)

	go func(r *io.PipeReader) {
		scanner := bufio.NewScanner(r)
		scanner.Split(bufio.ScanRunes)
		for scanner.Scan() {
			data := scanner.Bytes()
			text := base64.StdEncoding.EncodeToString(data)
			message := common.Message{
				Key:  key,
				Type: common.Body,
				Body: text,
			}
			response <- message
		}
		if err := scanner.Err(); err != nil {
			log.WithFields(log.Fields{"error": err}).Error("Error with the exec scanner.")
		}
	}(outputReader)

	startConfig := dockerClient.StartExecOptions{
		Detach:       false,
		Tty:          true,
		RawTerminal:  true,
		InputStream:  inputReader,
		OutputStream: outputWriter,
	}

	client.StartExec(execObj.ID, startConfig)
}

func convert(execMap map[string]interface{}) dockerClient.CreateExecOptions {
	// Not fancy at all
	config := dockerClient.CreateExecOptions{}

	if param, ok := execMap["AttachStdin"]; ok {
		if val, ok := param.(bool); ok {
			config.AttachStdin = val
		}
	}

	if param, ok := execMap["AttachStdout"]; ok {
		if val, ok := param.(bool); ok {
			config.AttachStdout = val
		}
	}

	if param, ok := execMap["AttachStderr"]; ok {
		if val, ok := param.(bool); ok {
			config.AttachStderr = val
		}
	}

	if param, ok := execMap["Tty"]; ok {
		if val, ok := param.(bool); ok {
			config.Tty = val
		}
	}

	if param, ok := execMap["Container"]; ok {
		if val, ok := param.(string); ok {
			config.Container = val
		}
	}

	if param, ok := execMap["Cmd"]; ok {
		cmd := []string{}
		if list, ok := param.([]interface{}); ok {
			for _, item := range list {
				if val, ok := item.(string); ok {
					cmd = append(cmd, val)
				}
			}
		}
		config.Cmd = cmd
	}

	return config
}
<|endoftext|>


<commit_before>package ast

type Node interface {
	isNode()
}

type Statement interface {
	isStatement()
}

type Expression interface {
	isExpression()
}

type Identifier interface {
	Expression
	isIdentifier()
}

//
// Statements
//

type AliasDecl struct {
	Name string
	Type Type
}

type ClassDecl struct {
	Name   string
	Supers []Identifier
	Body   []Node
}

type VarDecl struct {
	Name  string
	Type  *Type
	Value Expression
}

type GetterStmt struct {
	Body []Node
}

type SetterStmt struct {
	SetterVariable *VarDecl
	Body           []Node
}

type PropertyDecl struct {
	Name   string
	Type   *Type
	Getter *GetterStmt
	Setter *SetterStmt
}

type EnumDecl struct {
	Name    string
	Members []string
}

type ExtensionDecl struct {
	Name   string
	Supers []Identifier
	Body   []Node
}

type InterfaceDecl struct {
	Name   string
	Supers []Identifier
	Body   []Node
}

type PackageDecl struct {
	Name string
}

type ImportDecl struct {
	Name string
}

//
// Expressions
//

type FunctionExpr struct {
	Name       string
	Parameters []*VarDecl
	RetType    *Type
	Body       []Node
}

type ExternFuncExpr struct {
	Name       string
	Parameters []*VarDecl
	RetType    *Type
}

type ArrayExpr struct {
	Members []Expression
}

type LoopExpr struct {
	Initializer  Expression
	Condition    Expression
	Afterthought Expression

	CheckTime LoopConditionTime
	Body      []Node
}

type UnaryExpr struct {
	Operation string
	Operand   Expression
}

type BinaryExpr struct {
	Operation string
	LHS       Expression
	RHS       Expression
}

type CallExpr struct {
	Object    Expression
	Arguments []Expression
}

type ArrayAccessExpr struct {
	Object Expression
	Index  Expression
}

type MemberAccessExpr struct {
	Object Expression
	Name   string
}

//
// Identifiers
//

type NamedIDExpr struct {
	Name string
}

type IDAccessExpr struct {
	LHS Identifier
	RHS string
}

//
// Implement interfaces to enforce types
//

func (s AliasDecl) isNode()        {}
func (s ClassDecl) isNode()        {}
func (s VarDecl) isNode()          {}
func (s GetterStmt) isNode()       {}
func (s SetterStmt) isNode()       {}
func (s PropertyDecl) isNode()     {}
func (s EnumDecl) isNode()         {}
func (s ExtensionDecl) isNode()    {}
func (s InterfaceDecl) isNode()    {}
func (s PackageDecl) isNode()      {}
func (s ImportDecl) isNode()       {}
func (e FunctionExpr) isNode()     {}
func (e ExternFuncExpr) isNode()   {}
func (e ArrayExpr) isNode()        {}
func (e LoopExpr) isNode()         {}
func (e UnaryExpr) isNode()        {}
func (e BinaryExpr) isNode()       {}
func (e CallExpr) isNode()         {}
func (e ArrayAccessExpr) isNode()  {}
func (e MemberAccessExpr) isNode() {}
func (i NamedIDExpr) isNode()      {}
func (i IDAccessExpr) isNode()     {}

func (s AliasDecl) isStatement()     {}
func (s ClassDecl) isStatement()     {}
func (s VarDecl) isStatement()       {}
func (s GetterStmt) isStatement()    {}
func (s SetterStmt) isStatement()    {}
func (s PropertyDecl) isStatement()  {}
func (s EnumDecl) isStatement()      {}
func (s ExtensionDecl) isStatement() {}
func (s InterfaceDecl) isStatement() {}
func (s PackageDecl) isStatement()   {}
func (s ImportDecl) isStatement()    {}

func (e FunctionExpr) isExpression()     {}
func (e ExternFuncExpr) isExpression()   {}
func (e ArrayExpr) isExpression()        {}
func (e LoopExpr) isExpression()         {}
func (e UnaryExpr) isExpression()        {}
func (e BinaryExpr) isExpression()       {}
func (e CallExpr) isExpression()         {}
func (e ArrayAccessExpr) isExpression()  {}
func (e MemberAccessExpr) isExpression() {}
func (i NamedIDExpr) isExpression()      {}
func (i IDAccessExpr) isExpression()     {}

func (i NamedIDExpr) isIdentifier()  {}
func (i IDAccessExpr) isIdentifier() {}
<commit_msg>Add missing AST nodes for constants<commit_after>package ast

type Node interface {
	isNode()
}

type Statement interface {
	isStatement()
}

type Expression interface {
	isExpression()
}

type Identifier interface {
	Expression
	isIdentifier()
}

//
// Statements
//

type AliasDecl struct {
	Name string
	Type Type
}

type ClassDecl struct {
	Name   string
	Supers []Identifier
	Body   []Node
}

type VarDecl struct {
	Name  string
	Type  *Type
	Value Expression
}

type GetterStmt struct {
	Body []Node
}

type SetterStmt struct {
	SetterVariable *VarDecl
	Body           []Node
}

type PropertyDecl struct {
	Name   string
	Type   *Type
	Getter *GetterStmt
	Setter *SetterStmt
}

type EnumDecl struct {
	Name    string
	Members []string
}

type ExtensionDecl struct {
	Name   string
	Supers []Identifier
	Body   []Node
}

type InterfaceDecl struct {
	Name   string
	Supers []Identifier
	Body   []Node
}

type PackageDecl struct {
	Name string
}

type ImportDecl struct {
	Name string
}

//
// Expressions
//

type FunctionExpr struct {
	Name       string
	Parameters []*VarDecl
	RetType    *Type
	Body       []Node
}

type ExternFuncExpr struct {
	Name       string
	Parameters []*VarDecl
	RetType    *Type
}

type ArrayExpr struct {
	Members []Expression
}

type LoopExpr struct {
	Initializer  Expression
	Condition    Expression
	Afterthought Expression

	CheckTime LoopConditionTime
	Body      []Node
}

type UnaryExpr struct {
	Operation string
	Operand   Expression
}

type BinaryExpr struct {
	Operation string
	LHS       Expression
	RHS       Expression
}

type CallExpr struct {
	Object    Expression
	Arguments []Expression
}

type ArrayAccessExpr struct {
	Object Expression
	Index  Expression
}

type MemberAccessExpr struct {
	Object Expression
	Name   string
}

type StringExpr struct {
	Value string
}

type BoolExpr struct {
	Value bool
}

type CharExpr struct {
	Value byte
}

type IntExpr struct {
	Value int64
	Size  int
}

type UIntExpr struct {
	Value uint64
	Size  int
}

type DoubleExpr struct {
	Value float64
}

type FloatExpr struct {
	Value float32
}

//
// Identifiers
//

type NamedIDExpr struct {
	Name string
}

type IDAccessExpr struct {
	LHS Identifier
	RHS string
}

//
// Implement interfaces to enforce types
//

func (s AliasDecl) isNode()        {}
func (s ClassDecl) isNode()        {}
func (s VarDecl) isNode()          {}
func (s GetterStmt) isNode()       {}
func (s SetterStmt) isNode()       {}
func (s PropertyDecl) isNode()     {}
func (s EnumDecl) isNode()         {}
func (s ExtensionDecl) isNode()    {}
func (s InterfaceDecl) isNode()    {}
func (s PackageDecl) isNode()      {}
func (s ImportDecl) isNode()       {}
func (e FunctionExpr) isNode()     {}
func (e ExternFuncExpr) isNode()   {}
func (e ArrayExpr) isNode()        {}
func (e LoopExpr) isNode()         {}
func (e UnaryExpr) isNode()        {}
func (e BinaryExpr) isNode()       {}
func (e CallExpr) isNode()         {}
func (e ArrayAccessExpr) isNode()  {}
func (e MemberAccessExpr) isNode() {}
func (e StringExpr) isNode()       {}
func (e BoolExpr) isNode()         {}
func (e CharExpr) isNode()         {}
func (e IntExpr) isNode()          {}
func (e UIntExpr) isNode()         {}
func (e DoubleExpr) isNode()       {}
func (e FloatExpr) isNode()        {}
func (i NamedIDExpr) isNode()      {}
func (i IDAccessExpr) isNode()     {}

func (s AliasDecl) isStatement()     {}
func (s ClassDecl) isStatement()     {}
func (s VarDecl) isStatement()       {}
func (s GetterStmt) isStatement()    {}
func (s SetterStmt) isStatement()    {}
func (s PropertyDecl) isStatement()  {}
func (s EnumDecl) isStatement()      {}
func (s ExtensionDecl) isStatement() {}
func (s InterfaceDecl) isStatement() {}
func (s PackageDecl) isStatement()   {}
func (s ImportDecl) isStatement()    {}

func (e FunctionExpr) isExpression()     {}
func (e ExternFuncExpr) isExpression()   {}
func (e ArrayExpr) isExpression()        {}
func (e LoopExpr) isExpression()         {}
func (e UnaryExpr) isExpression()        {}
func (e BinaryExpr) isExpression()       {}
func (e CallExpr) isExpression()         {}
func (e ArrayAccessExpr) isExpression()  {}
func (e MemberAccessExpr) isExpression() {}
func (e StringExpr) isExpression()       {}
func (e BoolExpr) isExpression()         {}
func (e CharExpr) isExpression()         {}
func (e IntExpr) isExpression()          {}
func (e UIntExpr) isExpression()         {}
func (e DoubleExpr) isExpression()       {}
func (e FloatExpr) isExpression()        {}
func (i NamedIDExpr) isExpression()      {}
func (i IDAccessExpr) isExpression()     {}

func (i NamedIDExpr) isIdentifier()  {}
func (i IDAccessExpr) isIdentifier() {}
<|endoftext|>


<commit_before>// Copyright 2018 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package devices helps to query DPDK compatibles devices and to bind/unbind drivers
package devices

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

var defaultTimeoutLimitation = 5 * time.Second

// FindDefaultDpdkDriver returns a default DPDK driver that the given NIC can
// use.
func FindDefaultDpdkDriver(nicName string) string {
	driver, err := readlinkBaseCmd(pathSysClassNetDeviceDriver.With(nicName))
	if err != nil {
		return DefaultDpdkDriver
	}
	switch driver {
	case DriverHvNetvcs:
		return DriverUioHvGeneric
	default:
		return DefaultDpdkDriver
	}
}

// GetDeviceID returns the device ID of given NIC name.
func GetDeviceID(nicName string) (string, error) {
	// DEV_ID=$(basename $(readlink /sys/class/net/<nicName>))
	raw, err := readlinkCmd(pathSysClassNetDevice.With(nicName))
	if err != nil {
		return "", err
	}
	// raw should be like /sys/devices/pci0002:00/0000:00:08.0/virtio2/net/ens8
	// or /sys/devices/pci0000:00/0000:00:01.0/0000:03:00.2/net/ens4f2
	raws := strings.Split(raw, "/")
	if len(raws) < 6 {
		return "", fmt.Errorf("path not correct")
	}
	if IsPciID.Match([]byte(raws[5])) {
		return raws[5], nil
	} else if IsPciID.Match([]byte(raws[4])) {
		return raws[4], nil
	} else {
		return "", fmt.Errorf("can't get device ID from path: %s", raw)
	}
}

// IsModuleLoaded checks if the kernel has already loaded the driver or not.
func IsModuleLoaded(driver string) bool {
	output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "lsmod")
	if err != nil {
		// Can't run lsmod, return false
		return false
	}
	lines := strings.Split(string(output), "\n")
	for _, line := range lines {
		if checkModuleLine(line, driver) {
			return true
		}
	}
	return false
}

func writeToTargetWithData(sysfs string, flag int, mode os.FileMode, data string) error {
	writer, err := os.OpenFile(sysfs, flag, mode)
	if err != nil {
		return fmt.Errorf("OpenFile failed: %s", err.Error())
	}
	defer writer.Close()

	_, err = writer.Write([]byte(data))
	if err != nil {
		return fmt.Errorf("WriteFile failed: %s", err.Error())
	}

	return nil
}

func readlinkCmd(path string) (string, error) {
	output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "readlink", "-f", path)
	if err != nil {
		return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error())
	}
	outputStr := strings.Trim(string(output), "\n")
	return outputStr, nil
}

func readlinkBaseCmd(path string) (string, error) {
	outputStr, err := readlinkCmd(path)
	if err != nil {
		return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error())
	}
	result := filepath.Base(outputStr)
	return result, nil
}

func checkModuleLine(line, driver string) bool {
	if !strings.Contains(line, driver) {
		return false
	}
	elements := strings.Split(line, " ")
	return elements[0] == driver
}

func cmdOutputWithTimeout(duration time.Duration, cmd string, parameters ...string) ([]byte, error) {
	ctx, cancel := context.WithTimeout(context.Background(), duration)
	defer cancel()
	output, err := exec.CommandContext(ctx, cmd, parameters...).Output()
	if err != nil {
		return nil, err
	}
	return output, nil
}
<commit_msg>devices: Adjust PciID Detection for Azure VMs<commit_after>// Copyright 2018 Intel Corporation.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package devices helps to query DPDK compatibles devices and to bind/unbind drivers
package devices

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

var defaultTimeoutLimitation = 5 * time.Second

// FindDefaultDpdkDriver returns a default DPDK driver that the given NIC can
// use.
func FindDefaultDpdkDriver(nicName string) string {
	driver, err := readlinkBaseCmd(pathSysClassNetDeviceDriver.With(nicName))
	if err != nil {
		return DefaultDpdkDriver
	}
	switch driver {
	case DriverHvNetvcs:
		return DriverUioHvGeneric
	default:
		return DefaultDpdkDriver
	}
}

// GetDeviceID returns the device ID of given NIC name.
func GetDeviceID(nicName string) (string, error) {
	// DEV_ID=$(basename $(readlink /sys/class/net/<nicName>))
	raw, err := readlinkCmd(pathSysClassNetDevice.With(nicName))
	if err != nil {
		return "", err
	}
	// raw should be like /sys/devices/pci0002:00/0000:00:08.0/virtio2/net/ens8
	// or /sys/devices/pci0000:00/0000:00:01.0/0000:03:00.2/net/ens4f2
	raws := strings.Split(raw, "/")
	if len(raws) < 6 {
		return "", fmt.Errorf("path not correct")
	}
	if IsPciID.Match([]byte(raws[5])) {
		return raws[5], nil
	} else if IsPciID.Match([]byte(raws[4])) {
		return raws[4], nil
	} else if len(raws) >= 10 && IsPciID.Match([]byte(raws[10])) {
		return raws[10], nil
	} else {
		return "", fmt.Errorf("can't get device ID from path: %s", raw)
	}
}

// IsModuleLoaded checks if the kernel has already loaded the driver or not.
func IsModuleLoaded(driver string) bool {
	output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "lsmod")
	if err != nil {
		// Can't run lsmod, return false
		return false
	}
	lines := strings.Split(string(output), "\n")
	for _, line := range lines {
		if checkModuleLine(line, driver) {
			return true
		}
	}
	return false
}

func writeToTargetWithData(sysfs string, flag int, mode os.FileMode, data string) error {
	writer, err := os.OpenFile(sysfs, flag, mode)
	if err != nil {
		return fmt.Errorf("OpenFile failed: %s", err.Error())
	}
	defer writer.Close()

	_, err = writer.Write([]byte(data))
	if err != nil {
		return fmt.Errorf("WriteFile failed: %s", err.Error())
	}

	return nil
}

func readlinkCmd(path string) (string, error) {
	output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "readlink", "-f", path)
	if err != nil {
		return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error())
	}
	outputStr := strings.Trim(string(output), "\n")
	return outputStr, nil
}

func readlinkBaseCmd(path string) (string, error) {
	outputStr, err := readlinkCmd(path)
	if err != nil {
		return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error())
	}
	result := filepath.Base(outputStr)
	return result, nil
}

func checkModuleLine(line, driver string) bool {
	if !strings.Contains(line, driver) {
		return false
	}
	elements := strings.Split(line, " ")
	return elements[0] == driver
}

func cmdOutputWithTimeout(duration time.Duration, cmd string, parameters ...string) ([]byte, error) {
	ctx, cancel := context.WithTimeout(context.Background(), duration)
	defer cancel()
	output, err := exec.CommandContext(ctx, cmd, parameters...).Output()
	if err != nil {
		return nil, err
	}
	return output, nil
}
<|endoftext|>


<commit_before>package env

import (
	"bytes"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-redis/redis"
	"golang.org/x/crypto/bcrypt"
	"strings"
	"time"
)

const (
	createSession         = "INSERT INTO sessions (validator, selector, user_id, exp) VALUES ($1, $2, $3, $4)"
	selectSession         = "SELECT users.id, users.email, users.first_name, users.last_name, users.gender, users.dob, sessions.validator, sessions.exp FROM sessions JOIN users WHERE users.selector=$1 "
	validateUser          = "SELECT id, selector, validator, first_name, last_name, gender, dob FROM users WHERE email=$1"
	invalidateSession     = "DELETE FROM sessions WHERE selector=$1"
	invalidateAllSession  = "DELETE FROM sessions WHERE user_id=$1"
	getAllSessionsForUser = "SELECT selector FROM sessions WHERE user_id=$1"

	selectorLen  = 12
	validatorLen = 20
)

type sessionDatastore interface {
	CreateSession(username, password string) (*User, string, error)
	CheckSession(token string) (*User, error)
	InvalidateSession(token string) error
	InvalidateAllSessions(usr User) error
}

func (d *db) CreateSession(email, password string) (*User, string, error) {

	usr := &User{}
	var validator string

	//TODO check login times to prevent spam

	err := d.DB.QueryRow(validateUser, email).Scan(&usr.ID, &usr.sel, &validator, &usr.FirstName, &usr.LastName, &usr.Gender, &usr.DateOfBirth)
	if err != nil {
		return nil, "", errors.New("couldn't find user")
	}

	err = bcrypt.CompareHashAndPassword([]byte(validator), []byte(password))
	if err != nil {
		//TODO send goroutine to add to spam
		return nil, "", errors.New("incorrect password")
	}

	session := d.insertSession(*usr)

	return usr, session, nil
}

func (d *db) insertSession(user User) string {
	validator := d.GenerateSelector(validatorLen)
	selector := d.GenerateSelector(selectorLen)
	exp := time.Now().Add(time.Hour * 2).UnixNano() //TODO

	hashValidator := sha256.Sum256([]byte(validator))

	_, err := d.DB.Exec(createSession, hashValidator, selector, user.ID, exp)
	if err != nil {
		d.Panicf("Could not insert session: %v", err)
	}

	return fmt.Sprintf("%v:%v", selector, validator)
}

func (d *db) InvalidateSession(token string) error {
	split := strings.Split(token, ":")
	selector := split[0]

	_, err := d.DB.Exec(invalidateSession, selector)
	if err != nil {
		return errors.New("couldn't invalidate sessions")
	}

	err = d.Del(selector).Err()

	return nil
}

func (d *db) InvalidateAllSessions(usr User) error {
	var v []string

	rows, err := d.DB.Query(getAllSessionsForUser, usr.ID)
	for rows.Next() {
		var validator string
		rows.Scan(&validator)
		v = append(v, validator)
	}

	err = d.Del(v...).Err()

	_, err = d.DB.Exec(invalidateAllSession, usr.ID)
	if err != nil {
		return errors.New("couldn't invalidate all sessions")
	}

	return nil
}

func (d *db) CheckSession(token string) (*User, error) {
	split := strings.Split(token, ":")
	selector := split[0]
	validator := sha256.Sum256([]byte(split[1]))
	var valQuery string
	var exp int64

	var usr User

	//Check Redis cache

	jsonUser, err := d.Get(selector).Result()
	if err == redis.Nil {
		goto query
	}

	err = json.Unmarshal([]byte(jsonUser), usr)
	if err != nil {
		d.Println("error parsing redis token")
		goto query

	}

	goto check

query: // Skip to the SQL query

	err = d.QueryRow(selectSession, selector).Scan(&usr.ID, &usr.Email, &usr.FirstName, &usr.LastName, usr.Gender, usr.DateOfBirth, &valQuery, &exp)
	if err != nil {
		return nil, errors.New("no validator found")
	}

check:

	if bytes.Equal(validator[:], []byte(valQuery)) { // TODO also check exp
		return nil, errors.New("validator != valQ")
	}

	if time.Now().UnixNano() < exp { //TODO come up with when to update cache
		go d.updateCache(usr, token)
	}

	return &usr, nil

}

func (d *db) updateCache(usr User, token string) error {
	split := strings.Split(token, ":")
	selector := split[0]
	//var validator string //Add to json
	var exp int64

	json, err := json.Marshal(usr)
	if err != nil {
		return errors.New("falied to marshal user")
	}

	err = d.Set(selector, string(json), time.Duration(exp-time.Now().UnixNano())).Err()
	if err != nil {
		return errors.New("couldn't update cache")
	}

	return nil
}
<commit_msg>encoded string<commit_after>package env

import (
	"bytes"
	"crypto/sha256"
	"encoding/json"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/go-redis/redis"
	"golang.org/x/crypto/bcrypt"
	"strings"
	"time"
)

const (
	createSession         = "INSERT INTO sessions (validator, selector, user_id, exp) VALUES ($1, $2, $3, $4)"
	selectSession         = "SELECT users.id, users.email, users.first_name, users.last_name, users.gender, users.dob, sessions.validator, sessions.exp FROM sessions JOIN users WHERE users.selector=$1 "
	validateUser          = "SELECT id, selector, validator, first_name, last_name, gender, dob FROM users WHERE email=$1"
	invalidateSession     = "DELETE FROM sessions WHERE selector=$1"
	invalidateAllSession  = "DELETE FROM sessions WHERE user_id=$1"
	getAllSessionsForUser = "SELECT selector FROM sessions WHERE user_id=$1"

	selectorLen  = 12
	validatorLen = 20
)

type sessionDatastore interface {
	CreateSession(username, password string) (*User, string, error)
	CheckSession(token string) (*User, error)
	InvalidateSession(token string) error
	InvalidateAllSessions(usr User) error
}

func (d *db) CreateSession(email, password string) (*User, string, error) {

	usr := &User{}
	var validator string

	//TODO check login times to prevent spam

	err := d.DB.QueryRow(validateUser, email).Scan(&usr.ID, &usr.sel, &validator, &usr.FirstName, &usr.LastName, &usr.Gender, &usr.DateOfBirth)
	if err != nil {
		return nil, "", errors.New("couldn't find user")
	}

	err = bcrypt.CompareHashAndPassword([]byte(validator), []byte(password))
	if err != nil {
		//TODO send goroutine to add to spam
		return nil, "", errors.New("incorrect password")
	}

	session := d.insertSession(*usr)

	return usr, session, nil
}

func (d *db) insertSession(user User) string {
	validator := d.GenerateSelector(validatorLen)
	selector := d.GenerateSelector(selectorLen)
	exp := time.Now().Add(time.Hour * 2).UnixNano() //TODO

	hashValidator := sha256.Sum256([]byte(validator))
	hashValStr := base64.StdEncoding.EncodeToString(hashValidator[:])

	_, err := d.DB.Exec(createSession, hashValStr, selector, user.ID, exp)
	if err != nil {
		d.Panicf("Could not insert session: %v", err)
	}

	return fmt.Sprintf("%v:%v", selector, validator)
}

func (d *db) InvalidateSession(token string) error {
	split := strings.Split(token, ":")
	selector := split[0]

	_, err := d.DB.Exec(invalidateSession, selector)
	if err != nil {
		return errors.New("couldn't invalidate sessions")
	}

	err = d.Del(selector).Err()

	return nil
}

func (d *db) InvalidateAllSessions(usr User) error {
	var v []string

	rows, err := d.DB.Query(getAllSessionsForUser, usr.ID)
	for rows.Next() {
		var validator string
		rows.Scan(&validator)
		v = append(v, validator)
	}

	err = d.Del(v...).Err()

	_, err = d.DB.Exec(invalidateAllSession, usr.ID)
	if err != nil {
		return errors.New("couldn't invalidate all sessions")
	}

	return nil
}

func (d *db) CheckSession(token string) (*User, error) {
	split := strings.Split(token, ":")
	selector := split[0]
	validator := sha256.Sum256([]byte(split[1]))
	var valQuery string
	var exp int64

	var usr User

	//Check Redis cache

	jsonUser, err := d.Get(selector).Result()
	if err == redis.Nil {
		goto query
	}

	err = json.Unmarshal([]byte(jsonUser), usr)
	if err != nil {
		d.Println("error parsing redis token")
		goto query

	}

	goto check

query: // Skip to the SQL query

	err = d.QueryRow(selectSession, selector).Scan(&usr.ID, &usr.Email, &usr.FirstName, &usr.LastName, usr.Gender, usr.DateOfBirth, &valQuery, &exp)
	if err != nil {
		return nil, errors.New("no validator found")
	}

check:

	q, err := base64.StdEncoding.DecodeString(valQuery)
	if bytes.Equal(validator[:], q) { // TODO also check exp
		return nil, errors.New("validator != valQ")
	}

	if time.Now().UnixNano() < exp { //TODO come up with when to update cache
		go d.updateCache(usr, token)
	}

	return &usr, nil

}

func (d *db) updateCache(usr User, token string) error {
	split := strings.Split(token, ":")
	selector := split[0]
	//var validator string //Add to json
	var exp int64

	json, err := json.Marshal(usr)
	if err != nil {
		return errors.New("falied to marshal user")
	}

	err = d.Set(selector, string(json), time.Duration(exp-time.Now().UnixNano())).Err()
	if err != nil {
		return errors.New("couldn't update cache")
	}

	return nil
}
<|endoftext|>


<commit_before>package atmel8

import (
	"errors"
	"fmt"
)

var (
	ErrLDILowRegister        = errors.New("ldi can only use a high register (r16 - r31)")
	ErrLDIInvalidRegisterStr = "R%v is not a valid register"
)

type CPU struct {
	registers []uint8
	rMin      uint8
	rMax      uint8
	carryFlag bool
}

// NewCPU initializes CPU with 32 registers (R0-R31). R16-R31 are exposed to
// user.
func NewCPU() *CPU {
	return &CPU{
		registers: make([]uint8, 32, 32),
		rMin:      16,
		rMax:      31,
	}
}

// LDI is 'Load Immediate'; it loads given value into given register.
//
// rDestIndex must be: 16 <= d <= 31.
func (c *CPU) LDI(rDestIndex, value uint8) error {
	if rDestIndex < c.rMin {
		return ErrLDILowRegister
	}

	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] = value

	return nil
}

// ADD is 'Add without Carry'; it adds two given registers. It stores the
// results in the 1st register. It sets C flag if result overflows.
//
// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) ADD(rDestIndex, rIndex uint8) error {
	return c.add(rDestIndex, rIndex, false)
}

// ADD is 'Add with Carry'; it adds two given registers & carry from last op.
// It stores the results in the 1st register. It sets C flag if result
// overflows.
//
// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) ADC(rDestIndex, rIndex uint8) error {
	return c.add(rDestIndex, rIndex, true)
}

// INC is 'Increment'; it increments value at register by 1. It OP causes
// overflow, it does NOT set carry flag.
//
// rDestIndex must be: 0 <= d <= 31.
func (c *CPU) INC(rDestIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] += 1

	return nil
}

// INC is 'Decrement'; it decrements value at register by 1. It OP causes
// overflow, it does NOT set carry flag.
//
// rDestIndex must be: 0 <= d <= 31.
func (c *CPU) DEC(rDestIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] -= 1

	return nil
}

// CLR is 'Clear Register'; it clears value at given register.
//
// rDestIndex must be: 0 <= d <= 31.
func (c *CPU) CLR(rDestIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] = 0

	return nil
}

// MOV is 'Copy Register'; contrary to name, it only copies values from
// rOrgIndex to rDestIndex, not move them.
//
//
// rDestIndex and rOrgIndex must be: 0 <= d <= 31.
func (c *CPU) MOV(rDestIndex, rOrgIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex, rOrgIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] = c.registers[rOrgIndex]

	return nil

}

func (c *CPU) SUB(rDestIndex, rIndex uint8) error {
	return c.sub(rDestIndex, rIndex, false)
}

func (c *CPU) SBC(rDestIndex, rIndex uint8) error {
	return c.sub(rDestIndex, rIndex, true)
}

// SEC is 'Set Carry Flag'; it sets carry flag.
func (c *CPU) SEC() {
	c.carryFlag = true
}

// SEC is 'Clear Carry Flag'; it clears carry flag.
func (c *CPU) CLC() {
	c.carryFlag = false
}

///// private

// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) add(rDestIndex, rIndex uint8, carry bool) error {
	if err := c.checkRegisterOutofRange(rDestIndex, rIndex); err != nil {
		return err
	}

	v1, v2 := uint16(c.registers[rDestIndex]), uint16(c.registers[rIndex])

	var v3 uint16 = 0
	if carry && c.carryFlag {
		v3 = 1
		c.carryFlag = false
	}

	result := v1 + v2 + v3
	if result > 255 {
		c.carryFlag = true
	}

	c.registers[rDestIndex] = uint8(v1 + v2 + v3)

	return nil
}

func (c *CPU) sub(rDestIndex, rIndex uint8, carry bool) error {
	if err := c.checkRegisterOutofRange(rDestIndex, rIndex); err != nil {
		return err
	}

	v1, v2 := int16(c.registers[rDestIndex]), int16(c.registers[rIndex])

	var v3 int16 = 0
	if carry && c.carryFlag {
		v3 = 1
		c.carryFlag = false
	}

	result := v1 - v2 - v3
	if result < 0 {
		c.carryFlag = true
	}

	c.registers[rDestIndex] = uint8(v1 - v2 - v3)

	return nil
}

func (c *CPU) checkRegisterOutofRange(rIndexes ...uint8) error {
	for _, rIndex := range rIndexes {
		if rIndex > c.rMax {
			return fmt.Errorf(ErrLDIInvalidRegisterStr, rIndex)
		}
	}

	return nil
}
<commit_msg>add docs<commit_after>package atmel8

import (
	"errors"
	"fmt"
)

var (
	ErrLDILowRegister        = errors.New("ldi can only use a high register (r16 - r31)")
	ErrLDIInvalidRegisterStr = "R%v is not a valid register"
)

type CPU struct {
	registers []uint8
	rMin      uint8
	rMax      uint8
	carryFlag bool
}

// NewCPU initializes CPU with 32 registers (R0-R31). R16-R31 are exposed to
// user.
func NewCPU() *CPU {
	return &CPU{
		registers: make([]uint8, 32, 32),
		rMin:      16,
		rMax:      31,
	}
}

// LDI is 'Load Immediate'; it loads given value into given register.
//
// rDestIndex must be: 16 <= d <= 31.
func (c *CPU) LDI(rDestIndex, value uint8) error {
	if rDestIndex < c.rMin {
		return ErrLDILowRegister
	}

	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] = value

	return nil
}

// ADD is 'Add without Carry'; it adds two given registers. It stores the
// results in the 1st register. It sets C flag if result overflows.
//
// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) ADD(rDestIndex, rIndex uint8) error {
	return c.add(rDestIndex, rIndex, false)
}

// ADD is 'Add with Carry'; it adds two given registers & carry from last op if
// any. It stores the results in the 1st register. It sets C flag if result
// overflows.
//
// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) ADC(rDestIndex, rIndex uint8) error {
	return c.add(rDestIndex, rIndex, true)
}

// INC is 'Increment'; it increments value at register by 1. It OP causes
// overflow, it does NOT set carry flag.
//
// rDestIndex must be: 0 <= d <= 31.
func (c *CPU) INC(rDestIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] += 1

	return nil
}

// INC is 'Decrement'; it decrements value at register by 1. It OP causes
// overflow, it does NOT set carry flag.
//
// rDestIndex must be: 0 <= d <= 31.
func (c *CPU) DEC(rDestIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] -= 1

	return nil
}

// CLR is 'Clear Register'; it clears value at given register.
//
// rDestIndex must be: 0 <= d <= 31.
func (c *CPU) CLR(rDestIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] = 0

	return nil
}

// MOV is 'Copy Register'; contrary to name, it only copies values from
// rOrgIndex to rDestIndex, not move them.
//
//
// rDestIndex and rOrgIndex must be: 0 <= d <= 31.
func (c *CPU) MOV(rDestIndex, rOrgIndex uint8) error {
	if err := c.checkRegisterOutofRange(rDestIndex, rOrgIndex); err != nil {
		return err
	}

	c.registers[rDestIndex] = c.registers[rOrgIndex]

	return nil

}

// SUB is 'Subtract without Carry'; it substracts two given registers. It stores
// the results in the 1st register. It sets C flag if result overflows.
//
// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) SUB(rDestIndex, rIndex uint8) error {
	return c.sub(rDestIndex, rIndex, false)
}

// ADD is 'Subtract with Carry'; it subtrcats two given registers & carry from last
// op if any. It stores the results in the 1st register. It sets C flag if result
// overflows.
//
// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) SBC(rDestIndex, rIndex uint8) error {
	return c.sub(rDestIndex, rIndex, true)
}

// SEC is 'Set Carry Flag'; it sets carry flag.
func (c *CPU) SEC() {
	c.carryFlag = true
}

// SEC is 'Clear Carry Flag'; it clears carry flag.
func (c *CPU) CLC() {
	c.carryFlag = false
}

///// private

// rDestIndex and rIndex must be: 0 <= d <= 31.
func (c *CPU) add(rDestIndex, rIndex uint8, carry bool) error {
	if err := c.checkRegisterOutofRange(rDestIndex, rIndex); err != nil {
		return err
	}

	v1, v2 := uint16(c.registers[rDestIndex]), uint16(c.registers[rIndex])

	var v3 uint16 = 0
	if carry && c.carryFlag {
		v3 = 1
		c.carryFlag = false
	}

	result := v1 + v2 + v3
	if result > 255 {
		c.carryFlag = true
	}

	c.registers[rDestIndex] = uint8(v1 + v2 + v3)

	return nil
}

func (c *CPU) sub(rDestIndex, rIndex uint8, carry bool) error {
	if err := c.checkRegisterOutofRange(rDestIndex, rIndex); err != nil {
		return err
	}

	v1, v2 := int16(c.registers[rDestIndex]), int16(c.registers[rIndex])

	var v3 int16 = 0
	if carry && c.carryFlag {
		v3 = 1
		c.carryFlag = false
	}

	result := v1 - v2 - v3
	if result < 0 {
		c.carryFlag = true
	}

	c.registers[rDestIndex] = uint8(v1 - v2 - v3)

	return nil
}

func (c *CPU) checkRegisterOutofRange(rIndexes ...uint8) error {
	for _, rIndex := range rIndexes {
		if rIndex > c.rMax {
			return fmt.Errorf(ErrLDIInvalidRegisterStr, rIndex)
		}
	}

	return nil
}
<|endoftext|>