<commit_before>package validation

import (
	"regexp"
)

func Eq(a interface{}) Validator {
	return ValidatorFunc(func(v Valuer, r Requester) error {
		if a != v.Value() {
			return ErrEq.Fmt([]interface{}{v.Name(), a}...)
		}
		return nil
	})
}

func Regexp(p *regexp.Regexp) Validator {
	return ValidatorFunc(func(v Valuer, r Requester) error {
		if !p.MatchString(v.String()) {
			return ErrRegexp.Fmt([]interface{}{v.Name(), p.String()}...)
		}
		return nil
	})
}

<commit_msg>Implementing validation condition<commit_after>package validation

import (
	"regexp"
)

func Eq(a interface{}) Validator {
	return ValidatorFunc(func(v Valuer, r Requester) error {
		if a != v.Value() {
			return ErrEq.Fmt([]interface{}{v.Name(), a}...)
		}
		return nil
	})
}

func Regexp(p *regexp.Regexp) Validator {
	return ValidatorFunc(func(v Valuer, r Requester) error {
		if !p.MatchString(v.String()) {
			return ErrRegexp.Fmt([]interface{}{v.Name(), p.String()}...)
		}
		return nil
	})
}

func Condition(f func(Valuer, Requester) bool, validators ...Validator) Validator {
	return ValidatorFunc(func(v Valuer, r Requester) error {
		if f(v, r) {
			for _, va := range validators {
				err := va.Validate(v, r)
				if err != nil {
					return err
				}
			}
		}
		return nil
	})
}
<|endoftext|>


<commit_before>package task

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"runtime"
	"strings"
	"text/template"

	"github.com/BurntSushi/toml"
	"gopkg.in/yaml.v2"
)

var (
	// TaskvarsFilePath file containing additional variables
	TaskvarsFilePath = "Taskvars"
)

func (t Task) handleVariables() (map[string]string, error) {
	localVariables := make(map[string]string)
	for key, value := range t.Vars {
		localVariables[key] = value
	}
	if fileVariables, err := readTaskvarsFile(); err == nil {
		for key, value := range fileVariables {
			localVariables[key] = value
		}
	} else {
		return nil, err
	}
	for key, value := range getEnvironmentVariables() {
		localVariables[key] = value
	}
	return localVariables, nil
}

var templateFuncs = template.FuncMap{
	"OS":   func() string { return runtime.GOOS },
	"ARCH": func() string { return runtime.GOARCH },
	"IsSH": func() bool { return ShExists },
}

// ReplaceVariables writes vars into initial string
func ReplaceVariables(initial string, vars map[string]string) (string, error) {
	t, err := template.New("").Funcs(templateFuncs).Parse(initial)
	if err != nil {
		return "", err
	}
	b := bytes.NewBuffer(nil)
	if err = t.Execute(b, vars); err != nil {
		return "", err
	}
	return b.String(), nil
}

// GetEnvironmentVariables returns environment variables as map
func getEnvironmentVariables() map[string]string {
	type getKeyValFunc func(item string) (key, val string)
	getEnvironment := func(data []string, getKeyVal getKeyValFunc) map[string]string {
		items := make(map[string]string)
		for _, item := range data {
			key, val := getKeyVal(item)
			items[key] = val
		}
		return items
	}
	return getEnvironment(os.Environ(), func(item string) (key, val string) {
		splits := strings.Split(item, "=")
		key = splits[0]
		val = splits[1]
		return
	})
}

func readTaskvarsFile() (map[string]string, error) {
	var variables map[string]string
	if b, err := ioutil.ReadFile(TaskvarsFilePath + ".yml"); err == nil {
		if err := yaml.Unmarshal(b, &variables); err != nil {
			return nil, err
		}
		return variables, nil
	}
	if b, err := ioutil.ReadFile(TaskvarsFilePath + ".json"); err == nil {
		if err := json.Unmarshal(b, &variables); err != nil {
			return nil, err
		}
		return variables, nil
	}
	if b, err := ioutil.ReadFile(TaskvarsFilePath + ".toml"); err == nil {
		if err := toml.Unmarshal(b, &variables); err != nil {
			return nil, err
		}
		return variables, nil
	}
	return variables, nil
}
<commit_msg>First implementation of dynamic variables<commit_after>package task

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"text/template"

	"fmt"

	"github.com/BurntSushi/toml"
	"gopkg.in/yaml.v2"
)

var (
	// TaskvarsFilePath file containing additional variables
	TaskvarsFilePath = "Taskvars"
	// DynamicVariablePattern is a pattern to test if a variable should get filled from running the content. It must contain a command group
	DynamicVariablePattern = "^@(?P<command>.*)" // alternative proposal: ^$((?P<command>.*))$
	// ErrCommandGroupNotFound returned when the command group is not present
	ErrCommandGroupNotFound = fmt.Errorf("%s does not contain the command group", DynamicVariablePattern)
)

func handleDynamicVariableContent(value string) (string, error) {
	if value == "" {
		return value, nil
	}
	re := regexp.MustCompile(DynamicVariablePattern)
	if !re.MatchString(value) {
		return value, nil
	}
	subExpressionIndex := 0
	for index, value := range re.SubexpNames() {
		if value == "command" {
			subExpressionIndex = index
			break
		}
	}
	if subExpressionIndex == 0 {
		return "", ErrCommandGroupNotFound
	}
	var cmd *exec.Cmd
	if ShExists {
		cmd = exec.Command(ShPath, "-c", re.FindStringSubmatch(value)[subExpressionIndex])
	} else {
		cmd = exec.Command("cmd", "/C", re.FindStringSubmatch(value)[subExpressionIndex])
	}
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	bytes, err := cmd.Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(bytes)), nil
}

func (t Task) handleVariables() (map[string]string, error) {
	localVariables := make(map[string]string)
	for key, value := range t.Vars {
		val, err := handleDynamicVariableContent(value)
		if err != nil {
			return nil, err
		}
		localVariables[key] = val
	}
	if fileVariables, err := readTaskvarsFile(); err == nil {
		for key, value := range fileVariables {
			val, err := handleDynamicVariableContent(value)
			if err != nil {
				return nil, err
			}
			localVariables[key] = val
		}
	} else {
		return nil, err
	}
	for key, value := range getEnvironmentVariables() {
		val, err := handleDynamicVariableContent(value)
		if err != nil {
			return nil, err
		}
		localVariables[key] = val
	}
	return localVariables, nil
}

var templateFuncs = template.FuncMap{
	"OS":   func() string { return runtime.GOOS },
	"ARCH": func() string { return runtime.GOARCH },
	"IsSH": func() bool { return ShExists },
}

// ReplaceVariables writes vars into initial string
func ReplaceVariables(initial string, vars map[string]string) (string, error) {
	t, err := template.New("").Funcs(templateFuncs).Parse(initial)
	if err != nil {
		return "", err
	}
	b := bytes.NewBuffer(nil)
	if err = t.Execute(b, vars); err != nil {
		return "", err
	}
	return b.String(), nil
}

// GetEnvironmentVariables returns environment variables as map
func getEnvironmentVariables() map[string]string {
	type getKeyValFunc func(item string) (key, val string)
	getEnvironment := func(data []string, getKeyVal getKeyValFunc) map[string]string {
		items := make(map[string]string)
		for _, item := range data {
			key, val := getKeyVal(item)
			items[key] = val
		}
		return items
	}
	return getEnvironment(os.Environ(), func(item string) (key, val string) {
		splits := strings.Split(item, "=")
		key = splits[0]
		val = splits[1]
		return
	})
}

func readTaskvarsFile() (map[string]string, error) {
	var variables map[string]string
	if b, err := ioutil.ReadFile(TaskvarsFilePath + ".yml"); err == nil {
		if err := yaml.Unmarshal(b, &variables); err != nil {
			return nil, err
		}
		return variables, nil
	}
	if b, err := ioutil.ReadFile(TaskvarsFilePath + ".json"); err == nil {
		if err := json.Unmarshal(b, &variables); err != nil {
			return nil, err
		}
		return variables, nil
	}
	if b, err := ioutil.ReadFile(TaskvarsFilePath + ".toml"); err == nil {
		if err := toml.Unmarshal(b, &variables); err != nil {
			return nil, err
		}
		return variables, nil
	}
	return variables, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	. "github.com/limetext/text"
	"testing"
)

type indentTest struct {
	text                     string
	translate_tabs_to_spaces interface{}
	tab_size                 interface{}
	sel                      []Region
	expect                   string
}

func runIndentTest(t *testing.T, tests []indentTest, command string) {
	ed := GetEditor()
	w := ed.NewWindow()
	defer w.Close()

	for i, test := range tests {
		v := w.NewFile()
		defer func() {
			v.SetScratch(true)
			v.Close()
		}()

		e := v.BeginEdit()
		v.Insert(e, 0, test.text)
		v.EndEdit(e)

		v.Sel().Clear()
		for _, r := range test.sel {
			v.Sel().Add(r)
		}
		v.Settings().Set("translate_tabs_to_spaces", test.translate_tabs_to_spaces)
		v.Settings().Set("tab_size", test.tab_size)

		ed.CommandHandler().RunTextCommand(v, command, nil)
		if d := v.Buffer().Substr(Region{0, v.Buffer().Size()}); d != test.expect {
			t.Errorf("Test %d: Expected \n%s, but got \n%s", i, test.expect, d)
		}
	}
}

func TestIndent(t *testing.T) {
	tests := []indentTest{
		{ // translate_tabs_to_spaces = false
			// indent should be "\t"
			"a\n b\n  c\n   d\n",
			false,
			4,
			[]Region{{0, 1}},
			"\ta\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = nil
			// indent should be "\t"
			"a\n b\n  c\n   d\n",
			nil,
			1,
			[]Region{{0, 1}},
			"\ta\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = 2
			// indent should be "  "
			"a\n b\n  c\n   d\n",
			true,
			2,
			[]Region{{0, 1}},
			"  a\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = nil
			// indent should be "    "
			"a\n b\n  c\n   d\n",
			true,
			nil,
			[]Region{{0, 1}},
			"    a\n b\n  c\n   d\n",
		},
		{ // region include the 1st line and the 4th line
			// indent should add to the begining of 1st and 4th line
			"a\n b\n  c\n   d\n",
			false,
			1,
			[]Region{{0, 1}, {11, 12}},
			"\ta\n b\n  c\n\t   d\n",
		},
		{ // region selected reversely
			// should perform indent
			"a\n b\n  c\n   d\n",
			false,
			1,
			[]Region{{3, 0}},
			"\ta\n\t b\n  c\n   d\n",
		},
	}

	runIndentTest(t, tests, "indent")
}

func TestUnindent(t *testing.T) {
	tests := []indentTest{
		{ // translate_tabs_to_spaces = false
			// indent should be "\t"
			"\ta\n  b\n      c\n\t  d\n",
			false,
			4,
			[]Region{{0, 19}},
			"a\nb\n  c\n  d\n",
		},
		{ // translate_tabs_to_spaces = nil
			// indent should be "\t"
			"\ta\n b\n  c\n   d\n",
			nil,
			1,
			[]Region{{0, 1}},
			"a\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = 2
			// indent should be "  "
			"  a\n b\n  c\n   d\n",
			true,
			2,
			[]Region{{0, 1}},
			"a\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = nil
			// indent should be "    "
			"    a\n b\n  c\n   d\n",
			true,
			nil,
			[]Region{{0, 1}},
			"a\n b\n  c\n   d\n",
		},
		{ // region include the 1st line and the 4th line
			// unindent should remove from the begining of 1st and 4th line
			"\ta\n b\n  c\n \t   d\n",
			false,
			1,
			[]Region{{0, 1}, {11, 12}},
			"a\n b\n  c\n\t   d\n",
		},
		{ // region selected reversely
			// should perform unindent
			"\ta\n\t b\n  c\n   d\n",
			false,
			4,
			[]Region{{3, 0}},
			"a\n b\n  c\n   d\n",
		},
	}

	runIndentTest(t, tests, "unindent")
}
<commit_msg>indent.go coverage to 100%<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	. "github.com/limetext/text"
	"testing"
)

type indentTest struct {
	text                     string
	translate_tabs_to_spaces interface{}
	tab_size                 interface{}
	sel                      []Region
	expect                   string
}

func runIndentTest(t *testing.T, tests []indentTest, command string) {
	ed := GetEditor()
	w := ed.NewWindow()
	defer w.Close()

	for i, test := range tests {
		v := w.NewFile()
		defer func() {
			v.SetScratch(true)
			v.Close()
		}()

		e := v.BeginEdit()
		v.Insert(e, 0, test.text)
		v.EndEdit(e)

		v.Sel().Clear()
		for _, r := range test.sel {
			v.Sel().Add(r)
		}
		v.Settings().Set("translate_tabs_to_spaces", test.translate_tabs_to_spaces)
		v.Settings().Set("tab_size", test.tab_size)

		ed.CommandHandler().RunTextCommand(v, command, nil)
		if d := v.Buffer().Substr(Region{0, v.Buffer().Size()}); d != test.expect {
			t.Errorf("Test %d: Expected \n%s, but got \n%s", i, test.expect, d)
		}
	}
}

func TestIndent(t *testing.T) {
	tests := []indentTest{
		{ // translate_tabs_to_spaces = false
			// indent should be "\t"
			"a\n b\n  c\n   d\n",
			false,
			4,
			[]Region{{0, 1}},
			"\ta\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = nil
			// indent should be "\t"
			"a\n b\n  c\n   d\n",
			nil,
			1,
			[]Region{{0, 1}},
			"\ta\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = 2
			// indent should be "  "
			"a\n b\n  c\n   d\n",
			true,
			2,
			[]Region{{0, 1}},
			"  a\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = nil
			// indent should be "    "
			"a\n b\n  c\n   d\n",
			true,
			nil,
			[]Region{{0, 1}},
			"    a\n b\n  c\n   d\n",
		},
		{ // region include the 1st line and the 4th line
			// indent should add to the begining of 1st and 4th line
			"a\n b\n  c\n   d\n",
			false,
			1,
			[]Region{{0, 1}, {11, 12}},
			"\ta\n b\n  c\n\t   d\n",
		},
		{ // region selected reversely
			// should perform indent
			"a\n b\n  c\n   d\n",
			false,
			1,
			[]Region{{3, 0}},
			"\ta\n\t b\n  c\n   d\n",
		},
	}

	runIndentTest(t, tests, "indent")
}

func TestUnindent(t *testing.T) {
	tests := []indentTest{
		{ // translate_tabs_to_spaces = false
			// indent should be "\t"
			"\ta\n  b\n      c\n\t  d\n",
			false,
			4,
			[]Region{{0, 19}},
			"a\nb\n  c\n  d\n",
		},
		{ // translate_tabs_to_spaces = nil
			// indent should be "\t"
			"\ta\n b\n  c\n   d\n",
			nil,
			1,
			[]Region{{0, 1}},
			"a\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = 2
			// indent should be "  "
			"  a\n b\n  c\n   d\n",
			true,
			2,
			[]Region{{0, 1}},
			"a\n b\n  c\n   d\n",
		},
		{ // translate_tabs_to_spaces = true and tab_size = nil
			// indent should be "    "
			"    a\n b\n  c\n   d\n",
			true,
			nil,
			[]Region{{0, 1}},
			"a\n b\n  c\n   d\n",
		},
		{ // region include the 1st line and the 4th line
			// unindent should remove from the begining of 1st and 4th line
			"\ta\n b\n  c\n \t   d\n",
			false,
			1,
			[]Region{{0, 1}, {11, 12}},
			"a\n b\n  c\n\t   d\n",
		},
		{ // region selected reversely
			// should perform unindent
			"\ta\n\t b\n  c\n   d\n",
			false,
			4,
			[]Region{{3, 0}},
			"a\n b\n  c\n   d\n",
		},
		{ // empty strings
			// should perform unindent
			"",
			false,
			nil,
			[]Region{{0, 0}},
			"",
		},
	}

	runIndentTest(t, tests, "unindent")
}
<|endoftext|>


<commit_before>// Copyright © 2016 Theotime LEVEQUE theotime@protonmail.com
//
// 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 packagemanagers

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thylong/ian/backend/command"
)

// Pip immutable instance.
var Pip = PipPackageManager{Path: filepath.Clean("/usr/local/bin/pip"), Name: "pip"}

// ErrPipMissingFeature is returned when triggering an unsupported feature.
var ErrPipMissingFeature = errors.New("pip is not designed to support this feature")

// PipPackageManager is a (widely used) unofficial Mac OS package manager.
// (more: https://pip.sh/)
type PipPackageManager struct {
	Path string
	Name string
}

// Install given Pip package.
func (pm *PipPackageManager) Install(packageName string) (err error) {
	if err = command.ExecuteCommand(execCommand(pm.Path, "install", "-U", packageName)); err != nil {
		return fmt.Errorf("Cannot %s install %s: %s", pm.Name, packageName, err)
	}
	return err
}

// Uninstall given Pip package.
func (pm *PipPackageManager) Uninstall(packageName string) (err error) {
	if err = command.ExecuteCommand(execCommand(pm.Path, "uninstall", "-U", packageName)); err != nil {
		return fmt.Errorf("Cannot %s uninstall %s: %s", pm.Name, packageName, err)
	}
	return err
}

// Cleanup the pip cache.
// This is done by default since pip 6.0
func (pm *PipPackageManager) Cleanup() error {
	return ErrPipMissingFeature
}

// UpdateOne pulls last versions infos from related repositories.
// This is not performing any updates and should be coupled
// with upgradeAll command.
func (pm *PipPackageManager) UpdateOne(packageName string) (err error) {
	return ErrPipMissingFeature
}

// UpgradeOne Pip packages to the last known versions.
func (pm *PipPackageManager) UpgradeOne(packageName string) (err error) {
	return pm.Install(packageName)
}

// UpdateAll pulls last versions infos from realted repositories.
// This is not performing any updates and should be coupled
// with upgradeAll command.
func (pm *PipPackageManager) UpdateAll() (err error) {
	// TODO: Implementation
	return ErrPipMissingFeature
}

// UpgradeAll Pip packages to the last known versions.
func (pm *PipPackageManager) UpgradeAll() (err error) {
	// TODO: Implementation
	return ErrPipMissingFeature
}

// IsInstalled returns true if Pip executable is found.
func (pm *PipPackageManager) IsInstalled() bool {
	if _, err := os.Stat(pm.Path); err != nil {
		return false
	}
	return true
}

// IsOSPackageManager returns false because pip is never the main OS Package Manager.
func (pm *PipPackageManager) IsOSPackageManager() bool {
	return false
}

// GetExecPath return immutable path to Pip executable.
func (pm *PipPackageManager) GetExecPath() string {
	return pm.Path
}

// GetName return the name of the package manager.
func (pm *PipPackageManager) GetName() string {
	return pm.Name
}

// Setup installs Cask
func (pm *PipPackageManager) Setup() (err error) {
	return nil
}
<commit_msg>PackageManager: Fix pip on MacOS Sierra.<commit_after>// Copyright © 2016 Theotime LEVEQUE theotime@protonmail.com
//
// 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 packagemanagers

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thylong/ian/backend/command"
)

// Pip immutable instance.
var Pip = PipPackageManager{Path: GetDefaultPath(), Name: "pip"}

// ErrPipMissingFeature is returned when triggering an unsupported feature.
var ErrPipMissingFeature = errors.New("pip is not designed to support this feature")

// PipPackageManager is a (widely used) unofficial Mac OS package manager.
// (more: https://pip.sh/)
type PipPackageManager struct {
	Path string
	Name string
}

// Install given Pip package.
func (pm *PipPackageManager) Install(packageName string) (err error) {
	if err = command.ExecuteCommand(execCommand(pm.Path, "install", "-U", packageName)); err != nil {
		return fmt.Errorf("Cannot %s install %s: %s", pm.Name, packageName, err)
	}
	return err
}

// Uninstall given Pip package.
func (pm *PipPackageManager) Uninstall(packageName string) (err error) {
	if err = command.ExecuteCommand(execCommand(pm.Path, "uninstall", "-U", packageName)); err != nil {
		return fmt.Errorf("Cannot %s uninstall %s: %s", pm.Name, packageName, err)
	}
	return err
}

// Cleanup the pip cache.
// This is done by default since pip 6.0
func (pm *PipPackageManager) Cleanup() error {
	return ErrPipMissingFeature
}

// UpdateOne pulls last versions infos from related repositories.
// This is not performing any updates and should be coupled
// with upgradeAll command.
func (pm *PipPackageManager) UpdateOne(packageName string) (err error) {
	return ErrPipMissingFeature
}

// UpgradeOne Pip packages to the last known versions.
func (pm *PipPackageManager) UpgradeOne(packageName string) (err error) {
	return pm.Install(packageName)
}

// UpdateAll pulls last versions infos from realted repositories.
// This is not performing any updates and should be coupled
// with upgradeAll command.
func (pm *PipPackageManager) UpdateAll() (err error) {
	// TODO: Implementation
	return ErrPipMissingFeature
}

// UpgradeAll Pip packages to the last known versions.
func (pm *PipPackageManager) UpgradeAll() (err error) {
	// TODO: Implementation
	return ErrPipMissingFeature
}

// IsInstalled returns true if Pip executable is found.
func (pm *PipPackageManager) IsInstalled() bool {
	if _, err := os.Stat(pm.Path); err != nil {
		return false
	}
	return true
}

// IsOSPackageManager returns false because pip is never the main OS Package Manager.
func (pm *PipPackageManager) IsOSPackageManager() bool {
	return false
}

// GetExecPath return immutable path to Pip executable.
func (pm *PipPackageManager) GetExecPath() string {
	return pm.Path
}

// GetDefaultPath return default path to Pip executable.
func GetDefaultPath() string {
	defaultPath := filepath.Clean("/usr/local/bin/pip")
	if _, err := os.Stat(defaultPath); err != nil {
		if _, err := os.Stat(fmt.Sprintf("%s%s", defaultPath, "2")); err != nil {
			return defaultPath
		}
		return fmt.Sprintf("%s%s", defaultPath, "2")
	}
	return defaultPath
}

// GetName return the name of the package manager.
func (pm *PipPackageManager) GetName() string {
	return pm.Name
}

// Setup installs Cask
func (pm *PipPackageManager) Setup() (err error) {
	return nil
}
<|endoftext|>


<commit_before>package db

import (
	"crypto/sha256"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"

	sq "github.com/Masterminds/squirrel"
	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/db/lock"
)

var ErrResourceCacheAlreadyExists = errors.New("resource-cache-already-exists")
var ErrResourceCacheDisappeared = errors.New("resource-cache-disappeared")

// ResourceCache represents an instance of a ResourceConfig's version.
//
// A ResourceCache is created by a `get`, an `image_resource`, or a resource
// type in a pipeline.
//
// ResourceCaches are garbage-collected by gc.ResourceCacheCollector.
type ResourceCacheDescriptor struct {
	ResourceConfigDescriptor ResourceConfigDescriptor // The resource configuration.
	Version                  atc.Version              // The version of the resource.
	Params                   atc.Params               // The params used when fetching the version.
}

func (cache *ResourceCacheDescriptor) findOrCreate(
	tx Tx,
	lockFactory lock.LockFactory,
	conn Conn,
) (UsedResourceCache, error) {
	resourceConfig, err := cache.ResourceConfigDescriptor.findOrCreate(tx, lockFactory, conn)
	if err != nil {
		return nil, err
	}

	rc, found, err := cache.findWithResourceConfig(tx, resourceConfig, lockFactory, conn)
	if err != nil {
		return nil, err
	}

	if !found {
		var id int
		err = psql.Insert("resource_caches").
			Columns(
				"resource_config_id",
				"version",
				"version_md5",
				"params_hash",
			).
			Values(
				resourceConfig.ID(),
				cache.version(),
				sq.Expr("md5(?)", cache.version()),
				paramsHash(cache.Params),
			).
			Suffix(`
				ON CONFLICT (resource_config_id, version_md5, params_hash) DO UPDATE SET
				resource_config_id = EXCLUDED.resource_config_id,
				version = EXCLUDED.version,
				version_md5 = EXCLUDED.version_md5,
				params_hash = EXCLUDED.params_hash
				RETURNING id
			`).
			RunWith(tx).
			QueryRow().
			Scan(&id)
		if err != nil {
			return nil, err
		}

		rc = &usedResourceCache{
			id:             id,
			version:        cache.Version,
			resourceConfig: resourceConfig,
			lockFactory:    lockFactory,
			conn:           conn,
		}
	}

	return rc, nil
}

func (cache *ResourceCacheDescriptor) use(
	tx Tx,
	rc UsedResourceCache,
	user ResourceCacheUser,
) error {
	cols := user.SQLMap()
	cols["resource_cache_id"] = rc.ID()

	var resourceCacheUseExists int
	err := psql.Select("1").
		From("resource_cache_uses").
		Where(sq.Eq(cols)).
		RunWith(tx).
		QueryRow().
		Scan(&resourceCacheUseExists)
	if err != nil {
		if err != sql.ErrNoRows {
			return err
		}
	}

	if err == nil {
		// use already exists
		return nil
	}

	_, err = psql.Insert("resource_cache_uses").
		SetMap(cols).
		RunWith(tx).
		Exec()
	return err
}

func (cache *ResourceCacheDescriptor) findWithResourceConfig(tx Tx, resourceConfig ResourceConfig, lockFactory lock.LockFactory, conn Conn) (UsedResourceCache, bool, error) {
	var id int
	err := psql.Select("id").
		From("resource_caches").
		Where(sq.Eq{
			"resource_config_id": resourceConfig.ID(),
			"params_hash":        paramsHash(cache.Params),
		}).
		Where(sq.Expr("version_md5 = md5(?)", cache.version())).
		Suffix("FOR SHARE").
		RunWith(tx).
		QueryRow().
		Scan(&id)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, false, nil
		}

		return nil, false, err
	}

	return &usedResourceCache{
		id:             id,
		version:        cache.Version,
		resourceConfig: resourceConfig,
		lockFactory:    lockFactory,
		conn:           conn,
	}, true, nil
}

func (cache *ResourceCacheDescriptor) version() string {
	j, _ := json.Marshal(cache.Version)
	return string(j)
}

func paramsHash(p atc.Params) string {
	if p != nil {
		return mapHash(p)
	}

	return mapHash(atc.Params{})
}

// UsedResourceCache is created whenever a ResourceCache is Created and/or
// Used.
//
// So long as the UsedResourceCache exists, the underlying ResourceCache can
// not be removed.
//
// UsedResourceCaches become unused by the gc.ResourceCacheCollector, which may
// then lead to the ResourceCache being garbage-collected.
//
// See FindOrCreateForBuild, FindOrCreateForResource, and
// FindOrCreateForResourceType for more information on when it becomes unused.

//go:generate counterfeiter . UsedResourceCache

type UsedResourceCache interface {
	ID() int
	Version() atc.Version
	LoadVersionMetadata() ([]atc.MetadataField, error)

	ResourceConfig() ResourceConfig

	Destroy(Tx) (bool, error)
	BaseResourceType() *UsedBaseResourceType
}

type usedResourceCache struct {
	id             int
	resourceConfig ResourceConfig
	version        atc.Version

	lockFactory lock.LockFactory
	conn        Conn
}

func (cache *usedResourceCache) ID() int                        { return cache.id }
func (cache *usedResourceCache) ResourceConfig() ResourceConfig { return cache.resourceConfig }
func (cache *usedResourceCache) Version() atc.Version           { return cache.version }

func (cache *usedResourceCache) Destroy(tx Tx) (bool, error) {
	rows, err := psql.Delete("resource_caches").
		Where(sq.Eq{
			"id": cache.id,
		}).
		RunWith(tx).
		Exec()
	if err != nil {
		return false, err
	}

	affected, err := rows.RowsAffected()
	if err != nil {
		return false, err
	}

	if affected == 0 {
		return false, ErrResourceCacheDisappeared
	}

	return true, nil
}

func (cache *usedResourceCache) BaseResourceType() *UsedBaseResourceType {
	if cache.resourceConfig.CreatedByBaseResourceType() != nil {
		return cache.resourceConfig.CreatedByBaseResourceType()
	}

	return cache.resourceConfig.CreatedByResourceCache().BaseResourceType()
}

func (cache *usedResourceCache) LoadVersionMetadata() ([]atc.MetadataField, error) {
	r := &resourceConfigVersion{conn: cache.conn}

	versionJSON, _ := json.Marshal(cache.Version())
	row := resourceConfigVersionQuery.
		Where(sq.Expr("v.version_md5 = md5(?)", versionJSON)).
		RunWith(r.conn).
		QueryRow()

	err := scanResourceConfigVersion(r, row)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	return r.metadata.ToATCMetadata(), nil
}

func mapHash(m map[string]interface{}) string {
	j, _ := json.Marshal(m)
	return fmt.Sprintf("%x", sha256.Sum256(j))
}
<commit_msg>address review comments.<commit_after>package db

import (
	"crypto/sha256"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"

	sq "github.com/Masterminds/squirrel"
	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/db/lock"
)

var ErrResourceCacheAlreadyExists = errors.New("resource-cache-already-exists")
var ErrResourceCacheDisappeared = errors.New("resource-cache-disappeared")

// ResourceCache represents an instance of a ResourceConfig's version.
//
// A ResourceCache is created by a `get`, an `image_resource`, or a resource
// type in a pipeline.
//
// ResourceCaches are garbage-collected by gc.ResourceCacheCollector.
type ResourceCacheDescriptor struct {
	ResourceConfigDescriptor ResourceConfigDescriptor // The resource configuration.
	Version                  atc.Version              // The version of the resource.
	Params                   atc.Params               // The params used when fetching the version.
}

func (cache *ResourceCacheDescriptor) findOrCreate(
	tx Tx,
	lockFactory lock.LockFactory,
	conn Conn,
) (UsedResourceCache, error) {
	resourceConfig, err := cache.ResourceConfigDescriptor.findOrCreate(tx, lockFactory, conn)
	if err != nil {
		return nil, err
	}

	rc, found, err := cache.findWithResourceConfig(tx, resourceConfig, lockFactory, conn)
	if err != nil {
		return nil, err
	}

	if !found {
		var id int
		err = psql.Insert("resource_caches").
			Columns(
				"resource_config_id",
				"version",
				"version_md5",
				"params_hash",
			).
			Values(
				resourceConfig.ID(),
				cache.version(),
				sq.Expr("md5(?)", cache.version()),
				paramsHash(cache.Params),
			).
			Suffix(`
				ON CONFLICT (resource_config_id, version_md5, params_hash) DO UPDATE SET
				resource_config_id = EXCLUDED.resource_config_id,
				version = EXCLUDED.version,
				version_md5 = EXCLUDED.version_md5,
				params_hash = EXCLUDED.params_hash
				RETURNING id
			`).
			RunWith(tx).
			QueryRow().
			Scan(&id)
		if err != nil {
			return nil, err
		}

		rc = &usedResourceCache{
			id:             id,
			version:        cache.Version,
			resourceConfig: resourceConfig,
			lockFactory:    lockFactory,
			conn:           conn,
		}
	}

	return rc, nil
}

func (cache *ResourceCacheDescriptor) use(
	tx Tx,
	rc UsedResourceCache,
	user ResourceCacheUser,
) error {
	cols := user.SQLMap()
	cols["resource_cache_id"] = rc.ID()

	var resourceCacheUseExists int
	err := psql.Select("1").
		From("resource_cache_uses").
		Where(sq.Eq(cols)).
		RunWith(tx).
		QueryRow().
		Scan(&resourceCacheUseExists)
	if err != nil {
		if err != sql.ErrNoRows {
			return err
		}
	}

	if err == nil {
		// use already exists
		return nil
	}

	_, err = psql.Insert("resource_cache_uses").
		SetMap(cols).
		RunWith(tx).
		Exec()
	return err
}

func (cache *ResourceCacheDescriptor) findWithResourceConfig(tx Tx, resourceConfig ResourceConfig, lockFactory lock.LockFactory, conn Conn) (UsedResourceCache, bool, error) {
	var id int
	err := psql.Select("id").
		From("resource_caches").
		Where(sq.Eq{
			"resource_config_id": resourceConfig.ID(),
			"params_hash":        paramsHash(cache.Params),
		}).
		Where(sq.Expr("version_md5 = md5(?)", cache.version())).
		Suffix("FOR SHARE").
		RunWith(tx).
		QueryRow().
		Scan(&id)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, false, nil
		}

		return nil, false, err
	}

	return &usedResourceCache{
		id:             id,
		version:        cache.Version,
		resourceConfig: resourceConfig,
		lockFactory:    lockFactory,
		conn:           conn,
	}, true, nil
}

func (cache *ResourceCacheDescriptor) version() string {
	j, _ := json.Marshal(cache.Version)
	return string(j)
}

func paramsHash(p atc.Params) string {
	if p != nil {
		return mapHash(p)
	}

	return mapHash(atc.Params{})
}

// UsedResourceCache is created whenever a ResourceCache is Created and/or
// Used.
//
// So long as the UsedResourceCache exists, the underlying ResourceCache can
// not be removed.
//
// UsedResourceCaches become unused by the gc.ResourceCacheCollector, which may
// then lead to the ResourceCache being garbage-collected.
//
// See FindOrCreateForBuild, FindOrCreateForResource, and
// FindOrCreateForResourceType for more information on when it becomes unused.

//go:generate counterfeiter . UsedResourceCache

type UsedResourceCache interface {
	ID() int
	Version() atc.Version
	LoadVersionMetadata() ([]atc.MetadataField, error)

	ResourceConfig() ResourceConfig

	Destroy(Tx) (bool, error)
	BaseResourceType() *UsedBaseResourceType
}

type usedResourceCache struct {
	id             int
	resourceConfig ResourceConfig
	version        atc.Version

	lockFactory lock.LockFactory
	conn        Conn
}

func (cache *usedResourceCache) ID() int                        { return cache.id }
func (cache *usedResourceCache) ResourceConfig() ResourceConfig { return cache.resourceConfig }
func (cache *usedResourceCache) Version() atc.Version           { return cache.version }

func (cache *usedResourceCache) Destroy(tx Tx) (bool, error) {
	rows, err := psql.Delete("resource_caches").
		Where(sq.Eq{
			"id": cache.id,
		}).
		RunWith(tx).
		Exec()
	if err != nil {
		return false, err
	}

	affected, err := rows.RowsAffected()
	if err != nil {
		return false, err
	}

	if affected == 0 {
		return false, ErrResourceCacheDisappeared
	}

	return true, nil
}

func (cache *usedResourceCache) BaseResourceType() *UsedBaseResourceType {
	if cache.resourceConfig.CreatedByBaseResourceType() != nil {
		return cache.resourceConfig.CreatedByBaseResourceType()
	}

	return cache.resourceConfig.CreatedByResourceCache().BaseResourceType()
}

func (cache *usedResourceCache) LoadVersionMetadata() ([]atc.MetadataField, error) {
	r := &resourceConfigVersion{conn: cache.conn}

	versionJSON, _ := json.Marshal(cache.Version())
	row := resourceConfigVersionQuery.
		LeftJoin("resource_config_scopes rcs ON v.resource_config_scope_id = rcs.id").
		Where(sq.Expr("v.version_md5 = md5(?)", versionJSON)).
		Where(sq.Eq{"rcs.resource_config_id": cache.resourceConfig.ID()}).
		RunWith(r.conn).
		QueryRow()

	err := scanResourceConfigVersion(r, row)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	return r.metadata.ToATCMetadata(), nil
}

func mapHash(m map[string]interface{}) string {
	j, _ := json.Marshal(m)
	return fmt.Sprintf("%x", sha256.Sum256(j))
}
<|endoftext|>


<commit_before>package backend

import (
	"time"

	"github.com/fzzy/radix/redis"
)

// RedisStore structure for our redis store
type RedisStore struct {
	client *redis.Client
}

// Init connects to our local redis store (assumption is it's running defaults)
func (r *RedisStore) Init() error {
	var err error
	r.client, err = redis.DialTimeout("tcp", "127.0.0.1:6379", time.Duration(10)*time.Second)
	return err
}

// Close ends our session with redis
func (r *RedisStore) Close() {
	r.client.Close()
}

// Add inserts a key into the redis backend
func (r *RedisStore) Add(user string, key string) (bool, error) {
	var resp = true
	reply := r.client.Cmd("rpush", user, key)
	if reply.Err != nil {
		return false, reply.Err
	}
	return resp, nil
}

// RM removes a key from the lset
func (r *RedisStore) RM(user string, key string) (bool, error) {
	return true, nil
}
<commit_msg>finish redis backend<commit_after>package backend

import (
	"time"

	"github.com/fzzy/radix/redis"
)

// RedisStore structure for our redis store
type RedisStore struct {
	client *redis.Client
}

// Init connects to our local redis store (assumption is it's running defaults)
func (r *RedisStore) Init() error {
	var err error
	r.client, err = redis.DialTimeout("tcp", "127.0.0.1:6379", time.Duration(10)*time.Second)
	return err
}

// Close ends our session with redis
func (r *RedisStore) Close() {
	r.client.Close()
}

// Add inserts a key into the redis backend
func (r *RedisStore) Add(user string, key string) (bool, error) {
	reply := r.client.Cmd("rpush", user, key)
	if reply.Err != nil {
		return false, reply.Err
	}
	return true, nil
}

// RM removes a key from the lset
func (r *RedisStore) RM(user string, key string) (bool, error) {
	reply := r.client.Cmd("lrem", user, -1, key)

	if reply.Err != nil {
		return false, reply.Err
	}
	return true, nil
}

// RMAll removes all the keys for a given user
func (r *RedisStore) RMAll(user string) (bool, error) {
	reply := r.client.Cmd("del", user)
	if reply.Err != nil {
		return false, reply.Err
	}

	return true, nil
}

// Get returns all keys for user
func (r *RedisStore) Get(user string) ([]string, error) {
	keys, err := r.client.Cmd("lrange", user, 0, 1).List()
	if err != nil {
		return nil, err
	}

	return keys, nil
}

// GetAll returns all keys for all users
func (r *RedisStore) GetAll() ([]string, error) {
	var s []string
	reply := r.client.Cmd("keys", "*")
	keys, err := reply.List()
	if err != nil {
		return nil, err
	}

	for _, k := range keys {
		uKeys, err := r.client.Cmd("lrange", k, 0, -1).List()
		if err != nil {
			return nil, err
		}
		for i := range uKeys {
			s = append(s, uKeys[i])
		}
	}

	return s, nil
}

// GetCount gets a count of all the keys
func (r *RedisStore) GetCount() (int, error) {
	s := 0
	keys, err := r.client.Cmd("keys", "*").List()
	if err != nil {
		return -1, err
	}

	for _, k := range keys {
		uKeys, err := r.client.Cmd("lrange", k, 0, -1).List()
		if err != nil {
			return -1, err
		}
		for _ = range uKeys {
			s++
		}
	}

	return s, nil
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowstorm"
	"github.com/ava-labs/avalanchego/vms/components/avax"
)

var (
	errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo")
	errWrongAssetID    = errors.New("asset ID must be AVAX in the atomic tx")
	errMissingUTXO     = errors.New("missing utxo")
	errUnknownTx       = errors.New("transaction is unknown")
	errRejectedTx      = errors.New("transaction is rejected")
)

// UniqueTx provides a de-duplication service for txs. This only provides a
// performance boost
type UniqueTx struct {
	*TxCachedState

	vm   *VM
	txID ids.ID
}

type TxCachedState struct {
	*Tx

	unique, verifiedTx, verifiedState bool
	validity                          error

	inputs     []ids.ID
	inputUTXOs []*avax.UTXOID
	utxos      []*avax.UTXO
	deps       []snowstorm.Tx

	status choices.Status
}

func (tx *UniqueTx) refresh() {
	tx.vm.numTxRefreshes.Inc()

	if tx.TxCachedState == nil {
		tx.TxCachedState = &TxCachedState{}
	}
	if tx.unique {
		return
	}
	unique := tx.vm.state.DeduplicateTx(tx)
	prevTx := tx.Tx
	if unique == tx {
		tx.vm.numTxRefreshMisses.Inc()

		// If no one was in the cache, make sure that there wasn't an
		// intermediate object whose state I must reflect
		if status, err := tx.vm.state.GetStatus(tx.ID()); err == nil {
			tx.status = status
		}
		tx.unique = true
	} else {
		tx.vm.numTxRefreshHits.Inc()

		// If someone is in the cache, they must be up to date

		// This ensures that every unique tx object points to the same tx state
		tx.TxCachedState = unique.TxCachedState
	}

	if tx.Tx != nil {
		return
	}

	if prevTx == nil {
		if innerTx, err := tx.vm.state.GetTx(tx.ID()); err == nil {
			tx.Tx = innerTx
		}
	} else {
		tx.Tx = prevTx
	}
}

// Evict is called when this UniqueTx will no longer be returned from a cache
// lookup
func (tx *UniqueTx) Evict() {
	// Lock is already held here
	tx.unique = false
	tx.deps = nil
}

func (tx *UniqueTx) setStatus(status choices.Status) error {
	tx.refresh()
	if tx.status == status {
		return nil
	}
	tx.status = status
	return tx.vm.state.PutStatus(tx.ID(), status)
}

// ID returns the wrapped txID
func (tx *UniqueTx) ID() ids.ID       { return tx.txID }
func (tx *UniqueTx) Key() interface{} { return tx.txID }

// Accept is called when the transaction was finalized as accepted by consensus
func (tx *UniqueTx) Accept() error {
	if s := tx.Status(); s != choices.Processing {
		tx.vm.ctx.Log.Error("Failed to accept tx %s because the tx is in state %s", tx.txID, s)
		return fmt.Errorf("transaction has invalid status: %s", s)
	}

	defer tx.vm.db.Abort()

	// Remove spent utxos
	for _, utxo := range tx.InputUTXOs() {
		if utxo.Symbolic() {
			// If the UTXO is symbolic, it can't be spent
			continue
		}
		utxoID := utxo.InputID()
		if err := tx.vm.state.DeleteUTXO(utxoID); err != nil {
			tx.vm.ctx.Log.Error("Failed to spend utxo %s due to %s", utxoID, err)
			return err
		}
	}

	// Add new utxos
	for _, utxo := range tx.UTXOs() {
		if err := tx.vm.state.PutUTXO(utxo.InputID(), utxo); err != nil {
			tx.vm.ctx.Log.Error("Failed to fund utxo %s due to %s", utxo.InputID(), err)
			return err
		}
	}

	if err := tx.setStatus(choices.Accepted); err != nil {
		tx.vm.ctx.Log.Error("Failed to accept tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()

	commitBatch, err := tx.vm.db.CommitBatch()
	if err != nil {
		tx.vm.ctx.Log.Error("Failed to calculate CommitBatch for %s due to %s", txID, err)
		return err
	}

	if err := tx.ExecuteWithSideEffects(tx.vm, commitBatch); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit accept %s due to %s", txID, err)
		return err
	}

	tx.vm.ctx.Log.Verbo("Accepted Tx: %s", txID)

	tx.vm.pubsub.Publish(txID, NewPubSubFilterer(tx.Tx))
	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Reject is called when the transaction was finalized as rejected by consensus
func (tx *UniqueTx) Reject() error {
	defer tx.vm.db.Abort()

	if err := tx.setStatus(choices.Rejected); err != nil {
		tx.vm.ctx.Log.Error("Failed to reject tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()
	tx.vm.ctx.Log.Debug("Rejecting Tx: %s", txID)

	if err := tx.vm.db.Commit(); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit reject %s due to %s", tx.txID, err)
		return err
	}

	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Status returns the current status of this transaction
func (tx *UniqueTx) Status() choices.Status {
	tx.refresh()
	return tx.status
}

// Dependencies returns the set of transactions this transaction builds on
func (tx *UniqueTx) Dependencies() []snowstorm.Tx {
	tx.refresh()
	if tx.Tx == nil || len(tx.deps) != 0 {
		return tx.deps
	}

	txIDs := ids.Set{}
	for _, in := range tx.InputUTXOs() {
		if in.Symbolic() {
			continue
		}
		txID, _ := in.InputSource()
		if txIDs.Contains(txID) {
			continue
		}
		txIDs.Add(txID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: txID,
		})
	}
	consumedIDs := tx.Tx.ConsumedAssetIDs()
	for assetID := range tx.Tx.AssetIDs() {
		if consumedIDs.Contains(assetID) || txIDs.Contains(assetID) {
			continue
		}
		txIDs.Add(assetID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: assetID,
		})
	}
	return tx.deps
}

// InputIDs returns the set of utxoIDs this transaction consumes
func (tx *UniqueTx) InputIDs() []ids.ID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputs) != 0 {
		return tx.inputs
	}

	inputUTXOs := tx.InputUTXOs()
	tx.inputs = make([]ids.ID, len(inputUTXOs))
	for i, utxo := range inputUTXOs {
		tx.inputs[i] = utxo.InputID()
	}
	return tx.inputs
}

// InputUTXOs returns the utxos that will be consumed on tx acceptance
func (tx *UniqueTx) InputUTXOs() []*avax.UTXOID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputUTXOs) != 0 {
		return tx.inputUTXOs
	}
	tx.inputUTXOs = tx.Tx.InputUTXOs()
	return tx.inputUTXOs
}

// UTXOs returns the utxos that will be added to the UTXO set on tx acceptance
func (tx *UniqueTx) UTXOs() []*avax.UTXO {
	tx.refresh()
	if tx.Tx == nil || len(tx.utxos) != 0 {
		return tx.utxos
	}
	tx.utxos = tx.Tx.UTXOs()
	return tx.utxos
}

// Bytes returns the binary representation of this transaction
func (tx *UniqueTx) Bytes() []byte {
	tx.refresh()
	return tx.Tx.Bytes()
}

func (tx *UniqueTx) verifyWithoutCacheWrites() error {
	switch status := tx.Status(); status {
	case choices.Unknown:
		return errUnknownTx
	case choices.Accepted:
		return nil
	case choices.Rejected:
		return errRejectedTx
	default:
		return tx.SemanticVerify()
	}
}

// Verify the validity of this transaction
func (tx *UniqueTx) Verify() error {
	if err := tx.verifyWithoutCacheWrites(); err != nil {
		return err
	}

	tx.verifiedState = true
	return nil
}

// SyntacticVerify verifies that this transaction is well formed
func (tx *UniqueTx) SyntacticVerify() error {
	tx.refresh()

	if tx.Tx == nil {
		return errUnknownTx
	}

	if tx.verifiedTx {
		return tx.validity
	}

	tx.verifiedTx = true
	tx.validity = tx.Tx.SyntacticVerify(
		tx.vm.ctx,
		tx.vm.codec,
		tx.vm.feeAssetID,
		tx.vm.txFee,
		tx.vm.creationTxFee,
		len(tx.vm.fxs),
	)
	return tx.validity
}

// SemanticVerify the validity of this transaction
func (tx *UniqueTx) SemanticVerify() error {
	// SyntacticVerify sets the error on validity and is checked in the next
	// statement
	_ = tx.SyntacticVerify()

	if tx.validity != nil || tx.verifiedState {
		return tx.validity
	}

	return tx.Tx.SemanticVerify(tx.vm, tx.UnsignedTx)
}
<commit_msg>assert the *avm.UniqueTx implements snowstorm.Tx<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package avm

import (
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowstorm"
	"github.com/ava-labs/avalanchego/vms/components/avax"
)

var (
	errAssetIDMismatch = errors.New("asset IDs in the input don't match the utxo")
	errWrongAssetID    = errors.New("asset ID must be AVAX in the atomic tx")
	errMissingUTXO     = errors.New("missing utxo")
	errUnknownTx       = errors.New("transaction is unknown")
	errRejectedTx      = errors.New("transaction is rejected")
)

var _ snowstorm.Tx = &UniqueTx{}

// UniqueTx provides a de-duplication service for txs. This only provides a
// performance boost
type UniqueTx struct {
	*TxCachedState

	vm   *VM
	txID ids.ID
}

type TxCachedState struct {
	*Tx

	unique, verifiedTx, verifiedState bool
	validity                          error

	inputs     []ids.ID
	inputUTXOs []*avax.UTXOID
	utxos      []*avax.UTXO
	deps       []snowstorm.Tx

	status choices.Status
}

func (tx *UniqueTx) refresh() {
	tx.vm.numTxRefreshes.Inc()

	if tx.TxCachedState == nil {
		tx.TxCachedState = &TxCachedState{}
	}
	if tx.unique {
		return
	}
	unique := tx.vm.state.DeduplicateTx(tx)
	prevTx := tx.Tx
	if unique == tx {
		tx.vm.numTxRefreshMisses.Inc()

		// If no one was in the cache, make sure that there wasn't an
		// intermediate object whose state I must reflect
		if status, err := tx.vm.state.GetStatus(tx.ID()); err == nil {
			tx.status = status
		}
		tx.unique = true
	} else {
		tx.vm.numTxRefreshHits.Inc()

		// If someone is in the cache, they must be up to date

		// This ensures that every unique tx object points to the same tx state
		tx.TxCachedState = unique.TxCachedState
	}

	if tx.Tx != nil {
		return
	}

	if prevTx == nil {
		if innerTx, err := tx.vm.state.GetTx(tx.ID()); err == nil {
			tx.Tx = innerTx
		}
	} else {
		tx.Tx = prevTx
	}
}

// Evict is called when this UniqueTx will no longer be returned from a cache
// lookup
func (tx *UniqueTx) Evict() {
	// Lock is already held here
	tx.unique = false
	tx.deps = nil
}

func (tx *UniqueTx) setStatus(status choices.Status) error {
	tx.refresh()
	if tx.status == status {
		return nil
	}
	tx.status = status
	return tx.vm.state.PutStatus(tx.ID(), status)
}

// ID returns the wrapped txID
func (tx *UniqueTx) ID() ids.ID       { return tx.txID }
func (tx *UniqueTx) Key() interface{} { return tx.txID }

// Accept is called when the transaction was finalized as accepted by consensus
func (tx *UniqueTx) Accept() error {
	if s := tx.Status(); s != choices.Processing {
		tx.vm.ctx.Log.Error("Failed to accept tx %s because the tx is in state %s", tx.txID, s)
		return fmt.Errorf("transaction has invalid status: %s", s)
	}

	defer tx.vm.db.Abort()

	// Remove spent utxos
	for _, utxo := range tx.InputUTXOs() {
		if utxo.Symbolic() {
			// If the UTXO is symbolic, it can't be spent
			continue
		}
		utxoID := utxo.InputID()
		if err := tx.vm.state.DeleteUTXO(utxoID); err != nil {
			tx.vm.ctx.Log.Error("Failed to spend utxo %s due to %s", utxoID, err)
			return err
		}
	}

	// Add new utxos
	for _, utxo := range tx.UTXOs() {
		if err := tx.vm.state.PutUTXO(utxo.InputID(), utxo); err != nil {
			tx.vm.ctx.Log.Error("Failed to fund utxo %s due to %s", utxo.InputID(), err)
			return err
		}
	}

	if err := tx.setStatus(choices.Accepted); err != nil {
		tx.vm.ctx.Log.Error("Failed to accept tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()

	commitBatch, err := tx.vm.db.CommitBatch()
	if err != nil {
		tx.vm.ctx.Log.Error("Failed to calculate CommitBatch for %s due to %s", txID, err)
		return err
	}

	if err := tx.ExecuteWithSideEffects(tx.vm, commitBatch); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit accept %s due to %s", txID, err)
		return err
	}

	tx.vm.ctx.Log.Verbo("Accepted Tx: %s", txID)

	tx.vm.pubsub.Publish(txID, NewPubSubFilterer(tx.Tx))
	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Reject is called when the transaction was finalized as rejected by consensus
func (tx *UniqueTx) Reject() error {
	defer tx.vm.db.Abort()

	if err := tx.setStatus(choices.Rejected); err != nil {
		tx.vm.ctx.Log.Error("Failed to reject tx %s due to %s", tx.txID, err)
		return err
	}

	txID := tx.ID()
	tx.vm.ctx.Log.Debug("Rejecting Tx: %s", txID)

	if err := tx.vm.db.Commit(); err != nil {
		tx.vm.ctx.Log.Error("Failed to commit reject %s due to %s", tx.txID, err)
		return err
	}

	tx.vm.walletService.decided(txID)

	tx.deps = nil // Needed to prevent a memory leak

	return nil
}

// Status returns the current status of this transaction
func (tx *UniqueTx) Status() choices.Status {
	tx.refresh()
	return tx.status
}

// Dependencies returns the set of transactions this transaction builds on
func (tx *UniqueTx) Dependencies() []snowstorm.Tx {
	tx.refresh()
	if tx.Tx == nil || len(tx.deps) != 0 {
		return tx.deps
	}

	txIDs := ids.Set{}
	for _, in := range tx.InputUTXOs() {
		if in.Symbolic() {
			continue
		}
		txID, _ := in.InputSource()
		if txIDs.Contains(txID) {
			continue
		}
		txIDs.Add(txID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: txID,
		})
	}
	consumedIDs := tx.Tx.ConsumedAssetIDs()
	for assetID := range tx.Tx.AssetIDs() {
		if consumedIDs.Contains(assetID) || txIDs.Contains(assetID) {
			continue
		}
		txIDs.Add(assetID)
		tx.deps = append(tx.deps, &UniqueTx{
			vm:   tx.vm,
			txID: assetID,
		})
	}
	return tx.deps
}

// InputIDs returns the set of utxoIDs this transaction consumes
func (tx *UniqueTx) InputIDs() []ids.ID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputs) != 0 {
		return tx.inputs
	}

	inputUTXOs := tx.InputUTXOs()
	tx.inputs = make([]ids.ID, len(inputUTXOs))
	for i, utxo := range inputUTXOs {
		tx.inputs[i] = utxo.InputID()
	}
	return tx.inputs
}

// InputUTXOs returns the utxos that will be consumed on tx acceptance
func (tx *UniqueTx) InputUTXOs() []*avax.UTXOID {
	tx.refresh()
	if tx.Tx == nil || len(tx.inputUTXOs) != 0 {
		return tx.inputUTXOs
	}
	tx.inputUTXOs = tx.Tx.InputUTXOs()
	return tx.inputUTXOs
}

// UTXOs returns the utxos that will be added to the UTXO set on tx acceptance
func (tx *UniqueTx) UTXOs() []*avax.UTXO {
	tx.refresh()
	if tx.Tx == nil || len(tx.utxos) != 0 {
		return tx.utxos
	}
	tx.utxos = tx.Tx.UTXOs()
	return tx.utxos
}

// Bytes returns the binary representation of this transaction
func (tx *UniqueTx) Bytes() []byte {
	tx.refresh()
	return tx.Tx.Bytes()
}

func (tx *UniqueTx) verifyWithoutCacheWrites() error {
	switch status := tx.Status(); status {
	case choices.Unknown:
		return errUnknownTx
	case choices.Accepted:
		return nil
	case choices.Rejected:
		return errRejectedTx
	default:
		return tx.SemanticVerify()
	}
}

// Verify the validity of this transaction
func (tx *UniqueTx) Verify() error {
	if err := tx.verifyWithoutCacheWrites(); err != nil {
		return err
	}

	tx.verifiedState = true
	return nil
}

// SyntacticVerify verifies that this transaction is well formed
func (tx *UniqueTx) SyntacticVerify() error {
	tx.refresh()

	if tx.Tx == nil {
		return errUnknownTx
	}

	if tx.verifiedTx {
		return tx.validity
	}

	tx.verifiedTx = true
	tx.validity = tx.Tx.SyntacticVerify(
		tx.vm.ctx,
		tx.vm.codec,
		tx.vm.feeAssetID,
		tx.vm.txFee,
		tx.vm.creationTxFee,
		len(tx.vm.fxs),
	)
	return tx.validity
}

// SemanticVerify the validity of this transaction
func (tx *UniqueTx) SemanticVerify() error {
	// SyntacticVerify sets the error on validity and is checked in the next
	// statement
	_ = tx.SyntacticVerify()

	if tx.validity != nil || tx.verifiedState {
		return tx.validity
	}

	return tx.Tx.SemanticVerify(tx.vm, tx.UnsignedTx)
}
<|endoftext|>


<commit_before>package blocks

import (
	"log"
)

var (
	Library     BlockLibrary
	libraryBlob string
)

// A block library is a collection of possible block templates
type BlockLibrary map[string]Block

func BuildLibrary() {
	log.Println("building block library")
	Library = make(map[string]Block)

	// BLOCKS

	Library["ticker"] = Block{
		Template: &BlockTemplate{
			BlockType:  "ticker",
			RouteNames: []string{},
		},
		Routine: Ticker,
	}

	Library["connection"] = Block{
		Template: &BlockTemplate{
			BlockType:  "connection",
			RouteNames: []string{"last_seen"},
		},
		Routine: Connection,
	}

	Library["tolog"] = Block{
		Template: &BlockTemplate{
			BlockType:  "tolog",
			RouteNames: []string{},
		},
		Routine: ToLog,
	}
}
<commit_msg>only one definition for blocktype now<commit_after>package blocks

import (
	"log"
)

var (
	Library     BlockLibrary
	libraryBlob string
)

// A block library is a collection of possible block templates
type BlockLibrary map[string]Block

// TODO: library should probably become a struct at some point...
func AddBlock(name string, routeNames []string, routine BlockRoutine){
	Library[name] = Block{
		Template: &BlockTemplate{
			BlockType: name,
			RouteNames: routeNames,
		},
		Routine: routine,
	}
}

func BuildLibrary() {
	log.Println("building block library")
	Library = make(map[string]Block)

	// BLOCKS
	AddBlock("ticker", []string{}, Ticker)
	AddBlock("connection", []string{"last_seen"}, Connection)
	AddBlock("tolog", []string{}, ToLog)
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Fabian Wenzelmann

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package stack

type stackValue interface{}

type stackNode struct {
	value stackValue
	next  *stackNode
}

// Stack is a simple LIFO (last-in-first-out) datatype.
type Stack struct {
	first *stackNode
	size  int
}

func EmptyStack() *Stack {
	return &Stack{nil, 0}
}

func (this *Stack) Length() int {
	return this.size
}

func (this *Stack) Empty() bool {
	return this.Length() == 0
}

func (this *Stack) Push(val stackValue) {
	this.first = &stackNode{val, this.first}
	this.size++
}

func (this *Stack) Pop() stackValue {
	if this.Empty() {
		return nil
	}
	v := this.first.value
	this.first = this.first.next
	this.size--
	return v
}
<commit_msg>comments for stack<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Fabian Wenzelmann

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// Package provides a simple LIFO (last-in-first-out) stack.
package stack

// Type for elements stored in the stack nodes.
type StackValue interface{}

// A node has a value it stores and a pointer to the next node (nil if there is
// no next node).
type stackNode struct {
	value StackValue
	next  *stackNode
}

// Stack is a simple LIFO (last-in-first-out) datatype.
type Stack struct {
	first *stackNode
	size  int
}

// EmptyStack creates a new stack without any elements.
func EmptyStack() *Stack {
	return &Stack{nil, 0}
}

// Length returns the number of elements currently on the stack.
func (this *Stack) Length() int {
	return this.size
}

// Empty returns true if there are no elements on the stack.
func (this *Stack) Empty() bool {
	return this.Length() == 0
}

// Push adds a new value to the front of the stack.
func (this *Stack) Push(val StackValue) {
	this.first = &stackNode{val, this.first}
	this.size++
}

// Pop returns the first element of the stack, nil if the stack is empty.
func (this *Stack) Pop() StackValue {
	if this.Empty() {
		return nil
	}
	v := this.first.value
	this.first = this.first.next
	this.size--
	return v
}
<|endoftext|>


<commit_before>package state

import (
	"github.com/hashicorp/terraform/terraform"
)

// State is the collection of all state interfaces.
type State interface {
	StateReader
	StateWriter
	StateRefresher
	StatePersister
}

// StateReader is the interface for things that can return a state. Retrieving
// the state here must not error. Loading the state fresh (an operation that
// can likely error) should be implemented by RefreshState. If a state hasn't
// been loaded yet, it is okay for State to return nil.
type StateReader interface {
	State() *terraform.State
}

// StateWriter is the interface that must be implemented by something that
// can write a state. Writing the state can be cached or in-memory, as
// full persistence should be implemented by StatePersister.
type StateWriter interface {
	WriteState(*terraform.State) error
}

// StateRefresher is the interface that is implemented by something that
// can load a state. This might be refreshing it from a remote location or
// it might simply be reloading it from disk.
type StateRefresher interface {
	RefreshState() error
}

// StatePersister is implemented to truly persist a state. Whereas StateWriter
// is allowed to perhaps be caching in memory, PersistState must write the
// state to some durable storage.
type StatePersister interface {
	PersistState() error
}

// Locker is implemented to lock state during command execution.
type Locker interface {
	Lock(reason string) error
	Unlock() error
}
<commit_msg>Change lock reason -> info<commit_after>package state

import (
	"github.com/hashicorp/terraform/terraform"
)

// State is the collection of all state interfaces.
type State interface {
	StateReader
	StateWriter
	StateRefresher
	StatePersister
}

// StateReader is the interface for things that can return a state. Retrieving
// the state here must not error. Loading the state fresh (an operation that
// can likely error) should be implemented by RefreshState. If a state hasn't
// been loaded yet, it is okay for State to return nil.
type StateReader interface {
	State() *terraform.State
}

// StateWriter is the interface that must be implemented by something that
// can write a state. Writing the state can be cached or in-memory, as
// full persistence should be implemented by StatePersister.
type StateWriter interface {
	WriteState(*terraform.State) error
}

// StateRefresher is the interface that is implemented by something that
// can load a state. This might be refreshing it from a remote location or
// it might simply be reloading it from disk.
type StateRefresher interface {
	RefreshState() error
}

// StatePersister is implemented to truly persist a state. Whereas StateWriter
// is allowed to perhaps be caching in memory, PersistState must write the
// state to some durable storage.
type StatePersister interface {
	PersistState() error
}

// Locker is implemented to lock state during command execution.
// The optional info parameter can be recorded with the lock, but the
// implementation should not depend in its value.
type Locker interface {
	Lock(info string) error
	Unlock() error
}
<|endoftext|>


<commit_before>package backends

import (
	"encoding/json"
	"fmt"
	"github.com/docker/libswarm/beam"
	"github.com/dotcloud/docker/api"
	"github.com/dotcloud/docker/pkg/version"
	"github.com/gorilla/mux"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

func DockerServer() beam.Sender {
	backend := beam.NewServer()
	backend.OnSpawn(beam.Handler(func(ctx *beam.Message) error {
		instance := beam.Task(func(in beam.Receiver, out beam.Sender) {
			url := "tcp://localhost:4243"
			if len(ctx.Args) > 0 {
				url = ctx.Args[0]
			}
			err := listenAndServe(url, out)
			if err != nil {
				fmt.Printf("listenAndServe: %v", err)
			}
		})
		_, err := ctx.Ret.Send(&beam.Message{Verb: beam.Ack, Ret: instance})
		return err
	}))
	return backend
}

type HttpApiFunc func(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error

func listenAndServe(urlStr string, out beam.Sender) error {
	fmt.Println("Starting Docker server...")
	r, err := createRouter(out)
	if err != nil {
		return err
	}

	parsedUrl, err := url.Parse(urlStr)
	if err != nil {
		return err
	}

	var hostAndPath string
	// For Unix sockets we need to capture the path as well as the host
	if parsedUrl.Scheme == "unix" {
		hostAndPath = "/" + parsedUrl.Host + parsedUrl.Path
	} else {
		hostAndPath = parsedUrl.Host
	}

	l, err := net.Listen(parsedUrl.Scheme, hostAndPath)
	if err != nil {
		return err
	}
	httpSrv := http.Server{Addr: hostAndPath, Handler: r}
	return httpSrv.Serve(l)
}

func ping(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	_, err := w.Write([]byte{'O', 'K'})
	return err
}

func getContainersJSON(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if err := r.ParseForm(); err != nil {
		return err
	}

	o := beam.Obj(out)
	names, err := o.Ls()
	if err != nil {
		return err
	}

	var responses []interface{}

	for _, name := range names {
		_, containerOut, err := o.Attach(name)
		if err != nil {
			return err
		}
		container := beam.Obj(containerOut)
		responseJson, err := container.Get()
		if err != nil {
			return err
		}
		var response struct {
			ID      string
			Created string
			Name    string
			Config  struct {
				Cmd   []string
				Image string
			}
			State struct {
				Running    bool
				StartedAt  string
				FinishedAt string
				ExitCode   int
			}
		}
		if err = json.Unmarshal([]byte(responseJson), &response); err != nil {
			return err
		}
		created, err := time.Parse(time.RFC3339, response.Created)
		if err != nil {
			return err
		}
		var state string
		if response.State.Running {
			state = "Up"
		} else {
			state = fmt.Sprintf("Exited (%d)", response.State.ExitCode)
		}
		responses = append(responses, map[string]interface{}{
			"Id":      response.ID,
			"Command": strings.Join(response.Config.Cmd, " "),
			"Created": created.Unix(),
			"Image":   response.Config.Image,
			"Names":   []string{response.Name},
			"Ports":   []string{},
			"Status":  state,
		})
	}

	return writeJSON(w, http.StatusOK, responses)
}

func postContainersCreate(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if err := r.ParseForm(); err != nil {
		return nil
	}

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

	container, err := beam.Obj(out).Spawn(string(body))
	if err != nil {
		return err
	}

	responseJson, err := container.Get()
	if err != nil {
		return err
	}

	var response struct{ Id string }
	if err = json.Unmarshal([]byte(responseJson), &response); err != nil {
		return err
	}
	return writeJSON(w, http.StatusCreated, response)
}

func postContainersStart(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if vars == nil {
		return fmt.Errorf("Missing parameter")
	}

	// TODO: r.Body

	name := vars["name"]
	_, containerOut, err := beam.Obj(out).Attach(name)
	container := beam.Obj(containerOut)
	if err != nil {
		return err
	}
	if err := container.Start(); err != nil {
		return err
	}

	w.WriteHeader(http.StatusNoContent)
	return nil
}

func postContainersStop(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if vars == nil {
		return fmt.Errorf("Missing parameter")
	}

	name := vars["name"]
	_, containerOut, err := beam.Obj(out).Attach(name)
	container := beam.Obj(containerOut)
	if err != nil {
		return err
	}
	if err := container.Stop(); err != nil {
		return err
	}

	w.WriteHeader(http.StatusNoContent)
	return nil
}

func createRouter(out beam.Sender) (*mux.Router, error) {
	r := mux.NewRouter()
	m := map[string]map[string]HttpApiFunc{
		"GET": {
			"/_ping":           ping,
			"/containers/json": getContainersJSON,
		},
		"POST": {
			"/containers/create":          postContainersCreate,
			"/containers/{name:.*}/start": postContainersStart,
			"/containers/{name:.*}/stop":  postContainersStop,
		},
		"DELETE":  {},
		"OPTIONS": {},
	}

	for method, routes := range m {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			localMethod := method

			f := makeHttpHandler(out, localMethod, localRoute, localFct, version.Version("0.11.0"))

			// add the new route
			if localRoute == "" {
				r.Methods(localMethod).HandlerFunc(f)
			} else {
				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
			}
		}
	}

	return r, nil
}

func makeHttpHandler(out beam.Sender, localMethod string, localRoute string, handlerFunc HttpApiFunc, dockerVersion version.Version) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// log the request
		fmt.Printf("Calling %s %s\n", localMethod, localRoute)

		version := version.Version(mux.Vars(r)["version"])
		if version == "" {
			version = api.APIVERSION
		}

		if err := handlerFunc(out, version, w, r, mux.Vars(r)); err != nil {
			fmt.Printf("Error: %s", err)
		}
	}
}

func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	enc := json.NewEncoder(w)
	return enc.Encode(v)
}
<commit_msg>Added port mapping output to dockerserver<commit_after>package backends

import (
	"encoding/json"
	"fmt"
	"github.com/docker/libswarm/beam"
	"github.com/dotcloud/docker/api"
	"github.com/dotcloud/docker/pkg/version"
	"github.com/gorilla/mux"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
	"strconv"
)

func DockerServer() beam.Sender {
	backend := beam.NewServer()
	backend.OnSpawn(beam.Handler(func(ctx *beam.Message) error {
		instance := beam.Task(func(in beam.Receiver, out beam.Sender) {
			url := "tcp://localhost:4243"
			if len(ctx.Args) > 0 {
				url = ctx.Args[0]
			}
			err := listenAndServe(url, out)
			if err != nil {
				fmt.Printf("listenAndServe: %v", err)
			}
		})
		_, err := ctx.Ret.Send(&beam.Message{Verb: beam.Ack, Ret: instance})
		return err
	}))
	return backend
}

type HttpApiFunc func(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error

func listenAndServe(urlStr string, out beam.Sender) error {
	fmt.Println("Starting Docker server...")
	r, err := createRouter(out)
	if err != nil {
		return err
	}

	parsedUrl, err := url.Parse(urlStr)
	if err != nil {
		return err
	}

	var hostAndPath string
	// For Unix sockets we need to capture the path as well as the host
	if parsedUrl.Scheme == "unix" {
		hostAndPath = "/" + parsedUrl.Host + parsedUrl.Path
	} else {
		hostAndPath = parsedUrl.Host
	}

	l, err := net.Listen(parsedUrl.Scheme, hostAndPath)
	if err != nil {
		return err
	}
	httpSrv := http.Server{Addr: hostAndPath, Handler: r}
	return httpSrv.Serve(l)
}

func ping(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	_, err := w.Write([]byte{'O', 'K'})
	return err
}

func getContainersJSON(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if err := r.ParseForm(); err != nil {
		return err
	}

	o := beam.Obj(out)
	names, err := o.Ls()
	if err != nil {
		return err
	}

	var responses []interface{}

	for _, name := range names {
		_, containerOut, err := o.Attach(name)
		if err != nil {
			return err
		}
		container := beam.Obj(containerOut)
		responseJson, err := container.Get()
		if err != nil {
			return err
		}
		var response struct {
			ID      string
			Created string
			Name    string
			Config  struct {
				Cmd   []string
				Image string
			}
			State struct {
				Running    bool
				StartedAt  string
				FinishedAt string
				ExitCode   int
			}
			NetworkSettings struct {
				Ports map[string][]map[string]string
			}
		}
		if err = json.Unmarshal([]byte(responseJson), &response); err != nil {
			return err
		}
		created, err := time.Parse(time.RFC3339, response.Created)
		if err != nil {
			return err
		}
		var state string
		if response.State.Running {
			state = "Up"
		} else {
			state = fmt.Sprintf("Exited (%d)", response.State.ExitCode)
		}
		type port struct {
			IP string
			PrivatePort int
			PublicPort int
			Type string
		}
		var ports []port
		for p, mappings := range response.NetworkSettings.Ports {
			var portnum int
			var proto string
			_, err := fmt.Sscanf(p, "%d/%s", &portnum, &proto)
			if err != nil {
				return err
			}
			if len(mappings) > 0 {
				for _, mapping := range mappings {
					hostPort, err := strconv.Atoi(mapping["HostPort"])
					if err != nil {
						return err
					}
					newport := port{
						IP: mapping["HostIp"],
						PrivatePort: portnum,
						PublicPort: hostPort,
						Type: proto,
					}
					ports = append(ports, newport)
				}
			} else {
				newport := port{
					PublicPort: portnum,
					Type: proto,
				}
				ports = append(ports, newport)
			}
		}
		responses = append(responses, map[string]interface{}{
			"Id":      response.ID,
			"Command": strings.Join(response.Config.Cmd, " "),
			"Created": created.Unix(),
			"Image":   response.Config.Image,
			"Names":   []string{response.Name},
			"Ports":   ports,
			"Status":  state,
		})
	}

	return writeJSON(w, http.StatusOK, responses)
}

func postContainersCreate(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if err := r.ParseForm(); err != nil {
		return nil
	}

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

	container, err := beam.Obj(out).Spawn(string(body))
	if err != nil {
		return err
	}

	responseJson, err := container.Get()
	if err != nil {
		return err
	}

	var response struct{ Id string }
	if err = json.Unmarshal([]byte(responseJson), &response); err != nil {
		return err
	}
	return writeJSON(w, http.StatusCreated, response)
}

func postContainersStart(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if vars == nil {
		return fmt.Errorf("Missing parameter")
	}

	// TODO: r.Body

	name := vars["name"]
	_, containerOut, err := beam.Obj(out).Attach(name)
	container := beam.Obj(containerOut)
	if err != nil {
		return err
	}
	if err := container.Start(); err != nil {
		return err
	}

	w.WriteHeader(http.StatusNoContent)
	return nil
}

func postContainersStop(out beam.Sender, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
	if vars == nil {
		return fmt.Errorf("Missing parameter")
	}

	name := vars["name"]
	_, containerOut, err := beam.Obj(out).Attach(name)
	container := beam.Obj(containerOut)
	if err != nil {
		return err
	}
	if err := container.Stop(); err != nil {
		return err
	}

	w.WriteHeader(http.StatusNoContent)
	return nil
}

func createRouter(out beam.Sender) (*mux.Router, error) {
	r := mux.NewRouter()
	m := map[string]map[string]HttpApiFunc{
		"GET": {
			"/_ping":           ping,
			"/containers/json": getContainersJSON,
		},
		"POST": {
			"/containers/create":          postContainersCreate,
			"/containers/{name:.*}/start": postContainersStart,
			"/containers/{name:.*}/stop":  postContainersStop,
		},
		"DELETE":  {},
		"OPTIONS": {},
	}

	for method, routes := range m {
		for route, fct := range routes {
			localRoute := route
			localFct := fct
			localMethod := method

			f := makeHttpHandler(out, localMethod, localRoute, localFct, version.Version("0.11.0"))

			// add the new route
			if localRoute == "" {
				r.Methods(localMethod).HandlerFunc(f)
			} else {
				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
			}
		}
	}

	return r, nil
}

func makeHttpHandler(out beam.Sender, localMethod string, localRoute string, handlerFunc HttpApiFunc, dockerVersion version.Version) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// log the request
		fmt.Printf("Calling %s %s\n", localMethod, localRoute)

		version := version.Version(mux.Vars(r)["version"])
		if version == "" {
			version = api.APIVERSION
		}

		if err := handlerFunc(out, version, w, r, mux.Vars(r)); err != nil {
			fmt.Printf("Error: %s", err)
		}
	}
}

func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	enc := json.NewEncoder(w)
	return enc.Encode(v)
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Shannon Wynter
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package refresh

import (
	"crypto/tls"
	"fmt"
	"github.com/pkg/errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/fellou89/caddy-cache"
	"github.com/fellou89/caddy-cache/storage"
	"github.com/fellou89/caddy-reauth/backend"
)

// Backend name
const Backend = "refresh"

// DefaultTimeout for sub requests
const DefaultTimeout = time.Minute

// Refresh backend provides authentication against a refresh token endpoint.
// If the refresh request returns a http 200 status code then the user
// is considered logged in.
type Refresh struct {
	refreshUrl         string
	cacheConfig        *cache.Config
	refreshCache       *cache.HTTPCache
	timeout            time.Duration
	insecureSkipVerify bool
	followRedirects    bool
	passCookies        bool
}

func init() {
	err := backend.Register(Backend, constructor)
	if err != nil {
		panic(err)
	}
}

func noRedirectsPolicy(req *http.Request, via []*http.Request) error {
	return errors.New("follow redirects disabled")
}

func constructor(config string) (backend.Backend, error) {
	options, err := backend.ParseOptions(config)
	if err != nil {
		return nil, err
	}

	s, found := options["url"]
	if !found {
		return nil, errors.New("url is a required parameter")
	}

	u, err := url.Parse(s)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse url "+s)
	}

	rf := &Refresh{
		refreshUrl:   u.String(),
		refreshCache: cache.NewHTTPCache(),
		cacheConfig: &cache.Config{
			Path:        "tmp",
			LockTimeout: time.Duration(5) * time.Minute,
		},
	}

	val, err := parseDurationOption(options, "timeout")
	if err != nil {
		return nil, err
	} else {
		rf.timeout = val
	}

	bval, err := parseBoolOption(options, "skipverify")
	if err != nil {
		return nil, err
	} else {
		rf.insecureSkipVerify = bval
	}

	bval, err = parseBoolOption(options, "follow")
	if err != nil {
		return nil, err
	} else {
		rf.followRedirects = bval
	}

	bval, err = parseBoolOption(options, "cookies")
	if err != nil {
		return nil, err
	} else {
		rf.passCookies = bval
	}

	// Cache config
	if s, found := options["cache_path"]; found {
		rf.cacheConfig.Path = s
	}

	val, err = parseDurationOption(options, "lock_timeout")
	if err != nil {
		return nil, err
	} else {
		rf.cacheConfig.LockTimeout = val
	}

	// Can't really define cache rules in one line, it would require refactor of parsing configs
	// so for now these two Config params stay out, and since cacheRules will be nil,
	// neither is used when creating HTTPCacheEntries
	//
	// DefaultMaxAge: time.Duration(5) * time.Minute,
	// CacheRules:    []cache.CacheRule{},
	//
	// rf.cacheConfig.DefaultMaxAge = parseDurationOption(options, "max_age")

	return rf, nil
}

func parseBoolOption(options map[string]string, key string) (bool, error) {
	if s, found := options[key]; found {
		if b, err := strconv.ParseBool(s); err != nil {
			return false, errors.Wrap(err, fmt.Sprintf("unable to parse %s %s", key, s))
		} else {
			return b, nil
		}
	}
	return false, nil
}

func parseDurationOption(options map[string]string, key string) (time.Duration, error) {
	if s, found := options[key]; found {
		if d, err := time.ParseDuration(s); err != nil {
			return time.Duration(0), errors.Wrap(err, fmt.Sprintf("unable to parse %s %s", key, s))
		} else {
			return d, nil
		}
	}
	return DefaultTimeout, nil
}

func fetchRefresh(client *http.Client, key string, req *http.Request, refreshRequest *http.Request, cacheConfig *cache.Config) (*cache.HTTPCacheEntry, []byte, error) {
	response := cache.NewResponse()

	if resp, err := client.Do(refreshRequest); err != nil {
		return nil, nil, err

	} else {
		if resp.StatusCode != 200 {
			return nil, nil, errors.New("Response from refresh request was not 200")
		}

		if body, err := ioutil.ReadAll(resp.Body); err != nil {
			return nil, nil, errors.Wrap(err, "Error reading response body")

		} else {
			// This creates cache files that can only live for 3 hours,
			// implementing token expiry without having to parse jwt token
			response.Header().Set("Cache-Control", "public,max-age=10800")
			response.WriteHeader(resp.StatusCode)

			return cache.NewHTTPCacheEntry(key, req, response, cacheConfig), body, nil
		}
	}
}

// Authenticate fulfils the backend interface
func (h Refresh) Authenticate(r *http.Request) (bool, error) {
	if r.Header.Get("Authorization") == "" {
		return false, errors.New("Missing Authorization Header")
	}
	jwtToken := strings.Split(r.Header.Get("Authorization"), " ")[1]

	if _, freshness := h.refreshCache.GetFreshness(r, jwtToken); freshness == 0 {
		// get value stored in cache to pass on context
		return true, nil

	} else if freshness == 2 {
		return false, nil
	}

	c := &http.Client{
		Timeout: h.timeout,
	}

	if !h.followRedirects {
		c.CheckRedirect = noRedirectsPolicy
	}

	req, err := http.NewRequest("GET", h.refreshUrl, nil)
	if err != nil {
		return false, err
	}

	if req.URL.Scheme == "https" && h.insecureSkipVerify {
		c.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	if h.passCookies {
		for _, c := range r.Cookies() {
			req.AddCookie(c)
		}
	}

	if entry, body, err := fetchRefresh(c, jwtToken, r, req, h.cacheConfig); err != nil {
		return false, err

	} else {
		if fileStore, err := storage.NewFileStorage(h.cacheConfig.Path); err != nil {
			return false, errors.Wrap(err, "Error setting up file storage for cache")

		} else {
			entry.Response.SetBody(fileStore)
		}

		// pass value to be cached on to context
		entry.Response.Write(body)
		h.refreshCache.Put(r, entry)
		return true, nil
	}

	return true, nil
}
<commit_msg>switching to full functionality<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Shannon Wynter
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package refresh

import (
	// "net/http/httptest"
	// "bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	// "io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/fellou89/caddy-cache"
	"github.com/fellou89/caddy-cache/storage"
	"github.com/fellou89/caddy-reauth/backend"
	. "github.com/fellou89/caddy-secrets"
)

// Backend name
const Backend = "refresh"

// DefaultTimeout for sub requests
const DefaultTimeout = time.Minute

// Refresh backend provides authentication against a refresh token endpoint.
// If the refresh request returns a http 200 status code then the user
// is considered logged in.
type Refresh struct {
	refreshUrl         string
	cacheConfig        *cache.Config
	refreshCache       *cache.HTTPCache
	timeout            time.Duration
	insecureSkipVerify bool
	followRedirects    bool
	passCookies        bool
}

var SecurityContext map[string]interface{}
var accessToken string

func init() {
	err := backend.Register(Backend, constructor)
	if err != nil {
		panic(err)
	}
}

func noRedirectsPolicy(req *http.Request, via []*http.Request) error {
	return errors.New("follow redirects disabled")
}

func constructor(config string) (backend.Backend, error) {
	options, err := backend.ParseOptions(config)
	if err != nil {
		return nil, err
	}

	s, found := options["url"]
	if !found {
		return nil, errors.New("url is a required parameter")
	}

	u, err := url.Parse(s)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse url "+s)
	}

	rf := &Refresh{
		refreshUrl:   u.String(),
		refreshCache: cache.NewHTTPCache(),
		cacheConfig: &cache.Config{
			Path:        "tmp",
			LockTimeout: time.Duration(5) * time.Minute,
		},
	}

	val, err := parseDurationOption(options, "timeout")
	if err != nil {
		return nil, err
	} else {
		rf.timeout = val
	}

	bval, err := parseBoolOption(options, "skipverify")
	if err != nil {
		return nil, err
	} else {
		rf.insecureSkipVerify = bval
	}

	bval, err = parseBoolOption(options, "follow")
	if err != nil {
		return nil, err
	} else {
		rf.followRedirects = bval
	}

	bval, err = parseBoolOption(options, "cookies")
	if err != nil {
		return nil, err
	} else {
		rf.passCookies = bval
	}

	// Cache config
	if s, found := options["cache_path"]; found {
		rf.cacheConfig.Path = s
	}

	val, err = parseDurationOption(options, "lock_timeout")
	if err != nil {
		return nil, err
	} else {
		rf.cacheConfig.LockTimeout = val
	}

	// Can't really define cache rules in one line, it would require refactor of parsing configs
	// so for now these two Config params stay out, and since cacheRules will be nil,
	// neither is used when creating HTTPCacheEntries
	//
	// DefaultMaxAge: time.Duration(5) * time.Minute,
	// CacheRules:    []cache.CacheRule{},
	//
	// rf.cacheConfig.DefaultMaxAge = parseDurationOption(options, "max_age")

	return rf, nil
}

func parseBoolOption(options map[string]string, key string) (bool, error) {
	if s, found := options[key]; found {
		if b, err := strconv.ParseBool(s); err != nil {
			return false, errors.Wrap(err, fmt.Sprintf("unable to parse %s %s", key, s))
		} else {
			return b, nil
		}
	}
	return false, nil
}

func parseDurationOption(options map[string]string, key string) (time.Duration, error) {
	if s, found := options[key]; found {
		if d, err := time.ParseDuration(s); err != nil {
			return time.Duration(0), errors.Wrap(err, fmt.Sprintf("unable to parse %s %s", key, s))
		} else {
			return d, nil
		}
	}
	return DefaultTimeout, nil
}

func (h Refresh) refreshRequestObject(c *http.Client, requestToAuth *http.Request) (*http.Request, error) {
	refreshToken := SecretsMap[0].Value.(string)
	data := url.Values{}
	data.Set("grant_type", "refresh_token")
	data.Add("refresh_token", refreshToken)

	refreshTokenReq, err := http.NewRequest("POST",
		"https://n0pwyybuji.execute-api.us-west-2.amazonaws.com/pre_prod/aqfer/auth/v1/access_token",
		strings.NewReader(data.Encode()))
	if err != nil {
		return nil, err
	}

	if refreshTokenReq.URL.Scheme == "https" && h.insecureSkipVerify {
		c.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	if h.passCookies {
		for _, c := range requestToAuth.Cookies() {
			refreshTokenReq.AddCookie(c)
		}
	}

	return refreshTokenReq, nil
}

func (h *Refresh) GetAccessToken(c *http.Client, refreshTokenReq *http.Request) error {
	refreshTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	if refreshResp, err := c.Do(refreshTokenReq); err != nil {
		return errors.Wrap(err, "Error requesting access token")

	} else {
		if refreshBody, err := ioutil.ReadAll(refreshResp.Body); err != nil {
			return errors.Wrap(err, "Error reading response body from access token refresh")

		} else {
			var b map[string]interface{}
			json.Unmarshal(refreshBody, &b)
			if b["message"] == "Forbidden" {
				return errors.New("Auth endpoint returned Forbidden")
			}
			accessToken = b["jwt_token"].(string)

			return h.newEntry(accessToken, refreshResp.StatusCode, refreshTokenReq, refreshBody)
		}
	}
}

func (h Refresh) requestSecurityContext(c *http.Client, requestToAuth *http.Request, clientJwtToken string) (interface{}, error) {
	if securityContextReq, err := http.NewRequest("GET",
		"https://n0pwyybuji.execute-api.us-west-2.amazonaws.com/pre_prod/aqfer/auth/v1/security_context?access_token="+clientJwtToken, nil); err != nil {

		return nil, err
	} else {

		securityContextReq.Header.Add("Authorization", "Bearer "+accessToken)
		if securityContextResp, err := c.Do(securityContextReq); err != nil {
			return nil, err

		} else {
			if securityContextResp.StatusCode == 400 {
				return nil, errors.New("Invalid response from security context endpoint")
			}

			if securityContextBody, err := ioutil.ReadAll(securityContextResp.Body); err != nil {
				return nil, errors.Wrap(err, "Error reading response body from security context request")

			} else {
				var b map[string]interface{}
				json.Unmarshal(securityContextBody, &b)
				if b["message"] == "Forbidden" || b["error"] != nil {
					return nil, errors.New("Auth endpoint returned Forbidden")
				}

				if err := h.newEntry(clientJwtToken, securityContextResp.StatusCode, requestToAuth, securityContextBody); err != nil {
					return nil, err
				} else {
					return b, nil
				}
			}
		}
	}
}

func (h Refresh) newEntry(key string, statusCode int, req *http.Request, body []byte) error {
	response := cache.NewResponse()
	// This creates cache files that can only live for 3 hours,
	// implementing token expiry without having to parse jwt token
	response.Header().Set("Cache-Control", "public,max-age=10800")
	response.WriteHeader(statusCode)

	// TODO: should I be using a lock?
	// lock := handler.URLLocks.Adquire(getKey(r))

	entry := cache.NewHTTPCacheEntry(key, req, response, h.cacheConfig)

	fileStore, err := storage.NewFileStorage(h.cacheConfig.Path)
	if err != nil {
		return errors.Wrap(err, "Error setting up file storage for cache")
	}
	entry.Response.SetBody(fileStore)
	entry.Response.Write(body)
	entry.Response.Close()
	h.refreshCache.Put(req, entry)
	// lock.Unlock()

	return nil
}

// Authenticate fulfils the backend interface
func (h Refresh) Authenticate(requestToAuth *http.Request) (bool, error) {
	if requestToAuth.Header.Get("Authorization") == "" {
		// No Token, Unauthorized response
		return false, nil
	}
	authHeader := strings.Split(requestToAuth.Header.Get("Authorization"), " ")
	if len(authHeader) != 2 || authHeader[0] != "Bearer" {
		return false, errors.New("Authorization token not properly formatted")
	}
	clientJwtToken := authHeader[1]

	c := &http.Client{Timeout: h.timeout}
	if !h.followRedirects {
		c.CheckRedirect = noRedirectsPolicy
	}

	// puts together refresh request to get access token
	refreshTokenReq, err := h.refreshRequestObject(c, requestToAuth)
	if err != nil {
		return false, err
	}

	if len(accessToken) == 0 { // no access token stored, request one
		if err := h.GetAccessToken(c, refreshTokenReq); err != nil {
			return false, err
		}

	} else { // access token stored; if not fresh, get new one
		if _, freshness := h.refreshCache.GetFreshness(refreshTokenReq, accessToken); freshness == 2 {
			if err := h.GetAccessToken(c, refreshTokenReq); err != nil {
				return false, err
			}
		}
	}

	// now that an access token is stored in cache, check client token freshness and get security context
	if entry, freshness := h.refreshCache.GetFreshness(requestToAuth, clientJwtToken); freshness == 0 {
		if securityContextBody, err := entry.Response.Read(); err != nil {
			return false, errors.Wrap(err, "Error reading security context from cache")

		} else {
			// security context pulled from cache and put in play
			var b interface{}
			json.Unmarshal(securityContextBody, &b)
			SecurityContext[clientJwtToken] = b
		}

	} else if freshness == 1 { // client token is not stored
		if securityContext, err := h.requestSecurityContext(c, requestToAuth, clientJwtToken); err != nil {
			if err.Error() == "Invalid response from security context endpoint" {
				// Unauthorized from security context endpoint, TODO: check with Thiru if this is correct
				return false, nil

			} else {
				return false, err
			}
		} else {
			// storing security context for first time
			SecurityContext[clientJwtToken] = securityContext
		}

	} else if freshness == 2 {
		// client token expired, Unauthorized response
		return false, nil
	}

	return true, nil
}
<|endoftext|>