<commit_before>package presilo

import (
	"bytes"
	"fmt"
	"strings"
)

/*
  Generates valid Java code for a given schema.
*/
func GenerateJava(schema *ObjectSchema, module string) string {

	var ret bytes.Buffer

	ret.WriteString("package " + module)
	ret.WriteString("\n")
	ret.WriteString(generateJavaImports(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaTypeDeclaration(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaConstructor(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaFunctions(schema))
	ret.WriteString("\n}\n")

	return ret.String()
}

func generateJavaImports(schema *ObjectSchema) string {

  // import regex if we need it
  if(containsRegexpMatch(schema)) {
    return "import java.util.regex.Pattern;\n\n"
  }
  return ""
}

func generateJavaTypeDeclaration(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var propertyName string
  var toWrite string

  toWrite = fmt.Sprintf("public class %s\n{\n", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for propertyName, subschema = range schema.Properties {

    propertyName = ToJavaCase(propertyName)
    toWrite = fmt.Sprintf("\n\tprotected %s %s;", generateJavaTypeForSchema(subschema), propertyName)
    ret.WriteString(toWrite)
  }

  return ret.String()
}

func generateJavaConstructor(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var declarations, setters []string
  var propertyName string
  var toWrite string

  toWrite = fmt.Sprintf("\n\tpublic %s(", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for _, propertyName = range schema.RequiredProperties {

    subschema = schema.Properties[propertyName]
    propertyName = ToJavaCase(propertyName)

    toWrite = fmt.Sprintf("%s %s", generateJavaTypeForSchema(subschema), propertyName)
    declarations = append(declarations, toWrite)

    toWrite = fmt.Sprintf("\n\t\tset%s(%s);", ToCamelCase(propertyName), propertyName)
    setters = append(setters, toWrite)
  }

  toWrite = strings.Join(declarations, ",")
  ret.WriteString(toWrite)
  ret.WriteString(")\n\t{")

  for _, setter := range setters {
    ret.WriteString(setter)
  }

  ret.WriteString("\n\t}\n")
  return ret.String()
}

func generateJavaFunctions(schema *ObjectSchema) string {

  var ret bytes.Buffer
  return ret.String()
}

func generateJavaTypeForSchema(subschema TypeSchema) string {
  return "String"
}
<commit_msg>Implemented basic setters/getters<commit_after>package presilo

import (
	"bytes"
	"fmt"
	"strings"
)

/*
  Generates valid Java code for a given schema.
*/
func GenerateJava(schema *ObjectSchema, module string) string {

	var ret bytes.Buffer

	ret.WriteString("package " + module)
	ret.WriteString("\n")
	ret.WriteString(generateJavaImports(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaTypeDeclaration(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaConstructor(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaFunctions(schema))
	ret.WriteString("\n}\n")

	return ret.String()
}

func generateJavaImports(schema *ObjectSchema) string {

  // import regex if we need it
  if(containsRegexpMatch(schema)) {
    return "import java.util.regex.Pattern;\n\n"
  }
  return ""
}

func generateJavaTypeDeclaration(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var propertyName string
  var toWrite string

  toWrite = fmt.Sprintf("public class %s\n{\n", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for propertyName, subschema = range schema.Properties {

    propertyName = ToJavaCase(propertyName)
    toWrite = fmt.Sprintf("\n\tprotected %s %s;", generateJavaTypeForSchema(subschema), propertyName)
    ret.WriteString(toWrite)
  }

  return ret.String()
}

func generateJavaConstructor(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var declarations, setters []string
  var propertyName string
  var toWrite string

  toWrite = fmt.Sprintf("\n\tpublic %s(", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for _, propertyName = range schema.RequiredProperties {

    subschema = schema.Properties[propertyName]
    propertyName = ToJavaCase(propertyName)

    toWrite = fmt.Sprintf("%s %s", generateJavaTypeForSchema(subschema), propertyName)
    declarations = append(declarations, toWrite)

    toWrite = fmt.Sprintf("\n\t\tset%s(%s);", ToCamelCase(propertyName), propertyName)
    setters = append(setters, toWrite)
  }

  toWrite = strings.Join(declarations, ",")
  ret.WriteString(toWrite)
  ret.WriteString(")\n\t{")

  for _, setter := range setters {
    ret.WriteString(setter)
  }

  ret.WriteString("\n\t}\n")
  return ret.String()
}

func generateJavaFunctions(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var toWrite string
  var propertyName, properName, camelName, typeName string

  for propertyName, subschema = range schema.Properties {

    properName = ToJavaCase(propertyName)
    camelName = ToCamelCase(propertyName)
    typeName = generateJavaTypeForSchema(subschema)

    // getter
    toWrite = fmt.Sprintf("\n\tpublic %s get%s()\n\t{", typeName, camelName)
    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\treturn this.%s;\n\t}", properName)
    ret.WriteString(toWrite)

    // setter
    toWrite = fmt.Sprintf("\n\tpublic void set%s(%s value)\n\t{", camelName, typeName)
    ret.WriteString(toWrite)

    switch subschema.GetSchemaType() {
    case SCHEMATYPE_BOOLEAN:
      toWrite = ""
    case SCHEMATYPE_STRING:
      toWrite = generateJavaStringSetter(subschema.(*StringSchema))
    case SCHEMATYPE_INTEGER: fallthrough
    case SCHEMATYPE_NUMBER:
      toWrite = generateJavaNumericSetter(subschema.(NumericSchemaType))
    case SCHEMATYPE_OBJECT:
      toWrite = generateJavaObjectSetter(subschema.(*ObjectSchema))
    case SCHEMATYPE_ARRAY:
      toWrite = generateJavaArraySetter(subschema.(*ArraySchema))
    }

    toWrite = fmt.Sprintf("\n\t\t%s = value;", properName)
    ret.WriteString(toWrite)

    ret.WriteString("\n\t}\n")
  }

  return ret.String()
}

func generateJavaRangeCheck(value interface{}, reference string, format string, exclusive bool, comparator, exclusiveComparator string) string {

	var ret bytes.Buffer
	var toWrite, compareString string

	if(exclusive) {
		compareString = exclusiveComparator
	} else {
		compareString = comparator
	}

	toWrite = "\n\tif("+ reference +" " + compareString + " " +format+ ")\n\t{"
	toWrite = fmt.Sprintf(toWrite, value)
	ret.WriteString(toWrite)

	toWrite = fmt.Sprintf("\n\t\tthrow new Exception(\"Property '\"+value+\"' was out of range.\")\n\t}\n")
	ret.WriteString(toWrite)

	return ret.String()
}

func generateJavaStringSetter(schema *StringSchema) string {

  var ret bytes.Buffer

  // TODO: null check
  // TODO: length checks
  // TODO: pattern checks
  return ret.String()
}

func generateJavaNumericSetter(schema NumericSchemaType) string {

  var ret bytes.Buffer

  // TODO: null check
  // TODO: min/max checks
  // TODO: multiple checks
  return ret.String()
}

func generateJavaObjectSetter(schema *ObjectSchema) string {

  var ret bytes.Buffer

  // TODO: null check
  return ret.String()
}

func generateJavaArraySetter(schema *ArraySchema) string {

  var ret bytes.Buffer

  // TODO: null check
  // TODO: min/max length checks
  return ret.String()
}

func generateJavaTypeForSchema(subschema TypeSchema) string {

  switch(subschema.GetSchemaType()) {
  case SCHEMATYPE_NUMBER: return "double"
  case SCHEMATYPE_INTEGER: return "int"
  case SCHEMATYPE_ARRAY: return subschema.GetTitle() + "[]"
  case SCHEMATYPE_OBJECT: return subschema.GetTitle()
  case SCHEMATYPE_STRING: return "String"
  case SCHEMATYPE_BOOLEAN: return "boolean"
  }

  return "Object"
}
<|endoftext|>


<commit_before>package ghealth

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gocraft/health"
	"os"
	"time"
)

const (
	defaultStreamKey = "github.com/utrack/gin-health"
	defaultJobKey    = "github.com/utrack/gin-health|job"
)

// NewStream initializes health sink to statsd using supplied
// statsd address (IP:PORT) and appname.
// Falls back to stdout if none supplied.
// Also creates Json sink for healthd at supplied address
// (serversink) if not empty.
//
// statsd: StatsD address and port.
//
// appname Application name for StatsD.
//
// serversink: Bind address for Json sink, empty if not needed.
func NewStream(statsd string, appname string, serversink string) *health.Stream {
	var stream = health.NewStream()

	if statsd != "" {
		statsdSink, err := health.NewStatsDSink(statsd, appname)
		if err != nil {
			fmt.Println("HEALTH: Adding stdout health sink...")
			stream.AddSink(&health.WriterSink{os.Stdout})
			stream.EventErr("new_statsd_sink", err)
		} else {
			fmt.Println("HEALTH: Adding statsd health sink...")
			stream.AddSink(statsdSink)
		}
	} else {
		fmt.Println("HEALTH: Adding stdout health sink...")
		stream.AddSink(&health.WriterSink{os.Stdout})
	}

	if serversink != "" {
		sink := health.NewJsonPollingSink(time.Minute, time.Minute*5)
		stream.AddSink(sink)
		sink.StartServer(serversink)
		fmt.Println("HEALTH: Adding json health sink...")
	}
	return stream
}

// Use this method to inject the middleware.
func Health(stream *health.Stream) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set(defaultStreamKey, stream)
		c.Next()
	}
}

// Job creates a new job with given name.
//
// c: current Gin context.
//
// name: Job's name.
func Job(c *gin.Context, name string) *health.Job {
	job := c.MustGet(defaultStreamKey).(*health.Stream).NewJob(name)
	c.Set(defaultJobKey, time.Now())
	return job
}

// TimeSince is a little helper over time.Time.TimeSince
// which returns the interval in nanoseconds,
// suitable for feeding to job.Timing.
func TimeSince(t time.Time) int64 {
	return time.Since(t).Nanoseconds()
}
<commit_msg>Godoc examples<commit_after>/*
Package ghealth provides a Gin middleware
to gocraft/health performance monitoring toolkit.

By default it creates StatsD sink, falling back to stdout if
error happened or StatsD server was not provided.

Example

	func main() {
		// Standard router initialization
		router := gin.Default()

		// First, you need to create a new stream...

		// Simplest sink, stdout only
		hstream := ghealth.NewStream("", "", "")

		// STDOUT and JSON sinks; creates independent http server on port 5020
		hstream := ghealth.NewStream("", "", "127.0.0.1:5020")

		// StatsD and JSON sinks
		hstream := ghealth.NewStream("statsd.server:5000", "yourappname", "127.0.0.1:5020")

		// It's a standard *health.Stream, so you can do anything you want!
		hstream.AddSink(&health.WriterSink{os.Stdout})

		router.Use(ghealth.Health(hstream))
		...
	}

	var someRoute gin.HandlerFunc = func(c *gin.Context) {
		// Retrieve a job object
		job := ghealth.Job(c, "some_route")

		// It's a *health.Job, read health godoc for more info :)
		job.Event("some_event")
	}

At the moment it wasn't heavily tested. I'm still not sure how health
implements concurrency, but as long as stream is thread-safe - ghealth
should be, too.
*/
package ghealth

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gocraft/health"
	"os"
	"time"
)

const (
	defaultStreamKey = "github.com/utrack/gin-health"
	defaultJobKey    = "github.com/utrack/gin-health|job"
)

// NewStream initializes health sink to statsd using supplied
// statsd address (IP:PORT) and appname.
// Falls back to stdout if none supplied.
// Also creates Json sink for healthd at supplied address
// (serversink) if not empty.
//
// statsd: StatsD address and port.
//
// appname Application name for StatsD.
//
// serversink: Bind address for Json sink, empty if not needed.
func NewStream(statsd string, appname string, serversink string) *health.Stream {
	var stream = health.NewStream()

	if statsd != "" {
		statsdSink, err := health.NewStatsDSink(statsd, appname)
		if err != nil {
			fmt.Println("HEALTH: Adding stdout health sink...")
			stream.AddSink(&health.WriterSink{os.Stdout})
			stream.EventErr("new_statsd_sink", err)
		} else {
			fmt.Println("HEALTH: Adding statsd health sink...")
			stream.AddSink(statsdSink)
		}
	} else {
		fmt.Println("HEALTH: Adding stdout health sink...")
		stream.AddSink(&health.WriterSink{os.Stdout})
	}

	if serversink != "" {
		sink := health.NewJsonPollingSink(time.Minute, time.Minute*5)
		stream.AddSink(sink)
		sink.StartServer(serversink)
		fmt.Println("HEALTH: Adding json health sink...")
	}
	return stream
}

// Use this method to inject the middleware.
func Health(stream *health.Stream) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set(defaultStreamKey, stream)
		c.Next()
	}
}

// Job creates a new job with given name.
//
// c: current Gin context.
//
// name: Job's name.
func Job(c *gin.Context, name string) *health.Job {
	job := c.MustGet(defaultStreamKey).(*health.Stream).NewJob(name)
	c.Set(defaultJobKey, time.Now())
	return job
}

// TimeSince is a little helper over time.Time.TimeSince
// which returns the interval in nanoseconds,
// suitable for feeding to job.Timing.
func TimeSince(t time.Time) int64 {
	return time.Since(t).Nanoseconds()
}
<|endoftext|>


<commit_before>// Package git provides basic git client functionality
package git

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

var (
	repoParentDir string
	username      string
	password      string
)

func init() {
	repoParentDir = os.TempDir()
}

func SetParentDir(path string) {
	repoParentDir = path
}

func ParentDir() string {
	return repoParentDir
}

func SetAuth(user string, pwd string) {
	username = user
	password = pwd
}

func GenerateCloneURL(repositoryFullName string) string {
	return fmt.Sprintf("https://%s:%s@github.com/%s.git", username, password, repositoryFullName)
}

func Exists(repositoryPath string) bool {
	_, err := os.Stat(repositoryPath)
	return !os.IsNotExist(err)
}

func GetRepositoryFilePath(name string) string {
	return path.Join(repoParentDir, name)
}

// Clone executes a git clone command on the system. It returns the path to the repository on the system.
func Clone(repositoryUrl string) (string, error) {
	orgName := extractOrgFromURL(repositoryUrl)
	repoName := extractRepoNameFromURL(repositoryUrl)
	repositoryPath := path.Join(repoParentDir, orgName, repoName)

	log.Println("git.clone.started:", repositoryPath)
	defer log.Println("git.clone.finished:", repositoryPath)

	cmd := exec.Command("git", "clone", repositoryUrl, repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.clone.failed:", repositoryPath, err.Error())
		return "", err
	}

	return repositoryPath, nil
}

// Calls git fetch inside repository path
func Fetch(repositoryPath string) error {
	log.Println("git.fetch.started:", repositoryPath)
	defer log.Println("git.fetch.finished:", repositoryPath)

	cmd := exec.Command("git", "fetch", "origin")
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.fetch.failed:", repositoryPath, err.Error())
		return err
	}

	return nil
}

// Checks out a given git ref inside repository path
func Checkout(repositoryPath, gitRef string) error {
	log.Println("git.checkout.started:", repositoryPath, gitRef)
	defer log.Println("git.checkout.finished:", repositoryPath, gitRef)

	cmd := exec.Command("git", "checkout", gitRef)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.checkout.failed:", repositoryPath, err.Error())
		return err
	}

	return nil
}

// Does hard reset inside repository path
func Reset(repositoryPath, branch string) error {
	log.Println("git.reset.started:", repositoryPath, branch)
	defer log.Println("git.reset.finished:", repositoryPath, branch)

	cmd := exec.Command("git", "reset", "--hard", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.reset.failed:", repositoryPath, err.Error())
		return err
	}

	return nil
}

// Rebases branch with baseBranch inside repository path
func Rebase(repositoryPath, baseBranch string) error {
	log.Println("git.rebase.started:", repositoryPath, baseBranch)
	defer log.Println("git.rebase.finished:", repositoryPath, baseBranch)

	cmd := exec.Command("git", "rebase", baseBranch)
	cmd.Dir = path.Join(repositoryPath)

	if err := cmd.Run(); err != nil {
		log.Printf("git.rebase.abort.started repo: %s, err: %s \n", repositoryPath, err.Error())

		abortCmd := exec.Command("git", "rebase", "--abort")
		abortCmd.Dir = path.Join(repositoryPath)

		if err := abortCmd.Run(); err != nil {
			log.Println("git.rebase.abort.failed:", repositoryPath)
		} else {
			log.Println("git.rebase.abort.finished:", repositoryPath)
		}

		return err
	}

	return nil
}

// Pushes branch back to origin
func Push(repositoryPath, branch string) error {
	log.Println("git.push.started:", repositoryPath, branch)
	defer log.Println("git.push.finished:", repositoryPath, branch)

	cmd := exec.Command("git", "push", "--force", "origin", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.push.failed:", repositoryPath, err.Error())
		return err
	}

	return nil
}

func Clean() {
	log.Printf("git.cache.cleanup.started: path: %s\n", repoParentDir)

	if err := os.RemoveAll(repoParentDir); err != nil {
		log.Fatalf("git.cache.cleanup.failed: path: %s, error: %s\n", repoParentDir, err.Error())
	}

	log.Printf("git.cache.cleanup.finished")
}

func extractOrgFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	return splitBySlash[len(splitBySlash)-2]
}

func extractRepoNameFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	repoNameWithExt := splitBySlash[len(splitBySlash)-1]
	return strings.Split(repoNameWithExt, ".")[0]
}
<commit_msg>Don't log complete messages after they fail<commit_after>// Package git provides basic git client functionality
package git

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
)

var (
	repoParentDir string
	username      string
	password      string
)

func init() {
	repoParentDir = os.TempDir()
}

func SetParentDir(path string) {
	repoParentDir = path
}

func ParentDir() string {
	return repoParentDir
}

func SetAuth(user string, pwd string) {
	username = user
	password = pwd
}

func GenerateCloneURL(repositoryFullName string) string {
	return fmt.Sprintf("https://%s:%s@github.com/%s.git", username, password, repositoryFullName)
}

func Exists(repositoryPath string) bool {
	_, err := os.Stat(repositoryPath)
	return !os.IsNotExist(err)
}

func GetRepositoryFilePath(name string) string {
	return path.Join(repoParentDir, name)
}

// Clone executes a git clone command on the system. It returns the path to the repository on the system.
func Clone(repositoryUrl string) (string, error) {
	orgName := extractOrgFromURL(repositoryUrl)
	repoName := extractRepoNameFromURL(repositoryUrl)
	repositoryPath := path.Join(repoParentDir, orgName, repoName)

	log.Println("git.clone.started:", repositoryPath)

	cmd := exec.Command("git", "clone", repositoryUrl, repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.clone.failed:", repositoryPath, err.Error())
		return "", err
	}

	log.Println("git.clone.finished:", repositoryPath)

	return repositoryPath, nil
}

// Calls git fetch inside repository path
func Fetch(repositoryPath string) error {
	log.Println("git.fetch.started:", repositoryPath)

	cmd := exec.Command("git", "fetch", "origin")
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.fetch.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.fetch.finished:", repositoryPath)

	return nil
}

// Checks out a given git ref inside repository path
func Checkout(repositoryPath, gitRef string) error {
	log.Println("git.checkout.started:", repositoryPath, gitRef)

	cmd := exec.Command("git", "checkout", gitRef)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.checkout.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.checkout.finished:", repositoryPath, gitRef)

	return nil
}

// Does hard reset inside repository path
func Reset(repositoryPath, branch string) error {
	log.Println("git.reset.started:", repositoryPath, branch)

	cmd := exec.Command("git", "reset", "--hard", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.reset.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.reset.finished:", repositoryPath, branch)

	return nil
}

// Rebases branch with baseBranch inside repository path
func Rebase(repositoryPath, baseBranch string) error {
	log.Println("git.rebase.started:", repositoryPath, baseBranch)

	cmd := exec.Command("git", "rebase", baseBranch)
	cmd.Dir = path.Join(repositoryPath)

	if err := cmd.Run(); err != nil {
		log.Printf("git.rebase.abort.started repo: %s, err: %s \n", repositoryPath, err.Error())

		abortCmd := exec.Command("git", "rebase", "--abort")
		abortCmd.Dir = path.Join(repositoryPath)

		if err := abortCmd.Run(); err != nil {
			log.Println("git.rebase.abort.failed:", repositoryPath)
		} else {
			log.Println("git.rebase.abort.finished:", repositoryPath)
		}

		return err
	}

	log.Println("git.rebase.finished:", repositoryPath, baseBranch)

	return nil
}

// Pushes branch back to origin
func Push(repositoryPath, branch string) error {
	log.Println("git.push.started:", repositoryPath, branch)

	cmd := exec.Command("git", "push", "--force", "origin", branch)
	cmd.Dir = path.Join(repositoryPath)
	if err := cmd.Run(); err != nil {
		log.Println("git.push.failed:", repositoryPath, err.Error())
		return err
	}

	log.Println("git.push.finished:", repositoryPath, branch)

	return nil
}

func Clean() {
	log.Printf("git.cache.cleanup.started: path: %s\n", repoParentDir)

	if err := os.RemoveAll(repoParentDir); err != nil {
		log.Fatalf("git.cache.cleanup.failed: path: %s, error: %s\n", repoParentDir, err.Error())
	}

	log.Printf("git.cache.cleanup.finished")
}

func extractOrgFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	return splitBySlash[len(splitBySlash)-2]
}

func extractRepoNameFromURL(githubURL string) string {
	splitBySlash := strings.Split(githubURL, "/")
	repoNameWithExt := splitBySlash[len(splitBySlash)-1]
	return strings.Split(repoNameWithExt, ".")[0]
}
<|endoftext|>


<commit_before>
package git

import (
    "fmt"
    "os"
    "os/exec"
    "bytes"
    "strings"
)

type Command struct {
    command string
    parameters []string
    output string
}

var p = fmt.Println
var pf = fmt.Printf

func init() {
    // Do some init magic?
}

// pull from remote
func Pull() {
    fmt.Println("Pulling...")
    params := []string{"push"}
    execute(Command{"git", params, ""})
}

// push to remote
func Push() {
    fmt.Println("Pushing...")
    params := []string{"push"}
    execute(Command{"git", params, ""})
}

// checkout branch and pull from remote

// commit with message, appending branch name in the front of the comment
func Commit(message string) {
    fmt.Println("Commiting")

    currentBranchName := getCurrentBranchName()

    if(currentBranchName != "") {
        params := []string{"commit", "-m " + currentBranchName + ": " + message}
        execute(Command{"git", params, ""})
    } else {
        p("Could not detect branch name. Exiting...")
        os.Exit(1)
    }

}

// make appending the commend configurable via a config file

// create branch both locally and remotely and check it out
// completely pull/update current branch to branch off from before creating the new one

// delete remote branch

// delete all local branches but the one you are currently on
func DeleteOldBranches() {
    // Add user confirmation with a warning that this will FORCE delete!!!
    params := []string{ "branch | grep -v \"master\" | grep -v \\* | xargs git branch -D" }
    execute(Command{"git", params, ""})
}

// merge with --no-ff flag

// ================ HELPERS ================

func execute(command Command) (string){
    cmd := exec.Command(command.command, command.parameters...)

    var out bytes.Buffer
    var stderr bytes.Buffer

    cmd.Stdout = &out
    cmd.Stderr = &stderr
    err := cmd.Run()

    if err != nil {
        fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
        os.Exit(1)
    }
    return out.String()
}

func getCurrentBranchName() (string){
    params := []string{"branch"}
    result := strings.Split(execute(Command{"git", params, ""}), "\n")
    var branchName string

    for _,value := range result {
        position := strings.Index(value, "* ")
        if position != -1 {
            words := strings.Split(value, " ")
            branchName = strings.Trim(words[1], "\n")
            return branchName
        }
    }

    return ""
}
<commit_msg> master: removed colon between branch name and commit message - closes worp1900/worp#5<commit_after>
package git

import (
    "fmt"
    "os"
    "os/exec"
    "bytes"
    "strings"
)

type Command struct {
    command string
    parameters []string
    output string
}

var p = fmt.Println
var pf = fmt.Printf

func init() {
    // Do some init magic?
}

// pull from remote
func Pull() {
    fmt.Println("Pulling...")
    params := []string{"push"}
    execute(Command{"git", params, ""})
}

// push to remote
func Push() {
    fmt.Println("Pushing...")
    params := []string{"push"}
    execute(Command{"git", params, ""})
}

// checkout branch and pull from remote

// commit with message, appending branch name in the front of the comment
func Commit(message string) {
    fmt.Println("Commiting")

    currentBranchName := getCurrentBranchName()

    if(currentBranchName != "") {
        params := []string{"commit", "-m " + currentBranchName + " " + message}
        execute(Command{"git", params, ""})
    } else {
        p("Could not detect branch name. Exiting...")
        os.Exit(1)
    }

}

// make appending the commend configurable via a config file

// create branch both locally and remotely and check it out
// completely pull/update current branch to branch off from before creating the new one

// delete remote branch

// delete all local branches but the one you are currently on
func DeleteOldBranches() {
    // Add user confirmation with a warning that this will FORCE delete!!!
    params := []string{ "branch | grep -v \"master\" | grep -v \\* | xargs git branch -D" }
    execute(Command{"git", params, ""})
}

// merge with --no-ff flag

// ================ HELPERS ================

func execute(command Command) (string){
    cmd := exec.Command(command.command, command.parameters...)

    var out bytes.Buffer
    var stderr bytes.Buffer

    cmd.Stdout = &out
    cmd.Stderr = &stderr
    err := cmd.Run()

    if err != nil {
        fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
        os.Exit(1)
    }
    return out.String()
}

func getCurrentBranchName() (string){
    params := []string{"branch"}
    result := strings.Split(execute(Command{"git", params, ""}), "\n")
    var branchName string

    for _,value := range result {
        position := strings.Index(value, "* ")
        if position != -1 {
            words := strings.Split(value, " ")
            branchName = strings.Trim(words[1], "\n")
            return branchName
        }
    }

    return ""
}
<|endoftext|>


<commit_before>package dbo

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/byuoitav/authmiddleware/bearertoken"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

// GetData will run a get on the url, and attempt to fill the interface provided from the returned JSON.
func GetData(url string, structToFill interface{}) error {
	log.Printf("Getting data from URL: %s...", url)
	// Make an HTTP client so we can add custom headers (currently used for adding in the Bearer token for inter-microservice communication)

	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)

	if len(os.Getenv("LOCAL_ENVIRONMENT")) == 0 {

		log.Printf("Adding the bearer token for inter-service communication")

		token, err := bearertoken.GetToken()
		if err != nil {
			return err
		}

		req.Header.Set("Authorization", "Bearer "+token.Token)
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(b, structToFill)
	if err != nil {
		return err
	}
	log.Printf("Done.")
	return nil
}

// GetAllRawCommands retrieves all the commands
func GetAllRawCommands() (commands []accessors.RawCommand, err error) {
	log.Printf("Getting all commands.")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/commands"
	err = GetData(url, &commands)

	if err != nil {
		log.Printf("Error: %s", err.Error())
		return
	}

	log.Printf("Done.")
	return
}

// GetRoomByInfo simply retrieves a device's information from the databse.
func GetRoomByInfo(buildingName string, roomName string) (toReturn accessors.Room, err error) {
	log.Printf("Getting room %s in building %s...", roomName, buildingName)
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName, &toReturn)
	return
}

// GetDeviceByName simply retrieves a device's information from the databse.
func GetDeviceByName(buildingName string, roomName string, deviceName string) (toReturn accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices/"+deviceName, &toReturn)
	return
}

// GetDevicesByRoom will jut get the devices based on the room.
func GetDevicesByRoom(buildingName string, roomName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices", &toReturn)
	return
}

// GetDevicesByBuildingAndRoomAndRole will get the devices with the given role from the DB
func GetDevicesByBuildingAndRoomAndRole(building string, room string, roleName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+building+"/rooms/"+room+"/devices/roles/"+roleName, &toReturn)
	return
}

// SetAudioInDB will set the audio levels in the database
func SetAudioInDB(building string, room string, device accessors.Device) error {
	log.Printf("Updating audio levels in DB.")

	if device.Volume != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/volume/" + strconv.Itoa(*device.Volume)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	if device.Muted != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/muted/" + strconv.FormatBool(*device.Muted)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	return nil
}

// GetBuildings will return all buildings
func GetBuildings() ([]accessors.Building, error) {
	log.Printf("getting all buildings...")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings"
	var buildings []accessors.Building
	err := GetData(url, &buildings)

	return buildings, err
}

// GetRooms returns all the rooms in a given building
func GetRoomsByBuilding(building string) ([]accessors.Room, error) {
	log.Printf("getting all rooms from %v ...", building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms"
	var rooms []accessors.Room
	err := GetData(url, &rooms)
	return rooms, err
}
<commit_msg>AddDevices()<commit_after>package dbo

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/byuoitav/authmiddleware/bearertoken"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

// GetData will run a get on the url, and attempt to fill the interface provided from the returned JSON.
func GetData(url string, structToFill interface{}) error {
	log.Printf("Getting data from URL: %s...", url)
	// Make an HTTP client so we can add custom headers (currently used for adding in the Bearer token for inter-microservice communication)

	client := &http.Client{}
	req, _ := http.NewRequest("GET", url, nil)

	if len(os.Getenv("LOCAL_ENVIRONMENT")) == 0 {

		log.Printf("Adding the bearer token for inter-service communication")

		token, err := bearertoken.GetToken()
		if err != nil {
			return err
		}

		req.Header.Set("Authorization", "Bearer "+token.Token)
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(b, structToFill)
	if err != nil {
		return err
	}
	log.Printf("Done.")
	return nil
}

func WriteData(url string, structToAdd interface{}) error {

}

// GetAllRawCommands retrieves all the commands
func GetAllRawCommands() (commands []accessors.RawCommand, err error) {
	log.Printf("Getting all commands.")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/commands"
	err = GetData(url, &commands)

	if err != nil {
		log.Printf("Error: %s", err.Error())
		return
	}

	log.Printf("Done.")
	return
}

// GetRoomByInfo simply retrieves a device's information from the databse.
func GetRoomByInfo(buildingName string, roomName string) (toReturn accessors.Room, err error) {
	log.Printf("Getting room %s in building %s...", roomName, buildingName)
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName, &toReturn)
	return
}

// GetDeviceByName simply retrieves a device's information from the databse.
func GetDeviceByName(buildingName string, roomName string, deviceName string) (toReturn accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices/"+deviceName, &toReturn)
	return
}

// GetDevicesByRoom will jut get the devices based on the room.
func GetDevicesByRoom(buildingName string, roomName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices", &toReturn)
	return
}

// GetDevicesByBuildingAndRoomAndRole will get the devices with the given role from the DB
func GetDevicesByBuildingAndRoomAndRole(building string, room string, roleName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+building+"/rooms/"+room+"/devices/roles/"+roleName, &toReturn)
	return
}

// SetAudioInDB will set the audio levels in the database
func SetAudioInDB(building string, room string, device accessors.Device) error {
	log.Printf("Updating audio levels in DB.")

	if device.Volume != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/volume/" + strconv.Itoa(*device.Volume)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	if device.Muted != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/muted/" + strconv.FormatBool(*device.Muted)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	return nil
}

// GetBuildings will return all buildings
func GetBuildings() ([]accessors.Building, error) {
	log.Printf("getting all buildings...")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings"
	var buildings []accessors.Building
	err := GetData(url, &buildings)

	return buildings, err
}

// GetRooms returns all the rooms in a given building
func GetRoomsByBuilding(building string) ([]accessors.Room, error) {
	log.Printf("getting all rooms from %v ...", building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms"
	var rooms []accessors.Room
	err := GetData(url, &rooms)
	return rooms, err
}

// AddBuilding asdf
func AddBuilding(building accessors.Building) (accessors.Building, error) {
	log.Printf("adding building %v to database", building.Shortname)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"

	"github.com/deis/deisctl/client"
	"github.com/deis/deisctl/cmd"
	"github.com/deis/deisctl/constant"
	"github.com/deis/deisctl/updatectl"
	"github.com/deis/deisctl/utils"
	docopt "github.com/docopt/docopt-go"
)

func exit(err error, code int) {
	fmt.Printf("Error: %v\n", err)
	os.Exit(code)
}

func Update(args []string) {

	if len(args) != 4 {
		fmt.Println("unsufficient args")
		fmt.Println("usage:  deisctl update instance deis")
		return
	}
	if args[2] != "instance" && args[3] != "deis" {
		fmt.Println("wrong args ")
		fmt.Println("usage:  deisctl update instance deis")
		return
	}
	Args := []string{
		"instance",
		"deis",
		"--clients-per-app=1",
		"--min-sleep=5",
		"--max-sleep=10",
	}
	if err := utils.Execute(constant.HooksDir + "pre-update"); err != nil {
		fmt.Println("pre-updatehook failed")
	}
	updatectl.Update(Args)
	if err := utils.Execute(constant.HooksDir + "post-update"); err != nil {
		fmt.Println("post-updatehook failed")
	}
}

func setGlobalFlags(args map[string]interface{}) {
	client.Flags.Debug = args["--debug"].(bool)
	client.Flags.Version = args["--version"].(bool)
	client.Flags.Endpoint = args["--endpoint"].(string)
	client.Flags.EtcdKeyPrefix = args["--etcd-key-prefix"].(string)
	client.Flags.EtcdKeyFile = args["--etcd-keyfile"].(string)
	client.Flags.EtcdCertFile = args["--etcd-certfile"].(string)
	client.Flags.EtcdCAFile = args["--etcd-cafile"].(string)
	//client.Flags.UseAPI = args["--experimental-api"].(bool)
	client.Flags.KnownHostsFile = args["--known-hosts-file"].(string)
	client.Flags.StrictHostKeyChecking = args["--strict-host-key-checking"].(bool)
	timeout, _ := strconv.ParseFloat(args["--request-timeout"].(string), 64)
	client.Flags.RequestTimeout = timeout
	tunnel := args["--tunnel"].(string)
	if tunnel != "" {
		client.Flags.Tunnel = tunnel
	} else {
		client.Flags.Tunnel = os.Getenv("DEISCTL_TUNNEL")
	}
}

func main() {
	usage := `Deis Control Utility

Usage:
  deisctl <command> [<target>...] [options]

Example Commands:

  deisctl install
  deisctl uninstall
  deisctl list
  deisctl scale router=2
  deisctl start router@2
  deisctl stop router builder
  deisctl status controller
  deisctl journal controller

Options:
  --debug                     print debug information to stderr
  --version                   print version and exit
  --endpoint=<url>            etcd endpoint for fleet [default: http://127.0.0.1:4001]
  --etcd-key-prefix=<path>    keyspace for fleet data in etcd [default: /_coreos.com/fleet/]
  --etcd-keyfile=<path>       etcd key file authentication [default: ]
  --etcd-certfile=<path>      etcd cert file authentication [default: ]
  --etcd-cafile=<path>        etcd CA file authentication [default: ]
  --known-hosts-file=<path>   file used to store remote machine fingerprints [default: ~/.fleetctl/known_hosts]
  --strict-host-key-checking  verify SSH host keys [default: true]
  --tunnel=<host>             establish an SSH tunnel for communication with fleet and etcd [default: ]
  --request-timeout=<secs>    amount of time to allow a single request before considering it failed. [default: 3.0]
`
	// parse command-line arguments
	args, err := docopt.Parse(usage, nil, true, "", true)
	if err != nil {
		exit(err, 2)
	}
	command := args["<command>"]
	targets := args["<target>"].([]string)
	setGlobalFlags(args)
	// construct a client
	c, err := client.NewClient()
	if err != nil {
		exit(err, 1)
	}
	// dispatch the command
	switch command {
	case "list":
		err = cmd.ListUnits(c)
	case "list-units":
		err = cmd.ListUnits(c)
	case "list-unit-files":
		err = cmd.ListUnitFiles(c)
	case "scale":
		err = cmd.Scale(c, targets)
	case "start":
		err = cmd.Start(c, targets)
	case "stop":
		err = cmd.Stop(c, targets)
	case "status":
		err = cmd.Status(c, targets)
	case "journal":
		err = cmd.Journal(c, targets)
	case "install":
		err = cmd.Install(c, targets)
	case "uninstall":
		err = cmd.Uninstall(c, targets)
	case "update":
		Update(os.Args)
	default:
		fmt.Printf(usage)
		os.Exit(2)
	}
	if err != nil {
		exit(err, 1)
	}
}
<commit_msg>fix(ssh): switch to default known hosts<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"

	"github.com/deis/deisctl/client"
	"github.com/deis/deisctl/cmd"
	"github.com/deis/deisctl/constant"
	"github.com/deis/deisctl/updatectl"
	"github.com/deis/deisctl/utils"
	docopt "github.com/docopt/docopt-go"
)

func exit(err error, code int) {
	fmt.Printf("Error: %v\n", err)
	os.Exit(code)
}

func Update(args []string) {

	if len(args) != 4 {
		fmt.Println("unsufficient args")
		fmt.Println("usage:  deisctl update instance deis")
		return
	}
	if args[2] != "instance" && args[3] != "deis" {
		fmt.Println("wrong args ")
		fmt.Println("usage:  deisctl update instance deis")
		return
	}
	Args := []string{
		"instance",
		"deis",
		"--clients-per-app=1",
		"--min-sleep=5",
		"--max-sleep=10",
	}
	if err := utils.Execute(constant.HooksDir + "pre-update"); err != nil {
		fmt.Println("pre-updatehook failed")
	}
	updatectl.Update(Args)
	if err := utils.Execute(constant.HooksDir + "post-update"); err != nil {
		fmt.Println("post-updatehook failed")
	}
}

func setGlobalFlags(args map[string]interface{}) {
	client.Flags.Debug = args["--debug"].(bool)
	client.Flags.Version = args["--version"].(bool)
	client.Flags.Endpoint = args["--endpoint"].(string)
	client.Flags.EtcdKeyPrefix = args["--etcd-key-prefix"].(string)
	client.Flags.EtcdKeyFile = args["--etcd-keyfile"].(string)
	client.Flags.EtcdCertFile = args["--etcd-certfile"].(string)
	client.Flags.EtcdCAFile = args["--etcd-cafile"].(string)
	//client.Flags.UseAPI = args["--experimental-api"].(bool)
	client.Flags.KnownHostsFile = args["--known-hosts-file"].(string)
	client.Flags.StrictHostKeyChecking = args["--strict-host-key-checking"].(bool)
	timeout, _ := strconv.ParseFloat(args["--request-timeout"].(string), 64)
	client.Flags.RequestTimeout = timeout
	tunnel := args["--tunnel"].(string)
	if tunnel != "" {
		client.Flags.Tunnel = tunnel
	} else {
		client.Flags.Tunnel = os.Getenv("DEISCTL_TUNNEL")
	}
}

func main() {
	usage := `Deis Control Utility

Usage:
  deisctl <command> [<target>...] [options]

Example Commands:

  deisctl install
  deisctl uninstall
  deisctl list
  deisctl scale router=2
  deisctl start router@2
  deisctl stop router builder
  deisctl status controller
  deisctl journal controller

Options:
  --debug                     print debug information to stderr
  --version                   print version and exit
  --endpoint=<url>            etcd endpoint for fleet [default: http://127.0.0.1:4001]
  --etcd-key-prefix=<path>    keyspace for fleet data in etcd [default: /_coreos.com/fleet/]
  --etcd-keyfile=<path>       etcd key file authentication [default: ]
  --etcd-certfile=<path>      etcd cert file authentication [default: ]
  --etcd-cafile=<path>        etcd CA file authentication [default: ]
  --known-hosts-file=<path>   file used to store remote machine fingerprints [default: ~/.ssh/known_hosts]
  --strict-host-key-checking  verify SSH host keys [default: true]
  --tunnel=<host>             establish an SSH tunnel for communication with fleet and etcd [default: ]
  --request-timeout=<secs>    amount of time to allow a single request before considering it failed. [default: 3.0]
`
	// parse command-line arguments
	args, err := docopt.Parse(usage, nil, true, "", true)
	if err != nil {
		exit(err, 2)
	}
	command := args["<command>"]
	targets := args["<target>"].([]string)
	setGlobalFlags(args)
	// construct a client
	c, err := client.NewClient()
	if err != nil {
		exit(err, 1)
	}
	// dispatch the command
	switch command {
	case "list":
		err = cmd.ListUnits(c)
	case "list-units":
		err = cmd.ListUnits(c)
	case "list-unit-files":
		err = cmd.ListUnitFiles(c)
	case "scale":
		err = cmd.Scale(c, targets)
	case "start":
		err = cmd.Start(c, targets)
	case "stop":
		err = cmd.Stop(c, targets)
	case "status":
		err = cmd.Status(c, targets)
	case "journal":
		err = cmd.Journal(c, targets)
	case "install":
		err = cmd.Install(c, targets)
	case "uninstall":
		err = cmd.Uninstall(c, targets)
	case "update":
		Update(os.Args)
	default:
		fmt.Printf(usage)
		os.Exit(2)
	}
	if err != nil {
		exit(err, 1)
	}
}
<|endoftext|>


<commit_before>package tools

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/arduino/arduino-create-agent/utilities"
	"github.com/blang/semver"
	"github.com/xrash/smetrics"
)

type system struct {
	Host     string `json:"host"`
	URL      string `json:"url"`
	Name     string `json:"archiveFileName"`
	CheckSum string `json:"checksum"`
}

type tool struct {
	Name        string   `json:"name"`
	Version     string   `json:"version"`
	Systems     []system `json:"systems"`
	url         string
	destination string
}

type index struct {
	Packages []struct {
		Name  string `json:"name"`
		Tools []tool `json:"tools"`
	} `json:"packages"`
}

var systems = map[string]string{
	"linuxamd64":  "x86_64-linux-gnu",
	"linux386":    "i686-linux-gnu",
	"darwinamd64": "i386-apple-darwin11",
	"windows386":  "i686-mingw32",
}

func mimeType(data []byte) (string, error) {
	return http.DetectContentType(data[0:512]), nil
}

// Download will parse the index at the indexURL for the tool to download.
// It will extract it in a folder in .arduino-create, and it will update the
// Installed map.
//
// name contains the name of the tool.
// version contains the version of the tool.
// behaviour contains the strategy to use when there is already a tool installed
//
// If version is "latest" it will always download the latest version (regardless
// of the value of behaviour)
//
// If version is not "latest" and behaviour is "replace", it will download the
// version again. If instead behaviour is "keep" it will not download the version
// if it already exists.
func (t *Tools) Download(name, version, behaviour string) error {
	var key string
	if version == "latest" {
		key = name
	} else {
		key = name + "-" + version
	}

	// Check if it already exists
	if version != "latest" && behaviour == "keep" {
		if _, ok := t.installed[key]; ok {
			t.Logger.Println("The tool is already present on the system")
			return nil
		}
	}
	// Fetch the index
	resp, err := http.Get(t.IndexURL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Read the body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var data index
	json.Unmarshal(body, &data)

	t.Logger.Println(string(body))

	// Find the tool by name
	correctTool := findTool(name, version, data)

	if correctTool.Name == "" {
		return errors.New("We couldn't find a tool with the name " + name + " and version " + version)
	}

	// Find the url based on system
	var correctSystem system
	max_similarity := 0.8

	for _, s := range correctTool.Systems {
		similarity := smetrics.Jaro(s.Host, systems[runtime.GOOS+runtime.GOARCH])
		if similarity > max_similarity {
			correctSystem = s
			max_similarity = similarity
		}
	}

	// Download the tool
	t.Logger.Println("Downloading tool " + name + " from " + correctSystem.URL)
	resp, err = http.Get(correctSystem.URL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Read the body
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	// Checksum
	checksum := sha256.Sum256(body)
	checkSumString := "SHA-256:" + hex.EncodeToString(checksum[:sha256.Size])

	if checkSumString != correctSystem.CheckSum {
		return errors.New("Checksum doesn't match")
	}

	// Decompress
	t.Logger.Println("Unpacking tool " + name)

	location := path.Join(dir(), correctTool.Name, correctTool.Version)
	err = os.RemoveAll(location)

	if err != nil {
		return err
	}

	srcType, err := mimeType(body)
	if err != nil {
		return err
	}

	switch srcType {
	case "application/zip":
		location, err = extractZip(body, location)
	case "application/x-bz2":
	case "application/octet-stream":
		location, err = extractBz2(body, location)
	case "application/x-gzip":
		location, err = extractTarGz(body, location)
	default:
		return errors.New("Unknown extension for file " + correctSystem.URL)
	}

	if err != nil {
		return err
	}

	// Ensure that the files are executable
	t.Logger.Println("Ensure that the files are executable")

	// Update the tool map
	t.Logger.Println("Updating map with location " + location)

	t.installed[name] = location
	t.installed[name+"-"+correctTool.Version] = location
	return t.writeMap()
}

func findTool(name, version string, data index) tool {
	var correctTool tool
	correctTool.Version = "0.0"

	for _, p := range data.Packages {
		for _, t := range p.Tools {
			if version != "latest" {
				if t.Name == name && t.Version == version {
					correctTool = t
				}
			} else {
				// Find latest
				v1, _ := semver.Make(t.Version)
				v2, _ := semver.Make(correctTool.Version)
				if t.Name == name && v1.Compare(v2) > 0 {
					correctTool = t
				}
			}
		}
	}
	return correctTool
}

func findBaseDir(dirList []string) string {
	baseDir := ""
	for index, _ := range dirList {
		candidateBaseDir := dirList[index]
		for i := index; i < len(dirList); i++ {
			if !strings.Contains(dirList[i], candidateBaseDir) {
				return baseDir
			}
		}
		baseDir = candidateBaseDir
	}
	return baseDir
}

func extractZip(body []byte, location string) (string, error) {
	path, err := utilities.SaveFileonTempDir("tooldownloaded.zip", bytes.NewReader(body))
	r, err := zip.OpenReader(path)
	if err != nil {
		return location, err
	}

	var dirList []string

	for _, f := range r.File {
		if f.FileInfo().IsDir() {
			dirList = append(dirList, f.Name)
		}
	}

	basedir := findBaseDir(dirList)

	for _, f := range r.File {
		fullname := filepath.Join(location, strings.Replace(f.Name, basedir, "", -1))
		if f.FileInfo().IsDir() {
			os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
		} else {
			os.MkdirAll(filepath.Dir(fullname), 0755)
			perms := f.FileInfo().Mode().Perm()
			out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
			if err != nil {
				return location, err
			}
			rc, err := f.Open()
			if err != nil {
				return location, err
			}
			_, err = io.CopyN(out, rc, f.FileInfo().Size())
			if err != nil {
				return location, err
			}
			rc.Close()
			out.Close()

			mtime := f.FileInfo().ModTime()
			err = os.Chtimes(fullname, mtime, mtime)
			if err != nil {
				return location, err
			}
		}
	}
	return location, nil
}

func extractTarGz(body []byte, location string) (string, error) {
	bodyCopy := make([]byte, len(body))
	copy(bodyCopy, body)
	tarFile, _ := gzip.NewReader(bytes.NewReader(body))
	tarReader := tar.NewReader(tarFile)

	var dirList []string

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if header.FileInfo().IsDir() {
			dirList = append(dirList, header.Name)
		}
	}

	basedir := findBaseDir(dirList)

	tarFile, _ = gzip.NewReader(bytes.NewReader(bodyCopy))
	tarReader = tar.NewReader(tarFile)

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			//return location, err
		}

		path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
		info := header.FileInfo()

		if info.IsDir() {
			if err = os.MkdirAll(path, info.Mode()); err != nil {
				return location, err
			}
			continue
		}

		if header.Typeflag == tar.TypeSymlink {
			err = os.Symlink(header.Linkname, path)
			continue
		}

		file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
		if err != nil {
			//return location, err
		}
		defer file.Close()
		_, err = io.Copy(file, tarReader)
		if err != nil {
			//return location, err
		}
	}
	return location, nil
}

func extractBz2(body []byte, location string) (string, error) {
	bodyCopy := make([]byte, len(body))
	copy(bodyCopy, body)
	tarFile := bzip2.NewReader(bytes.NewReader(body))
	tarReader := tar.NewReader(tarFile)

	var dirList []string

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if header.FileInfo().IsDir() {
			dirList = append(dirList, header.Name)
		}
	}

	basedir := findBaseDir(dirList)

	tarFile = bzip2.NewReader(bytes.NewReader(bodyCopy))
	tarReader = tar.NewReader(tarFile)

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			//return location, err
		}

		path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
		info := header.FileInfo()

		if info.IsDir() {
			if err = os.MkdirAll(path, info.Mode()); err != nil {
				return location, err
			}
			continue
		}

		if header.Typeflag == tar.TypeSymlink {
			err = os.Symlink(header.Linkname, path)
			continue
		}

		file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
		if err != nil {
			//return location, err
		}
		defer file.Close()
		_, err = io.Copy(file, tarReader)
		if err != nil {
			//return location, err
		}
	}
	return location, nil
}

func makeExecutable(location string) error {
	location = path.Join(location, "bin")
	files, err := ioutil.ReadDir(location)
	if err != nil {
		return err
	}

	for _, file := range files {
		err = os.Chmod(path.Join(location, file.Name()), 0755)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>execute post_install.bat if contained in downloaded tool<commit_after>package tools

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/arduino/arduino-create-agent/utilities"
	"github.com/blang/semver"
	"github.com/xrash/smetrics"
)

type system struct {
	Host     string `json:"host"`
	URL      string `json:"url"`
	Name     string `json:"archiveFileName"`
	CheckSum string `json:"checksum"`
}

type tool struct {
	Name        string   `json:"name"`
	Version     string   `json:"version"`
	Systems     []system `json:"systems"`
	url         string
	destination string
}

type index struct {
	Packages []struct {
		Name  string `json:"name"`
		Tools []tool `json:"tools"`
	} `json:"packages"`
}

var systems = map[string]string{
	"linuxamd64":  "x86_64-linux-gnu",
	"linux386":    "i686-linux-gnu",
	"darwinamd64": "i386-apple-darwin11",
	"windows386":  "i686-mingw32",
}

func mimeType(data []byte) (string, error) {
	return http.DetectContentType(data[0:512]), nil
}

// Download will parse the index at the indexURL for the tool to download.
// It will extract it in a folder in .arduino-create, and it will update the
// Installed map.
//
// name contains the name of the tool.
// version contains the version of the tool.
// behaviour contains the strategy to use when there is already a tool installed
//
// If version is "latest" it will always download the latest version (regardless
// of the value of behaviour)
//
// If version is not "latest" and behaviour is "replace", it will download the
// version again. If instead behaviour is "keep" it will not download the version
// if it already exists.
func (t *Tools) Download(name, version, behaviour string) error {
	var key string
	if version == "latest" {
		key = name
	} else {
		key = name + "-" + version
	}

	// Check if it already exists
	if version != "latest" && behaviour == "keep" {
		if _, ok := t.installed[key]; ok {
			t.Logger.Println("The tool is already present on the system")
			return nil
		}
	}
	// Fetch the index
	resp, err := http.Get(t.IndexURL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Read the body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	var data index
	json.Unmarshal(body, &data)

	t.Logger.Println(string(body))

	// Find the tool by name
	correctTool := findTool(name, version, data)

	if correctTool.Name == "" {
		return errors.New("We couldn't find a tool with the name " + name + " and version " + version)
	}

	// Find the url based on system
	var correctSystem system
	max_similarity := 0.8

	for _, s := range correctTool.Systems {
		similarity := smetrics.Jaro(s.Host, systems[runtime.GOOS+runtime.GOARCH])
		if similarity > max_similarity {
			correctSystem = s
			max_similarity = similarity
		}
	}

	// Download the tool
	t.Logger.Println("Downloading tool " + name + " from " + correctSystem.URL)
	resp, err = http.Get(correctSystem.URL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Read the body
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	// Checksum
	checksum := sha256.Sum256(body)
	checkSumString := "SHA-256:" + hex.EncodeToString(checksum[:sha256.Size])

	if checkSumString != correctSystem.CheckSum {
		return errors.New("Checksum doesn't match")
	}

	// Decompress
	t.Logger.Println("Unpacking tool " + name)

	location := path.Join(dir(), correctTool.Name, correctTool.Version)
	err = os.RemoveAll(location)

	if err != nil {
		return err
	}

	srcType, err := mimeType(body)
	if err != nil {
		return err
	}

	switch srcType {
	case "application/zip":
		location, err = extractZip(body, location)
	case "application/x-bz2":
	case "application/octet-stream":
		location, err = extractBz2(body, location)
	case "application/x-gzip":
		location, err = extractTarGz(body, location)
	default:
		return errors.New("Unknown extension for file " + correctSystem.URL)
	}

	if err != nil {
		return err
	}

	// Ensure that the files are executable
	t.Logger.Println("Ensure that the files are executable")

	// Update the tool map
	t.Logger.Println("Updating map with location " + location)

	t.installed[name] = location
	t.installed[name+"-"+correctTool.Version] = location
	return t.writeMap()
}

func findTool(name, version string, data index) tool {
	var correctTool tool
	correctTool.Version = "0.0"

	for _, p := range data.Packages {
		for _, t := range p.Tools {
			if version != "latest" {
				if t.Name == name && t.Version == version {
					correctTool = t
				}
			} else {
				// Find latest
				v1, _ := semver.Make(t.Version)
				v2, _ := semver.Make(correctTool.Version)
				if t.Name == name && v1.Compare(v2) > 0 {
					correctTool = t
				}
			}
		}
	}
	return correctTool
}

func findBaseDir(dirList []string) string {
	baseDir := ""
	for index, _ := range dirList {
		candidateBaseDir := dirList[index]
		for i := index; i < len(dirList); i++ {
			if !strings.Contains(dirList[i], candidateBaseDir) {
				return baseDir
			}
		}
		baseDir = candidateBaseDir
	}
	return baseDir
}

func extractZip(body []byte, location string) (string, error) {
	path, err := utilities.SaveFileonTempDir("tooldownloaded.zip", bytes.NewReader(body))
	r, err := zip.OpenReader(path)
	if err != nil {
		return location, err
	}

	var dirList []string

	for _, f := range r.File {
		if f.FileInfo().IsDir() {
			dirList = append(dirList, f.Name)
		}
	}

	basedir := findBaseDir(dirList)

	for _, f := range r.File {
		fullname := filepath.Join(location, strings.Replace(f.Name, basedir, "", -1))
		if f.FileInfo().IsDir() {
			os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
		} else {
			os.MkdirAll(filepath.Dir(fullname), 0755)
			perms := f.FileInfo().Mode().Perm()
			out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
			if err != nil {
				return location, err
			}
			rc, err := f.Open()
			if err != nil {
				return location, err
			}
			_, err = io.CopyN(out, rc, f.FileInfo().Size())
			if err != nil {
				return location, err
			}
			rc.Close()
			out.Close()

			mtime := f.FileInfo().ModTime()
			err = os.Chtimes(fullname, mtime, mtime)
			if err != nil {
				return location, err
			}
		}
	}
	return location, nil
}

func extractTarGz(body []byte, location string) (string, error) {
	bodyCopy := make([]byte, len(body))
	copy(bodyCopy, body)
	tarFile, _ := gzip.NewReader(bytes.NewReader(body))
	tarReader := tar.NewReader(tarFile)

	var dirList []string

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if header.FileInfo().IsDir() {
			dirList = append(dirList, header.Name)
		}
	}

	basedir := findBaseDir(dirList)

	tarFile, _ = gzip.NewReader(bytes.NewReader(bodyCopy))
	tarReader = tar.NewReader(tarFile)

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			//return location, err
		}

		path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
		info := header.FileInfo()

		if info.IsDir() {
			if err = os.MkdirAll(path, info.Mode()); err != nil {
				return location, err
			}
			continue
		}

		if header.Typeflag == tar.TypeSymlink {
			err = os.Symlink(header.Linkname, path)
			continue
		}

		file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
		if err != nil {
			//return location, err
		}
		defer file.Close()
		_, err = io.Copy(file, tarReader)
		if err != nil {
			//return location, err
		}
	}
	return location, nil
}

func installDrivers(location string) {
	if runtime.GOOS == "windows" {
		if _, err := os.Stat(filepath.Join(location, "post_install.bat")); err == nil {
			oscmd := exec.Command(filepath.Join(location, "post_install.bat"))
			TellCommandNotToSpawnShell(oscmd)
			oscmd.Run()
		}
	}
}

func extractBz2(body []byte, location string) (string, error) {
	bodyCopy := make([]byte, len(body))
	copy(bodyCopy, body)
	tarFile := bzip2.NewReader(bytes.NewReader(body))
	tarReader := tar.NewReader(tarFile)

	var dirList []string

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if header.FileInfo().IsDir() {
			dirList = append(dirList, header.Name)
		}
	}

	basedir := findBaseDir(dirList)

	tarFile = bzip2.NewReader(bytes.NewReader(bodyCopy))
	tarReader = tar.NewReader(tarFile)

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			//return location, err
		}

		path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
		info := header.FileInfo()

		if info.IsDir() {
			if err = os.MkdirAll(path, info.Mode()); err != nil {
				return location, err
			}
			continue
		}

		if header.Typeflag == tar.TypeSymlink {
			err = os.Symlink(header.Linkname, path)
			continue
		}

		file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
		if err != nil {
			//return location, err
		}
		defer file.Close()
		_, err = io.Copy(file, tarReader)
		if err != nil {
			//return location, err
		}
	}
	return location, nil
}

func makeExecutable(location string) error {
	location = path.Join(location, "bin")
	files, err := ioutil.ReadDir(location)
	if err != nil {
		return err
	}

	for _, file := range files {
		err = os.Chmod(path.Join(location, file.Name()), 0755)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package command

import (
	"bufio"
	"strings"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/backend"
	"github.com/hashicorp/terraform/helper/wrappedstreams"
	"github.com/hashicorp/terraform/repl"
	"github.com/hashicorp/terraform/tfdiags"

	"github.com/mitchellh/cli"
)

// ConsoleCommand is a Command implementation that applies a Terraform
// configuration and actually builds or changes infrastructure.
type ConsoleCommand struct {
	Meta
}

func (c *ConsoleCommand) Run(args []string) int {
	args, err := c.Meta.process(args, true)
	if err != nil {
		return 1
	}

	cmdFlags := c.Meta.flagSet("console")
	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	configPath, err := ModulePath(cmdFlags.Args())
	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	var diags tfdiags.Diagnostics

	backendConfig, backendDiags := c.loadBackendConfig(configPath)
	diags = diags.Append(backendDiags)
	if diags.HasErrors() {
		c.showDiagnostics(diags)
		return 1
	}

	// Load the backend
	b, backendDiags := c.Backend(&BackendOpts{
		Config: backendConfig,
	})
	diags = diags.Append(backendDiags)
	if backendDiags.HasErrors() {
		c.showDiagnostics(diags)
		return 1
	}

	// We require a local backend
	local, ok := b.(backend.Local)
	if !ok {
		c.showDiagnostics(diags) // in case of any warnings in here
		c.Ui.Error(ErrUnsupportedLocalOp)
		return 1
	}

	// Build the operation
	opReq := c.Operation(b)
	opReq.ConfigDir = configPath
	opReq.ConfigLoader, err = c.initConfigLoader()
	if err != nil {
		diags = diags.Append(err)
		c.showDiagnostics(diags)
		return 1
	}

	// Get the context
	ctx, _, ctxDiags := local.Context(opReq)
	diags = diags.Append(ctxDiags)
	if ctxDiags.HasErrors() {
		c.showDiagnostics(diags)
		return 1
	}

	defer func() {
		err := opReq.StateLocker.Unlock(nil)
		if err != nil {
			c.Ui.Error(err.Error())
		}
	}()

	// Setup the UI so we can output directly to stdout
	ui := &cli.BasicUi{
		Writer:      wrappedstreams.Stdout(),
		ErrorWriter: wrappedstreams.Stderr(),
	}

	// Before we can evaluate expressions, we must compute and populate any
	// derived values (input variables, local values, output values)
	// that are not stored in the persistent state.
	scope, scopeDiags := ctx.Eval(addrs.RootModuleInstance)
	diags = diags.Append(scopeDiags)
	if scope == nil {
		// scope is nil if there are errors so bad that we can't even build a scope.
		// Otherwise, we'll try to eval anyway.
		c.showDiagnostics(diags)
		return 1
	}
	if diags.HasErrors() {
		diags = diags.Append(tfdiags.SimpleWarning("Due to the problems above, some expressions may produce unexpected results."))
	}

	// Before we become interactive we'll show any diagnostics we encountered
	// during initialization, and then afterwards the driver will manage any
	// further diagnostics itself.
	c.showDiagnostics(diags)

	// IO Loop
	session := &repl.Session{
		Scope: scope,
	}

	// Determine if stdin is a pipe. If so, we evaluate directly.
	if c.StdinPiped() {
		return c.modePiped(session, ui)
	}

	return c.modeInteractive(session, ui)
}

func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
	var lastResult string
	scanner := bufio.NewScanner(wrappedstreams.Stdin())
	for scanner.Scan() {
		result, exit, diags := session.Handle(strings.TrimSpace(scanner.Text()))
		if diags.HasErrors() {
			// In piped mode we'll exit immediately on error.
			c.showDiagnostics(diags)
			return 1
		}
		if exit {
			return 0
		}

		// Store the last result
		lastResult = result
	}

	// Output the final result
	ui.Output(lastResult)

	return 0
}

func (c *ConsoleCommand) Help() string {
	helpText := `
Usage: terraform console [options] [DIR]

  Starts an interactive console for experimenting with Terraform
  interpolations.

  This will open an interactive console that you can use to type
  interpolations into and inspect their values. This command loads the
  current state. This lets you explore and test interpolations before
  using them in future configurations.

  This command will never modify your state.

  DIR can be set to a directory with a Terraform state to load. By
  default, this will default to the current working directory.

Options:

  -state=path            Path to read state. Defaults to "terraform.tfstate"

  -var 'foo=bar'         Set a variable in the Terraform configuration. This
                         flag can be set multiple times.

  -var-file=foo          Set variables in the Terraform configuration from
                         a file. If "terraform.tfvars" or any ".auto.tfvars"
                         files are present, they will be automatically loaded.

`
	return strings.TrimSpace(helpText)
}

func (c *ConsoleCommand) Synopsis() string {
	return "Interactive console for Terraform interpolations"
}
<commit_msg>command: Make input variable values available to "terraform console"<commit_after>package command

import (
	"bufio"
	"strings"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/backend"
	"github.com/hashicorp/terraform/helper/wrappedstreams"
	"github.com/hashicorp/terraform/repl"
	"github.com/hashicorp/terraform/tfdiags"

	"github.com/mitchellh/cli"
)

// ConsoleCommand is a Command implementation that applies a Terraform
// configuration and actually builds or changes infrastructure.
type ConsoleCommand struct {
	Meta
}

func (c *ConsoleCommand) Run(args []string) int {
	args, err := c.Meta.process(args, true)
	if err != nil {
		return 1
	}

	cmdFlags := c.Meta.flagSet("console")
	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	configPath, err := ModulePath(cmdFlags.Args())
	if err != nil {
		c.Ui.Error(err.Error())
		return 1
	}

	var diags tfdiags.Diagnostics

	backendConfig, backendDiags := c.loadBackendConfig(configPath)
	diags = diags.Append(backendDiags)
	if diags.HasErrors() {
		c.showDiagnostics(diags)
		return 1
	}

	// Load the backend
	b, backendDiags := c.Backend(&BackendOpts{
		Config: backendConfig,
	})
	diags = diags.Append(backendDiags)
	if backendDiags.HasErrors() {
		c.showDiagnostics(diags)
		return 1
	}

	// We require a local backend
	local, ok := b.(backend.Local)
	if !ok {
		c.showDiagnostics(diags) // in case of any warnings in here
		c.Ui.Error(ErrUnsupportedLocalOp)
		return 1
	}

	// Build the operation
	opReq := c.Operation(b)
	opReq.ConfigDir = configPath
	opReq.ConfigLoader, err = c.initConfigLoader()
	if err != nil {
		diags = diags.Append(err)
		c.showDiagnostics(diags)
		return 1
	}
	{
		var moreDiags tfdiags.Diagnostics
		opReq.Variables, moreDiags = c.collectVariableValues()
		diags = diags.Append(moreDiags)
		if moreDiags.HasErrors() {
			c.showDiagnostics(diags)
			return 1
		}
	}

	// Get the context
	ctx, _, ctxDiags := local.Context(opReq)
	diags = diags.Append(ctxDiags)
	if ctxDiags.HasErrors() {
		c.showDiagnostics(diags)
		return 1
	}

	defer func() {
		err := opReq.StateLocker.Unlock(nil)
		if err != nil {
			c.Ui.Error(err.Error())
		}
	}()

	// Setup the UI so we can output directly to stdout
	ui := &cli.BasicUi{
		Writer:      wrappedstreams.Stdout(),
		ErrorWriter: wrappedstreams.Stderr(),
	}

	// Before we can evaluate expressions, we must compute and populate any
	// derived values (input variables, local values, output values)
	// that are not stored in the persistent state.
	scope, scopeDiags := ctx.Eval(addrs.RootModuleInstance)
	diags = diags.Append(scopeDiags)
	if scope == nil {
		// scope is nil if there are errors so bad that we can't even build a scope.
		// Otherwise, we'll try to eval anyway.
		c.showDiagnostics(diags)
		return 1
	}
	if diags.HasErrors() {
		diags = diags.Append(tfdiags.SimpleWarning("Due to the problems above, some expressions may produce unexpected results."))
	}

	// Before we become interactive we'll show any diagnostics we encountered
	// during initialization, and then afterwards the driver will manage any
	// further diagnostics itself.
	c.showDiagnostics(diags)

	// IO Loop
	session := &repl.Session{
		Scope: scope,
	}

	// Determine if stdin is a pipe. If so, we evaluate directly.
	if c.StdinPiped() {
		return c.modePiped(session, ui)
	}

	return c.modeInteractive(session, ui)
}

func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
	var lastResult string
	scanner := bufio.NewScanner(wrappedstreams.Stdin())
	for scanner.Scan() {
		result, exit, diags := session.Handle(strings.TrimSpace(scanner.Text()))
		if diags.HasErrors() {
			// In piped mode we'll exit immediately on error.
			c.showDiagnostics(diags)
			return 1
		}
		if exit {
			return 0
		}

		// Store the last result
		lastResult = result
	}

	// Output the final result
	ui.Output(lastResult)

	return 0
}

func (c *ConsoleCommand) Help() string {
	helpText := `
Usage: terraform console [options] [DIR]

  Starts an interactive console for experimenting with Terraform
  interpolations.

  This will open an interactive console that you can use to type
  interpolations into and inspect their values. This command loads the
  current state. This lets you explore and test interpolations before
  using them in future configurations.

  This command will never modify your state.

  DIR can be set to a directory with a Terraform state to load. By
  default, this will default to the current working directory.

Options:

  -state=path            Path to read state. Defaults to "terraform.tfstate"

  -var 'foo=bar'         Set a variable in the Terraform configuration. This
                         flag can be set multiple times.

  -var-file=foo          Set variables in the Terraform configuration from
                         a file. If "terraform.tfvars" or any ".auto.tfvars"
                         files are present, they will be automatically loaded.

`
	return strings.TrimSpace(helpText)
}

func (c *ConsoleCommand) Synopsis() string {
	return "Interactive console for Terraform interpolations"
}
<|endoftext|>


<commit_before>package command

import (
	"bufio"
	"bytes"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"
	"unicode"

	"github.com/mitchellh/cli"
	"github.com/mitchellh/colorstring"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/command/format"
	"github.com/hashicorp/terraform/plans"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/terraform"
)

const defaultPeriodicUiTimer = 10 * time.Second
const maxIdLen = 80

type UiHook struct {
	terraform.NilHook

	Colorize        *colorstring.Colorize
	Ui              cli.Ui
	PeriodicUiTimer time.Duration

	l         sync.Mutex
	once      sync.Once
	resources map[string]uiResourceState
	ui        cli.Ui
}

var _ terraform.Hook = (*UiHook)(nil)

// uiResourceState tracks the state of a single resource
type uiResourceState struct {
	DispAddr       string
	IDKey, IDValue string
	Op             uiResourceOp
	Start          time.Time

	DoneCh chan struct{} // To be used for cancellation

	done chan struct{} // used to coordinate tests
}

// uiResourceOp is an enum for operations on a resource
type uiResourceOp byte

const (
	uiResourceUnknown uiResourceOp = iota
	uiResourceCreate
	uiResourceModify
	uiResourceDestroy
	uiResourceRead
)

func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) {
	h.once.Do(h.init)

	dispAddr := addr.String()
	if gen != states.CurrentGen {
		dispAddr = fmt.Sprintf("%s (%s)", dispAddr, gen)
	}

	var operation string
	var op uiResourceOp
	idKey, idValue := format.ObjectValueIDOrName(priorState)
	switch action {
	case plans.Delete:
		operation = "Destroying..."
		op = uiResourceDestroy
	case plans.Create:
		operation = "Creating..."
		op = uiResourceCreate
	case plans.Update:
		operation = "Modifying..."
		op = uiResourceModify
	case plans.Read:
		operation = "Reading..."
		op = uiResourceRead
	default:
		// We don't expect any other actions in here, so anything else is a
		// bug in the caller but we'll ignore it in order to be robust.
		h.ui.Output(fmt.Sprintf("(Unknown action %s for %s)", action, dispAddr))
		return terraform.HookActionContinue, nil
	}

	attrBuf := new(bytes.Buffer)

	// Get all the attributes that are changing, and sort them. Also
	// determine the longest key so that we can align them all.
	keyLen := 0

	// FIXME: This is stubbed out in preparation for rewriting it to use
	// a structural presentation rather than the old-style flatmap one.
	// We just assume no attributes at all for now, pending new code to
	// work with the two cty.Values we are given.
	dAttrs := map[string]terraform.ResourceAttrDiff{}
	keys := make([]string, 0, len(dAttrs))
	for key, _ := range dAttrs {
		keys = append(keys, key)
		if len(key) > keyLen {
			keyLen = len(key)
		}
	}
	sort.Strings(keys)

	// Go through and output each attribute
	for _, attrK := range keys {
		attrDiff := dAttrs[attrK]

		v := attrDiff.New
		u := attrDiff.Old
		if attrDiff.NewComputed {
			v = "<computed>"
		}

		if attrDiff.Sensitive {
			u = "<sensitive>"
			v = "<sensitive>"
		}

		attrBuf.WriteString(fmt.Sprintf(
			"  %s:%s %#v => %#v\n",
			attrK,
			strings.Repeat(" ", keyLen-len(attrK)),
			u,
			v))
	}

	attrString := strings.TrimSpace(attrBuf.String())
	if attrString != "" {
		attrString = "\n  " + attrString
	}

	var stateIdSuffix string
	if idKey != "" && idValue != "" {
		stateIdSuffix = fmt.Sprintf(" [%s=%s]", idKey, idValue)
	} else {
		// Make sure they are both empty so we can deal with this more
		// easily in the other hook methods.
		idKey = ""
		idValue = ""
	}

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: %s%s[reset]%s",
		dispAddr,
		operation,
		stateIdSuffix,
		attrString,
	)))

	key := addr.String()
	uiState := uiResourceState{
		DispAddr: key,
		IDKey:    idKey,
		IDValue:  idValue,
		Op:       op,
		Start:    time.Now().Round(time.Second),
		DoneCh:   make(chan struct{}),
		done:     make(chan struct{}),
	}

	h.l.Lock()
	h.resources[key] = uiState
	h.l.Unlock()

	// Start goroutine that shows progress
	go h.stillApplying(uiState)

	return terraform.HookActionContinue, nil
}

func (h *UiHook) stillApplying(state uiResourceState) {
	defer close(state.done)
	for {
		select {
		case <-state.DoneCh:
			return

		case <-time.After(h.PeriodicUiTimer):
			// Timer up, show status
		}

		var msg string
		switch state.Op {
		case uiResourceModify:
			msg = "Still modifying..."
		case uiResourceDestroy:
			msg = "Still destroying..."
		case uiResourceCreate:
			msg = "Still creating..."
		case uiResourceRead:
			msg = "Still reading..."
		case uiResourceUnknown:
			return
		}

		idSuffix := ""
		if state.IDKey != "" {
			idSuffix = fmt.Sprintf("%s=%s, ", state.IDKey, truncateId(state.IDValue, maxIdLen))
		}

		h.ui.Output(h.Colorize.Color(fmt.Sprintf(
			"[reset][bold]%s: %s [%s%s elapsed][reset]",
			state.DispAddr,
			msg,
			idSuffix,
			time.Now().Round(time.Second).Sub(state.Start),
		)))
	}
}

func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, applyerr error) (terraform.HookAction, error) {

	id := addr.String()

	h.l.Lock()
	state := h.resources[id]
	if state.DoneCh != nil {
		close(state.DoneCh)
	}

	delete(h.resources, id)
	h.l.Unlock()

	var stateIdSuffix string
	if k, v := format.ObjectValueID(newState); k != "" && v != "" {
		stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v)
	}

	var msg string
	switch state.Op {
	case uiResourceModify:
		msg = "Modifications complete"
	case uiResourceDestroy:
		msg = "Destruction complete"
	case uiResourceCreate:
		msg = "Creation complete"
	case uiResourceRead:
		msg = "Read complete"
	case uiResourceUnknown:
		return terraform.HookActionContinue, nil
	}

	if applyerr != nil {
		// Errors are collected and printed in ApplyCommand, no need to duplicate
		return terraform.HookActionContinue, nil
	}

	colorized := h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: %s after %s%s[reset]",
		addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix))

	h.ui.Output(colorized)

	return terraform.HookActionContinue, nil
}

func (h *UiHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (terraform.HookAction, error) {
	return terraform.HookActionContinue, nil
}

func (h *UiHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) {
	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: Provisioning with '%s'...[reset]",
		addr, typeName,
	)))
	return terraform.HookActionContinue, nil
}

func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) {
	var buf bytes.Buffer
	buf.WriteString(h.Colorize.Color("[reset]"))

	prefix := fmt.Sprintf("%s (%s): ", addr, typeName)
	s := bufio.NewScanner(strings.NewReader(msg))
	s.Split(scanLines)
	for s.Scan() {
		line := strings.TrimRightFunc(s.Text(), unicode.IsSpace)
		if line != "" {
			buf.WriteString(fmt.Sprintf("%s%s\n", prefix, line))
		}
	}

	h.ui.Output(strings.TrimSpace(buf.String()))
}

func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (terraform.HookAction, error) {
	h.once.Do(h.init)

	var stateIdSuffix string
	if k, v := format.ObjectValueID(priorState); k != "" && v != "" {
		stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v)
	}

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: Refreshing state...%s",
		addr, stateIdSuffix)))
	return terraform.HookActionContinue, nil
}

func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (terraform.HookAction, error) {
	h.once.Do(h.init)

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: Importing from ID %q...",
		addr, importID,
	)))
	return terraform.HookActionContinue, nil
}

func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (terraform.HookAction, error) {
	h.once.Do(h.init)

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold][green]%s: Import prepared!", addr)))
	for _, s := range imported {
		h.ui.Output(h.Colorize.Color(fmt.Sprintf(
			"[reset][green]  Prepared %s for import",
			s.TypeName,
		)))
	}

	return terraform.HookActionContinue, nil
}

func (h *UiHook) init() {
	if h.Colorize == nil {
		panic("colorize not given")
	}
	if h.PeriodicUiTimer == 0 {
		h.PeriodicUiTimer = defaultPeriodicUiTimer
	}

	h.resources = make(map[string]uiResourceState)

	// Wrap the ui so that it is safe for concurrency regardless of the
	// underlying reader/writer that is in place.
	h.ui = &cli.ConcurrentUi{Ui: h.Ui}
}

// scanLines is basically copied from the Go standard library except
// we've modified it to also fine `\r`.
func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	if i := bytes.IndexByte(data, '\r'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

func truncateId(id string, maxLen int) string {
	// Note that the id may contain multibyte characters.
	// We need to truncate it to maxLen characters, not maxLen bytes.
	rid := []rune(id)
	totalLength := len(rid)
	if totalLength <= maxLen {
		return id
	}
	if maxLen < 5 {
		// We don't shorten to less than 5 chars
		// as that would be pointless with ... (3 chars)
		maxLen = 5
	}

	dots := []rune("...")
	partLen := maxLen / 2

	leftIdx := partLen - 1
	leftPart := rid[0:leftIdx]

	rightIdx := totalLength - partLen - 1

	overlap := maxLen - (partLen*2 + len(dots))
	if overlap < 0 {
		rightIdx -= overlap
	}

	rightPart := rid[rightIdx:]

	return string(leftPart) + string(dots) + string(rightPart)
}
<commit_msg>command: Remove stubbed-out apply diff output code<commit_after>package command

import (
	"bufio"
	"bytes"
	"fmt"
	"strings"
	"sync"
	"time"
	"unicode"

	"github.com/mitchellh/cli"
	"github.com/mitchellh/colorstring"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/command/format"
	"github.com/hashicorp/terraform/plans"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/terraform"
)

const defaultPeriodicUiTimer = 10 * time.Second
const maxIdLen = 80

type UiHook struct {
	terraform.NilHook

	Colorize        *colorstring.Colorize
	Ui              cli.Ui
	PeriodicUiTimer time.Duration

	l         sync.Mutex
	once      sync.Once
	resources map[string]uiResourceState
	ui        cli.Ui
}

var _ terraform.Hook = (*UiHook)(nil)

// uiResourceState tracks the state of a single resource
type uiResourceState struct {
	DispAddr       string
	IDKey, IDValue string
	Op             uiResourceOp
	Start          time.Time

	DoneCh chan struct{} // To be used for cancellation

	done chan struct{} // used to coordinate tests
}

// uiResourceOp is an enum for operations on a resource
type uiResourceOp byte

const (
	uiResourceUnknown uiResourceOp = iota
	uiResourceCreate
	uiResourceModify
	uiResourceDestroy
	uiResourceRead
)

func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) {
	h.once.Do(h.init)

	dispAddr := addr.String()
	if gen != states.CurrentGen {
		dispAddr = fmt.Sprintf("%s (%s)", dispAddr, gen)
	}

	var operation string
	var op uiResourceOp
	idKey, idValue := format.ObjectValueIDOrName(priorState)
	switch action {
	case plans.Delete:
		operation = "Destroying..."
		op = uiResourceDestroy
	case plans.Create:
		operation = "Creating..."
		op = uiResourceCreate
	case plans.Update:
		operation = "Modifying..."
		op = uiResourceModify
	case plans.Read:
		operation = "Reading..."
		op = uiResourceRead
	default:
		// We don't expect any other actions in here, so anything else is a
		// bug in the caller but we'll ignore it in order to be robust.
		h.ui.Output(fmt.Sprintf("(Unknown action %s for %s)", action, dispAddr))
		return terraform.HookActionContinue, nil
	}

	var stateIdSuffix string
	if idKey != "" && idValue != "" {
		stateIdSuffix = fmt.Sprintf(" [%s=%s]", idKey, idValue)
	} else {
		// Make sure they are both empty so we can deal with this more
		// easily in the other hook methods.
		idKey = ""
		idValue = ""
	}

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: %s%s[reset]",
		dispAddr,
		operation,
		stateIdSuffix,
	)))

	key := addr.String()
	uiState := uiResourceState{
		DispAddr: key,
		IDKey:    idKey,
		IDValue:  idValue,
		Op:       op,
		Start:    time.Now().Round(time.Second),
		DoneCh:   make(chan struct{}),
		done:     make(chan struct{}),
	}

	h.l.Lock()
	h.resources[key] = uiState
	h.l.Unlock()

	// Start goroutine that shows progress
	go h.stillApplying(uiState)

	return terraform.HookActionContinue, nil
}

func (h *UiHook) stillApplying(state uiResourceState) {
	defer close(state.done)
	for {
		select {
		case <-state.DoneCh:
			return

		case <-time.After(h.PeriodicUiTimer):
			// Timer up, show status
		}

		var msg string
		switch state.Op {
		case uiResourceModify:
			msg = "Still modifying..."
		case uiResourceDestroy:
			msg = "Still destroying..."
		case uiResourceCreate:
			msg = "Still creating..."
		case uiResourceRead:
			msg = "Still reading..."
		case uiResourceUnknown:
			return
		}

		idSuffix := ""
		if state.IDKey != "" {
			idSuffix = fmt.Sprintf("%s=%s, ", state.IDKey, truncateId(state.IDValue, maxIdLen))
		}

		h.ui.Output(h.Colorize.Color(fmt.Sprintf(
			"[reset][bold]%s: %s [%s%s elapsed][reset]",
			state.DispAddr,
			msg,
			idSuffix,
			time.Now().Round(time.Second).Sub(state.Start),
		)))
	}
}

func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, applyerr error) (terraform.HookAction, error) {

	id := addr.String()

	h.l.Lock()
	state := h.resources[id]
	if state.DoneCh != nil {
		close(state.DoneCh)
	}

	delete(h.resources, id)
	h.l.Unlock()

	var stateIdSuffix string
	if k, v := format.ObjectValueID(newState); k != "" && v != "" {
		stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v)
	}

	var msg string
	switch state.Op {
	case uiResourceModify:
		msg = "Modifications complete"
	case uiResourceDestroy:
		msg = "Destruction complete"
	case uiResourceCreate:
		msg = "Creation complete"
	case uiResourceRead:
		msg = "Read complete"
	case uiResourceUnknown:
		return terraform.HookActionContinue, nil
	}

	if applyerr != nil {
		// Errors are collected and printed in ApplyCommand, no need to duplicate
		return terraform.HookActionContinue, nil
	}

	colorized := h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: %s after %s%s[reset]",
		addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix))

	h.ui.Output(colorized)

	return terraform.HookActionContinue, nil
}

func (h *UiHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (terraform.HookAction, error) {
	return terraform.HookActionContinue, nil
}

func (h *UiHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) {
	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: Provisioning with '%s'...[reset]",
		addr, typeName,
	)))
	return terraform.HookActionContinue, nil
}

func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) {
	var buf bytes.Buffer
	buf.WriteString(h.Colorize.Color("[reset]"))

	prefix := fmt.Sprintf("%s (%s): ", addr, typeName)
	s := bufio.NewScanner(strings.NewReader(msg))
	s.Split(scanLines)
	for s.Scan() {
		line := strings.TrimRightFunc(s.Text(), unicode.IsSpace)
		if line != "" {
			buf.WriteString(fmt.Sprintf("%s%s\n", prefix, line))
		}
	}

	h.ui.Output(strings.TrimSpace(buf.String()))
}

func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (terraform.HookAction, error) {
	h.once.Do(h.init)

	var stateIdSuffix string
	if k, v := format.ObjectValueID(priorState); k != "" && v != "" {
		stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v)
	}

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: Refreshing state...%s",
		addr, stateIdSuffix)))
	return terraform.HookActionContinue, nil
}

func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (terraform.HookAction, error) {
	h.once.Do(h.init)

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold]%s: Importing from ID %q...",
		addr, importID,
	)))
	return terraform.HookActionContinue, nil
}

func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (terraform.HookAction, error) {
	h.once.Do(h.init)

	h.ui.Output(h.Colorize.Color(fmt.Sprintf(
		"[reset][bold][green]%s: Import prepared!", addr)))
	for _, s := range imported {
		h.ui.Output(h.Colorize.Color(fmt.Sprintf(
			"[reset][green]  Prepared %s for import",
			s.TypeName,
		)))
	}

	return terraform.HookActionContinue, nil
}

func (h *UiHook) init() {
	if h.Colorize == nil {
		panic("colorize not given")
	}
	if h.PeriodicUiTimer == 0 {
		h.PeriodicUiTimer = defaultPeriodicUiTimer
	}

	h.resources = make(map[string]uiResourceState)

	// Wrap the ui so that it is safe for concurrency regardless of the
	// underlying reader/writer that is in place.
	h.ui = &cli.ConcurrentUi{Ui: h.Ui}
}

// scanLines is basically copied from the Go standard library except
// we've modified it to also fine `\r`.
func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	if i := bytes.IndexByte(data, '\r'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

func truncateId(id string, maxLen int) string {
	// Note that the id may contain multibyte characters.
	// We need to truncate it to maxLen characters, not maxLen bytes.
	rid := []rune(id)
	totalLength := len(rid)
	if totalLength <= maxLen {
		return id
	}
	if maxLen < 5 {
		// We don't shorten to less than 5 chars
		// as that would be pointless with ... (3 chars)
		maxLen = 5
	}

	dots := []rune("...")
	partLen := maxLen / 2

	leftIdx := partLen - 1
	leftPart := rid[0:leftIdx]

	rightIdx := totalLength - partLen - 1

	overlap := maxLen - (partLen*2 + len(dots))
	if overlap < 0 {
		rightIdx -= overlap
	}

	rightPart := rid[rightIdx:]

	return string(leftPart) + string(dots) + string(rightPart)
}
<|endoftext|>