<commit_before>package dockerfile

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

type Statement interface{}

type Dockerfile struct {
	Source     string
	Statements []Statement
	From       *From
}

type Comment struct {
	Line  int
	Lines string
}

type From struct {
	Line  int
	Image string
}

type Maintainer struct {
	Line int
	Name string
}

type Run struct {
	Line    int
	Command string
}

type Label struct {
	Line  int
	Pairs string
}

type Expose struct {
	Line  int
	Ports string
}

type Env struct {
	Line  int
	Pairs string
}

type Add struct {
	Line  int
	Paths string
}

type Copy struct {
	Line  int
	Paths string
}

type Entrypoint struct {
	Line    int
	CmdLine string
}

type Volume struct {
	Line  int
	Names string
}

type User struct {
	Line int
	Name string
}

type Workdir struct {
	Line int
	Path string
}

type Arg struct {
	Line int
	Name string
}

type Onbuild struct {
	Line  			int
	Instruction string
}

type Stopsignal struct {
	Line   int
	Signal string
}

type Healthcheck struct {
	Line    int
	Command string
}

type Shell struct {
	Line    int
	CmdLine string
}

type Cmd struct {
	Line    int
	CmdLine string
}

var (
	statements = map[string]func(file *Dockerfile, line int, tokens *Tokens) Statement{
		"FROM": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			file.From = &From{Line: line, Image: tokens.NextLine()}
			return file.From
		},
		"MAINTAINER": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Maintainer{Line: line, Name: tokens.NextLine()}
		},
		"RUN": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Run{Line: line, Command: tokens.NextLine()}
		},
		"CMD": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Cmd{Line: line, CmdLine: tokens.NextLine()}
		},
		"LABEL": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Label{Line: line, Pairs: tokens.NextLine()}
		},
		"EXPOSE": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Expose{Line: line, Ports: tokens.NextLine()}
		},
		"ENV": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Env{Line: line, Pairs: tokens.NextLine()}
		},
		"ADD": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Add{Line: line, Paths: tokens.NextLine()}
		},
		"COPY": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Copy{Line: line, Paths: tokens.NextLine()}
		},
		"ENTRYPOINT": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Entrypoint{Line: line, CmdLine: tokens.NextLine()}
		},
		"VOLUME": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Volume{Line: line, Names: tokens.NextLine()}
		},
		"USER": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &User{Line: line, Name: tokens.NextLine()}
		},
		"WORKDIR": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Workdir{Line: line, Path: tokens.NextLine()}
		},
		"ARG": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Arg{Line: line, Name: tokens.NextLine()}
		},
		"ONBUILD": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Onbuild{Line: line, Instruction: tokens.NextLine()}
		},
		"STOPSIGNAL": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Stopsignal{Line: line, Signal: tokens.NextLine()}
		},
		"HEALTHCHECK": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Healthcheck{Line: line, Command: tokens.NextLine()}
		},
		"SHELL": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Shell{Line: line, CmdLine: tokens.NextLine()}
		},
		"#": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Comment{Line: line, Lines: tokens.NextComment()}
		},
	}
)

// Parses a dockerfile from a reader. Returns an error if
// an unknown token is encountered.
//
// See https://docs.docker.com/engine/reference/builder/
func Parse(input io.Reader, file *Dockerfile, source ...string) (err error) {
	tokens := NewTokens(input)

	for tokens.HasNext {
		token := tokens.NextToken()

		if "" == token {
			continue
		} else if statement, ok := statements[token]; ok {
			file.Statements = append(file.Statements, statement(file, tokens.Line, tokens))
		} else {
			return fmt.Errorf("Cannot handle token `%s` on line %d", token, tokens.Line)
		}
	}

	if len(source) > 0 {
		file.Source = source[0]
	} else {
		file.Source = fmt.Sprintf("%T", input)
	}

	return nil
}

// Parses a dockerfile from a file. Returns an error if
// the file cannot be opened, is a directory or when parsing
// encounters an error
func ParseFile(name string, file *Dockerfile) (err error) {
	stat, err := os.Stat(name)
	if err != nil {
		return err
	}

	if stat.IsDir() {
		return fmt.Errorf("The given file `%s` is a directory\n", name)
	}

	input, err := os.Open(name)
	if err != nil {
		return err
	}

	defer input.Close()
	return Parse(bufio.NewReader(input), file, name)
}

// Extends parser. Example:
//
// 		type Include struct {
// 		  Line      int
// 		  Reference string
// 		}
//
// 		Extend("INCLUDE", func(file *Dockerfile, line int, tokens *Tokens) Statement {
// 		  return &Include{Line: line, Reference: tokens.NextLine()}
// 		})
//
func Extend(name string, extension func(file *Dockerfile, line int, tokens *Tokens) Statement) {
	statements[name] = extension
}<commit_msg>QA: Apidocs<commit_after>package dockerfile

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

type Statement interface{}

type Dockerfile struct {
	Source     string
	Statements []Statement
	From       *From
}

type Comment struct {
	Line  int
	Lines string
}

type From struct {
	Line  int
	Image string
}

type Maintainer struct {
	Line int
	Name string
}

type Run struct {
	Line    int
	Command string
}

type Label struct {
	Line  int
	Pairs string
}

type Expose struct {
	Line  int
	Ports string
}

type Env struct {
	Line  int
	Pairs string
}

type Add struct {
	Line  int
	Paths string
}

type Copy struct {
	Line  int
	Paths string
}

type Entrypoint struct {
	Line    int
	CmdLine string
}

type Volume struct {
	Line  int
	Names string
}

type User struct {
	Line int
	Name string
}

type Workdir struct {
	Line int
	Path string
}

type Arg struct {
	Line int
	Name string
}

type Onbuild struct {
	Line  			int
	Instruction string
}

type Stopsignal struct {
	Line   int
	Signal string
}

type Healthcheck struct {
	Line    int
	Command string
}

type Shell struct {
	Line    int
	CmdLine string
}

type Cmd struct {
	Line    int
	CmdLine string
}

// Builtins (v1.12), see https://docs.docker.com/engine/reference/builder/
var (
	statements = map[string]func(file *Dockerfile, line int, tokens *Tokens) Statement{
		"FROM": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			file.From = &From{Line: line, Image: tokens.NextLine()}
			return file.From
		},
		"MAINTAINER": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Maintainer{Line: line, Name: tokens.NextLine()}
		},
		"RUN": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Run{Line: line, Command: tokens.NextLine()}
		},
		"CMD": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Cmd{Line: line, CmdLine: tokens.NextLine()}
		},
		"LABEL": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Label{Line: line, Pairs: tokens.NextLine()}
		},
		"EXPOSE": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Expose{Line: line, Ports: tokens.NextLine()}
		},
		"ENV": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Env{Line: line, Pairs: tokens.NextLine()}
		},
		"ADD": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Add{Line: line, Paths: tokens.NextLine()}
		},
		"COPY": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Copy{Line: line, Paths: tokens.NextLine()}
		},
		"ENTRYPOINT": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Entrypoint{Line: line, CmdLine: tokens.NextLine()}
		},
		"VOLUME": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Volume{Line: line, Names: tokens.NextLine()}
		},
		"USER": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &User{Line: line, Name: tokens.NextLine()}
		},
		"WORKDIR": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Workdir{Line: line, Path: tokens.NextLine()}
		},
		"ARG": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Arg{Line: line, Name: tokens.NextLine()}
		},
		"ONBUILD": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Onbuild{Line: line, Instruction: tokens.NextLine()}
		},
		"STOPSIGNAL": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Stopsignal{Line: line, Signal: tokens.NextLine()}
		},
		"HEALTHCHECK": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Healthcheck{Line: line, Command: tokens.NextLine()}
		},
		"SHELL": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Shell{Line: line, CmdLine: tokens.NextLine()}
		},
		"#": func(file *Dockerfile, line int, tokens *Tokens) Statement {
			return &Comment{Line: line, Lines: tokens.NextComment()}
		},
	}
)

// Parses a dockerfile from a reader. Returns an error if
// an unknown token is encountered.
func Parse(input io.Reader, file *Dockerfile, source ...string) (err error) {
	tokens := NewTokens(input)

	for tokens.HasNext {
		token := tokens.NextToken()

		if "" == token {
			continue
		} else if statement, ok := statements[token]; ok {
			file.Statements = append(file.Statements, statement(file, tokens.Line, tokens))
		} else {
			return fmt.Errorf("Cannot handle token `%s` on line %d", token, tokens.Line)
		}
	}

	if len(source) > 0 {
		file.Source = source[0]
	} else {
		file.Source = fmt.Sprintf("%T", input)
	}

	return nil
}

// Parses a dockerfile from a file. Returns an error if
// the file cannot be opened, is a directory or when parsing
// encounters an error
func ParseFile(name string, file *Dockerfile) (err error) {
	stat, err := os.Stat(name)
	if err != nil {
		return err
	}

	if stat.IsDir() {
		return fmt.Errorf("The given file `%s` is a directory\n", name)
	}

	input, err := os.Open(name)
	if err != nil {
		return err
	}

	defer input.Close()
	return Parse(bufio.NewReader(input), file, name)
}

// Extends parser. Example:
//
// 		type Include struct {
// 		  Line      int
// 		  Reference string
// 		}
//
// 		Extend("INCLUDE", func(file *Dockerfile, line int, tokens *Tokens) Statement {
// 		  return &Include{Line: line, Reference: tokens.NextLine()}
// 		})
//
func Extend(name string, extension func(file *Dockerfile, line int, tokens *Tokens) Statement) {
	statements[name] = extension
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
)

// Upgrade the package cache and update the installed packages (using apt).
func UpdatePackages() *ShellCommand {
	return And("apt-get update", "DEBIAN_FRONTEND=noninteractive apt-get upgrade -y")
}

// Install the given packages using apt-get. At least one package must be given (pkgs can be left empty).
func InstallPackages(pkg string, pkgs ...string) *ShellCommand {
	return &ShellCommand{
		Command: fmt.Sprintf("DEBIAN_FRONTEND=noninteractive apt-get install -y %s %s", pkg, strings.Join(pkgs, " ")),
	}
}

// PinPackage pins package via dpkg --set-selections
func PinPackage(name string) *ShellCommand {
	return Shell(fmt.Sprintf(`echo "%s hold" | dpkg --set-selections`, name))
}

// StartOrRestart starts or restarts a service configured with upstart
func StartOrRestart(service string) *ShellCommand {
	return Shell(fmt.Sprintf("if status %s | grep running; then restart %s ; else start %s; fi", service, service, service))
}
<commit_msg>added support for apt pkg update for selected repos<commit_after>package main

import (
	"fmt"
	"strings"
)

// Upgrade the package cache and update the installed packages (using apt).
func UpdatePackages() *ShellCommand {
	return And("apt-get update", "DEBIAN_FRONTEND=noninteractive apt-get upgrade -y")
}

// Update the package cache for a given repository only. Repo selection is done
// via the name of apt's configuration file taken from /etc/apt/sources.list.d.
// This is much faster if you just added a repo and want to install software as
// you need not update all other packages too (which most probably happened
// just recently during provisioning).
func UpdateSelectedRepoPackages(repoConfigPath string) *ShellCommand {
	return &ShellCommand{
		Command: fmt.Sprintf(
			`apt-get update -o Dir::Etc::sourcelist="sources.list.d/%s" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"`,
			repoConfigPath,
		),
	}
}

// Install the given packages using apt-get. At least one package must be given (pkgs can be left empty).
func InstallPackages(pkg string, pkgs ...string) *ShellCommand {
	return &ShellCommand{
		Command: fmt.Sprintf("DEBIAN_FRONTEND=noninteractive apt-get install -y %s %s", pkg, strings.Join(pkgs, " ")),
	}
}

// PinPackage pins package via dpkg --set-selections
func PinPackage(name string) *ShellCommand {
	return Shell(fmt.Sprintf(`echo "%s hold" | dpkg --set-selections`, name))
}

// StartOrRestart starts or restarts a service configured with upstart
func StartOrRestart(service string) *ShellCommand {
	return Shell(fmt.Sprintf("if status %s | grep running; then restart %s ; else start %s; fi", service, service, service))
}
<|endoftext|>


<commit_before>package terraform

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/cloudfoundry/bosh-bootloader/fileio"
	"github.com/cloudfoundry/bosh-bootloader/storage"
)

var redactedError = "Some output has been redacted, use `bbl latest-error` to see it or run again with --debug for additional debug output"

type Executor struct {
	cmd        terraformCmd
	stateStore stateStore
	fs         fs
	debug      bool
}

type tfOutput struct {
	Sensitive bool
	Type      string
	Value     interface{}
}

type terraformCmd interface {
	Run(stdout io.Writer, args []string, debug bool) error
}

type stateStore interface {
	GetTerraformDir() (string, error)
	GetVarsDir() (string, error)
}

type fs interface {
	fileio.FileWriter
	fileio.DirReader
	fileio.Stater
}

func NewExecutor(cmd terraformCmd, stateStore stateStore, fs fs, debug bool) Executor {
	return Executor{
		cmd:        cmd,
		stateStore: stateStore,
		fs:         fs,
		debug:      debug,
	}
}

func (e Executor) Setup(template string, input map[string]interface{}) error {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}

	err = e.fs.WriteFile(filepath.Join(terraformDir, "bbl-template.tf"), []byte(template), storage.StateMode)
	if err != nil {
		return fmt.Errorf("Write terraform template: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return fmt.Errorf("Get vars dir: %s", err)
	}

	err = os.MkdirAll(filepath.Join(terraformDir, ".terraform"), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Create .terraform directory: %s", err)
	}

	err = e.fs.WriteFile(filepath.Join(terraformDir, ".terraform", ".gitignore"), []byte("*\n"), storage.StateMode)
	if err != nil {
		return fmt.Errorf("Write .gitignore for terraform binaries: %s", err)
	}

	err = e.fs.WriteFile(filepath.Join(varsDir, "bbl.tfvars"), []byte(formatVars(input)), storage.StateMode)
	if err != nil {
		return fmt.Errorf("Write terraform vars: %s", err)
	}

	return nil
}

func formatVars(inputs map[string]interface{}) string {
	formattedVars := ""
	for name, value := range inputs {
		if vString, ok := value.(string); ok {
			vString = fmt.Sprintf(`"%s"`, vString)
			if strings.Contains(vString, "\n") {
				vString = strings.Replace(vString, "\n", "\\n", -1)
			}
			value = vString
		} else if valList, ok := value.([]string); ok {
			value = fmt.Sprintf(`["%s"]`, strings.Join(valList, `","`))
		}
		formattedVars = fmt.Sprintf("%s\n%s=%s", formattedVars, name, value)
	}
	return formattedVars
}

func (e Executor) runTFCommand(args []string) error {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return fmt.Errorf("Get vars dir: %s", err)
	}
	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")
	args = append(args,
		"-state", tfStatePath,
	)

	varsFiles, err := e.fs.ReadDir(varsDir)
	if err != nil {
		return fmt.Errorf("Read contents of vars directory: %s", err) // not tested
	}
	for _, file := range varsFiles {
		if strings.HasSuffix(file.Name(), ".tfvars") {
			args = append(args,
				"-var-file", filepath.Join(varsDir, file.Name()),
			)
		}
	}

	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}
	// TODO: test this after things pass-ish and we fix cmd
	args = append(args, terraformDir)

	err = e.cmd.Run(os.Stdout, args, e.debug)
	if err != nil {
		if e.debug {
			return err
		} else {
			return fmt.Errorf(redactedError)
		}
	}

	return nil
}

func (e Executor) Init() error {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, []string{"init", terraformDir}, e.debug)
	if err != nil {
		return fmt.Errorf("Run terraform init: %s", err)
	}

	return nil
}

func (e Executor) Apply(credentials map[string]string) error {
	args := []string{"apply", "--auto-approve"}
	for key, value := range credentials {
		arg := fmt.Sprintf("%s=%s", key, value)
		args = append(args, "-var", arg)
	}
	return e.runTFCommand(args)
}

func (e Executor) Destroy(credentials map[string]string) error {
	args := []string{"destroy", "-force"}
	for key, value := range credentials {
		arg := fmt.Sprintf("%s=%s", key, value)
		args = append(args, "-var", arg)
	}
	return e.runTFCommand(args)
}

func (e Executor) Version() (string, error) {
	buffer := bytes.NewBuffer([]byte{})
	err := e.cmd.Run(buffer, []string{"version"}, true)
	if err != nil {
		return "", err
	}
	versionOutput := buffer.String()
	regex := regexp.MustCompile(`\d+.\d+.\d+`)

	version := regex.FindString(versionOutput)
	if version == "" {
		return "", errors.New("Terraform version could not be parsed")
	}

	return version, nil
}

func (e Executor) Output(outputName string) (string, error) {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, []string{"init", terraformDir}, e.debug)
	if err != nil {
		return "", fmt.Errorf("Run terraform init in terraform dir: %s", err)
	}

	args := []string{"output", outputName, "-state", filepath.Join(varsDir, "terraform.tfstate"), terraformDir}
	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, args, true)
	if err != nil {
		return "", fmt.Errorf("Run terraform output -state: %s", err)
	}

	return strings.TrimSuffix(buffer.String(), "\n"), nil
}

func (e Executor) Outputs() (map[string]interface{}, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Get vars dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, []string{"init", varsDir}, false)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform init in vars dir: %s", err)
	}

	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, []string{"output", "--json", "-state", filepath.Join(varsDir, "terraform.tfstate")}, true)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform output --json in vars dir: %s", err)
	}

	tfOutputs := map[string]tfOutput{}
	err = json.Unmarshal(buffer.Bytes(), &tfOutputs)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Unmarshal terraform output: %s", err)
	}

	outputs := map[string]interface{}{}
	for tfKey, tfValue := range tfOutputs {
		outputs[tfKey] = tfValue.Value
	}

	return outputs, nil
}

func (e Executor) IsPaved() (bool, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return false, fmt.Errorf("Get vars dir: %s", err)
	}

	if _, err := e.fs.Stat(filepath.Join(varsDir, "terraform.tfstate")); err != nil {
		return false, nil
	} else {
		return true, nil
	}
}
<commit_msg>Clean up terraform executor.<commit_after>package terraform

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/cloudfoundry/bosh-bootloader/fileio"
	"github.com/cloudfoundry/bosh-bootloader/storage"
)

var redactedError = "Some output has been redacted, use `bbl latest-error` to see it or run again with --debug for additional debug output"

type Executor struct {
	cmd        terraformCmd
	stateStore stateStore
	fs         fs
	debug      bool
}

type tfOutput struct {
	Sensitive bool
	Type      string
	Value     interface{}
}

type terraformCmd interface {
	Run(stdout io.Writer, args []string, debug bool) error
}

type stateStore interface {
	GetTerraformDir() (string, error)
	GetVarsDir() (string, error)
}

type fs interface {
	fileio.FileWriter
	fileio.DirReader
	fileio.Stater
}

func NewExecutor(cmd terraformCmd, stateStore stateStore, fs fs, debug bool) Executor {
	return Executor{
		cmd:        cmd,
		stateStore: stateStore,
		fs:         fs,
		debug:      debug,
	}
}

func (e Executor) Setup(template string, input map[string]interface{}) error {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}

	err = e.fs.WriteFile(filepath.Join(terraformDir, "bbl-template.tf"), []byte(template), storage.StateMode)
	if err != nil {
		return fmt.Errorf("Write terraform template: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return fmt.Errorf("Get vars dir: %s", err)
	}

	err = os.MkdirAll(filepath.Join(terraformDir, ".terraform"), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Create .terraform directory: %s", err)
	}

	err = e.fs.WriteFile(filepath.Join(terraformDir, ".terraform", ".gitignore"), []byte("*\n"), storage.StateMode)
	if err != nil {
		return fmt.Errorf("Write .gitignore for terraform binaries: %s", err)
	}

	err = e.fs.WriteFile(filepath.Join(varsDir, "bbl.tfvars"), []byte(formatVars(input)), storage.StateMode)
	if err != nil {
		return fmt.Errorf("Write terraform vars: %s", err)
	}

	return nil
}

func formatVars(inputs map[string]interface{}) string {
	formattedVars := ""
	for name, value := range inputs {
		if vString, ok := value.(string); ok {
			vString = fmt.Sprintf(`"%s"`, vString)
			if strings.Contains(vString, "\n") {
				vString = strings.Replace(vString, "\n", "\\n", -1)
			}
			value = vString
		} else if valList, ok := value.([]string); ok {
			value = fmt.Sprintf(`["%s"]`, strings.Join(valList, `","`))
		}
		formattedVars = fmt.Sprintf("%s\n%s=%s", formattedVars, name, value)
	}
	return formattedVars
}

func (e Executor) runTFCommand(args []string) error {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return fmt.Errorf("Get vars dir: %s", err)
	}

	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")
	args = append(args, "-state", tfStatePath)

	varsFiles, err := e.fs.ReadDir(varsDir)
	if err != nil {
		return fmt.Errorf("Read contents of vars directory: %s", err)
	}

	for _, file := range varsFiles {
		if strings.HasSuffix(file.Name(), ".tfvars") {
			args = append(args, "-var-file", filepath.Join(varsDir, file.Name()))
		}
	}

	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}
	// TODO: test this after things pass-ish and we fix cmd
	args = append(args, terraformDir)

	err = e.cmd.Run(os.Stdout, args, e.debug)
	if err != nil {
		if e.debug {
			return err
		}
		return fmt.Errorf(redactedError)
	}

	return nil
}

func (e Executor) Init() error {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, []string{"init", terraformDir}, e.debug)
	if err != nil {
		return fmt.Errorf("Run terraform init: %s", err)
	}

	return nil
}

func (e Executor) Apply(credentials map[string]string) error {
	args := []string{"apply", "--auto-approve"}
	for key, value := range credentials {
		arg := fmt.Sprintf("%s=%s", key, value)
		args = append(args, "-var", arg)
	}
	return e.runTFCommand(args)
}

func (e Executor) Destroy(credentials map[string]string) error {
	args := []string{"destroy", "-force"}
	for key, value := range credentials {
		arg := fmt.Sprintf("%s=%s", key, value)
		args = append(args, "-var", arg)
	}
	return e.runTFCommand(args)
}

func (e Executor) Version() (string, error) {
	buffer := bytes.NewBuffer([]byte{})
	err := e.cmd.Run(buffer, []string{"version"}, true)
	if err != nil {
		return "", err
	}
	versionOutput := buffer.String()
	regex := regexp.MustCompile(`\d+.\d+.\d+`)

	version := regex.FindString(versionOutput)
	if version == "" {
		return "", errors.New("Terraform version could not be parsed")
	}

	return version, nil
}

func (e Executor) Output(outputName string) (string, error) {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, []string{"init", terraformDir}, e.debug)
	if err != nil {
		return "", fmt.Errorf("Run terraform init in terraform dir: %s", err)
	}

	args := []string{"output", outputName, "-state", filepath.Join(varsDir, "terraform.tfstate"), terraformDir}
	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, args, true)
	if err != nil {
		return "", fmt.Errorf("Run terraform output -state: %s", err)
	}

	return strings.TrimSuffix(buffer.String(), "\n"), nil
}

func (e Executor) Outputs() (map[string]interface{}, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Get vars dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, []string{"init", varsDir}, false)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform init in vars dir: %s", err)
	}

	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, []string{"output", "--json", "-state", filepath.Join(varsDir, "terraform.tfstate")}, true)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform output --json in vars dir: %s", err)
	}

	tfOutputs := map[string]tfOutput{}
	err = json.Unmarshal(buffer.Bytes(), &tfOutputs)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Unmarshal terraform output: %s", err)
	}

	outputs := map[string]interface{}{}
	for tfKey, tfValue := range tfOutputs {
		outputs[tfKey] = tfValue.Value
	}

	return outputs, nil
}

func (e Executor) IsPaved() (bool, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return false, fmt.Errorf("Get vars dir: %s", err)
	}

	if _, err := e.fs.Stat(filepath.Join(varsDir, "terraform.tfstate")); err != nil {
		return false, nil
	} else {
		return true, nil
	}
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/config"
	"github.com/mitchellh/copystructure"
	"github.com/mitchellh/reflectwalk"
)

// ResourceProvisionerConfig is used to pair a provisioner
// with its provided configuration. This allows us to use singleton
// instances of each ResourceProvisioner and to keep the relevant
// configuration instead of instantiating a new Provisioner for each
// resource.
type ResourceProvisionerConfig struct {
	Type        string
	Provisioner ResourceProvisioner
	Config      *ResourceConfig
	RawConfig   *config.RawConfig
	ConnInfo    *config.RawConfig
}

// Resource encapsulates a resource, its configuration, its provider,
// its current state, and potentially a desired diff from the state it
// wants to reach.
type Resource struct {
	// These are all used by the new EvalNode stuff.
	Name       string
	Type       string
	CountIndex int

	// These aren't really used anymore anywhere, but we keep them around
	// since we haven't done a proper cleanup yet.
	Id           string
	Info         *InstanceInfo
	Config       *ResourceConfig
	Dependencies []string
	Diff         *InstanceDiff
	Provider     ResourceProvider
	State        *InstanceState
	Provisioners []*ResourceProvisionerConfig
	Flags        ResourceFlag
}

// ResourceKind specifies what kind of instance we're working with, whether
// its a primary instance, a tainted instance, or an orphan.
type ResourceFlag byte

// InstanceInfo is used to hold information about the instance and/or
// resource being modified.
type InstanceInfo struct {
	// Id is a unique name to represent this instance. This is not related
	// to InstanceState.ID in any way.
	Id string

	// ModulePath is the complete path of the module containing this
	// instance.
	ModulePath []string

	// Type is the resource type of this instance
	Type string

	// uniqueExtra is an internal field that can be populated to supply
	// extra metadata that is used to identify a unique instance in
	// the graph walk. This will be appended to HumanID when uniqueId
	// is called.
	uniqueExtra string
}

// HumanId is a unique Id that is human-friendly and useful for UI elements.
func (i *InstanceInfo) HumanId() string {
	if i == nil {
		return "<nil>"
	}

	if len(i.ModulePath) <= 1 {
		return i.Id
	}

	return fmt.Sprintf(
		"module.%s.%s",
		strings.Join(i.ModulePath[1:], "."),
		i.Id)
}

func (i *InstanceInfo) uniqueId() string {
	prefix := i.HumanId()
	if v := i.uniqueExtra; v != "" {
		prefix += " " + v
	}

	return prefix
}

// ResourceConfig holds the configuration given for a resource. This is
// done instead of a raw `map[string]interface{}` type so that rich
// methods can be added to it to make dealing with it easier.
type ResourceConfig struct {
	ComputedKeys []string
	Raw          map[string]interface{}
	Config       map[string]interface{}

	raw *config.RawConfig
}

// NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
	result := &ResourceConfig{raw: c}
	result.interpolateForce()
	return result
}

// DeepCopy performs a deep copy of the configuration. This makes it safe
// to modify any of the structures that are part of the resource config without
// affecting the original configuration.
func (c *ResourceConfig) DeepCopy() *ResourceConfig {
	// DeepCopying a nil should return a nil to avoid panics
	if c == nil {
		return nil
	}

	// Copy, this will copy all the exported attributes
	copy, err := copystructure.Config{Lock: true}.Copy(c)
	if err != nil {
		panic(err)
	}

	// Force the type
	result := copy.(*ResourceConfig)

	// For the raw configuration, we can just use its own copy method
	result.raw = c.raw.Copy()

	return result
}

// Equal checks the equality of two resource configs.
func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
	// If either are nil, then they're only equal if they're both nil
	if c == nil || c2 == nil {
		return c == c2
	}

	// Sort the computed keys so they're deterministic
	sort.Strings(c.ComputedKeys)
	sort.Strings(c2.ComputedKeys)

	// Two resource configs if their exported properties are equal.
	// We don't compare "raw" because it is never used again after
	// initialization and for all intents and purposes they are equal
	// if the exported properties are equal.
	check := [][2]interface{}{
		{c.ComputedKeys, c2.ComputedKeys},
		{c.Raw, c2.Raw},
		{c.Config, c2.Config},
	}
	for _, pair := range check {
		if !reflect.DeepEqual(pair[0], pair[1]) {
			return false
		}
	}

	return true
}

// CheckSet checks that the given list of configuration keys is
// properly set. If not, errors are returned for each unset key.
//
// This is useful to be called in the Validate method of a ResourceProvider.
func (c *ResourceConfig) CheckSet(keys []string) []error {
	var errs []error

	for _, k := range keys {
		if !c.IsSet(k) {
			errs = append(errs, fmt.Errorf("%s must be set", k))
		}
	}

	return errs
}

// Get looks up a configuration value by key and returns the value.
//
// The second return value is true if the get was successful. Get will
// return the raw value if the key is computed, so you should pair this
// with IsComputed.
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
	// We aim to get a value from the configuration. If it is computed,
	// then we return the pure raw value.
	source := c.Config
	if c.IsComputed(k) {
		source = c.Raw
	}

	return c.get(k, source)
}

// GetRaw looks up a configuration value by key and returns the value,
// from the raw, uninterpolated config.
//
// The second return value is true if the get was successful. Get will
// not succeed if the value is being computed.
func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
	return c.get(k, c.Raw)
}

// IsComputed returns whether the given key is computed or not.
func (c *ResourceConfig) IsComputed(k string) bool {
	// The next thing we do is check the config if we get a computed
	// value out of it.
	v, ok := c.get(k, c.Config)
	if !ok {
		return false
	}

	// If value is nil, then it isn't computed
	if v == nil {
		return false
	}

	// Test if the value contains an unknown value
	var w unknownCheckWalker
	if err := reflectwalk.Walk(v, &w); err != nil {
		panic(err)
	}

	return w.Unknown
}

// IsSet checks if the key in the configuration is set. A key is set if
// it has a value or the value is being computed (is unknown currently).
//
// This function should be used rather than checking the keys of the
// raw configuration itself, since a key may be omitted from the raw
// configuration if it is being computed.
func (c *ResourceConfig) IsSet(k string) bool {
	if c == nil {
		return false
	}

	if c.IsComputed(k) {
		return true
	}

	if _, ok := c.Get(k); ok {
		return true
	}

	return false
}

func (c *ResourceConfig) get(
	k string, raw map[string]interface{}) (interface{}, bool) {
	parts := strings.Split(k, ".")
	if len(parts) == 1 && parts[0] == "" {
		parts = nil
	}

	var current interface{} = raw
	var previous interface{} = nil
	for i, part := range parts {
		if current == nil {
			return nil, false
		}

		cv := reflect.ValueOf(current)
		switch cv.Kind() {
		case reflect.Map:
			previous = current
			v := cv.MapIndex(reflect.ValueOf(part))
			if !v.IsValid() {
				if i > 0 && i != (len(parts)-1) {
					tryKey := strings.Join(parts[i:], ".")
					v := cv.MapIndex(reflect.ValueOf(tryKey))
					if !v.IsValid() {
						return nil, false
					}

					return v.Interface(), true
				}

				return nil, false
			}

			current = v.Interface()
		case reflect.Slice:
			previous = current

			if part == "#" {
				// If any value in a list is computed, this whole thing
				// is computed and we can't read any part of it.
				for i := 0; i < cv.Len(); i++ {
					if v := cv.Index(i).Interface(); v == unknownValue() {
						return v, true
					}
				}

				current = cv.Len()
			} else {
				i, err := strconv.ParseInt(part, 0, 0)
				if err != nil {
					return nil, false
				}
				if i >= int64(cv.Len()) {
					return nil, false
				}
				current = cv.Index(int(i)).Interface()
			}
		case reflect.String:
			// This happens when map keys contain "." and have a common
			// prefix so were split as path components above.
			actualKey := strings.Join(parts[i-1:], ".")
			if prevMap, ok := previous.(map[string]interface{}); ok {
				return prevMap[actualKey], true
			}

			return nil, false
		default:
			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
		}
	}

	return current, true
}

// interpolateForce is a temporary thing. We want to get rid of interpolate
// above and likewise this, but it can only be done after the f-ast-graph
// refactor is complete.
func (c *ResourceConfig) interpolateForce() {
	if c.raw == nil {
		var err error
		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
		if err != nil {
			panic(err)
		}
	}

	c.ComputedKeys = c.raw.UnknownKeys()
	c.Raw = c.raw.RawMap()
	c.Config = c.raw.Config()
}

// unknownCheckWalker
type unknownCheckWalker struct {
	Unknown bool
}

func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
	if v.Interface() == unknownValue() {
		w.Unknown = true
	}

	return nil
}
<commit_msg>ResourceConfig.get should never return (nil, true)<commit_after>package terraform

import (
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform/config"
	"github.com/mitchellh/copystructure"
	"github.com/mitchellh/reflectwalk"
)

// ResourceProvisionerConfig is used to pair a provisioner
// with its provided configuration. This allows us to use singleton
// instances of each ResourceProvisioner and to keep the relevant
// configuration instead of instantiating a new Provisioner for each
// resource.
type ResourceProvisionerConfig struct {
	Type        string
	Provisioner ResourceProvisioner
	Config      *ResourceConfig
	RawConfig   *config.RawConfig
	ConnInfo    *config.RawConfig
}

// Resource encapsulates a resource, its configuration, its provider,
// its current state, and potentially a desired diff from the state it
// wants to reach.
type Resource struct {
	// These are all used by the new EvalNode stuff.
	Name       string
	Type       string
	CountIndex int

	// These aren't really used anymore anywhere, but we keep them around
	// since we haven't done a proper cleanup yet.
	Id           string
	Info         *InstanceInfo
	Config       *ResourceConfig
	Dependencies []string
	Diff         *InstanceDiff
	Provider     ResourceProvider
	State        *InstanceState
	Provisioners []*ResourceProvisionerConfig
	Flags        ResourceFlag
}

// ResourceKind specifies what kind of instance we're working with, whether
// its a primary instance, a tainted instance, or an orphan.
type ResourceFlag byte

// InstanceInfo is used to hold information about the instance and/or
// resource being modified.
type InstanceInfo struct {
	// Id is a unique name to represent this instance. This is not related
	// to InstanceState.ID in any way.
	Id string

	// ModulePath is the complete path of the module containing this
	// instance.
	ModulePath []string

	// Type is the resource type of this instance
	Type string

	// uniqueExtra is an internal field that can be populated to supply
	// extra metadata that is used to identify a unique instance in
	// the graph walk. This will be appended to HumanID when uniqueId
	// is called.
	uniqueExtra string
}

// HumanId is a unique Id that is human-friendly and useful for UI elements.
func (i *InstanceInfo) HumanId() string {
	if i == nil {
		return "<nil>"
	}

	if len(i.ModulePath) <= 1 {
		return i.Id
	}

	return fmt.Sprintf(
		"module.%s.%s",
		strings.Join(i.ModulePath[1:], "."),
		i.Id)
}

func (i *InstanceInfo) uniqueId() string {
	prefix := i.HumanId()
	if v := i.uniqueExtra; v != "" {
		prefix += " " + v
	}

	return prefix
}

// ResourceConfig holds the configuration given for a resource. This is
// done instead of a raw `map[string]interface{}` type so that rich
// methods can be added to it to make dealing with it easier.
type ResourceConfig struct {
	ComputedKeys []string
	Raw          map[string]interface{}
	Config       map[string]interface{}

	raw *config.RawConfig
}

// NewResourceConfig creates a new ResourceConfig from a config.RawConfig.
func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
	result := &ResourceConfig{raw: c}
	result.interpolateForce()
	return result
}

// DeepCopy performs a deep copy of the configuration. This makes it safe
// to modify any of the structures that are part of the resource config without
// affecting the original configuration.
func (c *ResourceConfig) DeepCopy() *ResourceConfig {
	// DeepCopying a nil should return a nil to avoid panics
	if c == nil {
		return nil
	}

	// Copy, this will copy all the exported attributes
	copy, err := copystructure.Config{Lock: true}.Copy(c)
	if err != nil {
		panic(err)
	}

	// Force the type
	result := copy.(*ResourceConfig)

	// For the raw configuration, we can just use its own copy method
	result.raw = c.raw.Copy()

	return result
}

// Equal checks the equality of two resource configs.
func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
	// If either are nil, then they're only equal if they're both nil
	if c == nil || c2 == nil {
		return c == c2
	}

	// Sort the computed keys so they're deterministic
	sort.Strings(c.ComputedKeys)
	sort.Strings(c2.ComputedKeys)

	// Two resource configs if their exported properties are equal.
	// We don't compare "raw" because it is never used again after
	// initialization and for all intents and purposes they are equal
	// if the exported properties are equal.
	check := [][2]interface{}{
		{c.ComputedKeys, c2.ComputedKeys},
		{c.Raw, c2.Raw},
		{c.Config, c2.Config},
	}
	for _, pair := range check {
		if !reflect.DeepEqual(pair[0], pair[1]) {
			return false
		}
	}

	return true
}

// CheckSet checks that the given list of configuration keys is
// properly set. If not, errors are returned for each unset key.
//
// This is useful to be called in the Validate method of a ResourceProvider.
func (c *ResourceConfig) CheckSet(keys []string) []error {
	var errs []error

	for _, k := range keys {
		if !c.IsSet(k) {
			errs = append(errs, fmt.Errorf("%s must be set", k))
		}
	}

	return errs
}

// Get looks up a configuration value by key and returns the value.
//
// The second return value is true if the get was successful. Get will
// return the raw value if the key is computed, so you should pair this
// with IsComputed.
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
	// We aim to get a value from the configuration. If it is computed,
	// then we return the pure raw value.
	source := c.Config
	if c.IsComputed(k) {
		source = c.Raw
	}

	return c.get(k, source)
}

// GetRaw looks up a configuration value by key and returns the value,
// from the raw, uninterpolated config.
//
// The second return value is true if the get was successful. Get will
// not succeed if the value is being computed.
func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
	return c.get(k, c.Raw)
}

// IsComputed returns whether the given key is computed or not.
func (c *ResourceConfig) IsComputed(k string) bool {
	// The next thing we do is check the config if we get a computed
	// value out of it.
	v, ok := c.get(k, c.Config)
	if !ok {
		return false
	}

	// If value is nil, then it isn't computed
	if v == nil {
		return false
	}

	// Test if the value contains an unknown value
	var w unknownCheckWalker
	if err := reflectwalk.Walk(v, &w); err != nil {
		panic(err)
	}

	return w.Unknown
}

// IsSet checks if the key in the configuration is set. A key is set if
// it has a value or the value is being computed (is unknown currently).
//
// This function should be used rather than checking the keys of the
// raw configuration itself, since a key may be omitted from the raw
// configuration if it is being computed.
func (c *ResourceConfig) IsSet(k string) bool {
	if c == nil {
		return false
	}

	if c.IsComputed(k) {
		return true
	}

	if _, ok := c.Get(k); ok {
		return true
	}

	return false
}

func (c *ResourceConfig) get(
	k string, raw map[string]interface{}) (interface{}, bool) {
	parts := strings.Split(k, ".")
	if len(parts) == 1 && parts[0] == "" {
		parts = nil
	}

	var current interface{} = raw
	var previous interface{} = nil
	for i, part := range parts {
		if current == nil {
			return nil, false
		}

		cv := reflect.ValueOf(current)
		switch cv.Kind() {
		case reflect.Map:
			previous = current
			v := cv.MapIndex(reflect.ValueOf(part))
			if !v.IsValid() {
				if i > 0 && i != (len(parts)-1) {
					tryKey := strings.Join(parts[i:], ".")
					v := cv.MapIndex(reflect.ValueOf(tryKey))
					if !v.IsValid() {
						return nil, false
					}

					return v.Interface(), true
				}

				return nil, false
			}

			current = v.Interface()
		case reflect.Slice:
			previous = current

			if part == "#" {
				// If any value in a list is computed, this whole thing
				// is computed and we can't read any part of it.
				for i := 0; i < cv.Len(); i++ {
					if v := cv.Index(i).Interface(); v == unknownValue() {
						return v, true
					}
				}

				current = cv.Len()
			} else {
				i, err := strconv.ParseInt(part, 0, 0)
				if err != nil {
					return nil, false
				}
				if i >= int64(cv.Len()) {
					return nil, false
				}
				current = cv.Index(int(i)).Interface()
			}
		case reflect.String:
			// This happens when map keys contain "." and have a common
			// prefix so were split as path components above.
			actualKey := strings.Join(parts[i-1:], ".")
			if prevMap, ok := previous.(map[string]interface{}); ok {
				v, ok := prevMap[actualKey]
				return v, ok
			}

			return nil, false
		default:
			panic(fmt.Sprintf("Unknown kind: %s", cv.Kind()))
		}
	}

	return current, true
}

// interpolateForce is a temporary thing. We want to get rid of interpolate
// above and likewise this, but it can only be done after the f-ast-graph
// refactor is complete.
func (c *ResourceConfig) interpolateForce() {
	if c.raw == nil {
		var err error
		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
		if err != nil {
			panic(err)
		}
	}

	c.ComputedKeys = c.raw.UnknownKeys()
	c.Raw = c.raw.RawMap()
	c.Config = c.raw.Config()
}

// unknownCheckWalker
type unknownCheckWalker struct {
	Unknown bool
}

func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
	if v.Interface() == unknownValue() {
		w.Unknown = true
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Frederik Zipp. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package geom

import (
	"math"
	"unsafe"
)

// A Mat4 represents a 4x4 matrix.
type Mat4 [4][4]float32

// id is the 4x4 identity matrix.
var id = Mat4{
	{1, 0, 0, 0},
	{0, 1, 0, 0},
	{0, 0, 1, 0},
	{0, 0, 0, 1},
}

// Id sets m to be the identity matrix and returns m.
func (m *Mat4) Id() *Mat4 {
	*m = id
	return m
}

// Det calculates the determinant of 4x4 matrix m.
func (m *Mat4) Det() float32 {
	return m[0][3]*m[1][2]*m[2][1]*m[3][0] - m[0][2]*m[1][3]*m[2][1]*m[3][0] -
		m[0][3]*m[1][1]*m[2][2]*m[3][0] + m[0][1]*m[1][3]*m[2][2]*m[3][0] +
		m[0][2]*m[1][1]*m[2][3]*m[3][0] - m[0][1]*m[1][2]*m[2][3]*m[3][0] -
		m[0][3]*m[1][2]*m[2][0]*m[3][1] + m[0][2]*m[1][3]*m[2][0]*m[3][1] +
		m[0][3]*m[1][0]*m[2][2]*m[3][1] - m[0][0]*m[1][3]*m[2][2]*m[3][1] -
		m[0][2]*m[1][0]*m[2][3]*m[3][1] + m[0][0]*m[1][2]*m[2][3]*m[3][1] +
		m[0][3]*m[1][1]*m[2][0]*m[3][2] - m[0][1]*m[1][3]*m[2][0]*m[3][2] -
		m[0][3]*m[1][0]*m[2][1]*m[3][2] + m[0][0]*m[1][3]*m[2][1]*m[3][2] +
		m[0][1]*m[1][0]*m[2][3]*m[3][2] - m[0][0]*m[1][1]*m[2][3]*m[3][2] -
		m[0][2]*m[1][1]*m[2][0]*m[3][3] + m[0][1]*m[1][2]*m[2][0]*m[3][3] +
		m[0][2]*m[1][0]*m[2][1]*m[3][3] - m[0][0]*m[1][2]*m[2][1]*m[3][3] -
		m[0][1]*m[1][0]*m[2][2]*m[3][3] + m[0][0]*m[1][1]*m[2][2]*m[3][3]
}

func (m *Mat4) Mul(a *Mat4, b *Mat4) *Mat4 {
	*m = Mat4{
		{
			a[0][0]*b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0] + a[0][3]*b[3][0],
			a[0][0]*b[0][1] + a[0][1]*b[1][1] + a[0][2]*b[2][1] + a[0][3]*b[3][1],
			a[0][0]*b[0][2] + a[0][1]*b[1][2] + a[0][2]*b[2][2] + a[0][3]*b[3][2],
			a[0][0]*b[0][3] + a[0][1]*b[1][3] + a[0][2]*b[2][3] + a[0][3]*b[3][3],
		},
		{
			a[1][0]*b[0][0] + a[1][1]*b[1][0] + a[1][2]*b[2][0] + a[1][3]*b[3][0],
			a[1][0]*b[0][1] + a[1][1]*b[1][1] + a[1][2]*b[2][1] + a[1][3]*b[3][1],
			a[1][0]*b[0][2] + a[1][1]*b[1][2] + a[1][2]*b[2][2] + a[1][3]*b[3][2],
			a[1][0]*b[0][3] + a[1][1]*b[1][3] + a[1][2]*b[2][3] + a[1][3]*b[3][3],
		},
		{
			a[2][0]*b[0][0] + a[2][1]*b[1][0] + a[2][2]*b[2][0] + a[2][3]*b[3][0],
			a[2][0]*b[0][1] + a[2][1]*b[1][1] + a[2][2]*b[2][1] + a[2][3]*b[3][1],
			a[2][0]*b[0][2] + a[2][1]*b[1][2] + a[2][2]*b[2][2] + a[2][3]*b[3][2],
			a[2][0]*b[0][3] + a[2][1]*b[1][3] + a[2][2]*b[2][3] + a[2][3]*b[3][3],
		},
		{
			a[3][0]*b[0][0] + a[3][1]*b[1][0] + a[3][2]*b[2][0] + a[3][3]*b[3][0],
			a[3][0]*b[0][1] + a[3][1]*b[1][1] + a[3][2]*b[2][1] + a[3][3]*b[3][1],
			a[3][0]*b[0][2] + a[3][1]*b[1][2] + a[3][2]*b[2][2] + a[3][3]*b[3][2],
			a[3][0]*b[0][3] + a[3][1]*b[1][3] + a[3][2]*b[2][3] + a[3][3]*b[3][3],
		},
	}
	return m
}

// Ortho sets m to be an orthographic projection matrix with the given clipping
// planes and returns m.
func (m *Mat4) Ortho(left, right, bottom, top, near, far float32) *Mat4 {
	dx := left - right
	dy := bottom - top
	dz := near - far
	*m = Mat4{
		{-2 / dx, 0, 0, 0},
		{0, -2 / dy, 0, 0},
		{0, 0, 2 / dz, 0},
		{(left + right) / dx, (top + bottom) / dy, (far + near) / dz, 1},
	}
	return m
}

// Frustum sets m to be a frustum matrix with the given clipping planes and
// returns m.
func (m *Mat4) Frustum(left, right, bottom, top, near, far float32) *Mat4 {
	dx := right - left
	dy := top - bottom
	dz := near - far
	*m = Mat4{
		{(2 * near) / dx, 0, 0, 0},
		{0, (2 * near) / dy, 0, 0},
		{(left + right) / dx, (top + bottom) / dy, (far + near) / dz, -1},
		{0, 0, (2 * far * near) / dz, 0},
	}
	return m
}

// Perspective sets m to be a perspective matrix and returns m.
func (m *Mat4) Perspective(fovy, aspect, near, far float32) *Mat4 {
	f := 1 / float32(math.Tan(float64(fovy/2)))
	dz := near - far
	*m = Mat4{
		{f / aspect, 0, 0, 0},
		{0, f, 0, 0},
		{0, 0, (far + near) / dz, -1},
		{0, 0, (2 * far * near) / dz, 0},
	}
	return m
}

func (m *Mat4) LookAt(eye, center, up Vec3) *Mat4 {
	/* TODO
	f := center.Sub(eye).Norm()
	s := f.Cross(up.Norm())
	u := s.Cross(f)
	*/
	return m
}

// Floats returns a pointer to the matrix elements represented as a flat
// array of float32s.
func (m *Mat4) Floats() *[16]float32 {
	return (*[16]float32)(unsafe.Pointer(m))
}

func (m1 *Mat4) nearEq(m2 *Mat4) bool {
	for i := 0; i < 4; i++ {
		for j := 0; j < 4; j++ {
			if !nearEq(m1[i][j], m2[i][j], epsilon) {
				return false
			}
		}
	}
	return true
}
<commit_msg>don't expose LookAt method: not yet implemented<commit_after>// Copyright 2013 Frederik Zipp. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package geom

import (
	"math"
	"unsafe"
)

// A Mat4 represents a 4x4 matrix.
type Mat4 [4][4]float32

// id is the 4x4 identity matrix.
var id = Mat4{
	{1, 0, 0, 0},
	{0, 1, 0, 0},
	{0, 0, 1, 0},
	{0, 0, 0, 1},
}

// Id sets m to be the identity matrix and returns m.
func (m *Mat4) Id() *Mat4 {
	*m = id
	return m
}

// Det calculates the determinant of 4x4 matrix m.
func (m *Mat4) Det() float32 {
	return m[0][3]*m[1][2]*m[2][1]*m[3][0] - m[0][2]*m[1][3]*m[2][1]*m[3][0] -
		m[0][3]*m[1][1]*m[2][2]*m[3][0] + m[0][1]*m[1][3]*m[2][2]*m[3][0] +
		m[0][2]*m[1][1]*m[2][3]*m[3][0] - m[0][1]*m[1][2]*m[2][3]*m[3][0] -
		m[0][3]*m[1][2]*m[2][0]*m[3][1] + m[0][2]*m[1][3]*m[2][0]*m[3][1] +
		m[0][3]*m[1][0]*m[2][2]*m[3][1] - m[0][0]*m[1][3]*m[2][2]*m[3][1] -
		m[0][2]*m[1][0]*m[2][3]*m[3][1] + m[0][0]*m[1][2]*m[2][3]*m[3][1] +
		m[0][3]*m[1][1]*m[2][0]*m[3][2] - m[0][1]*m[1][3]*m[2][0]*m[3][2] -
		m[0][3]*m[1][0]*m[2][1]*m[3][2] + m[0][0]*m[1][3]*m[2][1]*m[3][2] +
		m[0][1]*m[1][0]*m[2][3]*m[3][2] - m[0][0]*m[1][1]*m[2][3]*m[3][2] -
		m[0][2]*m[1][1]*m[2][0]*m[3][3] + m[0][1]*m[1][2]*m[2][0]*m[3][3] +
		m[0][2]*m[1][0]*m[2][1]*m[3][3] - m[0][0]*m[1][2]*m[2][1]*m[3][3] -
		m[0][1]*m[1][0]*m[2][2]*m[3][3] + m[0][0]*m[1][1]*m[2][2]*m[3][3]
}

func (m *Mat4) Mul(a *Mat4, b *Mat4) *Mat4 {
	*m = Mat4{
		{
			a[0][0]*b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0] + a[0][3]*b[3][0],
			a[0][0]*b[0][1] + a[0][1]*b[1][1] + a[0][2]*b[2][1] + a[0][3]*b[3][1],
			a[0][0]*b[0][2] + a[0][1]*b[1][2] + a[0][2]*b[2][2] + a[0][3]*b[3][2],
			a[0][0]*b[0][3] + a[0][1]*b[1][3] + a[0][2]*b[2][3] + a[0][3]*b[3][3],
		},
		{
			a[1][0]*b[0][0] + a[1][1]*b[1][0] + a[1][2]*b[2][0] + a[1][3]*b[3][0],
			a[1][0]*b[0][1] + a[1][1]*b[1][1] + a[1][2]*b[2][1] + a[1][3]*b[3][1],
			a[1][0]*b[0][2] + a[1][1]*b[1][2] + a[1][2]*b[2][2] + a[1][3]*b[3][2],
			a[1][0]*b[0][3] + a[1][1]*b[1][3] + a[1][2]*b[2][3] + a[1][3]*b[3][3],
		},
		{
			a[2][0]*b[0][0] + a[2][1]*b[1][0] + a[2][2]*b[2][0] + a[2][3]*b[3][0],
			a[2][0]*b[0][1] + a[2][1]*b[1][1] + a[2][2]*b[2][1] + a[2][3]*b[3][1],
			a[2][0]*b[0][2] + a[2][1]*b[1][2] + a[2][2]*b[2][2] + a[2][3]*b[3][2],
			a[2][0]*b[0][3] + a[2][1]*b[1][3] + a[2][2]*b[2][3] + a[2][3]*b[3][3],
		},
		{
			a[3][0]*b[0][0] + a[3][1]*b[1][0] + a[3][2]*b[2][0] + a[3][3]*b[3][0],
			a[3][0]*b[0][1] + a[3][1]*b[1][1] + a[3][2]*b[2][1] + a[3][3]*b[3][1],
			a[3][0]*b[0][2] + a[3][1]*b[1][2] + a[3][2]*b[2][2] + a[3][3]*b[3][2],
			a[3][0]*b[0][3] + a[3][1]*b[1][3] + a[3][2]*b[2][3] + a[3][3]*b[3][3],
		},
	}
	return m
}

// Ortho sets m to be an orthographic projection matrix with the given clipping
// planes and returns m.
func (m *Mat4) Ortho(left, right, bottom, top, near, far float32) *Mat4 {
	dx := left - right
	dy := bottom - top
	dz := near - far
	*m = Mat4{
		{-2 / dx, 0, 0, 0},
		{0, -2 / dy, 0, 0},
		{0, 0, 2 / dz, 0},
		{(left + right) / dx, (top + bottom) / dy, (far + near) / dz, 1},
	}
	return m
}

// Frustum sets m to be a frustum matrix with the given clipping planes and
// returns m.
func (m *Mat4) Frustum(left, right, bottom, top, near, far float32) *Mat4 {
	dx := right - left
	dy := top - bottom
	dz := near - far
	*m = Mat4{
		{(2 * near) / dx, 0, 0, 0},
		{0, (2 * near) / dy, 0, 0},
		{(left + right) / dx, (top + bottom) / dy, (far + near) / dz, -1},
		{0, 0, (2 * far * near) / dz, 0},
	}
	return m
}

// Perspective sets m to be a perspective matrix and returns m.
func (m *Mat4) Perspective(fovy, aspect, near, far float32) *Mat4 {
	f := 1 / float32(math.Tan(float64(fovy/2)))
	dz := near - far
	*m = Mat4{
		{f / aspect, 0, 0, 0},
		{0, f, 0, 0},
		{0, 0, (far + near) / dz, -1},
		{0, 0, (2 * far * near) / dz, 0},
	}
	return m
}

/*
func (m *Mat4) LookAt(eye, center, up Vec3) *Mat4 {
	f := center.Sub(eye).Norm()
	s := f.Cross(up.Norm())
	u := s.Cross(f)
	// TODO
	return m
}
*/

// Floats returns a pointer to the matrix elements represented as a flat
// array of float32s.
func (m *Mat4) Floats() *[16]float32 {
	return (*[16]float32)(unsafe.Pointer(m))
}

func (m1 *Mat4) nearEq(m2 *Mat4) bool {
	for i := 0; i < 4; i++ {
		for j := 0; j < 4; j++ {
			if !nearEq(m1[i][j], m2[i][j], epsilon) {
				return false
			}
		}
	}
	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package translator

import (
	"fmt"

	"github.com/golang/glog"
	v3 "google.golang.org/api/monitoring/v3"

	"k8s.io/contrib/prometheus-to-sd/config"
)

// SendToStackdriver sends http request to Stackdriver to create the given timeserieses.
func SendToStackdriver(service *v3.Service, config *config.GceConfig, ts []*v3.TimeSeries) {
	if len(ts) == 0 {
		glog.Warningf("No metrics to send to Stackdriver")
		return
	}

	req := &v3.CreateTimeSeriesRequest{TimeSeries: ts}
	proj := fmt.Sprintf("projects/%s", config.Project)

	_, err := service.Projects.TimeSeries.Create(proj, req).Do()
	if err != nil {
		glog.Errorf("Error while sending request to Stackdriver %v", err)
	} else {
		glog.V(4).Infof("Successfully sent %v timeserieses to Stackdriver", len(req.TimeSeries))
	}
}
<commit_msg>Send timeserieses to stackdriver in batches.<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package translator

import (
	"fmt"
	"sync"

	"github.com/golang/glog"
	v3 "google.golang.org/api/monitoring/v3"

	"k8s.io/contrib/prometheus-to-sd/config"
)

const (
	maxTimeseriesesPerRequest = 200
)

// SendToStackdriver sends http request to Stackdriver to create the given timeserieses.
func SendToStackdriver(service *v3.Service, config *config.GceConfig, ts []*v3.TimeSeries) {
	if len(ts) == 0 {
		glog.Warningf("No metrics to send to Stackdriver")
		return
	}

	proj := fmt.Sprintf("projects/%s", config.Project)

	var wg sync.WaitGroup
	for i := 0; i < len(ts); i += maxTimeseriesesPerRequest {
		end := i + maxTimeseriesesPerRequest
		if end > len(ts) {
			end = len(ts)
		}
		wg.Add(1)
		go func(begin int, end int) {
			defer wg.Done()
			req := &v3.CreateTimeSeriesRequest{TimeSeries: ts[begin:end]}
			_, err := service.Projects.TimeSeries.Create(proj, req).Do()
			if err != nil {
				glog.Errorf("Error while sending request to Stackdriver %v", err)
			}
		}(i, end)
	}
	wg.Wait()
	glog.V(4).Infof("Successfully sent %v timeserieses to Stackdriver", len(ts))
}
<|endoftext|>


<commit_before>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package orchestrator

import (
	"github.com/andreaskoch/allmark2/common/config"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/paths"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/allmark2/dataaccess"
	"github.com/andreaskoch/allmark2/model"
	"github.com/andreaskoch/allmark2/services/converter"
	"github.com/andreaskoch/allmark2/services/parser"
	"github.com/andreaskoch/allmark2/web/view/viewmodel"
	"github.com/andreaskoch/allmark2/web/webpaths"
	"sort"
	"strings"
	"time"
)

func newBaseOrchestrator(logger logger.Logger, config config.Config, repository dataaccess.Repository, parser parser.Parser, converter converter.Converter, webPathProvider webpaths.WebPathProvider) *Orchestrator {
	return &Orchestrator{
		logger: logger,

		config:     config,
		repository: repository,
		parser:     parser,
		converter:  converter,

		webPathProvider: webPathProvider,
	}
}

type Orchestrator struct {
	logger logger.Logger

	config     config.Config
	repository dataaccess.Repository
	parser     parser.Parser
	converter  converter.Converter

	webPathProvider webpaths.WebPathProvider

	// caches
	itemsByAlias  map[string]*model.Item
	leafesByRoute map[string][]route.Route
}

func (orchestrator *Orchestrator) ResetCache() {
	orchestrator.itemsByAlias = make(map[string]*model.Item)
	orchestrator.leafesByRoute = make(map[string][]route.Route)
}

func (orchestrator *Orchestrator) ItemExists(route route.Route) bool {
	_, exists := orchestrator.repository.Item(route)
	return exists
}

func (orchestrator *Orchestrator) absolutePather(prefix string) paths.Pather {
	return orchestrator.webPathProvider.AbsolutePather(prefix)
}

func (orchestrator *Orchestrator) itemPather() paths.Pather {
	return orchestrator.webPathProvider.ItemPather()
}

func (orchestrator *Orchestrator) tagPather() paths.Pather {
	return orchestrator.webPathProvider.TagPather()
}

func (orchestrator *Orchestrator) relativePather(baseRoute route.Route) paths.Pather {
	return orchestrator.webPathProvider.RelativePather(baseRoute)
}

func (orchestrator *Orchestrator) parseItem(item *dataaccess.Item) *model.Item {
	parsedItem, err := orchestrator.parser.ParseItem(item)
	if err != nil {
		orchestrator.logger.Warn(err.Error())
		return nil
	}

	return parsedItem
}

func (orchestrator *Orchestrator) parseFile(file *dataaccess.File) *model.File {
	parsedFile, err := orchestrator.parser.ParseFile(file)
	if err != nil {
		orchestrator.logger.Warn(err.Error())
		return nil
	}

	return parsedFile
}

func (orchestrator *Orchestrator) rootItem() *model.Item {
	return orchestrator.parseItem(orchestrator.repository.Root())
}

func (orchestrator *Orchestrator) getItem(route route.Route) *model.Item {
	item, exists := orchestrator.repository.Item(route)
	if !exists {
		return nil
	}

	return orchestrator.parseItem(item)
}

func (orchestrator *Orchestrator) getLatestRoutesByPage(parentRoute route.Route, pageSize, page int) (routes []route.Route, found bool) {

	latestRoutes, found := orchestrator.getLatestRoutes(parentRoute)
	if !found {
		return []route.Route{}, false
	}

	// determine the start index
	startIndex := pageSize * (page - 1)
	if startIndex >= len(latestRoutes) {
		return []route.Route{}, false
	}

	// determine the end index
	endIndex := startIndex + pageSize
	if endIndex > len(latestRoutes) {
		endIndex = len(latestRoutes)
	}

	return latestRoutes[startIndex:endIndex], true
}

func (orchestrator *Orchestrator) getLatestRoutes(parentRoute route.Route) (routes []route.Route, found bool) {

	leafes := orchestrator.getAllLeafes(parentRoute)

	// collect the creation dates for all leafes
	routesAndDates := make([]routeAndDate, 0, len(leafes))
	for _, leaf := range leafes {
		creationDate, found := orchestrator.getCreationDate(leaf)
		if !found {
			// todo: log info
			continue
		}

		routesAndDates = append(routesAndDates, routeAndDate{leaf, creationDate})
	}

	// sort the leafes by date
	SortItemRoutesAndDatesBy(sortRoutesAndDatesDescending).Sort(routesAndDates)

	routes = make([]route.Route, 0)
	for _, routeAndDate := range routesAndDates {
		routes = append(routes, routeAndDate.route)
	}

	return routes, true
}

func (orchestrator *Orchestrator) getAllLeafes(parentRoute route.Route) []route.Route {

	// cache lookup
	key := parentRoute.Value()
	if leafes, isset := orchestrator.leafesByRoute[key]; isset {
		return leafes
	}

	childRoutes := make([]route.Route, 0)

	childItems := orchestrator.getChilds(parentRoute)
	if hasNoMoreChilds := len(childItems) == 0; hasNoMoreChilds {
		return []route.Route{parentRoute}
	}

	// recurse
	for _, childItem := range childItems {

		// skip locations
		if childItem.Type == model.TypeLocation {
			continue
		}

		childRoutes = append(childRoutes, orchestrator.getAllLeafes(childItem.Route())...)
	}

	// store the value
	orchestrator.leafesByRoute[key] = childRoutes

	return childRoutes

}

func (orchestrator *Orchestrator) getAllItems() []*model.Item {

	allItems := make([]*model.Item, 0)

	for _, repositoryItem := range orchestrator.repository.Items() {
		item := orchestrator.parseItem(repositoryItem)
		if item == nil {
			continue
		}

		allItems = append(allItems, item)
	}

	model.SortItemsBy(sortItemsByDate).Sort(allItems)

	return allItems
}

func (orchestrator *Orchestrator) getItems(pageSize, page int) []*model.Item {

	allItems := orchestrator.getAllItems()

	// determine the start index
	startIndex := pageSize * (page - 1)
	if startIndex >= len(allItems) {
		return []*model.Item{}
	}

	// determine the end index
	endIndex := startIndex + pageSize
	if endIndex > len(allItems) {
		endIndex = len(allItems)
	}

	return allItems[startIndex:endIndex]
}

func (orchestrator *Orchestrator) getCreationDate(itemRoute route.Route) (creationDate time.Time, found bool) {

	item := orchestrator.getItem(itemRoute)
	if item == nil {
		return time.Time{}, false
	}

	return item.MetaData.CreationDate, true
}

func (orchestrator *Orchestrator) getFile(route route.Route) *model.File {
	file, exists := orchestrator.repository.File(route)
	if !exists {
		return nil
	}

	return orchestrator.parseFile(file)
}

func (orchestrator *Orchestrator) getParent(route route.Route) *model.Item {
	parent := orchestrator.repository.Parent(route)
	if parent == nil {
		return nil
	}

	return orchestrator.parseItem(parent)
}

func (orchestrator *Orchestrator) getPrevious(currentRoute route.Route) *model.Item {

	latestRoutes, found := orchestrator.getLatestRoutes(route.New())
	if !found {
		return nil
	}

	// determine the position of the supplied route
	matchingIndex := -1
	for index, route := range latestRoutes {
		if route.Value() == currentRoute.Value() {
			matchingIndex = index
			break
		}
	}

	// abort if the route was not found at all
	if noMatchFound := (matchingIndex == -1); noMatchFound {
		return nil
	}

	// abort if there is no next item
	nextIndex := matchingIndex + 1
	if nextIndex >= len(latestRoutes) {
		return nil
	}

	// determine the next route
	nextRoute := latestRoutes[nextIndex]

	return orchestrator.getItem(nextRoute)
}

func (orchestrator *Orchestrator) getNext(currentRoute route.Route) *model.Item {

	latestRoutes, found := orchestrator.getLatestRoutes(route.New())
	if !found {
		return nil
	}

	// determine the position of the supplied route
	matchingIndex := -1
	for index, route := range latestRoutes {
		if route.Value() == currentRoute.Value() {
			matchingIndex = index
			break
		}
	}

	// abort if the route was not found at all
	if noMatchFound := (matchingIndex == -1); noMatchFound {
		return nil
	}

	// abort if there is no previous item
	previousIndex := matchingIndex - 1
	if noPreviousItem := (previousIndex < 0); noPreviousItem {
		return nil
	}

	// determine the previous route
	previousRoute := latestRoutes[previousIndex]

	return orchestrator.getItem(previousRoute)
}

func (orchestrator *Orchestrator) getChilds(route route.Route) (childs []*model.Item) {

	childs = make([]*model.Item, 0)

	for _, child := range orchestrator.repository.Childs(route) {
		parsed := orchestrator.parseItem(child)
		if parsed == nil {
			continue
		}

		childs = append(childs, parsed)
	}

	return childs
}

// Get the item that has the specified alias. Returns nil if there is no matching item.
func (orchestrator *Orchestrator) getItemByAlias(alias string) *model.Item {

	alias = strings.TrimSpace(strings.ToLower(alias))

	if orchestrator.itemsByAlias == nil {

		orchestrator.logger.Info("Initializing alias list")
		itemsByAlias := make(map[string]*model.Item)

		for _, repositoryItem := range orchestrator.repository.Items() {

			item := orchestrator.parseItem(repositoryItem)
			if item == nil {
				orchestrator.logger.Warn("Cannot parse repository item %q.", repositoryItem.String())
				continue
			}

			// continue items without an alias
			if item.MetaData.Alias == "" {
				continue
			}

			itemAlias := strings.TrimSpace(strings.ToLower(item.MetaData.Alias))
			itemsByAlias[itemAlias] = item
		}

		// refresh control
		go func() {
			for {
				select {
				case <-orchestrator.repository.AfterReindex():
					// reset the list
					orchestrator.logger.Info("Resetting the alias list")
					orchestrator.itemsByAlias = nil
				}
			}
		}()

		orchestrator.itemsByAlias = itemsByAlias
	}

	return orchestrator.itemsByAlias[alias]
}

func (orchestrator *Orchestrator) getAnalyticsSettings() viewmodel.Analytics {
	return viewmodel.Analytics{
		Enabled: orchestrator.config.Analytics.Enabled,
		GoogleAnalytics: viewmodel.GoogleAnalytics{
			Enabled:    orchestrator.config.Analytics.GoogleAnalytics.Enabled,
			TrackingId: orchestrator.config.Analytics.GoogleAnalytics.TrackingId,
		},
	}
}

// sort the models by date and name
func sortItemsByDate(model1, model2 *model.Item) bool {

	return model1.MetaData.CreationDate.Before(model2.MetaData.CreationDate)

}

func sortRoutesAndDatesDescending(itemRoute1, itemRoute2 routeAndDate) bool {
	return itemRoute1.date.After(itemRoute2.date)
}

type routeAndDate struct {
	route route.Route
	date  time.Time
}

type SortItemRoutesAndDatesBy func(itemRoute1, itemRoute2 routeAndDate) bool

func (by SortItemRoutesAndDatesBy) Sort(routesAndDates []routeAndDate) {
	sorter := &routeAndDateSorter{
		routesAndDates: routesAndDates,
		by:             by,
	}

	sort.Sort(sorter)
}

type routeAndDateSorter struct {
	routesAndDates []routeAndDate
	by             SortItemRoutesAndDatesBy
}

func (sorter *routeAndDateSorter) Len() int {
	return len(sorter.routesAndDates)
}

func (sorter *routeAndDateSorter) Swap(i, j int) {
	sorter.routesAndDates[i], sorter.routesAndDates[j] = sorter.routesAndDates[j], sorter.routesAndDates[i]
}

func (sorter *routeAndDateSorter) Less(i, j int) bool {
	return sorter.by(sorter.routesAndDates[i], sorter.routesAndDates[j])
}
<commit_msg>Caching: Cache the getAllItems method result<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package orchestrator

import (
	"github.com/andreaskoch/allmark2/common/config"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/paths"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/allmark2/dataaccess"
	"github.com/andreaskoch/allmark2/model"
	"github.com/andreaskoch/allmark2/services/converter"
	"github.com/andreaskoch/allmark2/services/parser"
	"github.com/andreaskoch/allmark2/web/view/viewmodel"
	"github.com/andreaskoch/allmark2/web/webpaths"
	"sort"
	"strings"
	"time"
)

func newBaseOrchestrator(logger logger.Logger, config config.Config, repository dataaccess.Repository, parser parser.Parser, converter converter.Converter, webPathProvider webpaths.WebPathProvider) *Orchestrator {
	return &Orchestrator{
		logger: logger,

		config:     config,
		repository: repository,
		parser:     parser,
		converter:  converter,

		webPathProvider: webPathProvider,
	}
}

type Orchestrator struct {
	logger logger.Logger

	config     config.Config
	repository dataaccess.Repository
	parser     parser.Parser
	converter  converter.Converter

	webPathProvider webpaths.WebPathProvider

	// caches
	items         []*model.Item
	itemsByAlias  map[string]*model.Item
	leafesByRoute map[string][]route.Route
}

func (orchestrator *Orchestrator) ResetCache() {
	orchestrator.items = nil
	orchestrator.itemsByAlias = make(map[string]*model.Item)
	orchestrator.leafesByRoute = make(map[string][]route.Route)
}

func (orchestrator *Orchestrator) ItemExists(route route.Route) bool {
	_, exists := orchestrator.repository.Item(route)
	return exists
}

func (orchestrator *Orchestrator) absolutePather(prefix string) paths.Pather {
	return orchestrator.webPathProvider.AbsolutePather(prefix)
}

func (orchestrator *Orchestrator) itemPather() paths.Pather {
	return orchestrator.webPathProvider.ItemPather()
}

func (orchestrator *Orchestrator) tagPather() paths.Pather {
	return orchestrator.webPathProvider.TagPather()
}

func (orchestrator *Orchestrator) relativePather(baseRoute route.Route) paths.Pather {
	return orchestrator.webPathProvider.RelativePather(baseRoute)
}

func (orchestrator *Orchestrator) parseItem(item *dataaccess.Item) *model.Item {
	parsedItem, err := orchestrator.parser.ParseItem(item)
	if err != nil {
		orchestrator.logger.Warn(err.Error())
		return nil
	}

	return parsedItem
}

func (orchestrator *Orchestrator) parseFile(file *dataaccess.File) *model.File {
	parsedFile, err := orchestrator.parser.ParseFile(file)
	if err != nil {
		orchestrator.logger.Warn(err.Error())
		return nil
	}

	return parsedFile
}

func (orchestrator *Orchestrator) rootItem() *model.Item {
	return orchestrator.parseItem(orchestrator.repository.Root())
}

func (orchestrator *Orchestrator) getItem(route route.Route) *model.Item {
	item, exists := orchestrator.repository.Item(route)
	if !exists {
		return nil
	}

	return orchestrator.parseItem(item)
}

func (orchestrator *Orchestrator) getLatestRoutesByPage(parentRoute route.Route, pageSize, page int) (routes []route.Route, found bool) {

	latestRoutes, found := orchestrator.getLatestRoutes(parentRoute)
	if !found {
		return []route.Route{}, false
	}

	// determine the start index
	startIndex := pageSize * (page - 1)
	if startIndex >= len(latestRoutes) {
		return []route.Route{}, false
	}

	// determine the end index
	endIndex := startIndex + pageSize
	if endIndex > len(latestRoutes) {
		endIndex = len(latestRoutes)
	}

	return latestRoutes[startIndex:endIndex], true
}

func (orchestrator *Orchestrator) getLatestRoutes(parentRoute route.Route) (routes []route.Route, found bool) {

	leafes := orchestrator.getAllLeafes(parentRoute)

	// collect the creation dates for all leafes
	routesAndDates := make([]routeAndDate, 0, len(leafes))
	for _, leaf := range leafes {
		creationDate, found := orchestrator.getCreationDate(leaf)
		if !found {
			// todo: log info
			continue
		}

		routesAndDates = append(routesAndDates, routeAndDate{leaf, creationDate})
	}

	// sort the leafes by date
	SortItemRoutesAndDatesBy(sortRoutesAndDatesDescending).Sort(routesAndDates)

	routes = make([]route.Route, 0)
	for _, routeAndDate := range routesAndDates {
		routes = append(routes, routeAndDate.route)
	}

	return routes, true
}

func (orchestrator *Orchestrator) getAllLeafes(parentRoute route.Route) []route.Route {

	// cache lookup
	key := parentRoute.Value()
	if leafes, isset := orchestrator.leafesByRoute[key]; isset {
		return leafes
	}

	childRoutes := make([]route.Route, 0)

	childItems := orchestrator.getChilds(parentRoute)
	if hasNoMoreChilds := len(childItems) == 0; hasNoMoreChilds {
		return []route.Route{parentRoute}
	}

	// recurse
	for _, childItem := range childItems {

		// skip locations
		if childItem.Type == model.TypeLocation {
			continue
		}

		childRoutes = append(childRoutes, orchestrator.getAllLeafes(childItem.Route())...)
	}

	// store the value
	orchestrator.leafesByRoute[key] = childRoutes

	return childRoutes

}

func (orchestrator *Orchestrator) getAllItems() []*model.Item {

	// load from cache
	if orchestrator.items != nil {
		return orchestrator.items
	}

	allItems := make([]*model.Item, 0)

	for _, repositoryItem := range orchestrator.repository.Items() {
		item := orchestrator.parseItem(repositoryItem)
		if item == nil {
			continue
		}

		allItems = append(allItems, item)
	}

	model.SortItemsBy(sortItemsByDate).Sort(allItems)

	// store to cache
	orchestrator.items = allItems

	return allItems
}

func (orchestrator *Orchestrator) getItems(pageSize, page int) []*model.Item {

	allItems := orchestrator.getAllItems()

	// determine the start index
	startIndex := pageSize * (page - 1)
	if startIndex >= len(allItems) {
		return []*model.Item{}
	}

	// determine the end index
	endIndex := startIndex + pageSize
	if endIndex > len(allItems) {
		endIndex = len(allItems)
	}

	return allItems[startIndex:endIndex]
}

func (orchestrator *Orchestrator) getCreationDate(itemRoute route.Route) (creationDate time.Time, found bool) {

	item := orchestrator.getItem(itemRoute)
	if item == nil {
		return time.Time{}, false
	}

	return item.MetaData.CreationDate, true
}

func (orchestrator *Orchestrator) getFile(route route.Route) *model.File {
	file, exists := orchestrator.repository.File(route)
	if !exists {
		return nil
	}

	return orchestrator.parseFile(file)
}

func (orchestrator *Orchestrator) getParent(route route.Route) *model.Item {
	parent := orchestrator.repository.Parent(route)
	if parent == nil {
		return nil
	}

	return orchestrator.parseItem(parent)
}

func (orchestrator *Orchestrator) getPrevious(currentRoute route.Route) *model.Item {

	latestRoutes, found := orchestrator.getLatestRoutes(route.New())
	if !found {
		return nil
	}

	// determine the position of the supplied route
	matchingIndex := -1
	for index, route := range latestRoutes {
		if route.Value() == currentRoute.Value() {
			matchingIndex = index
			break
		}
	}

	// abort if the route was not found at all
	if noMatchFound := (matchingIndex == -1); noMatchFound {
		return nil
	}

	// abort if there is no next item
	nextIndex := matchingIndex + 1
	if nextIndex >= len(latestRoutes) {
		return nil
	}

	// determine the next route
	nextRoute := latestRoutes[nextIndex]

	return orchestrator.getItem(nextRoute)
}

func (orchestrator *Orchestrator) getNext(currentRoute route.Route) *model.Item {

	latestRoutes, found := orchestrator.getLatestRoutes(route.New())
	if !found {
		return nil
	}

	// determine the position of the supplied route
	matchingIndex := -1
	for index, route := range latestRoutes {
		if route.Value() == currentRoute.Value() {
			matchingIndex = index
			break
		}
	}

	// abort if the route was not found at all
	if noMatchFound := (matchingIndex == -1); noMatchFound {
		return nil
	}

	// abort if there is no previous item
	previousIndex := matchingIndex - 1
	if noPreviousItem := (previousIndex < 0); noPreviousItem {
		return nil
	}

	// determine the previous route
	previousRoute := latestRoutes[previousIndex]

	return orchestrator.getItem(previousRoute)
}

func (orchestrator *Orchestrator) getChilds(route route.Route) (childs []*model.Item) {

	childs = make([]*model.Item, 0)

	for _, child := range orchestrator.repository.Childs(route) {
		parsed := orchestrator.parseItem(child)
		if parsed == nil {
			continue
		}

		childs = append(childs, parsed)
	}

	return childs
}

// Get the item that has the specified alias. Returns nil if there is no matching item.
func (orchestrator *Orchestrator) getItemByAlias(alias string) *model.Item {

	alias = strings.TrimSpace(strings.ToLower(alias))

	if orchestrator.itemsByAlias == nil {

		orchestrator.logger.Info("Initializing alias list")
		itemsByAlias := make(map[string]*model.Item)

		for _, repositoryItem := range orchestrator.repository.Items() {

			item := orchestrator.parseItem(repositoryItem)
			if item == nil {
				orchestrator.logger.Warn("Cannot parse repository item %q.", repositoryItem.String())
				continue
			}

			// continue items without an alias
			if item.MetaData.Alias == "" {
				continue
			}

			itemAlias := strings.TrimSpace(strings.ToLower(item.MetaData.Alias))
			itemsByAlias[itemAlias] = item
		}

		// refresh control
		go func() {
			for {
				select {
				case <-orchestrator.repository.AfterReindex():
					// reset the list
					orchestrator.logger.Info("Resetting the alias list")
					orchestrator.itemsByAlias = nil
				}
			}
		}()

		orchestrator.itemsByAlias = itemsByAlias
	}

	return orchestrator.itemsByAlias[alias]
}

func (orchestrator *Orchestrator) getAnalyticsSettings() viewmodel.Analytics {
	return viewmodel.Analytics{
		Enabled: orchestrator.config.Analytics.Enabled,
		GoogleAnalytics: viewmodel.GoogleAnalytics{
			Enabled:    orchestrator.config.Analytics.GoogleAnalytics.Enabled,
			TrackingId: orchestrator.config.Analytics.GoogleAnalytics.TrackingId,
		},
	}
}

// sort the models by date and name
func sortItemsByDate(model1, model2 *model.Item) bool {

	return model1.MetaData.CreationDate.Before(model2.MetaData.CreationDate)

}

func sortRoutesAndDatesDescending(itemRoute1, itemRoute2 routeAndDate) bool {
	return itemRoute1.date.After(itemRoute2.date)
}

type routeAndDate struct {
	route route.Route
	date  time.Time
}

type SortItemRoutesAndDatesBy func(itemRoute1, itemRoute2 routeAndDate) bool

func (by SortItemRoutesAndDatesBy) Sort(routesAndDates []routeAndDate) {
	sorter := &routeAndDateSorter{
		routesAndDates: routesAndDates,
		by:             by,
	}

	sort.Sort(sorter)
}

type routeAndDateSorter struct {
	routesAndDates []routeAndDate
	by             SortItemRoutesAndDatesBy
}

func (sorter *routeAndDateSorter) Len() int {
	return len(sorter.routesAndDates)
}

func (sorter *routeAndDateSorter) Swap(i, j int) {
	sorter.routesAndDates[i], sorter.routesAndDates[j] = sorter.routesAndDates[j], sorter.routesAndDates[i]
}

func (sorter *routeAndDateSorter) Less(i, j int) bool {
	return sorter.by(sorter.routesAndDates[i], sorter.routesAndDates[j])
}
<|endoftext|>


<commit_before>package nodetemplate

import (
	"fmt"
	"strings"

	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/store/transform"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/norman/types/values"
	"github.com/rancher/rancher/pkg/namespace"
	"github.com/rancher/rancher/pkg/ref"
	corev1 "github.com/rancher/types/apis/core/v1"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	managementschema "github.com/rancher/types/apis/management.cattle.io/v3/schema"
	client "github.com/rancher/types/client/management/v3"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/labels"
)

type nodeTemplateStore struct {
	types.Store
	NodePoolLister        v3.NodePoolLister
	CloudCredentialLister corev1.SecretLister
	NodeTemplateClient    v3.NodeTemplateInterface
}

func Wrap(store types.Store, npLister v3.NodePoolLister, secretLister corev1.SecretLister, ntClient v3.NodeTemplateInterface) types.Store {
	s := &nodeTemplateStore{
		Store:                 store,
		NodePoolLister:        npLister,
		CloudCredentialLister: secretLister,
		NodeTemplateClient:    ntClient,
	}
	return &transform.Store{
		Store:       s,
		Transformer: s.filterLegacyTemplates,
	}
}

func (s *nodeTemplateStore) filterLegacyTemplates(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, opt *types.QueryOptions) (map[string]interface{}, error) {
	ns, _ := data["namespaceId"].(string)
	name, _ := data["name"].(string)
	if ns != namespace.NodeTemplateGlobalNamespace {
		s.NodeTemplateClient.Controller().Enqueue(ns, name)
		return nil, nil
	}
	return data, nil
}

func (s *nodeTemplateStore) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
	pools, err := s.NodePoolLister.List("", labels.Everything())
	if err != nil {
		return nil, err
	}
	for _, pool := range pools {
		if pool.Spec.NodeTemplateName == id {
			return nil, httperror.NewAPIError(httperror.MethodNotAllowed, "Template is in use by a node pool.")
		}
	}
	return s.Store.Delete(apiContext, schema, id)
}

func (s *nodeTemplateStore) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
	if err := s.replaceCloudCredFields(apiContext, data); err != nil {
		return data, err
	}
	return s.Store.Create(apiContext, schema, data)
}

func (s *nodeTemplateStore) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
	if err := s.replaceCloudCredFields(apiContext, data); err != nil {
		return data, err
	}
	return s.Store.Update(apiContext, schema, data, id)
}

func (s *nodeTemplateStore) replaceCloudCredFields(apiContext *types.APIContext, data map[string]interface{}) error {
	credID := convert.ToString(values.GetValueN(data, "cloudCredentialId"))
	if credID == "" {
		return nil
	}
	var accessCred client.CloudCredential
	if err := access.ByID(apiContext, &managementschema.Version, client.CloudCredentialType, credID, &accessCred); err != nil {
		if apiError, ok := err.(*httperror.APIError); ok {
			if apiError.Code.Status == httperror.PermissionDenied.Status || apiError.Code.Status == httperror.NotFound.Status {
				return httperror.NewAPIError(httperror.NotFound, fmt.Sprintf("cloud credential not found"))
			}
		}
		return httperror.WrapAPIError(err, httperror.ServerError, fmt.Sprintf("error accessing cloud credential"))
	}
	ns, name := ref.Parse(credID)
	if ns == "" || name == "" {
		return httperror.NewAPIError(httperror.InvalidReference, fmt.Sprintf("invalid cloud credential %s", credID))
	}
	cred, err := s.CloudCredentialLister.Get(namespace.GlobalNamespace, name)
	if err != nil {
		return httperror.WrapAPIError(err, httperror.ServerError, fmt.Sprintf("error getting cloud cred %s: %v", credID, err))
	}
	if len(cred.Data) == 0 {
		return httperror.WrapAPIError(err, httperror.MissingRequired, fmt.Sprintf("empty credID data %s", credID))
	}
	configName, credConfigName := "", ""
	for key := range cred.Data {
		splitKey := strings.SplitN(key, "-", 2)
		if len(splitKey) == 2 && strings.HasSuffix(splitKey[0], "credentialConfig") {
			configName = strings.Replace(splitKey[0], "credential", "", 1)
			credConfigName = splitKey[0]
			break
		}
	}
	if configName == "" {
		return httperror.WrapAPIError(err, httperror.MissingRequired, fmt.Sprintf("empty configName for credID %s", configName))
	}
	toReplace := convert.ToMapInterface(values.GetValueN(data, configName))
	if len(toReplace) == 0 {
		return nil
	}
	var fields []string
	for key := range cred.Data {
		splitKey := strings.SplitN(key, "-", 2)
		if len(splitKey) == 2 && splitKey[0] == credConfigName {
			delete(toReplace, splitKey[1])
			fields = append(fields, splitKey[1])
		}
	}
	logrus.Debugf("replaceCloudCredFields: %v for credID %s", fields, credID)
	return nil
}
<commit_msg>Return migrated node template<commit_after>package nodetemplate

import (
	"fmt"
	"strings"

	"github.com/rancher/norman/api/access"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/store/transform"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/norman/types/values"
	"github.com/rancher/rancher/pkg/namespace"
	"github.com/rancher/rancher/pkg/ref"
	corev1 "github.com/rancher/types/apis/core/v1"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	managementschema "github.com/rancher/types/apis/management.cattle.io/v3/schema"
	client "github.com/rancher/types/client/management/v3"
	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/labels"
)

type nodeTemplateStore struct {
	types.Store
	NodePoolLister        v3.NodePoolLister
	CloudCredentialLister corev1.SecretLister
}

type transformer struct {
	NodeTemplateClient v3.NodeTemplateInterface
}

type nodeTemplateIDs struct {
	fullOriginalID string
	originalID     string
	originalNs     string
	fullMigratedID string
	migratedID     string
	migratedNs     string
}

func Wrap(store types.Store, npLister v3.NodePoolLister, secretLister corev1.SecretLister, ntClient v3.NodeTemplateInterface) types.Store {
	t := &transformer{
		NodeTemplateClient: ntClient,
	}
	s := &transform.Store{
		Store:       store,
		Transformer: t.filterLegacyTemplates,
	}
	return &nodeTemplateStore{
		Store:                 s,
		NodePoolLister:        npLister,
		CloudCredentialLister: secretLister,
	}
}

func (t *transformer) filterLegacyTemplates(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, opt *types.QueryOptions) (map[string]interface{}, error) {
	ns, _ := data["namespaceId"].(string)
	id, _ := data["id"].(string)
	if ns != namespace.NodeTemplateGlobalNamespace {
		// nodetemplate may not have been migrated yet, enqueue
		t.NodeTemplateClient.Controller().Enqueue(ns, strings.TrimPrefix(id, ns+":"))
		return nil, nil
	}
	return data, nil
}

func (s *nodeTemplateStore) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
	ids := getAllIDs(id)
	data, err := s.Store.ByID(apiContext, schema, ids.fullMigratedID)
	if err != nil {
		return nil, replaceIDInError(err, ids.migratedID, ids.originalID, ids.migratedNs, ids.originalNs)
	}

	data["namespaceId"] = ids.originalNs
	data["id"] = ids.fullOriginalID
	return data, nil
}

// getAllIDs returns the namespace and trimmed ID of the original ID
// and its corresponding migrated ID
func getAllIDs(id string) nodeTemplateIDs {
	originalNs, originalID := ref.Parse(id)
	fullMigratedID := getMigratedID(originalNs, originalID)
	migratedNs, migratedID := ref.Parse(fullMigratedID)
	return nodeTemplateIDs{
		fullOriginalID: id,
		originalID:     originalID,
		originalNs:     originalNs,
		fullMigratedID: fullMigratedID,
		migratedID:     migratedID,
		migratedNs:     migratedNs,
	}
}

func getMigratedID(ns, id string) string {
	if ns == namespace.NodeTemplateGlobalNamespace {
		return fmt.Sprintf("%s:%s", ns, id)
	}

	return fmt.Sprintf("%s:nt-%s-%s", namespace.NodeTemplateGlobalNamespace, ns, id)
}

func replaceIDInError(err error, id, newID, ns, newNs string) error {
	if apiError, ok := err.(*httperror.APIError); ok {
		msg := strings.Replace(apiError.Message, id, newID, -1)
		msg = strings.Replace(msg, ns, newNs, -1)
		err = httperror.NewAPIError(apiError.Code, msg)
	}
	return err
}

func (s *nodeTemplateStore) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
	ids := getAllIDs(id)
	pools, err := s.NodePoolLister.List("", labels.Everything())
	if err != nil {
		return nil, err
	}
	for _, pool := range pools {
		if pool.Spec.NodeTemplateName == ids.fullMigratedID {
			return nil, httperror.NewAPIError(httperror.MethodNotAllowed, "Template is in use by a node pool.")
		}
	}
	data, err := s.Store.Delete(apiContext, schema, ids.fullMigratedID)
	if err != nil {
		return nil, replaceIDInError(err, ids.migratedID, ids.originalID, ids.migratedNs, ids.originalNs)
	}

	if data != nil {
		data["namespaceId"] = ids.originalNs
		data["id"] = ids.fullOriginalID
	}

	return data, nil
}

func (s *nodeTemplateStore) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
	if err := s.replaceCloudCredFields(apiContext, data); err != nil {
		return data, err
	}
	return s.Store.Create(apiContext, schema, data)
}

func (s *nodeTemplateStore) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
	if err := s.replaceCloudCredFields(apiContext, data); err != nil {
		return data, err
	}

	ids := getAllIDs(id)
	data, err := s.Store.Update(apiContext, schema, data, ids.fullMigratedID)
	if err != nil {
		return nil, replaceIDInError(err, ids.migratedID, ids.originalID, ids.migratedNs, ids.originalNs)
	}

	data["namespaceId"] = ids.originalNs
	data["id"] = ids.fullOriginalID
	return data, nil
}

func (s *nodeTemplateStore) replaceCloudCredFields(apiContext *types.APIContext, data map[string]interface{}) error {
	credID := convert.ToString(values.GetValueN(data, "cloudCredentialId"))
	if credID == "" {
		return nil
	}
	var accessCred client.CloudCredential
	if err := access.ByID(apiContext, &managementschema.Version, client.CloudCredentialType, credID, &accessCred); err != nil {
		if apiError, ok := err.(*httperror.APIError); ok {
			if apiError.Code.Status == httperror.PermissionDenied.Status || apiError.Code.Status == httperror.NotFound.Status {
				return httperror.NewAPIError(httperror.NotFound, fmt.Sprintf("cloud credential not found"))
			}
		}
		return httperror.WrapAPIError(err, httperror.ServerError, fmt.Sprintf("error accessing cloud credential"))
	}
	ns, name := ref.Parse(credID)
	if ns == "" || name == "" {
		return httperror.NewAPIError(httperror.InvalidReference, fmt.Sprintf("invalid cloud credential %s", credID))
	}
	cred, err := s.CloudCredentialLister.Get(namespace.GlobalNamespace, name)
	if err != nil {
		return httperror.WrapAPIError(err, httperror.ServerError, fmt.Sprintf("error getting cloud cred %s: %v", credID, err))
	}
	if len(cred.Data) == 0 {
		return httperror.WrapAPIError(err, httperror.MissingRequired, fmt.Sprintf("empty credID data %s", credID))
	}
	configName, credConfigName := "", ""
	for key := range cred.Data {
		splitKey := strings.SplitN(key, "-", 2)
		if len(splitKey) == 2 && strings.HasSuffix(splitKey[0], "credentialConfig") {
			configName = strings.Replace(splitKey[0], "credential", "", 1)
			credConfigName = splitKey[0]
			break
		}
	}
	if configName == "" {
		return httperror.WrapAPIError(err, httperror.MissingRequired, fmt.Sprintf("empty configName for credID %s", configName))
	}
	toReplace := convert.ToMapInterface(values.GetValueN(data, configName))
	if len(toReplace) == 0 {
		return nil
	}
	var fields []string
	for key := range cred.Data {
		splitKey := strings.SplitN(key, "-", 2)
		if len(splitKey) == 2 && splitKey[0] == credConfigName {
			delete(toReplace, splitKey[1])
			fields = append(fields, splitKey[1])
		}
	}
	logrus.Debugf("replaceCloudCredFields: %v for credID %s", fields, credID)
	return nil
}
<|endoftext|>