<commit_before>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	// File is the default name of the JSON file where the config written.
	// The user can pass an alternate filename when using the CLI.
	File = ".exercism.json"
	// LegacyFile is the name of the original config file.
	// It is a misnomer, since the config was in json, not go.
	LegacyFile = ".exercism.go"

	// hostAPI is the endpoint to submit solutions to, and to get personalized data
	hostAPI = "http://exercism.io"
	// hostXAPI is the endpoint to fetch problems from
	hostXAPI = "http://x.exercism.io"

	// DirExercises is the default name of the directory for active users.
	// Make this non-exported when handlers.Login is deleted.
	DirExercises = "exercism"
)

var (
	errHomeNotFound = errors.New("unable to locate home directory")
)

// Config represents the settings for particular user.
// This defines both the auth for talking to the API, as well as
// where to put problems that get downloaded.
type Config struct {
	APIKey string `json:"apiKey"`
	Dir    string `json:"dir"`
	API    string `json:"api"`
	XAPI   string `json:"xapi"`
	File   string `json:"-"` // full path to config file
	home   string // cache user's home directory
}

// Home returns the user's canonical home directory.
// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func Home() (string, error) {
	var dir string
	if runtime.GOOS == "windows" {
		dir = os.Getenv("USERPROFILE")
		if dir == "" {
			dir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		}
	} else {
		dir = os.Getenv("HOME")
	}

	if dir == "" {
		return dir, errHomeNotFound
	}
	return dir, nil
}

// New returns a configuration struct with content from the exercism.json file
func New(path string) (*Config, error) {
	c := &Config{}
	err := c.load(path)
	return c, err
}

// Update sets new values where given.
func (c *Config) Update(key, host, dir, xapi string) {
	key = strings.TrimSpace(key)
	if key != "" {
		c.APIKey = key
	}

	host = strings.TrimSpace(host)
	if host != "" {
		c.API = host
	}

	dir = strings.TrimSpace(dir)
	if dir != "" {
		c.Dir = dir
	}

	xapi = strings.TrimSpace(xapi)
	if xapi != "" {
		c.XAPI = xapi
	}
}

// Write saves the config as JSON.
func (c *Config) Write() error {
	// truncates existing file if it exists
	f, err := os.Create(c.File)
	if err != nil {
		return err
	}
	defer f.Close()

	e := json.NewEncoder(f)
	return e.Encode(c)
}

func (c *Config) load(argPath string) error {
	path, err := c.resolvePath(argPath)
	if err != nil {
		return err
	}
	c.File = path

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

	// in case people manually update the config file
	// with weird formatting
	c.APIKey = strings.TrimSpace(c.APIKey)
	c.Dir = strings.TrimSpace(c.Dir)
	c.API = strings.TrimSpace(c.API)
	c.XAPI = strings.TrimSpace(c.XAPI)

	return c.setDefaults()
}

func (c *Config) read() error {
	if _, err := os.Stat(c.File); err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	f, err := os.Open(c.File)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := json.NewDecoder(f).Decode(&c); err != nil {
		var extra string
		if _, ok := err.(*json.SyntaxError); ok {
			extra = ":\nThe file contains invalid JSON syntax"
		}
		return fmt.Errorf("error parsing JSON in the config file %s%s: %s", f.Name(), extra, err)
	}

	return nil
}

// IsAuthenticated returns true if the config contains an API key.
// This does not check whether or not that key is valid.
func (c *Config) IsAuthenticated() bool {
	return c.APIKey != ""
}

// homeDir caches the lookup of the user's home directory.
func (c *Config) homeDir() (string, error) {
	if c.home != "" {
		return c.home, nil
	}
	return Home()
}

func (c *Config) resolvePath(argPath string) (string, error) {
	path := argPath
	if path == "" {
		path = filepath.Join("~", File)
	}
	h, err := c.homeDir()
	if err != nil {
		return "", err
	}
	path = strings.Replace(path, "~", h, 1)

	fi, _ := os.Stat(path)
	if fi != nil && fi.IsDir() {
		path = filepath.Join(path, File)
	}

	return path, nil
}

func (c *Config) setDefaults() error {
	if c.API == "" {
		c.API = hostAPI
	}

	if c.XAPI == "" {
		c.XAPI = hostXAPI
	}

	h, err := c.homeDir()
	if err != nil {
		return err
	}

	if c.Dir == "" {
		c.Dir = filepath.Join(h, DirExercises)
	}
	c.Dir = strings.Replace(c.Dir, "~", h, 1)

	return nil
}
<commit_msg>Highlight where the error occurred in the JSON syntax error<commit_after>package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	// File is the default name of the JSON file where the config written.
	// The user can pass an alternate filename when using the CLI.
	File = ".exercism.json"
	// LegacyFile is the name of the original config file.
	// It is a misnomer, since the config was in json, not go.
	LegacyFile = ".exercism.go"

	// hostAPI is the endpoint to submit solutions to, and to get personalized data
	hostAPI = "http://exercism.io"
	// hostXAPI is the endpoint to fetch problems from
	hostXAPI = "http://x.exercism.io"

	// DirExercises is the default name of the directory for active users.
	// Make this non-exported when handlers.Login is deleted.
	DirExercises = "exercism"
)

var (
	errHomeNotFound = errors.New("unable to locate home directory")
)

// Config represents the settings for particular user.
// This defines both the auth for talking to the API, as well as
// where to put problems that get downloaded.
type Config struct {
	APIKey string `json:"apiKey"`
	Dir    string `json:"dir"`
	API    string `json:"api"`
	XAPI   string `json:"xapi"`
	File   string `json:"-"` // full path to config file
	home   string // cache user's home directory
}

// Home returns the user's canonical home directory.
// See: http://stackoverflow.com/questions/7922270/obtain-users-home-directory
// we can't cross compile using cgo and use user.Current()
func Home() (string, error) {
	var dir string
	if runtime.GOOS == "windows" {
		dir = os.Getenv("USERPROFILE")
		if dir == "" {
			dir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		}
	} else {
		dir = os.Getenv("HOME")
	}

	if dir == "" {
		return dir, errHomeNotFound
	}
	return dir, nil
}

// New returns a configuration struct with content from the exercism.json file
func New(path string) (*Config, error) {
	c := &Config{}
	err := c.load(path)
	return c, err
}

// Update sets new values where given.
func (c *Config) Update(key, host, dir, xapi string) {
	key = strings.TrimSpace(key)
	if key != "" {
		c.APIKey = key
	}

	host = strings.TrimSpace(host)
	if host != "" {
		c.API = host
	}

	dir = strings.TrimSpace(dir)
	if dir != "" {
		c.Dir = dir
	}

	xapi = strings.TrimSpace(xapi)
	if xapi != "" {
		c.XAPI = xapi
	}
}

// Write saves the config as JSON.
func (c *Config) Write() error {
	// truncates existing file if it exists
	f, err := os.Create(c.File)
	if err != nil {
		return err
	}
	defer f.Close()

	e := json.NewEncoder(f)
	return e.Encode(c)
}

func (c *Config) load(argPath string) error {
	path, err := c.resolvePath(argPath)
	if err != nil {
		return err
	}
	c.File = path

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

	// in case people manually update the config file
	// with weird formatting
	c.APIKey = strings.TrimSpace(c.APIKey)
	c.Dir = strings.TrimSpace(c.Dir)
	c.API = strings.TrimSpace(c.API)
	c.XAPI = strings.TrimSpace(c.XAPI)

	return c.setDefaults()
}

func (c *Config) read() error {
	if _, err := os.Stat(c.File); err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	f, err := os.Open(c.File)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := json.NewDecoder(f).Decode(&c); err != nil {
		var extra string
		if serr, ok := err.(*json.SyntaxError); ok {
			if _, serr := f.Seek(0, os.SEEK_SET); serr != nil {
				log.Fatalf("seek error: %v", serr)
			}
			extra = fmt.Sprintf(":\nThe file contains invalid JSON syntax at '%s' <~",
				findInvalidJSON(f, serr.Offset))
		}
		return fmt.Errorf("error parsing JSON in the config file %s%s\n%s", f.Name(), extra, err)
	}

	return nil
}

func findInvalidJSON(f io.ReaderAt, pos int64) string {
	buf := make([]byte, 13)
	if _, err := f.ReadAt(buf, pos-13); err != nil {
		log.Fatalf("read error: %v", err)
	}

	return string(buf)
}

// IsAuthenticated returns true if the config contains an API key.
// This does not check whether or not that key is valid.
func (c *Config) IsAuthenticated() bool {
	return c.APIKey != ""
}

// homeDir caches the lookup of the user's home directory.
func (c *Config) homeDir() (string, error) {
	if c.home != "" {
		return c.home, nil
	}
	return Home()
}

func (c *Config) resolvePath(argPath string) (string, error) {
	path := argPath
	if path == "" {
		path = filepath.Join("~", File)
	}
	h, err := c.homeDir()
	if err != nil {
		return "", err
	}
	path = strings.Replace(path, "~", h, 1)

	fi, _ := os.Stat(path)
	if fi != nil && fi.IsDir() {
		path = filepath.Join(path, File)
	}

	return path, nil
}

func (c *Config) setDefaults() error {
	if c.API == "" {
		c.API = hostAPI
	}

	if c.XAPI == "" {
		c.XAPI = hostXAPI
	}

	h, err := c.homeDir()
	if err != nil {
		return err
	}

	if c.Dir == "" {
		c.Dir = filepath.Join(h, DirExercises)
	}
	c.Dir = strings.Replace(c.Dir, "~", h, 1)

	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"fmt"
	"os"
	"sort"

	"github.com/jessevdk/go-flags"
)

// Config stores the handler's configuration and UI interface parameters
type Config struct {
	Version          bool              `short:"V" long:"version" description:"Display version."`
	Loglevel         string            `short:"l" long:"loglevel" description:"Set loglevel ('debug', 'info', 'warn', 'error', 'fatal', 'panic')." env:"BIVAC_LOG_LEVEL" default:"info"`
	VolumesBlacklist []string          `short:"b" long:"blacklist" description:"Volumes to blacklist in backups." env:"BIVAC_VOLUMES_BLACKLIST" env-delim:","`
	VolumesWhitelist []string          `short:"w" long:"whitelist" description:"Only backup whitelisted volumes." env:"BIVAC_VOLUMES_WHITELIST" env-delim:","`
	Manpage          bool              `short:"m" long:"manpage" description:"Output manpage."`
	NoVerify         bool              `long:"no-verify" description:"Do not verify backup." env:"BIVAC_NO_VERIFY"`
	JSON             bool              `short:"j" long:"json" description:"Log as JSON (to stderr)." env:"BIVAC_JSON_OUTPUT"`
	Engine           string            `short:"E" long:"engine" description:"Backup engine to use." env:"BIVAC_ENGINE" default:"restic"`
	Orchestrator     string            `short:"o" long:"orchestrator" description:"Container orchestrator to use." env:"BIVAC_ORCHESTRATOR"`
	TargetURL        string            `short:"u" long:"target-url" description:"The target URL to push to." env:"BIVAC_TARGET_URL"`
	CheckEvery       string            `long:"check-every" description:"Time between backup checks." env:"BIVAC_CHECK_EVERY" default:"24h"`
	RemoveOlderThan  string            `long:"remove-older-than" description:"Remove backups older than the specified interval." env:"BIVAC_REMOVE_OLDER_THAN" default:"30D"`
	LabelPrefix      string            `long:"label-prefix" description:"The volume prefix label." env:"BIVAC_LABEL_PREFIX"`
	ExtraEnv         map[string]string `long:"extra-env" description:"Extra environment variables to share with workers." env:"BIVAC_EXTRA_ENV"`

	Restic struct {
		Image    string `long:"restic-image" description:"The restic docker image." env:"RESTIC_DOCKER_IMAGE" default:"restic/restic:latest"`
		Password string `long:"restic-password" description:"The restic backup password." env:"RESTIC_PASSWORD"`
	} `group:"Restic Options"`

	RClone struct {
		Image string `long:"rclone-image" description:"The rclone docker image." env:"RCLONE_DOCKER_IMAGE" default:"camptocamp/rclone:1.33-1"`
	} `group:"RClone Options"`

	Duplicity struct {
		Image           string `long:"duplicity-image" description:"The duplicity docker image." env:"DUPLICITY_DOCKER_IMAGE" default:"camptocamp/duplicity:latest"`
		FullIfOlderThan string `long:"full-if-older-than" description:"The number of days after which a full backup must be performed." env:"BIVAC_FULL_IF_OLDER_THAN" default:"15D"`
	} `group:"Duplicity Options"`

	Metrics struct {
		PushgatewayURL string `short:"g" long:"gateway-url" description:"The prometheus push gateway URL to use." env:"PUSHGATEWAY_URL"`
	} `group:"Metrics Options"`

	AWS struct {
		AccessKeyID     string `long:"aws-access-key-id" description:"The AWS access key ID." env:"AWS_ACCESS_KEY_ID"`
		SecretAccessKey string `long:"aws-secret-key-id" description:"The AWS secret access key." env:"AWS_SECRET_ACCESS_KEY"`
	} `group:"AWS Options"`

	Swift struct {
		Username          string `long:"swift-username" description:"The Swift user name." env:"SWIFT_USERNAME"`
		Password          string `long:"swift-password" description:"The Swift password." env:"SWIFT_PASSWORD"`
		AuthURL           string `long:"swift-auth_url" description:"The Swift auth URL." env:"SWIFT_AUTHURL"`
		TenantName        string `long:"swift-tenant-name" description:"The Swift tenant name." env:"SWIFT_TENANTNAME"`
		RegionName        string `long:"swift-region-name" description:"The Swift region name." env:"SWIFT_REGIONNAME"`
		UserDomainName    string `long:"swift-user-domain-name" description:"The Swift user domain name." env:"SWIFT_USER_DOMAIN_NAME"`
		ProjectName       string `long:"swift-project-name" description:"The Swift project name." env:"SWIFT_PROJECT_NAME"`
		ProjectDomainName string `long:"swift-project-domain-name" description:"The Swift project domain name." env:"SWIFT_PROJECT_DOMAIN_NAME"`
	} `group:"Swift Options"`

	Docker struct {
		Endpoint string `short:"e" long:"docker-endpoint" description:"The Docker endpoint." env:"DOCKER_ENDPOINT" default:"unix:///var/run/docker.sock"`
	} `group:"Docker Options"`

	Kubernetes struct {
		Namespace            string `long:"k8s-namespace" description:"Namespace where you want to run Bivac." env:"K8S_NAMESPACE"`
		KubeConfig           string `long:"k8s-kubeconfig" description:"Path to your kubeconfig file." env:"K8S_KUBECONFIG"`
		WorkerServiceAccount string `long:"k8s-worker-service-account" description:"Specify service account for workers." env:"K8S_WORKER_SERVICE_ACCOUNT"`
	} `group:"Kubernetes Options"`

	Cattle struct {
		Environment string `long:"cattle-env" description:"The Cattle environment." env:"CATTLE_ENV"`
		AccessKey   string `long:"cattle-accesskey" description:"The Cattle access key." env:"CATTLE_ACCESS_KEY"`
		SecretKey   string `long:"cattle-secretkey" description:"The Cattle secretkey." env:"CATTLE_SECRET_KEY"`
		URL         string `long:"cattle-url" description:"The Cattle url." env:"CATTLE_URL"`
	} `group:"Cattle Options"`
}

// LoadConfig loads the config from flags & environment
func LoadConfig(version string) *Config {
	var c Config
	parser := flags.NewParser(&c, flags.Default)
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}

	if c.Version {
		fmt.Printf("Bivac %v\n", version)
		os.Exit(0)
	}

	if c.Manpage {
		var buf bytes.Buffer
		parser.ShortDescription = "Docker volumes backup"
		parser.LongDescription = `Bivac lets you backup all your names Docker volumes using Duplicity or RClone.

Bivac supports multiple engines for performing the backup:

* Restic (default engine)

* RClone: use for heavy data that Restic/Duplicity cannot manage efficiently

* Duplicity
`
		parser.WriteManPage(&buf)
		fmt.Printf(buf.String())
		os.Exit(0)
	}

	sort.Strings(c.VolumesBlacklist)
	return &c
}
<commit_msg>update rclone image<commit_after>package config

import (
	"bytes"
	"fmt"
	"os"
	"sort"

	"github.com/jessevdk/go-flags"
)

// Config stores the handler's configuration and UI interface parameters
type Config struct {
	Version          bool              `short:"V" long:"version" description:"Display version."`
	Loglevel         string            `short:"l" long:"loglevel" description:"Set loglevel ('debug', 'info', 'warn', 'error', 'fatal', 'panic')." env:"BIVAC_LOG_LEVEL" default:"info"`
	VolumesBlacklist []string          `short:"b" long:"blacklist" description:"Volumes to blacklist in backups." env:"BIVAC_VOLUMES_BLACKLIST" env-delim:","`
	VolumesWhitelist []string          `short:"w" long:"whitelist" description:"Only backup whitelisted volumes." env:"BIVAC_VOLUMES_WHITELIST" env-delim:","`
	Manpage          bool              `short:"m" long:"manpage" description:"Output manpage."`
	NoVerify         bool              `long:"no-verify" description:"Do not verify backup." env:"BIVAC_NO_VERIFY"`
	JSON             bool              `short:"j" long:"json" description:"Log as JSON (to stderr)." env:"BIVAC_JSON_OUTPUT"`
	Engine           string            `short:"E" long:"engine" description:"Backup engine to use." env:"BIVAC_ENGINE" default:"restic"`
	Orchestrator     string            `short:"o" long:"orchestrator" description:"Container orchestrator to use." env:"BIVAC_ORCHESTRATOR"`
	TargetURL        string            `short:"u" long:"target-url" description:"The target URL to push to." env:"BIVAC_TARGET_URL"`
	CheckEvery       string            `long:"check-every" description:"Time between backup checks." env:"BIVAC_CHECK_EVERY" default:"24h"`
	RemoveOlderThan  string            `long:"remove-older-than" description:"Remove backups older than the specified interval." env:"BIVAC_REMOVE_OLDER_THAN" default:"30D"`
	LabelPrefix      string            `long:"label-prefix" description:"The volume prefix label." env:"BIVAC_LABEL_PREFIX"`
	ExtraEnv         map[string]string `long:"extra-env" description:"Extra environment variables to share with workers." env:"BIVAC_EXTRA_ENV"`

	Restic struct {
		Image    string `long:"restic-image" description:"The restic docker image." env:"RESTIC_DOCKER_IMAGE" default:"restic/restic:latest"`
		Password string `long:"restic-password" description:"The restic backup password." env:"RESTIC_PASSWORD"`
	} `group:"Restic Options"`

	RClone struct {
		Image string `long:"rclone-image" description:"The rclone docker image." env:"RCLONE_DOCKER_IMAGE" default:"camptocamp/rclone:1.42-1"`
	} `group:"RClone Options"`

	Duplicity struct {
		Image           string `long:"duplicity-image" description:"The duplicity docker image." env:"DUPLICITY_DOCKER_IMAGE" default:"camptocamp/duplicity:latest"`
		FullIfOlderThan string `long:"full-if-older-than" description:"The number of days after which a full backup must be performed." env:"BIVAC_FULL_IF_OLDER_THAN" default:"15D"`
	} `group:"Duplicity Options"`

	Metrics struct {
		PushgatewayURL string `short:"g" long:"gateway-url" description:"The prometheus push gateway URL to use." env:"PUSHGATEWAY_URL"`
	} `group:"Metrics Options"`

	AWS struct {
		AccessKeyID     string `long:"aws-access-key-id" description:"The AWS access key ID." env:"AWS_ACCESS_KEY_ID"`
		SecretAccessKey string `long:"aws-secret-key-id" description:"The AWS secret access key." env:"AWS_SECRET_ACCESS_KEY"`
	} `group:"AWS Options"`

	Swift struct {
		Username          string `long:"swift-username" description:"The Swift user name." env:"SWIFT_USERNAME"`
		Password          string `long:"swift-password" description:"The Swift password." env:"SWIFT_PASSWORD"`
		AuthURL           string `long:"swift-auth_url" description:"The Swift auth URL." env:"SWIFT_AUTHURL"`
		TenantName        string `long:"swift-tenant-name" description:"The Swift tenant name." env:"SWIFT_TENANTNAME"`
		RegionName        string `long:"swift-region-name" description:"The Swift region name." env:"SWIFT_REGIONNAME"`
		UserDomainName    string `long:"swift-user-domain-name" description:"The Swift user domain name." env:"SWIFT_USER_DOMAIN_NAME"`
		ProjectName       string `long:"swift-project-name" description:"The Swift project name." env:"SWIFT_PROJECT_NAME"`
		ProjectDomainName string `long:"swift-project-domain-name" description:"The Swift project domain name." env:"SWIFT_PROJECT_DOMAIN_NAME"`
	} `group:"Swift Options"`

	Docker struct {
		Endpoint string `short:"e" long:"docker-endpoint" description:"The Docker endpoint." env:"DOCKER_ENDPOINT" default:"unix:///var/run/docker.sock"`
	} `group:"Docker Options"`

	Kubernetes struct {
		Namespace            string `long:"k8s-namespace" description:"Namespace where you want to run Bivac." env:"K8S_NAMESPACE"`
		KubeConfig           string `long:"k8s-kubeconfig" description:"Path to your kubeconfig file." env:"K8S_KUBECONFIG"`
		WorkerServiceAccount string `long:"k8s-worker-service-account" description:"Specify service account for workers." env:"K8S_WORKER_SERVICE_ACCOUNT"`
	} `group:"Kubernetes Options"`

	Cattle struct {
		Environment string `long:"cattle-env" description:"The Cattle environment." env:"CATTLE_ENV"`
		AccessKey   string `long:"cattle-accesskey" description:"The Cattle access key." env:"CATTLE_ACCESS_KEY"`
		SecretKey   string `long:"cattle-secretkey" description:"The Cattle secretkey." env:"CATTLE_SECRET_KEY"`
		URL         string `long:"cattle-url" description:"The Cattle url." env:"CATTLE_URL"`
	} `group:"Cattle Options"`
}

// LoadConfig loads the config from flags & environment
func LoadConfig(version string) *Config {
	var c Config
	parser := flags.NewParser(&c, flags.Default)
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}

	if c.Version {
		fmt.Printf("Bivac %v\n", version)
		os.Exit(0)
	}

	if c.Manpage {
		var buf bytes.Buffer
		parser.ShortDescription = "Docker volumes backup"
		parser.LongDescription = `Bivac lets you backup all your names Docker volumes using Duplicity or RClone.

Bivac supports multiple engines for performing the backup:

* Restic (default engine)

* RClone: use for heavy data that Restic/Duplicity cannot manage efficiently

* Duplicity
`
		parser.WriteManPage(&buf)
		fmt.Printf(buf.String())
		os.Exit(0)
	}

	sort.Strings(c.VolumesBlacklist)
	return &c
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Christian Grabowski All rights reserved.
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 config

import (
	"io"
	"os"
	"strings"

	"github.com/BurntSushi/toml"
)

// HealthCheck is a struct to check for a service's 'upness'
type HealthCheck struct {
	Type              string // Either cmd, http_get, icmp_ping or ptrace_attach
	CMD               string
	Address           string
	ExpectedCondition string
	Retrys            int
}

// Service is a struct of the service to build
type Service struct {
	Name             string
	Tag              string
	TagType          string
	Path             string
	BuildLogFilePath string
	BuildCMD         []string
	TestCMD          []string
	CheckCMD         []string
	CreateCMD        []string
	UpdateCMD        []string
	HealthCheck      HealthCheck `toml:"[Services.HealthCheck]"`
	DependsOn        []string
}

// Project is the struct of the project to build
type Project struct {
	RepoURL         string
	CloneCMD        string
	AuthType        string
	SSHPrivKeyPath  string
	SSHPubKeyPath   string
	Username        string
	Password        string
	PromptForPWD    bool
	MaestrodHostEnv string
	MaestrodPortEnv string
	MaestrodHost    string
	MaestrodPort    int
}

// Environment is the config for the environment
type Environment struct {
	Env      []string
	ExecSync []string
	Exec     []string
}

// Artifact is struct for what artifacts to save post-pipeline
type Artifact struct {
	RuntimeFilePath string
	SaveFilePath    string
}

// CleanUp is a struct for the post-pipeline actions to clean up the build and save artifacts
type CleanUp struct {
	AdditionalCMDs []string
	InDaemon       bool
	Artifacts      []Artifact
}

// Config is a struct to parse the TOML config into
type Config struct {
	Environment Environment
	Project     Project
	Services    []Service
	CleanUp     CleanUp
}

func decode(r io.Reader) (*Config, error) {
	var conf Config
	if _, pErr := toml.DecodeReader(r, &conf); pErr != nil {
		return &conf, pErr
	}
	return &conf, nil
}

func loadLocal(path string) (*Config, error) {
	conf, readErr := os.OpenFile(path, os.O_RDONLY, 0644)
	if readErr != nil {
		return nil, readErr
	}
	return decode(conf)
}

// Load reads the config and returns a Config struct
func Load(path, clonePath string) (Config, error) {
	conf, rErr := loadLocal(path)
	if rErr != nil {
		return *conf, rErr
	}
	for i := range conf.Services {
		if strings.Contains(conf.Services[i].Path, ".") {
			conf.Services[i].Path = strings.Replace(conf.Services[i].Path, ".", clonePath, 1)
		}
	}
	return *conf, nil
}
<commit_msg>add s3 config support<commit_after>/*
Copyright 2016 Christian Grabowski All rights reserved.
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 config

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/BurntSushi/toml"
	"github.com/aws/aws-sdk-go/aws"
	awscreds "github.com/aws/aws-sdk-go/aws/credentials"
	awssession "github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

// HealthCheck is a struct to check for a service's 'upness'
type HealthCheck struct {
	Type              string // Either cmd, http_get, icmp_ping or ptrace_attach
	CMD               string
	Address           string
	ExpectedCondition string
	Retrys            int
}

// Service is a struct of the service to build
type Service struct {
	Name             string
	Tag              string
	TagType          string
	Path             string
	BuildLogFilePath string
	BuildCMD         []string
	TestCMD          []string
	CheckCMD         []string
	CreateCMD        []string
	UpdateCMD        []string
	HealthCheck      HealthCheck `toml:"[Services.HealthCheck]"`
	DependsOn        []string
}

// Project is the struct of the project to build
type Project struct {
	RepoURL         string
	CloneCMD        string
	AuthType        string
	SSHPrivKeyPath  string
	SSHPubKeyPath   string
	Username        string
	Password        string
	PromptForPWD    bool
	MaestrodHostEnv string
	MaestrodPortEnv string
	MaestrodHost    string
	MaestrodPort    int
}

// Environment is the config for the environment
type Environment struct {
	Env      []string
	ExecSync []string
	Exec     []string
}

// Artifact is struct for what artifacts to save post-pipeline
type Artifact struct {
	RuntimeFilePath string
	SaveFilePath    string
}

// CleanUp is a struct for the post-pipeline actions to clean up the build and save artifacts
type CleanUp struct {
	AdditionalCMDs []string
	InDaemon       bool
	Artifacts      []Artifact
}

// Config is a struct to parse the TOML config into
type Config struct {
	Environment Environment
	Project     Project
	Services    []Service
	CleanUp     CleanUp
}

type remoteConfig struct {
	Storage string
	Bucket  string
	Object  string
}

func parseRemote(path string) *remoteConfig {
	storageIdx := strings.Index(path, "://")
	pathSlice := strings.Split(path[storageIdx+1:], "/")
	obj := pathSlice[1]
	if len(pathSlice) > 2 {
		for i := 2; i < len(pathSlice); i++ {
			obj = fmt.Sprintf("%s/%s", obj, pathSlice[i])
		}
	}
	return &remoteConfig{
		Storage: path[0:storageIdx],
		Bucket:  pathSlice[0],
		Object:  obj,
	}
}

func decode(r io.Reader) (*Config, error) {
	var conf Config
	if _, pErr := toml.DecodeReader(r, &conf); pErr != nil {
		return &conf, pErr
	}
	return &conf, nil
}

func loadLocal(path string) (*Config, error) {
	conf, err := os.OpenFile(path, os.O_RDONLY, 0644)
	if err != nil {
		return nil, err
	}
	return decode(conf)
}

func loadS3(path string) (*Config, error) {
	remote := parseRemote(path)
	creds := awscreds.NewEnvCredentials()
	_, err := creds.Get()
	if err != nil {
		return nil, err
	}
	config := &aws.Config{
		Region:           aws.String(os.Getenv("AWS_S3_REGION")),
		Endpoint:         aws.String("s3.amazonaws.com"),
		S3ForcePathStyle: aws.Bool(true),
		Credentials:      creds,
		LogLevel:         aws.LogLevel(aws.LogLevelType(0)),
	}
	session := awssession.New(config)
	s3Client := s3.New(session)
	query := &s3.GetObjectInput{
		Bucket: aws.String(remote.Bucket),
		Key:    aws.String(remote.Object),
	}
	resp, err := s3Client.GetObject(query)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return decode(resp.Body)
}

// Load reads the config and returns a Config struct
func Load(path, clonePath string) (Config, error) {
	var (
		conf *Config
		rErr error
	)
	if strings.Contains(path, "s3://") {
		conf, rErr = loadS3(path)
	} else {
		conf, rErr = loadLocal(path)
	}
	if rErr != nil {
		return *conf, rErr
	}
	for i := range conf.Services {
		if strings.Contains(conf.Services[i].Path, ".") {
			conf.Services[i].Path = strings.Replace(conf.Services[i].Path, ".", clonePath, 1)
		}
	}
	return *conf, nil
}
<|endoftext|>


<commit_before>package controllers

import (
	"sort"

	"code.cloudfoundry.org/bbs/events"
	"code.cloudfoundry.org/bbs/models"
)

// LRP Instance Constraints:
// - an ActualLRP is identified by its ActualLRPKey + ActualLRPInstanceKey
// - as long as these don't change, any change in presence or state will result in a changed event
// - an ActualLRP transitioning from Unclaimed -> Claimed will result in a
//   changed event (this is not exactly an exception since an ActualLRPInstanceKey
//   doesn't have a defined identity by this constraint)
// - an ActualLRP cannot transition directly from having an
//   ActualLRPInstanceKey to having none (i.e. Running -> Unclaimed). This must
//   result in a removed and a created event.
//
// LRP Group emissions:
// - If the slot changes from nil to something, that's an ActualLRPCreated event
// - If the slot changes from something to nil, that's an ActualLRPRemoved event
// - If the Instance slot changes from one ActualLRPInstanceKey to another,
//   it's considered an ActualLRPCreated and ActualLRPRemoved event
// - If the instance slot does not change the ActualLRPInstanceKey, it's
//   considered an ActualLRPChanged event (including the transition from Running ->
//   Unclaimed, e.g. when the LRP crashes)
// - If the instance slot changes from Unclaimed to another state, it's still considered a changed event
//
// General:
//  - We should emit Crashed events followed by Create or Changed events where
//    the resulting LRP is in the running state before Remove events
//
// Events that follow are instance events. LRP group events go through a separate algorithm.
//
// ClaimActualLRP
//  Changes Allowed:
//      - Unclaimed -> Claimed (Changed event)
//      - Running -> Claimed (Changed event)
//      - Crashed -> Claimed (Changed event)
//      - Claimed -> Claimed (No event)
//
//  Transition is only allowed if LRPInstanceKey will be the same or if the LRP
//  is Unclaimed. No case this will result in any other event.
//
// UnclaimActualLRP
//  Changes Allowed:
//      Not Unclaimed -> Unclaimed (Should emit removed and created event)
//
// StartActualLRP
//  Changes Allowed:
//      nil -> Running (Created event)
//      Unclaimed -> Running (if instanceKey matches) (changed event)
//      Claimed -> Running (if instanceKey matches) (changed event)
//      Running -> Running (if instanceKey matches) (changed event) (Only allowed if netInfo has changed)
//
//      if the lrp being started is suspect:
//          do nothing
//      if suspect exists and it is not the lrp being started:
//          emit removed event for the suspect LRP
//
// CrashActualLRP
//  Changes Allowed:
//      Claimed -> Crashed (if instanceKey matches) (changed event)
//      Running -> Crashed (if instanceKey matches) (changed event)
//
//      Claimed -> Unclaimed  (if instanceKey matches) (if crashedCount is below) (created + removed event)
//      Running -> Unclaimed  (if instanceKey matches) (if crashCount is below) (created + removed event)
//
// FailActualLRP
//  Unclaimed -> Unclaimed (no events emitted)
//
// RemoveActualLRP
//  removed event

type EventCalculator struct {
	ActualLRPGroupHub    events.Hub
	ActualLRPInstanceHub events.Hub
}

// EmitEvents emits the events such as when the changes identified in the
// events are applied to the beforeSet the resulting state is equal to
// afterSet.  The beforeSet and afterSet are assumed to have the same process
// guid and index.
func (e EventCalculator) EmitEvents(beforeSet, afterSet []*models.ActualLRP) {
	events := []models.Event{}

	beforeGroup := models.ResolveActualLRPGroup(beforeSet)
	afterGroup := models.ResolveActualLRPGroup(removeNilLRPs(afterSet))

	for _, ev := range generateLRPGroupEvents(beforeGroup, afterGroup) {
		e.ActualLRPGroupHub.Emit(ev)
	}

	// stretch the two slices to be of equal size.  make sure we do this after
	// emitting the group events, otherwise ResolveActualLRPGroup will panic if
	// it encounters nil lrps.
	stretchSlice(&beforeSet, &afterSet)

	for i := range afterSet {
		events = append(events, generateLRPInstanceEvents(beforeSet[i], afterSet[i])...)
	}

	sort.Slice(events, func(i, j int) bool {
		return shouldEventComeFirst(events[i], events[j])
	})

	for _, ev := range events {
		e.ActualLRPInstanceHub.Emit(ev)
	}
}

// RecordChange returns a new LRP set with the before LRP replaced with after
// LRP.  The index of after and before is the same.  New LRPs (i.e. when before
// is nil) are appended to the end of the lrp slice.
func (e EventCalculator) RecordChange(before, after *models.ActualLRP, lrps []*models.ActualLRP) []*models.ActualLRP {
	found := false
	newLRPs := []*models.ActualLRP{}
	for _, l := range lrps {
		if l == nil {
			// this entry is recording a LRP removal, just skip it
			newLRPs = append(newLRPs, nil)
			continue
		}

		if before != nil && l.ActualLRPInstanceKey.Equal(before.ActualLRPInstanceKey) {
			newLRPs = append(newLRPs, after)
			found = true
		} else {
			newLRPs = append(newLRPs, l)
		}
	}

	if !found {
		return append(lrps, after)
	}

	return newLRPs
}

func generateCrashedInstanceEvents(before, after *models.ActualLRP) []models.Event {
	return wrapEvent(
		models.NewActualLRPCrashedEvent(before, after),
		models.NewActualLRPInstanceChangedEvent(before, after),
	)
}

func generateUpdateInstanceEvents(before, after *models.ActualLRP) []models.Event {
	return wrapEvent(
		models.NewActualLRPInstanceChangedEvent(before, after),
	)
}

func generateUnclaimedInstanceEvents(before, after *models.ActualLRP) []models.Event {
	events := []models.Event{}

	// This LRP probably transitioned from Claimed/Running -> Crashed ->
	// Unclaimed because it was restartable
	if after.CrashCount > before.CrashCount {
		events = append(events, models.NewActualLRPCrashedEvent(before, after))
	}

	if before.State == models.ActualLRPStateUnclaimed {
		// we can get here if auctioneer calls FailActualLRP which updates the
		// placement error but doesn't change the LRP's state
		return append(events, models.NewActualLRPInstanceChangedEvent(before, after))
	}

	return append(
		events,
		models.NewActualLRPInstanceCreatedEvent(after),
		models.NewActualLRPInstanceRemovedEvent(before),
	)
}

func generateLRPInstanceEvents(before, after *models.ActualLRP) []models.Event {
	if before.Equal(after) {
		// nothing changed
		return nil
	}

	if after == nil {
		return wrapEvent(models.NewActualLRPInstanceRemovedEvent(before))
	}

	if before == nil {
		return wrapEvent(models.NewActualLRPInstanceCreatedEvent(after))
	}

	switch after.State {
	case models.ActualLRPStateUnclaimed:
		return generateUnclaimedInstanceEvents(before, after)
	case models.ActualLRPStateCrashed:
		return generateCrashedInstanceEvents(before, after)
	case models.ActualLRPStateRunning:
		return generateUpdateInstanceEvents(before, after)
	default:
		return nil
	}
}

func generateCrashedGroupEvents(before, after *models.ActualLRP) []models.Event {
	return wrapEvent(
		models.NewActualLRPCrashedEvent(before, after),
		models.NewActualLRPChangedEvent(before.ToActualLRPGroup(), after.ToActualLRPGroup()),
	)
}

func generateUnclaimedGroupEvents(before, after *models.ActualLRP) []models.Event {
	events := []models.Event{}

	// This LRP probably transitioned from Claimed/Running -> Crashed ->
	// Unclaimed because it was restartable
	if after.CrashCount > before.CrashCount {
		events = append(events, models.NewActualLRPCrashedEvent(before, after))
	}

	return append(
		events,
		models.NewActualLRPChangedEvent(before.ToActualLRPGroup(), after.ToActualLRPGroup()),
	)
}

func generateUpdateGroupEvents(before, after *models.ActualLRP) []models.Event {
	if !after.ActualLRPInstanceKey.Equal(before.ActualLRPInstanceKey) {
		// an Ordinary LRP replaced Suspect LRP
		return wrapEvent(
			models.NewActualLRPCreatedEvent(after.ToActualLRPGroup()),
			models.NewActualLRPRemovedEvent(before.ToActualLRPGroup()),
		)
	}

	return nil
}

// The main difference between this function and generateLRPInstanceEvents
// (besides using different event types) is that the latter generates a
// remove+create events when the LRP is unclaimed.  This function return a
// ActualLRPChangedEvent instead to be compatible with olf subscribers.
func generateLRPInstanceGroupEvents(before, after *models.ActualLRP) []models.Event {
	if before.Equal(after) {
		// nothing changed
		return nil
	}

	if after == nil {
		return wrapEvent(models.NewActualLRPRemovedEvent(before.ToActualLRPGroup()))
	}

	if before == nil {
		return wrapEvent(models.NewActualLRPCreatedEvent(after.ToActualLRPGroup()))
	}

	switch after.State {
	case models.ActualLRPStateUnclaimed:
		return generateUnclaimedGroupEvents(before, after)
	case models.ActualLRPStateClaimed:
		return generateUpdateGroupEvents(before, after)
	case models.ActualLRPStateRunning:
		return generateUpdateGroupEvents(before, after)
	case models.ActualLRPStateCrashed:
		return generateCrashedGroupEvents(before, after)
	default:
		return nil
	}
}

// return the resulting lrp of the given event, that is the lrp being created
// or the lrp in the new lrp in a ActualLRPChanged event.  Returns nil for
// crashed and removed events.  Returns true iff this is a crashed event.
func getEventLRP(e models.Event) (*models.ActualLRP, bool) {
	switch x := e.(type) {
	case *models.ActualLRPCreatedEvent:
		lrp, _, _ := x.ActualLrpGroup.Resolve()
		return lrp, false
	case *models.ActualLRPChangedEvent:
		lrp, _, _ := x.After.Resolve()
		return lrp, false
	case *models.ActualLRPInstanceCreatedEvent:
		return x.ActualLrp, false
	case *models.ActualLRPInstanceChangedEvent:
		return x.After.ToActualLRP(x.ActualLRPKey, x.ActualLRPInstanceKey), false
	case *models.ActualLRPCrashedEvent:
		return nil, true
	}

	return nil, false
}

// determine if i should be emitted before j.  The ordering ensures continuous
// routability, so events with running instances should be emitted first
// followed by remove/crash events.
func shouldEventComeFirst(i, j models.Event) bool {
	first, firstCrashed := getEventLRP(i)
	second, secondCrashed := getEventLRP(j)

	// sort crashed events first to be backward compatible with the old
	// event stream which emitted the crashed event before the
	// remove/changed events.
	if firstCrashed {
		return !secondCrashed
	}

	if secondCrashed {
		return false
	}

	// i is a removed event.  These should be emitted last, so i cannot less
	// than j.
	if first == nil {
		return false
	}

	// j is a removed event.  These should be emitted last, so i is less than
	// (emitted before) j.
	if second == nil {
		return true
	}

	if first.State == models.ActualLRPStateRunning {
		// true if the lrp from the other event isn't running, otherwise
		// treat them as equal
		return second.State != models.ActualLRPStateRunning
	}

	return false
}

func generateLRPGroupEvents(before, after *models.ActualLRPGroup) []models.Event {
	events := generateLRPInstanceGroupEvents(before.Instance, after.Instance)
	events = append(events, generateLRPInstanceGroupEvents(before.Evacuating, after.Evacuating)...)

	sort.Slice(events, func(i, j int) bool {
		return shouldEventComeFirst(events[i], events[j])
	})

	return events
}

// A Helper function to remove null lrps that could be added to the set if an
// LRP is removed.
func removeNilLRPs(lrps []*models.ActualLRP) []*models.ActualLRP {
	newLRPs := []*models.ActualLRP{}
	for _, l := range lrps {
		if l == nil {
			continue
		}
		newLRPs = append(newLRPs, l)
	}
	return newLRPs
}

func stretchSlice(before, after *[]*models.ActualLRP) {
	if len(*before) < len(*after) {
		newLRPs := make([]*models.ActualLRP, len(*after))
		copy(newLRPs, *before)
		*before = newLRPs
	}
}

func wrapEvent(e ...models.Event) []models.Event {
	return e
}
<commit_msg>Simplify the event ordering logic by using an event score<commit_after>package controllers

import (
	"sort"

	"code.cloudfoundry.org/bbs/events"
	"code.cloudfoundry.org/bbs/models"
)

// LRP Instance Constraints:
// - an ActualLRP is identified by its ActualLRPKey + ActualLRPInstanceKey
// - as long as these don't change, any change in presence or state will result in a changed event
// - an ActualLRP transitioning from Unclaimed -> Claimed will result in a
//   changed event (this is not exactly an exception since an ActualLRPInstanceKey
//   doesn't have a defined identity by this constraint)
// - an ActualLRP cannot transition directly from having an
//   ActualLRPInstanceKey to having none (i.e. Running -> Unclaimed). This must
//   result in a removed and a created event.
//
// LRP Group emissions:
// - If the slot changes from nil to something, that's an ActualLRPCreated event
// - If the slot changes from something to nil, that's an ActualLRPRemoved event
// - If the Instance slot changes from one ActualLRPInstanceKey to another,
//   it's considered an ActualLRPCreated and ActualLRPRemoved event
// - If the instance slot does not change the ActualLRPInstanceKey, it's
//   considered an ActualLRPChanged event (including the transition from Running ->
//   Unclaimed, e.g. when the LRP crashes)
// - If the instance slot changes from Unclaimed to another state, it's still considered a changed event
//
// General:
//  - We should emit Crashed events followed by Create or Changed events where
//    the resulting LRP is in the running state before Remove events
//
// Events that follow are instance events. LRP group events go through a separate algorithm.
//
// ClaimActualLRP
//  Changes Allowed:
//      - Unclaimed -> Claimed (Changed event)
//      - Running -> Claimed (Changed event)
//      - Crashed -> Claimed (Changed event)
//      - Claimed -> Claimed (No event)
//
//  Transition is only allowed if LRPInstanceKey will be the same or if the LRP
//  is Unclaimed. No case this will result in any other event.
//
// UnclaimActualLRP
//  Changes Allowed:
//      Not Unclaimed -> Unclaimed (Should emit removed and created event)
//
// StartActualLRP
//  Changes Allowed:
//      nil -> Running (Created event)
//      Unclaimed -> Running (if instanceKey matches) (changed event)
//      Claimed -> Running (if instanceKey matches) (changed event)
//      Running -> Running (if instanceKey matches) (changed event) (Only allowed if netInfo has changed)
//
//      if the lrp being started is suspect:
//          do nothing
//      if suspect exists and it is not the lrp being started:
//          emit removed event for the suspect LRP
//
// CrashActualLRP
//  Changes Allowed:
//      Claimed -> Crashed (if instanceKey matches) (changed event)
//      Running -> Crashed (if instanceKey matches) (changed event)
//
//      Claimed -> Unclaimed  (if instanceKey matches) (if crashedCount is below) (created + removed event)
//      Running -> Unclaimed  (if instanceKey matches) (if crashCount is below) (created + removed event)
//
// FailActualLRP
//  Unclaimed -> Unclaimed (no events emitted)
//
// RemoveActualLRP
//  removed event

type EventCalculator struct {
	ActualLRPGroupHub    events.Hub
	ActualLRPInstanceHub events.Hub
}

// EmitEvents emits the events such as when the changes identified in the
// events are applied to the beforeSet the resulting state is equal to
// afterSet.  The beforeSet and afterSet are assumed to have the same process
// guid and index.
func (e EventCalculator) EmitEvents(beforeSet, afterSet []*models.ActualLRP) {
	events := []models.Event{}

	beforeGroup := models.ResolveActualLRPGroup(beforeSet)
	afterGroup := models.ResolveActualLRPGroup(removeNilLRPs(afterSet))

	for _, ev := range generateLRPGroupEvents(beforeGroup, afterGroup) {
		e.ActualLRPGroupHub.Emit(ev)
	}

	// stretch the two slices to be of equal size.  make sure we do this after
	// emitting the group events, otherwise ResolveActualLRPGroup will panic if
	// it encounters nil lrps.
	stretchSlice(&beforeSet, &afterSet)

	for i := range afterSet {
		events = append(events, generateLRPInstanceEvents(beforeSet[i], afterSet[i])...)
	}

	sort.Slice(events, func(i, j int) bool {
		return eventScore(events[i]) > eventScore(events[j])
	})

	for _, ev := range events {
		e.ActualLRPInstanceHub.Emit(ev)
	}
}

// RecordChange returns a new LRP set with the before LRP replaced with after
// LRP.  The index of after and before is the same.  New LRPs (i.e. when before
// is nil) are appended to the end of the lrp slice.
func (e EventCalculator) RecordChange(before, after *models.ActualLRP, lrps []*models.ActualLRP) []*models.ActualLRP {
	found := false
	newLRPs := []*models.ActualLRP{}
	for _, l := range lrps {
		if l == nil {
			// this entry is recording a LRP removal, just skip it
			newLRPs = append(newLRPs, nil)
			continue
		}

		if before != nil && l.ActualLRPInstanceKey.Equal(before.ActualLRPInstanceKey) {
			newLRPs = append(newLRPs, after)
			found = true
		} else {
			newLRPs = append(newLRPs, l)
		}
	}

	if !found {
		return append(lrps, after)
	}

	return newLRPs
}

func generateCrashedInstanceEvents(before, after *models.ActualLRP) []models.Event {
	return wrapEvent(
		models.NewActualLRPCrashedEvent(before, after),
		models.NewActualLRPInstanceChangedEvent(before, after),
	)
}

func generateUpdateInstanceEvents(before, after *models.ActualLRP) []models.Event {
	return wrapEvent(
		models.NewActualLRPInstanceChangedEvent(before, after),
	)
}

func generateUnclaimedInstanceEvents(before, after *models.ActualLRP) []models.Event {
	events := []models.Event{}

	// This LRP probably transitioned from Claimed/Running -> Crashed ->
	// Unclaimed because it was restartable
	if after.CrashCount > before.CrashCount {
		events = append(events, models.NewActualLRPCrashedEvent(before, after))
	}

	if before.State == models.ActualLRPStateUnclaimed {
		// we can get here if auctioneer calls FailActualLRP which updates the
		// placement error but doesn't change the LRP's state
		return append(events, models.NewActualLRPInstanceChangedEvent(before, after))
	}

	return append(
		events,
		models.NewActualLRPInstanceCreatedEvent(after),
		models.NewActualLRPInstanceRemovedEvent(before),
	)
}

func generateLRPInstanceEvents(before, after *models.ActualLRP) []models.Event {
	if before.Equal(after) {
		// nothing changed
		return nil
	}

	if after == nil {
		return wrapEvent(models.NewActualLRPInstanceRemovedEvent(before))
	}

	if before == nil {
		return wrapEvent(models.NewActualLRPInstanceCreatedEvent(after))
	}

	switch after.State {
	case models.ActualLRPStateUnclaimed:
		return generateUnclaimedInstanceEvents(before, after)
	case models.ActualLRPStateCrashed:
		return generateCrashedInstanceEvents(before, after)
	case models.ActualLRPStateRunning:
		return generateUpdateInstanceEvents(before, after)
	default:
		return nil
	}
}

func generateCrashedGroupEvents(before, after *models.ActualLRP) []models.Event {
	return wrapEvent(
		models.NewActualLRPCrashedEvent(before, after),
		models.NewActualLRPChangedEvent(before.ToActualLRPGroup(), after.ToActualLRPGroup()),
	)
}

func generateUnclaimedGroupEvents(before, after *models.ActualLRP) []models.Event {
	events := []models.Event{}

	// This LRP probably transitioned from Claimed/Running -> Crashed ->
	// Unclaimed because it was restartable
	if after.CrashCount > before.CrashCount {
		events = append(events, models.NewActualLRPCrashedEvent(before, after))
	}

	return append(
		events,
		models.NewActualLRPChangedEvent(before.ToActualLRPGroup(), after.ToActualLRPGroup()),
	)
}

func generateUpdateGroupEvents(before, after *models.ActualLRP) []models.Event {
	if !after.ActualLRPInstanceKey.Equal(before.ActualLRPInstanceKey) {
		// an Ordinary LRP replaced Suspect LRP
		return wrapEvent(
			models.NewActualLRPCreatedEvent(after.ToActualLRPGroup()),
			models.NewActualLRPRemovedEvent(before.ToActualLRPGroup()),
		)
	}

	return nil
}

// The main difference between this function and generateLRPInstanceEvents
// (besides using different event types) is that the latter generates a
// remove+create events when the LRP is unclaimed.  This function return a
// ActualLRPChangedEvent instead to be compatible with olf subscribers.
func generateLRPInstanceGroupEvents(before, after *models.ActualLRP) []models.Event {
	if before.Equal(after) {
		// nothing changed
		return nil
	}

	if after == nil {
		return wrapEvent(models.NewActualLRPRemovedEvent(before.ToActualLRPGroup()))
	}

	if before == nil {
		return wrapEvent(models.NewActualLRPCreatedEvent(after.ToActualLRPGroup()))
	}

	switch after.State {
	case models.ActualLRPStateUnclaimed:
		return generateUnclaimedGroupEvents(before, after)
	case models.ActualLRPStateClaimed:
		return generateUpdateGroupEvents(before, after)
	case models.ActualLRPStateRunning:
		return generateUpdateGroupEvents(before, after)
	case models.ActualLRPStateCrashed:
		return generateCrashedGroupEvents(before, after)
	default:
		return nil
	}
}

// return the resulting lrp of the given event, that is the lrp being created
// or the lrp in the new lrp in a ActualLRPChanged event.  Returns nil for
// crashed and removed events.  Returns true iff this is a crashed event.
func getEventLRP(e models.Event) (*models.ActualLRP, bool) {
	switch x := e.(type) {
	case *models.ActualLRPCreatedEvent:
		lrp, _, _ := x.ActualLrpGroup.Resolve()
		return lrp, false
	case *models.ActualLRPChangedEvent:
		lrp, _, _ := x.After.Resolve()
		return lrp, false
	case *models.ActualLRPInstanceCreatedEvent:
		return x.ActualLrp, false
	case *models.ActualLRPInstanceChangedEvent:
		return x.After.ToActualLRP(x.ActualLRPKey, x.ActualLRPInstanceKey), false
	case *models.ActualLRPCrashedEvent:
		return nil, true
	}

	return nil, false
}

// Determine the score of an event. An event with higher score should be
// emitted before lower ones.  The score based ordering ensures continuous
// routability, so events with running instances should be emitted first
// followed by remove events.
func eventScore(e models.Event) int {
	lrp, crashed := getEventLRP(e)

	// sort crashed events first to be backward compatible with the old
	// event stream which emitted the crashed event before the
	// remove/changed events.
	if crashed {
		return 2
	}

	// this is an event with a running instance, this should be emitted before
	// any other event, such as removed ro changed event to non-running state.
	if lrp != nil && lrp.State == models.ActualLRPStateRunning {
		return 1
	}

	// i is a removed event.  These should be emitted last, so i cannot less
	// than j.
	return 0
}

func generateLRPGroupEvents(before, after *models.ActualLRPGroup) []models.Event {
	events := generateLRPInstanceGroupEvents(before.Instance, after.Instance)
	events = append(events, generateLRPInstanceGroupEvents(before.Evacuating, after.Evacuating)...)

	sort.Slice(events, func(i, j int) bool {
		return eventScore(events[i]) > eventScore(events[j])
	})

	return events
}

// A Helper function to remove null lrps that could be added to the set if an
// LRP is removed.
func removeNilLRPs(lrps []*models.ActualLRP) []*models.ActualLRP {
	newLRPs := []*models.ActualLRP{}
	for _, l := range lrps {
		if l == nil {
			continue
		}
		newLRPs = append(newLRPs, l)
	}
	return newLRPs
}

func stretchSlice(before, after *[]*models.ActualLRP) {
	if len(*before) < len(*after) {
		newLRPs := make([]*models.ActualLRP, len(*after))
		copy(newLRPs, *before)
		*before = newLRPs
	}
}

func wrapEvent(e ...models.Event) []models.Event {
	return e
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package ccintf

//This package defines the interfaces that support runtime and
//communication between chaincode and peer (chaincode support).
//Currently inproccontroller uses it. dockercontroller does not.

import (
	"github.com/hyperledger/fabric/core/chaincode/persistence/intf"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// ChaincodeStream interface for stream between Peer and chaincode instance.
type ChaincodeStream interface {
	Send(*pb.ChaincodeMessage) error
	Recv() (*pb.ChaincodeMessage, error)
}

// CCSupport must be implemented by the chaincode support side in peer
// (such as chaincode_support)
type CCSupport interface {
	HandleChaincodeStream(ChaincodeStream) error
}

// CCID encapsulates chaincode ID
type CCID string

// String returns a string version of the chaincode ID
func (c CCID) String() string {
	return string(c)
}

// New returns a chaincode ID given the supplied package ID
func New(packageID persistence.PackageID) CCID {
	return CCID(packageID.String())
}
<commit_msg>[FAB-15484] Fix goimport errors in core/container<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package ccintf

//This package defines the interfaces that support runtime and
//communication between chaincode and peer (chaincode support).
//Currently inproccontroller uses it. dockercontroller does not.

import (
	persistence "github.com/hyperledger/fabric/core/chaincode/persistence/intf"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// ChaincodeStream interface for stream between Peer and chaincode instance.
type ChaincodeStream interface {
	Send(*pb.ChaincodeMessage) error
	Recv() (*pb.ChaincodeMessage, error)
}

// CCSupport must be implemented by the chaincode support side in peer
// (such as chaincode_support)
type CCSupport interface {
	HandleChaincodeStream(ChaincodeStream) error
}

// CCID encapsulates chaincode ID
type CCID string

// String returns a string version of the chaincode ID
func (c CCID) String() string {
	return string(c)
}

// New returns a chaincode ID given the supplied package ID
func New(packageID persistence.PackageID) CCID {
	return CCID(packageID.String())
}
<|endoftext|>


<commit_before>package example

import (
	"context"
	"fmt"
	stdjson "encoding/json"

	"github.com/bytom/blockchain/rpc"
	"github.com/bytom/crypto/ed25519/chainkd"
	"github.com/bytom/blockchain/query"
	"github.com/bytom/blockchain/txbuilder"
	bc "github.com/bytom/blockchain"
)

// TO DO: issue a asset to a account.
func IssueTest(client *rpc.Client, args []string) {
	// Create Account.
	fmt.Printf("To create Account:\n")
	xprv, _ := chainkd.NewXPrv(nil)
	xpub := xprv.XPub()
	fmt.Printf("xprv_account:%v\n", xprv)
	fmt.Printf("xpub_account:%v\n", xpub)
	type Ins struct {
	    RootXPubs []chainkd.XPub `json:"root_xpubs"`
		Quorum    int
		Alias     string
		Tags      map[string]interface{}
		ClientToken string `json:"client_token"`
	}
	var ins Ins
	ins.RootXPubs = []chainkd.XPub{xpub}
	ins.Quorum = 1
	ins.Alias = "alice"
	ins.Tags = map[string]interface{}{"test_tag": "v0",}
	ins.ClientToken = "account"
	account := make([]query.AnnotatedAccount, 1)
	client.Call(context.Background(), "/create-account", &[]Ins{ins,}, &account)
	fmt.Printf("account:%v\n", account)

	// Create Asset.
	fmt.Printf("To create Asset:\n")
	xprv_asset, _ := chainkd.NewXPrv(nil)
	xpub_asset := xprv_asset.XPub()
	fmt.Printf("xprv_asset:%v\n", xprv_asset)
	fmt.Printf("xpub_asset:%v\n", xpub_asset)
	type Ins_asset struct {
	    RootXPubs []chainkd.XPub `json:"root_xpubs"`
		Quorum    int
		Alias     string
		Tags      map[string]interface{}
		Definition  map[string]interface{}
		ClientToken string `json:"client_token"`
	}
	var ins_asset Ins_asset
	ins_asset.RootXPubs = []chainkd.XPub{xpub_asset}
	ins_asset.Quorum = 1
	ins_asset.Alias = "gold"
	ins_asset.Tags = map[string]interface{}{"test_tag": "v0",}
	ins_asset.Definition = map[string]interface{}{"test_definition": "v0"}
	ins_asset.ClientToken = "asset"
	asset := make([]query.AnnotatedAsset, 1)
	client.Call(context.Background(), "/create-asset", &[]Ins_asset{ins_asset,}, &asset)
	fmt.Printf("asset:%v\n", asset)

	// Build Transaction.
	fmt.Printf("To build transaction:\n")
	// Now Issue actions
	buildReqFmt := `
		{"actions": [
			{"type": "issue", "asset_id": "%s", "amount": 100},
			{"type": "control_account", "asset_id": "%s", "amount": 100, "account_id": "%s"}
		]}`
	buildReqStr := fmt.Sprintf(buildReqFmt, asset[0].ID.String(), asset[0].ID.String(), account[0].ID)
	var buildReq bc.BuildRequest
	err := stdjson.Unmarshal([]byte(buildReqStr), &buildReq)
	if err != nil {
		fmt.Printf("json Unmarshal error.")
	}
	tpl := make([]txbuilder.Template, 1)
	client.Call(context.Background(), "/build-transaction", []*bc.BuildRequest{&buildReq,}, &tpl)
	fmt.Printf("tpl:%v\n", tpl)

	// sign-transaction
	err = txbuilder.Sign(context.Background(), &tpl[0], []chainkd.XPub{xprv_asset.XPub()}, func(_ context.Context, _ chainkd.XPub, path [][]byte, data [32]byte) ([]byte, error) {
		derived := xprv_asset.Derive(path)
		return derived.Sign(data[:]), nil
	})
	if err != nil {
		fmt.Printf("sign-transaction error. err:%v\n", err)
	}
}
<commit_msg>Added log to print SigningInstructions's SignatureWitnesses.<commit_after>package example

import (
	"context"
	"fmt"
	stdjson "encoding/json"

	"github.com/bytom/blockchain/rpc"
	"github.com/bytom/crypto/ed25519/chainkd"
	"github.com/bytom/blockchain/query"
	"github.com/bytom/blockchain/txbuilder"
	bc "github.com/bytom/blockchain"
)

// TO DO: issue a asset to a account.
func IssueTest(client *rpc.Client, args []string) {
	// Create Account.
	fmt.Printf("To create Account:\n")
	xprv, _ := chainkd.NewXPrv(nil)
	xpub := xprv.XPub()
	fmt.Printf("xprv_account:%v\n", xprv)
	fmt.Printf("xpub_account:%v\n", xpub)
	type Ins struct {
	    RootXPubs []chainkd.XPub `json:"root_xpubs"`
		Quorum    int
		Alias     string
		Tags      map[string]interface{}
		ClientToken string `json:"client_token"`
	}
	var ins Ins
	ins.RootXPubs = []chainkd.XPub{xpub}
	ins.Quorum = 1
	ins.Alias = "alice"
	ins.Tags = map[string]interface{}{"test_tag": "v0",}
	ins.ClientToken = "account"
	account := make([]query.AnnotatedAccount, 1)
	client.Call(context.Background(), "/create-account", &[]Ins{ins,}, &account)
	fmt.Printf("account:%v\n", account)

	// Create Asset.
	fmt.Printf("To create Asset:\n")
	xprv_asset, _ := chainkd.NewXPrv(nil)
	xpub_asset := xprv_asset.XPub()
	fmt.Printf("xprv_asset:%v\n", xprv_asset)
	fmt.Printf("xpub_asset:%v\n", xpub_asset)
	type Ins_asset struct {
	    RootXPubs []chainkd.XPub `json:"root_xpubs"`
		Quorum    int
		Alias     string
		Tags      map[string]interface{}
		Definition  map[string]interface{}
		ClientToken string `json:"client_token"`
	}
	var ins_asset Ins_asset
	ins_asset.RootXPubs = []chainkd.XPub{xpub_asset}
	ins_asset.Quorum = 1
	ins_asset.Alias = "gold"
	ins_asset.Tags = map[string]interface{}{"test_tag": "v0",}
	ins_asset.Definition = map[string]interface{}{"test_definition": "v0"}
	ins_asset.ClientToken = "asset"
	asset := make([]query.AnnotatedAsset, 1)
	client.Call(context.Background(), "/create-asset", &[]Ins_asset{ins_asset,}, &asset)
	fmt.Printf("asset:%v\n", asset)

	// Build Transaction.
	fmt.Printf("To build transaction:\n")
	// Now Issue actions
	buildReqFmt := `
		{"actions": [
			{"type": "issue", "asset_id": "%s", "amount": 100},
			{"type": "control_account", "asset_id": "%s", "amount": 100, "account_id": "%s"}
		]}`
	buildReqStr := fmt.Sprintf(buildReqFmt, asset[0].ID.String(), asset[0].ID.String(), account[0].ID)
	var buildReq bc.BuildRequest
	err := stdjson.Unmarshal([]byte(buildReqStr), &buildReq)
	if err != nil {
		fmt.Printf("json Unmarshal error.")
	}
	tpl := make([]txbuilder.Template, 1)
	client.Call(context.Background(), "/build-transaction", []*bc.BuildRequest{&buildReq,}, &tpl)
	fmt.Printf("tpl:%v\n", tpl)

	// sign-transaction
	err = txbuilder.Sign(context.Background(), &tpl[0], []chainkd.XPub{xprv_asset.XPub()}, func(_ context.Context, _ chainkd.XPub, path [][]byte, data [32]byte) ([]byte, error) {
		derived := xprv_asset.Derive(path)
		return derived.Sign(data[:]), nil
	})
	if err != nil {
		fmt.Printf("sign-transaction error. err:%v\n", err)
	}
	fmt.Printf("sign tpl:%v\n", tpl[0])
	fmt.Printf("sign tpl's SigningInstructions:%v\n", tpl[0].SigningInstructions[0])
	fmt.Printf("SigningInstructions's SignatureWitnesses:%v\n", tpl[0].SigningInstructions[0].SignatureWitnesses[0])
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/pkg/errors"

	"github.com/versent/saml2aws/pkg/awsconfig"
	"github.com/versent/saml2aws/pkg/flags"
	"github.com/versent/saml2aws/pkg/shell"
)

// Exec execute the supplied command after seeding the environment
func Exec(execFlags *flags.LoginExecFlags, cmdline []string) error {

	if len(cmdline) < 1 {
		return fmt.Errorf("Command to execute required")
	}

	account, err := buildIdpAccount(execFlags)
	if err != nil {
		return errors.Wrap(err, "error building login details")
	}

	sharedCreds := awsconfig.NewSharedCredentials(account.Profile)

	// this checks if the credentials file has been created yet
	// can only really be triggered if saml2aws exec is run on a new
	// system prior to creating $HOME/.aws
	exist, err := sharedCreds.CredsExists()
	if err != nil {
		return errors.Wrap(err, "error loading credentials")
	}
	if !exist {
		fmt.Println("unable to load credentials, login required to create them")
		return nil
	}

	awsCreds, err := sharedCreds.Load()
	if err != nil {
		return errors.Wrap(err, "error loading credentials")
	}

	if awsCreds.Expires.Sub(time.Now()) < 0 {
		return errors.New("error aws credentials have expired")
	}

	ok, err := checkToken(account.Profile)
	if err != nil {
		return errors.Wrap(err, "error validating token")
	}

	if !ok {
		err = Login(execFlags)
	}
	if err != nil {
		return errors.Wrap(err, "error logging in")
	}

	if execFlags.ExecProfile != "" {
		// Assume the desired role before generating env vars
		awsCreds, err = assumeRoleWithProfile(execFlags.ExecProfile)
		if err != nil {
			return errors.Wrap(err,
				fmt.Sprintf("error acquiring credentials for profile: %s", execFlags.ExecProfile))
		}
	}

	return shell.ExecShellCmd(cmdline, shell.BuildEnvVars(awsCreds, account, execFlags))
}

// assumeRoleWithProfile uses an AWS profile (via ~/.aws/config) and performs (multiple levels of) role assumption
// This is extremely useful in the case of a central "authentication account" which then requires secondary, and
// often tertiary, role assumptions to acquire credentials for the target role.
func assumeRoleWithProfile(targetProfile string) (*awsconfig.AWSCredentials, error)  {
	// AWS session config with verbose errors on chained credential errors
	config := *aws.NewConfig().WithCredentialsChainVerboseErrors(true)

	// a session forcing usage of the aws config file, sets the target profile which will be found in the config
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		Config:            config,
		Profile:           targetProfile,
		SharedConfigState: session.SharedConfigEnable,
	}))

	// use an STS client to perform the multiple role assumptions
	stsClient := sts.New(sess)
	input := &sts.GetCallerIdentityInput{}
	_, err := stsClient.GetCallerIdentity(input)
	if err != nil {
		return nil, err
	}

	creds, err := sess.Config.Credentials.Get()
	if err != nil {
		return nil, err
	}
	return &awsconfig.AWSCredentials{
		AWSAccessKey:     creds.AccessKeyID,
		AWSSecretKey:     creds.SecretAccessKey,
		AWSSessionToken:  creds.SessionToken,
	}, nil
}

func checkToken(profile string) (bool, error) {
	sess, err := session.NewSessionWithOptions(session.Options{
		Profile: profile,
	})
	if err != nil {
		return false, err
	}

	svc := sts.New(sess)

	params := &sts.GetCallerIdentityInput{}

	_, err = svc.GetCallerIdentity(params)
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			if awsErr.Code() == "ExpiredToken" || awsErr.Code() == "NoCredentialProviders" {
				return false, nil
			}
		}

		return false, err
	}

	return true, nil
}
<commit_msg>Pass through session-duration<commit_after>package commands

import (
	"fmt"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/pkg/errors"

	"github.com/versent/saml2aws/pkg/awsconfig"
	"github.com/versent/saml2aws/pkg/flags"
	"github.com/versent/saml2aws/pkg/shell"
)

// Exec execute the supplied command after seeding the environment
func Exec(execFlags *flags.LoginExecFlags, cmdline []string) error {

	if len(cmdline) < 1 {
		return fmt.Errorf("Command to execute required")
	}

	account, err := buildIdpAccount(execFlags)
	if err != nil {
		return errors.Wrap(err, "error building login details")
	}

	sharedCreds := awsconfig.NewSharedCredentials(account.Profile)

	// this checks if the credentials file has been created yet
	// can only really be triggered if saml2aws exec is run on a new
	// system prior to creating $HOME/.aws
	exist, err := sharedCreds.CredsExists()
	if err != nil {
		return errors.Wrap(err, "error loading credentials")
	}
	if !exist {
		fmt.Println("unable to load credentials, login required to create them")
		return nil
	}

	awsCreds, err := sharedCreds.Load()
	if err != nil {
		return errors.Wrap(err, "error loading credentials")
	}

	if awsCreds.Expires.Sub(time.Now()) < 0 {
		return errors.New("error aws credentials have expired")
	}

	ok, err := checkToken(account.Profile)
	if err != nil {
		return errors.Wrap(err, "error validating token")
	}

	if !ok {
		err = Login(execFlags)
	}
	if err != nil {
		return errors.Wrap(err, "error logging in")
	}

	if execFlags.ExecProfile != "" {
		// Assume the desired role before generating env vars
		awsCreds, err = assumeRoleWithProfile(execFlags.ExecProfile, execFlags.CommonFlags.SessionDuration)
		if err != nil {
			return errors.Wrap(err,
				fmt.Sprintf("error acquiring credentials for profile: %s", execFlags.ExecProfile))
		}
	}

	return shell.ExecShellCmd(cmdline, shell.BuildEnvVars(awsCreds, account, execFlags))
}

// assumeRoleWithProfile uses an AWS profile (via ~/.aws/config) and performs (multiple levels of) role assumption
// This is extremely useful in the case of a central "authentication account" which then requires secondary, and
// often tertiary, role assumptions to acquire credentials for the target role.
func assumeRoleWithProfile(targetProfile string, sessionDuration int) (*awsconfig.AWSCredentials, error)  {
	// AWS session config with verbose errors on chained credential errors
	config := *aws.NewConfig().WithCredentialsChainVerboseErrors(true)
	print(strconv.Itoa(sessionDuration))
	duration, _  := time.ParseDuration(strconv.Itoa(sessionDuration) + "s")

	// a session forcing usage of the aws config file, sets the target profile which will be found in the config
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		Config:            	config,
		Profile:           	targetProfile,
		SharedConfigState: 	session.SharedConfigEnable,
		AssumeRoleDuration:   duration,
	}))

	// use an STS client to perform the multiple role assumptions
	stsClient := sts.New(sess)
	input := &sts.GetCallerIdentityInput{}
	_, err := stsClient.GetCallerIdentity(input)
	if err != nil {
		return nil, err
	}

	creds, err := sess.Config.Credentials.Get()
	if err != nil {
		return nil, err
	}
	return &awsconfig.AWSCredentials{
		AWSAccessKey:     creds.AccessKeyID,
		AWSSecretKey:     creds.SecretAccessKey,
		AWSSessionToken:  creds.SessionToken,
	}, nil
}

func checkToken(profile string) (bool, error) {
	sess, err := session.NewSessionWithOptions(session.Options{
		Profile: profile,
	})
	if err != nil {
		return false, err
	}

	svc := sts.New(sess)

	params := &sts.GetCallerIdentityInput{}

	_, err = svc.GetCallerIdentity(params)
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			if awsErr.Code() == "ExpiredToken" || awsErr.Code() == "NoCredentialProviders" {
				return false, nil
			}
		}

		return false, err
	}

	return true, nil
}
<|endoftext|>


<commit_before>//
// Blackblog
// Copyright 2012 Google Inc. All rights reserved.
//
// 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 main

import (
	"testing"
)

func TestGetPostsInDir(t *testing.T) {
	posts := GetPostsInDirectory("./tests")
	if len(posts) != 4 {
		t.Errorf("Expecting %d posts, only got %d", 4, len(posts))
	}

	for _, post := range posts {
		if post.Title == "" || post.Filename == "" {
			t.Errorf("Missing title or filename in post %v", post)
		}
	}
}

func TestSortedPosts(t *testing.T) {
	posts := []*Post{
		&Post{URLFragment: "alpha"},
		&Post{URLFragment: "b_test", Date: "6 January 2012"},
		&Post{URLFragment: "c_test", Date: "18 January 2012"},
		&Post{URLFragment: "test", Date: "7 February 2011"},
	}
	order := []string{
		"2011/2/test.html",
		"2012/1/b_test.html",
		"2012/1/c_test.html",
		"alpha.html",
	}

	postMap, sorted := SortPosts(posts)
	if len(posts) != len(postMap) || len(postMap) != len(sorted) {
		t.Errorf("Count of returned values mismatch, expected %d, got len(map) = %d, len(slice) = %d", len(posts), len(postMap), len(sorted))
	}

	for i, expected := range order {
		if expected != sorted[i] {
			t.Errorf("Sorted order mismatch. At %d, expected '%s', got '%s'", i, expected, sorted[i])
		}
		post, ok := postMap[expected]
		if !ok || post == nil {
			t.Errorf("Error getting post in map for '%s'", expected)
		}
	}
}

func TestGetRootPath(t *testing.T) {
	results := map[string]string {
		"2012/1/test.html": "../../",
		"index.html": "",
	}

	for k, v := range results {
		actual := getRootPath(k)
		if actual != v {
			t.Errorf("GetRootPath() fail for '%s', expected '%s', got '%s'", k, v, actual)
		}
	}
}
<commit_msg>Update test now that SortPosts has been removed.<commit_after>//
// Blackblog
// Copyright 2012 Google Inc. All rights reserved.
//
// 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 main

import (
	"sort"
	"testing"
)

func TestGetPostsInDir(t *testing.T) {
	posts := GetPostsInDirectory("./tests")
	if len(posts) != 4 {
		t.Errorf("Expecting %d posts, only got %d", 4, len(posts))
	}

	for _, post := range posts {
		if post.Title == "" || post.Filename == "" {
			t.Errorf("Missing title or filename in post %v", post)
		}
	}
}

func TestSortedPosts(t *testing.T) {
	posts := PostList{
		&Post{URLFragment: "alpha"},
		&Post{URLFragment: "b_test", Date: "6 January 2012"},
		&Post{URLFragment: "c_test", Date: "18 January 2012"},
		&Post{URLFragment: "test", Date: "7 February 2011"},
	}
	order := []string{
		"2011/2/test.html",
		"2012/1/b_test.html",
		"2012/1/c_test.html",
		"alpha.html",
	}

	sort.Sort(posts)

	for i, expected := range order {
		if expected != posts[i].CreateURL() {
			t.Errorf("Sorted order mismatch. At %d, expected '%s', got '%s'", i, expected, posts[i].CreateURL())
		}
	}
}

func TestGetRootPath(t *testing.T) {
	results := map[string]string{
		"2012/1/test.html": "../../",
		"index.html":       "",
	}

	for k, v := range results {
		actual := getRootPath(k)
		if actual != v {
			t.Errorf("GetRootPath() fail for '%s', expected '%s', got '%s'", k, v, actual)
		}
	}
}
<|endoftext|>


<commit_before>package hostcheck_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/plugins/hostcheck"
	"github.com/google/go-safeweb/safehttp/safehttptest"
	"github.com/google/safehtml"
)

func TestInterceptor(t *testing.T) {
	var test = []struct {
		name       string
		req        *http.Request
		wantStatus safehttp.StatusCode
	}{
		{
			name:       "Valid Host",
			req:        httptest.NewRequest(safehttp.MethodGet, "http://foo.com/", nil),
			wantStatus: safehttp.StatusOK,
		},
		{
			name:       "Invalid Host",
			req:        httptest.NewRequest(safehttp.MethodGet, "http://bar.com/", nil),
			wantStatus: safehttp.StatusNotFound,
		},
	}

	for _, tt := range test {
		t.Run(tt.name, func(t *testing.T) {
			mux := safehttp.NewServeMux(safehttp.DefaultDispatcher{})
			mux.Install(hostcheck.New("foo.com"))

			h := safehttp.HandlerFunc(func(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
				return w.Write(safehtml.HTMLEscaped("
Hello World!
"))
			})
			mux.Handle("/", safehttp.MethodGet, h)

			b := &strings.Builder{}
			rw := safehttptest.NewTestResponseWriter(b)
			mux.ServeHTTP(rw, tt.req)

			if rw.Status() != tt.wantStatus {
				t.Errorf("rw.Status(): got %v want %v", rw.Status(), tt.wantStatus)
			}
		})
	}
}
<commit_msg>Added missing copyright note.<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// 	https://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 hostcheck_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/google/go-safeweb/safehttp"
	"github.com/google/go-safeweb/safehttp/plugins/hostcheck"
	"github.com/google/go-safeweb/safehttp/safehttptest"
	"github.com/google/safehtml"
)

func TestInterceptor(t *testing.T) {
	var test = []struct {
		name       string
		req        *http.Request
		wantStatus safehttp.StatusCode
	}{
		{
			name:       "Valid Host",
			req:        httptest.NewRequest(safehttp.MethodGet, "http://foo.com/", nil),
			wantStatus: safehttp.StatusOK,
		},
		{
			name:       "Invalid Host",
			req:        httptest.NewRequest(safehttp.MethodGet, "http://bar.com/", nil),
			wantStatus: safehttp.StatusNotFound,
		},
	}

	for _, tt := range test {
		t.Run(tt.name, func(t *testing.T) {
			mux := safehttp.NewServeMux(safehttp.DefaultDispatcher{})
			mux.Install(hostcheck.New("foo.com"))

			h := safehttp.HandlerFunc(func(w *safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
				return w.Write(safehtml.HTMLEscaped("
Hello World!
"))
			})
			mux.Handle("/", safehttp.MethodGet, h)

			b := &strings.Builder{}
			rw := safehttptest.NewTestResponseWriter(b)
			mux.ServeHTTP(rw, tt.req)

			if rw.Status() != tt.wantStatus {
				t.Errorf("rw.Status(): got %v want %v", rw.Status(), tt.wantStatus)
			}
		})
	}
}
<|endoftext|>


<commit_before>package app

import (
	"database/sql"
	"io/ioutil"

	"github.com/gin-gonic/gin"
	"github.com/osuripple/api/common"
)

// Method wraps an API method to a HandlerFunc.
func Method(f func(md common.MethodData) common.Response, db *sql.DB, privilegesNeeded ...int) gin.HandlerFunc {
	return func(c *gin.Context) {
		initialCaretaker(c, f, db, privilegesNeeded...)
	}
}

func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.Response, db *sql.DB, privilegesNeeded ...int) {
	data, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		c.Error(err)
	}
	c.Request.Body.Close()

	token := ""
	switch {
	case c.Request.Header.Get("X-Ripple-Token") != "":
		token = c.Request.Header.Get("X-Ripple-Token")
	case c.Query("token") != "":
		token = c.Query("token")
	case c.Query("k") != "":
		token = c.Query("k")
	}

	md := common.MethodData{
		DB:          db,
		RequestData: data,
		C:           c,
	}
	if token != "" {
		tokenReal, exists := GetTokenFull(token, db)
		if exists {
			md.User = tokenReal
		}
	}

	missingPrivileges := 0
	for _, privilege := range privilegesNeeded {
		if int(md.User.Privileges)&privilege == 0 {
			missingPrivileges |= privilege
		}
	}
	if missingPrivileges != 0 {
		c.IndentedJSON(401, common.Response{
			Code:    401,
			Message: "You don't have the privilege(s): " + common.Privileges(missingPrivileges).String() + ".",
		})
		return
	}

	resp := f(md)
	if resp.Code == 0 {
		resp.Code = 500
	}
	if _, exists := c.GetQuery("pls200"); exists {
		c.IndentedJSON(200, resp)
	} else {
		c.IndentedJSON(resp.Code, resp)
	}
}
<commit_msg>Implement JSONP, also save a few bytes by using tabs in indent<commit_after>package app

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"

	"github.com/gin-gonic/gin"
	"github.com/osuripple/api/common"
)

// Method wraps an API method to a HandlerFunc.
func Method(f func(md common.MethodData) common.Response, db *sql.DB, privilegesNeeded ...int) gin.HandlerFunc {
	return func(c *gin.Context) {
		initialCaretaker(c, f, db, privilegesNeeded...)
	}
}

func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.Response, db *sql.DB, privilegesNeeded ...int) {
	data, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		c.Error(err)
	}
	c.Request.Body.Close()

	token := ""
	switch {
	case c.Request.Header.Get("X-Ripple-Token") != "":
		token = c.Request.Header.Get("X-Ripple-Token")
	case c.Query("token") != "":
		token = c.Query("token")
	case c.Query("k") != "":
		token = c.Query("k")
	}

	md := common.MethodData{
		DB:          db,
		RequestData: data,
		C:           c,
	}
	if token != "" {
		tokenReal, exists := GetTokenFull(token, db)
		if exists {
			md.User = tokenReal
		}
	}

	missingPrivileges := 0
	for _, privilege := range privilegesNeeded {
		if int(md.User.Privileges)&privilege == 0 {
			missingPrivileges |= privilege
		}
	}
	if missingPrivileges != 0 {
		c.IndentedJSON(401, common.Response{
			Code:    401,
			Message: "You don't have the privilege(s): " + common.Privileges(missingPrivileges).String() + ".",
		})
		return
	}

	resp := f(md)
	if resp.Code == 0 {
		resp.Code = 500
	}

	if _, exists := c.GetQuery("pls200"); exists {
		c.Writer.WriteHeader(200)
	} else {
		c.Writer.WriteHeader(resp.Code)
	}

	if _, exists := c.GetQuery("callback"); exists {
		c.Header("Content-Type", "application/javascript; charset=utf-8")
	} else {
		c.Header("Content-Type", "application/json; charset=utf-8")
	}

	mkjson(c, resp)
}

// mkjson auto indents json, and wraps json into a jsonp callback if specified by the request.
// then writes to the gin.Context the data.
func mkjson(c *gin.Context, data interface{}) {
	exported, err := json.MarshalIndent(data, "", "\t")
	if err != nil {
		c.Error(err)
		exported = []byte(`{ "code": 500, "message": "something has gone really really really really really really wrong.", "data": null }`)
	}
	cb := c.Query("callback")
	willcb := cb != "" && len(cb) < 100
	if willcb {
		c.Writer.Write([]byte("/**/ typeof " + cb + " === 'function' && " + cb + "("))
	}
	c.Writer.Write(exported)
	if willcb {
		c.Writer.Write([]byte(");"))
	}
}
<|endoftext|>


<commit_before>package prelude

const jsmapping = `
var $needsExternalization = function(t) {
  switch (t.kind) {
    case $kindBool:
    case $kindInt:
    case $kindInt8:
    case $kindInt16:
    case $kindInt32:
    case $kindUint:
    case $kindUint8:
    case $kindUint16:
    case $kindUint32:
    case $kindUintptr:
    case $kindFloat32:
    case $kindFloat64:
      return false;
    case $kindInterface:
      return t !== $packages["github.com/gopherjs/gopherjs/js"].Object;
    default:
      return true;
  }
};

var $externalize = function(v, t) {
  switch (t.kind) {
  case $kindBool:
  case $kindInt:
  case $kindInt8:
  case $kindInt16:
  case $kindInt32:
  case $kindUint:
  case $kindUint8:
  case $kindUint16:
  case $kindUint32:
  case $kindUintptr:
  case $kindFloat32:
  case $kindFloat64:
    return v;
  case $kindInt64:
  case $kindUint64:
    return $flatten64(v);
  case $kindArray:
    if ($needsExternalization(t.elem)) {
      return $mapArray(v, function(e) { return $externalize(e, t.elem); });
    }
    return v;
  case $kindFunc:
    if (v === $throwNilPointerError) {
      return null;
    }
    if (v.$externalizeWrapper === undefined) {
      $checkForDeadlock = false;
      var convert = false;
      var i;
      for (i = 0; i < t.params.length; i++) {
        convert = convert || (t.params[i] !== $packages["github.com/gopherjs/gopherjs/js"].Object);
      }
      for (i = 0; i < t.results.length; i++) {
        convert = convert || $needsExternalization(t.results[i]);
      }
      if (!convert) {
        return v;
      }
      v.$externalizeWrapper = function() {
        var args = [], i;
        for (i = 0; i < t.params.length; i++) {
          if (t.variadic && i === t.params.length - 1) {
            var vt = t.params[i].elem, varargs = [], j;
            for (j = i; j < arguments.length; j++) {
              varargs.push($internalize(arguments[j], vt));
            }
            args.push(new (t.params[i])(varargs));
            break;
          }
          args.push($internalize(arguments[i], t.params[i]));
        }
        var result = v.apply(this, args);
        switch (t.results.length) {
        case 0:
          return;
        case 1:
          return $externalize(result, t.results[0]);
        default:
          for (i = 0; i < t.results.length; i++) {
            result[i] = $externalize(result[i], t.results[i]);
          }
          return result;
        }
      };
    }
    return v.$externalizeWrapper;
  case $kindInterface:
    if (v === $ifaceNil) {
      return null;
    }
    if (t === $packages["github.com/gopherjs/gopherjs/js"].Object || v.constructor.kind === undefined) {
      return v;
    }
    return $externalize(v.$val, v.constructor);
  case $kindMap:
    var m = {};
    var keys = $keys(v), i;
    for (i = 0; i < keys.length; i++) {
      var entry = v[keys[i]];
      m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem);
    }
    return m;
  case $kindPtr:
    var o = {}, i;
    for (i = 0; i < t.methods.length; i++) {
      var m = t.methods[i];
      if (m[2] !== "") { /* not exported */
        continue;
      }
      (function(m) {
        o[m[1]] = $externalize(function() {
          return v[m[0]].apply(v, arguments);
        }, m[3]);
      })(m);
    }
    return o;
  case $kindSlice:
    if ($needsExternalization(t.elem)) {
      return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); });
    }
    return $sliceToArray(v);
  case $kindString:
    var s = "", r, i, j = 0;
    for (i = 0; i < v.length; i += r[1], j++) {
      r = $decodeRune(v, i);
      s += String.fromCharCode(r[0]);
    }
    return s;
  case $kindStruct:
    var timePkg = $packages["time"];
    if (timePkg && v.constructor === timePkg.Time.Ptr) {
      var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
      return new Date($flatten64(milli));
    }
    var o = {}, i;
    for (i = 0; i < t.fields.length; i++) {
      var f = t.fields[i];
      if (f[2] !== "") { /* not exported */
        continue;
      }
      o[f[1]] = $externalize(v[f[0]], f[3]);
    }
    return o;
  }
  $panic(new $String("cannot externalize " + t.string));
};

var $internalize = function(v, t, recv) {
  switch (t.kind) {
  case $kindBool:
    return !!v;
  case $kindInt:
    return parseInt(v);
  case $kindInt8:
    return parseInt(v) << 24 >> 24;
  case $kindInt16:
    return parseInt(v) << 16 >> 16;
  case $kindInt32:
    return parseInt(v) >> 0;
  case $kindUint:
    return parseInt(v);
  case $kindUint8:
    return parseInt(v) << 24 >>> 24;
  case $kindUint16:
    return parseInt(v) << 16 >>> 16;
  case $kindUint32:
  case $kindUintptr:
    return parseInt(v) >>> 0;
  case $kindInt64:
  case $kindUint64:
    return new t(0, v);
  case $kindFloat32:
  case $kindFloat64:
    return parseFloat(v);
  case $kindArray:
    if (v.length !== t.len) {
      $throwRuntimeError("got array with wrong size from JavaScript native");
    }
    return $mapArray(v, function(e) { return $internalize(e, t.elem); });
  case $kindFunc:
    return function() {
      var args = [], i;
      for (i = 0; i < t.params.length; i++) {
        if (t.variadic && i === t.params.length - 1) {
          var vt = t.params[i].elem, varargs = arguments[i], j;
          for (j = 0; j < varargs.$length; j++) {
            args.push($externalize(varargs.$array[varargs.$offset + j], vt));
          }
          break;
        }
        args.push($externalize(arguments[i], t.params[i]));
      }
      var result = v.apply(recv, args);
      switch (t.results.length) {
      case 0:
        return;
      case 1:
        return $internalize(result, t.results[0]);
      default:
        for (i = 0; i < t.results.length; i++) {
          result[i] = $internalize(result[i], t.results[i]);
        }
        return result;
      }
    };
  case $kindInterface:
    if (t === $packages["github.com/gopherjs/gopherjs/js"].Object) {
      return v;
    }
    if (v === null) {
      return $ifaceNil;
    }
    switch (v.constructor) {
    case Int8Array:
      return new ($sliceType($Int8))(v);
    case Int16Array:
      return new ($sliceType($Int16))(v);
    case Int32Array:
      return new ($sliceType($Int))(v);
    case Uint8Array:
      return new ($sliceType($Uint8))(v);
    case Uint16Array:
      return new ($sliceType($Uint16))(v);
    case Uint32Array:
      return new ($sliceType($Uint))(v);
    case Float32Array:
      return new ($sliceType($Float32))(v);
    case Float64Array:
      return new ($sliceType($Float64))(v);
    case Array:
      return $internalize(v, $sliceType($emptyInterface));
    case Boolean:
      return new $Bool(!!v);
    case Date:
      var timePkg = $packages["time"];
      if (timePkg) {
        return new timePkg.Time(timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000)));
      }
    case Function:
      var funcType = $funcType([$sliceType($emptyInterface)], [$packages["github.com/gopherjs/gopherjs/js"].Object], true);
      return new funcType($internalize(v, funcType));
    case Number:
      return new $Float64(parseFloat(v));
    case String:
      return new $String($internalize(v, $String));
    default:
      if ($global.Node && v instanceof $global.Node) {
        return v;
      }
      var mapType = $mapType($String, $emptyInterface);
      return new mapType($internalize(v, mapType));
    }
  case $kindMap:
    var m = new $Map();
    var keys = $keys(v), i;
    for (i = 0; i < keys.length; i++) {
      var key = $internalize(keys[i], t.key);
      m[key.$key ? key.$key() : key] = { k: key, v: $internalize(v[keys[i]], t.elem) };
    }
    return m;
  case $kindSlice:
    return new t($mapArray(v, function(e) { return $internalize(e, t.elem); }));
  case $kindString:
    v = String(v);
    var s = "", i;
    for (i = 0; i < v.length; i++) {
      s += $encodeRune(v.charCodeAt(i));
    }
    return s;
  default:
    $panic(new $String("cannot internalize " + t.string));
  }
};
`
<commit_msg>improved performance of externalization a bit (#142)<commit_after>package prelude

const jsmapping = `
var $needsExternalization = function(t) {
  switch (t.kind) {
    case $kindBool:
    case $kindInt:
    case $kindInt8:
    case $kindInt16:
    case $kindInt32:
    case $kindUint:
    case $kindUint8:
    case $kindUint16:
    case $kindUint32:
    case $kindUintptr:
    case $kindFloat32:
    case $kindFloat64:
      return false;
    case $kindInterface:
      return t !== $packages["github.com/gopherjs/gopherjs/js"].Object;
    default:
      return true;
  }
};

var $externalize = function(v, t) {
  switch (t.kind) {
  case $kindBool:
  case $kindInt:
  case $kindInt8:
  case $kindInt16:
  case $kindInt32:
  case $kindUint:
  case $kindUint8:
  case $kindUint16:
  case $kindUint32:
  case $kindUintptr:
  case $kindFloat32:
  case $kindFloat64:
    return v;
  case $kindInt64:
  case $kindUint64:
    return $flatten64(v);
  case $kindArray:
    if ($needsExternalization(t.elem)) {
      return $mapArray(v, function(e) { return $externalize(e, t.elem); });
    }
    return v;
  case $kindFunc:
    if (v === $throwNilPointerError) {
      return null;
    }
    if (v.$externalizeWrapper === undefined) {
      $checkForDeadlock = false;
      var convert = false;
      var i;
      for (i = 0; i < t.params.length; i++) {
        convert = convert || (t.params[i] !== $packages["github.com/gopherjs/gopherjs/js"].Object);
      }
      for (i = 0; i < t.results.length; i++) {
        convert = convert || $needsExternalization(t.results[i]);
      }
      v.$externalizeWrapper = v;
      if (convert) {
        v.$externalizeWrapper = function() {
          var args = [], i;
          for (i = 0; i < t.params.length; i++) {
            if (t.variadic && i === t.params.length - 1) {
              var vt = t.params[i].elem, varargs = [], j;
              for (j = i; j < arguments.length; j++) {
                varargs.push($internalize(arguments[j], vt));
              }
              args.push(new (t.params[i])(varargs));
              break;
            }
            args.push($internalize(arguments[i], t.params[i]));
          }
          var result = v.apply(this, args);
          switch (t.results.length) {
          case 0:
            return;
          case 1:
            return $externalize(result, t.results[0]);
          default:
            for (i = 0; i < t.results.length; i++) {
              result[i] = $externalize(result[i], t.results[i]);
            }
            return result;
          }
        };
      }
    }
    return v.$externalizeWrapper;
  case $kindInterface:
    if (v === $ifaceNil) {
      return null;
    }
    if (t === $packages["github.com/gopherjs/gopherjs/js"].Object || v.constructor.kind === undefined) {
      return v;
    }
    return $externalize(v.$val, v.constructor);
  case $kindMap:
    var m = {};
    var keys = $keys(v), i;
    for (i = 0; i < keys.length; i++) {
      var entry = v[keys[i]];
      m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem);
    }
    return m;
  case $kindPtr:
    var o = {}, i;
    for (i = 0; i < t.methods.length; i++) {
      var m = t.methods[i];
      if (m[2] !== "") { /* not exported */
        continue;
      }
      (function(m) {
        o[m[1]] = $externalize(function() {
          return v[m[0]].apply(v, arguments);
        }, m[3]);
      })(m);
    }
    return o;
  case $kindSlice:
    if ($needsExternalization(t.elem)) {
      return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); });
    }
    return $sliceToArray(v);
  case $kindString:
    if (v.search(/^[\x00-\x7F]*$/) !== -1) {
      return v;
    }
    var s = "", r, i;
    for (i = 0; i < v.length; i += r[1]) {
      r = $decodeRune(v, i);
      s += String.fromCharCode(r[0]);
    }
    return s;
  case $kindStruct:
    var timePkg = $packages["time"];
    if (timePkg && v.constructor === timePkg.Time.Ptr) {
      var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
      return new Date($flatten64(milli));
    }
    var o = {}, i;
    for (i = 0; i < t.fields.length; i++) {
      var f = t.fields[i];
      if (f[2] !== "") { /* not exported */
        continue;
      }
      o[f[1]] = $externalize(v[f[0]], f[3]);
    }
    return o;
  }
  $panic(new $String("cannot externalize " + t.string));
};

var $internalize = function(v, t, recv) {
  switch (t.kind) {
  case $kindBool:
    return !!v;
  case $kindInt:
    return parseInt(v);
  case $kindInt8:
    return parseInt(v) << 24 >> 24;
  case $kindInt16:
    return parseInt(v) << 16 >> 16;
  case $kindInt32:
    return parseInt(v) >> 0;
  case $kindUint:
    return parseInt(v);
  case $kindUint8:
    return parseInt(v) << 24 >>> 24;
  case $kindUint16:
    return parseInt(v) << 16 >>> 16;
  case $kindUint32:
  case $kindUintptr:
    return parseInt(v) >>> 0;
  case $kindInt64:
  case $kindUint64:
    return new t(0, v);
  case $kindFloat32:
  case $kindFloat64:
    return parseFloat(v);
  case $kindArray:
    if (v.length !== t.len) {
      $throwRuntimeError("got array with wrong size from JavaScript native");
    }
    return $mapArray(v, function(e) { return $internalize(e, t.elem); });
  case $kindFunc:
    return function() {
      var args = [], i;
      for (i = 0; i < t.params.length; i++) {
        if (t.variadic && i === t.params.length - 1) {
          var vt = t.params[i].elem, varargs = arguments[i], j;
          for (j = 0; j < varargs.$length; j++) {
            args.push($externalize(varargs.$array[varargs.$offset + j], vt));
          }
          break;
        }
        args.push($externalize(arguments[i], t.params[i]));
      }
      var result = v.apply(recv, args);
      switch (t.results.length) {
      case 0:
        return;
      case 1:
        return $internalize(result, t.results[0]);
      default:
        for (i = 0; i < t.results.length; i++) {
          result[i] = $internalize(result[i], t.results[i]);
        }
        return result;
      }
    };
  case $kindInterface:
    if (t === $packages["github.com/gopherjs/gopherjs/js"].Object) {
      return v;
    }
    if (v === null) {
      return $ifaceNil;
    }
    switch (v.constructor) {
    case Int8Array:
      return new ($sliceType($Int8))(v);
    case Int16Array:
      return new ($sliceType($Int16))(v);
    case Int32Array:
      return new ($sliceType($Int))(v);
    case Uint8Array:
      return new ($sliceType($Uint8))(v);
    case Uint16Array:
      return new ($sliceType($Uint16))(v);
    case Uint32Array:
      return new ($sliceType($Uint))(v);
    case Float32Array:
      return new ($sliceType($Float32))(v);
    case Float64Array:
      return new ($sliceType($Float64))(v);
    case Array:
      return $internalize(v, $sliceType($emptyInterface));
    case Boolean:
      return new $Bool(!!v);
    case Date:
      var timePkg = $packages["time"];
      if (timePkg) {
        return new timePkg.Time(timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000)));
      }
    case Function:
      var funcType = $funcType([$sliceType($emptyInterface)], [$packages["github.com/gopherjs/gopherjs/js"].Object], true);
      return new funcType($internalize(v, funcType));
    case Number:
      return new $Float64(parseFloat(v));
    case String:
      return new $String($internalize(v, $String));
    default:
      if ($global.Node && v instanceof $global.Node) {
        return v;
      }
      var mapType = $mapType($String, $emptyInterface);
      return new mapType($internalize(v, mapType));
    }
  case $kindMap:
    var m = new $Map();
    var keys = $keys(v), i;
    for (i = 0; i < keys.length; i++) {
      var key = $internalize(keys[i], t.key);
      m[key.$key ? key.$key() : key] = { k: key, v: $internalize(v[keys[i]], t.elem) };
    }
    return m;
  case $kindSlice:
    return new t($mapArray(v, function(e) { return $internalize(e, t.elem); }));
  case $kindString:
    v = String(v);
    if (v.search(/^[\x00-\x7F]*$/) !== -1) {
      return v;
    }
    var s = "", i;
    for (i = 0; i < v.length; i++) {
      s += $encodeRune(v.charCodeAt(i));
    }
    return s;
  default:
    $panic(new $String("cannot internalize " + t.string));
  }
};
`
<|endoftext|>