<commit_before>// Copyright 2018 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 (
	"archive/zip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

// Global for tracking whether we've warned user about delay.
var warned = false

// cmdServe is the "claat serve ..." subcommand.
func cmdServe() {
	var depsDir = "bower_components"
	var elemDir = "elements"
	var elemFile = elemDir + "/codelab.html"
	const codelabElem = `
<link rel="import" href="../bower_components/google-codelab-elements/google-codelab-elements.html">
`
	err := os.MkdirAll(depsDir, 0755)
	if err != nil {
		fatalf(err.Error())
	}
	// Go get the dependencies.
	err = fetchRepo(depsDir, "googlecodelabs/codelab-components#1.0.5")
	if err != nil {
		fatalf(err.Error())
	}
	os.Rename(depsDir+"/code-prettify", depsDir+"/google-prettify")
	err = os.MkdirAll(elemDir, 0755)
	if err != nil {
		fatalf(err.Error())
	}
	if _, err := os.Stat(elemFile); os.IsNotExist(err) {
		f, err := os.Create(elemFile)
		if err != nil {
			fatalf(err.Error())
		}
		defer f.Close()
		f.WriteString(codelabElem)
	}
	http.Handle("/", http.FileServer(http.Dir(".")))
	fmt.Println("Serving on " + *addr + ", opening browser tab now...")
	ch := make(chan error, 1)
	go func() {
		ch <- http.ListenAndServe(*addr, nil)
	}()
	openBrowser("http://" + *addr)
	fatalf("claat serve: %v", <-ch)
}

// downloadFile will download a url to a local file. It's efficient because it will
// write as it downloads and not load the whole file into memory.
func downloadFile(filepath string, url string) error {
	if !warned {
		fmt.Println("Fetching dependencies...")
		warned = true
	}
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, err = io.Copy(out, resp.Body)
	return err
}

// overrideSpec maps a given component name to desired spec, as described in fetchRepo
// doc comments.
var overrideSpec = map[string]string{
	"webcomponentsjs": "webcomponents/webcomponentsjs",
	"paper-behaviors": "PolymerElements/paper-behaviors#1.0.12",
}

// overrideComp maps a given spec, as described in fetchRepo doc comments to a
// desired component name.
var overrideComp = map[string]string{
	"googlecodelabs/codelab-components": "google-codelab-elements",
	"google/google-prettify":            "code-prettify",
}

// fetchRepo downloads a repo from github and unpacks it into basedir/dest,
// calling itself recursively for every dependency found in the unpacked bower.json.
// If the basedir/dest directory already exists, fetchRepo does nothing.
//
// The spec has the following format: "githubuser/repo#version".
// Any non-alphanumeric characters are stripped from the version.
// If version is missing, "master" is used instead.
// The bower registry is not used for naming resolution.
func fetchRepo(basedir, spec string) error {
	var user, repo, path, comp, ver string
	s := strings.Split(spec, "#")
	path = s[0]
	if len(s) > 1 {
		ver = s[1]
	}
	ver = strings.Trim(ver, "^~ ")
	if ver == "" {
		ver = "master"
	}
	s = strings.Split(path, "/")
	user = s[0]
	if len(s) > 1 {
		repo = s[1]
	}
	// Check exception map to see if we should override the component name.
	comp = repo
	if c, ok := overrideComp[path]; ok {
		comp = c
	}
	// if repo already exists locally, return immediately, we're done.
	if _, err := os.Stat(basedir + "/" + comp); err == nil {
		return nil
	}
	zipFile := basedir + "/" + comp + ".zip"
	url := "https://github.com/" + user + "/" + repo + "/archive/v" + ver + ".zip"
	err := downloadFile(zipFile, url)
	if err != nil {
		return err
	}
	// If get fails, it will download a file containing only "404: Not Found".
	// We check for that case by looking for an unusually small file.
	var st os.FileInfo
	if st, err = os.Stat(zipFile); err != nil {
		return err
	}
	if st.Size() < 20 {
		os.Remove(zipFile)
		url = "https://github.com/" + user + "/" + repo + "/archive/" + ver + ".zip"
		err = downloadFile(zipFile, url)
		if err != nil {
			return err
		}
	}
	err = unzip(zipFile, basedir)
	if err != nil {
		return err
	}
	os.Remove(zipFile)
	os.Rename(basedir+"/"+repo+"-"+ver, basedir+"/"+comp)
	// if unzipped archive contains a bower.json, parse it, and for each dependency therein,
	// recursively fetch the corresponding repo.
	bowerFile := basedir + "/" + comp + "/bower.json"
	if _, err := os.Stat(bowerFile); os.IsNotExist(err) {
		return nil
	}
	raw, err := ioutil.ReadFile(bowerFile)
	if err != nil {
		return err
	}
	var b struct {
		Dependencies map[string]string
	}
	err = json.Unmarshal(raw, &b)
	if err != nil {
		return err
	}
	// Check exception map to see if we should overide spec.
	for c, s := range b.Dependencies {
		if spec, ok := overrideSpec[c]; ok {
			s = spec
		}
		err = fetchRepo(basedir, s)
		if err != nil {
			return err
		}
	}
	return nil
}

// unzip a file to a dest directory.
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()
	os.MkdirAll(dest, 0755)
	// Closure to address file descriptors issue with all the deferred .Close() methods
	extractAndWriteFile := func(f *zip.File) error {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()
		path := filepath.Join(dest, f.Name)
		if f.FileInfo().IsDir() {
			return os.MkdirAll(path, f.Mode())
		}
		os.MkdirAll(filepath.Dir(path), f.Mode())
		f2, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return err
		}
		_, err = io.Copy(f2, rc)
		// Catch both io.Copy and file.Close errors but prefer returning the former.
		if err1 := f2.Close(); err1 != nil && err == nil {
			err = err1
		}
		return err
	}
	for _, f := range r.File {
		err := extractAndWriteFile(f)
		if err != nil {
			return err
		}
	}
	return nil
}

// openBrowser tries to open the URL in a browser.
func openBrowser(url string) error {
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start()
}
<commit_msg>hardcoded a bunch of version deps.<commit_after>// Copyright 2018 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 (
	"archive/zip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

// Global for tracking whether we've warned user about delay.
var warned = false

// cmdServe is the "claat serve ..." subcommand.
func cmdServe() {
	var depsDir = "bower_components"
	var elemDir = "elements"
	var elemFile = elemDir + "/codelab.html"
	const codelabElem = `
<link rel="import" href="../bower_components/google-codelab-elements/google-codelab-elements.html">
`
	err := os.MkdirAll(depsDir, 0755)
	if err != nil {
		fatalf(err.Error())
	}
	// Go get the dependencies.
	err = fetchRepo(depsDir, "googlecodelabs/codelab-components")
	if err != nil {
		fatalf(err.Error())
	}
	err = os.MkdirAll(elemDir, 0755)
	if err != nil {
		fatalf(err.Error())
	}
	if _, err := os.Stat(elemFile); os.IsNotExist(err) {
		f, err := os.Create(elemFile)
		if err != nil {
			fatalf(err.Error())
		}
		defer f.Close()
		f.WriteString(codelabElem)
	}
	http.Handle("/", http.FileServer(http.Dir(".")))
	fmt.Println("Serving on " + *addr + ", opening browser tab now...")
	ch := make(chan error, 1)
	go func() {
		ch <- http.ListenAndServe(*addr, nil)
	}()
	openBrowser("http://" + *addr)
	fatalf("claat serve: %v", <-ch)
}

// downloadFile will download a url to a local file. It's efficient because it will
// write as it downloads and not load the whole file into memory.
func downloadFile(filepath string, url string) error {
	if !warned {
		fmt.Println("Fetching dependencies...")
		warned = true
	}
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, err = io.Copy(out, resp.Body)
	return err
}

// overrideSpec maps a given component name to desired spec, as described in fetchRepo
// doc comments.
var overrideSpec = map[string]string{
	"accessibility-developer-tools": "PolymerElements/accessibility-developer-tools#2.11.0",
	"async":                         "PolymerElements/async#1.5.2",
	"font-roboto":                   "PolymerElements/font-roboto#1.0.1",
	"google-apis":                   "PolymerElements/google-apis#1.1.7",
	"google-codelab-elements":       "GoogleCodelabs/google-codelab-elements#1.0.5",
	"iron-a11y-announcer":           "PolymerElements/iron-a11y-announcer#1.0.5",
	"iron-a11y-keys-behavior":       "PolymerElements/iron-a11y-keys-behavior#1.1.9",
	"iron-autogrow-textarea":        "PolymerElements/iron-autogrow-textarea#1.0.15",
	"iron-behaviors":                "PolymerElements/iron-behaviors#1.0.17",
	"iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#1.0.5",
	"iron-collapse":                 "PolymerElements/iron-collapse#1.3.0",
	"iron-dropdown":                 "PolymerElements/iron-dropdown#1.5.5",
	"iron-fit-behavior":             "PolymerElements/iron-fit-behavior#1.2.6",
	"iron-flex-layout":              "PolymerElements/iron-flex-layout#1.3.2",
	"iron-form-element-behavior":    "PolymerElements/iron-form-element-behavior#1.0.6",
	"iron-icon":                     "PolymerElements/iron-icon#1.0.12",
	"iron-icons":                    "PolymerElements/iron-icons#1.2.0",
	"iron-iconset-svg":              "PolymerElements/iron-iconset-svg#1.1.0",
	"iron-input":                    "PolymerElements/iron-input#1.0.10",
	"iron-jsonp-library":            "PolymerElements/iron-jsonp-library#1.0.4",
	"iron-localstorage":             "PolymerElements/iron-localstorage#1.0.6",
	"iron-media-query":              "PolymerElements/iron-media-query#1.0.8",
	"iron-menu-behavior":            "PolymerElements/iron-menu-behavior#1.2.0",
	"iron-meta":                     "PolymerElements/iron-meta#1.1.2",
	"iron-overlay-behavior":         "PolymerElements/iron-overlay-behavior#1.10.3",
	"iron-pages":                    "PolymerElements/iron-pages#1.0.8",
	"iron-resizable-behavior":       "PolymerElements/iron-resizable-behavior#1.0.5",
	"iron-selector":                 "PolymerElements/iron-selector#1.5.2",
	"iron-validatable-behavior":     "PolymerElements/iron-validatable-behavior#1.1.1",
	"neon-animation":                "PolymerElements/neon-animation#1.2.4",
	"paper-behaviors":               "PolymerElements/paper-behaviors#1.0.12",
	"paper-button":                  "PolymerElements/paper-button#1.0.14",
	"paper-dialog-behavior":         "PolymerElements/paper-dialog-behavior#1.2.7",
	"paper-dialog":                  "PolymerElements/paper-dialog#1.1.0",
	"paper-drawer-panel":            "PolymerElements/paper-drawer-panel#1.0.11",
	"paper-dropdown-menu":           "PolymerElements/paper-dropdown-menu#1.5.0",
	"paper-fab":                     "PolymerElements/paper-fab#1.2.0",
	"paper-header-panel":            "PolymerElements/paper-header-panel#1.1.7",
	"paper-icon-button":             "PolymerElements/paper-icon-button#1.1.4",
	"paper-input":                   "PolymerElements/paper-input#1.1.23",
	"paper-item":                    "PolymerElements/paper-item#1.2.1",
	"paper-listbox":                 "PolymerElements/paper-listbox#1.1.2",
	"paper-material":                "PolymerElements/paper-material#1.0.6",
	"paper-menu-button":             "PolymerElements/paper-menu-button#1.5.2",
	"paper-menu":                    "PolymerElements/paper-menu#1.2.2",
	"paper-radio-button":            "PolymerElements/paper-radio-button#1.3.1",
	"paper-radio-group":             "PolymerElements/paper-radio-group#1.2.1",
	"paper-ripple":                  "PolymerElements/paper-ripple#1.0.9",
	"paper-scroll-header-panel":     "PolymerElements/paper-scroll-header-panel#1.0.16",
	"paper-styles":                  "PolymerElements/paper-styles#1.2.0",
	"paper-tabs":                    "PolymerElements/paper-tabs#1.8.0",
	"paper-toast":                   "PolymerElements/paper-toast#1.3.0",
	"paper-toolbar":                 "PolymerElements/paper-toolbar#1.1.7",
	"platinum-sw":                   "PolymerElements/platinum-sw#1.2.4",
	"polymer":                       "Polymer/polymer#1.4.0",
	"sinonjs":                       "PolymerElements/sinonjs#1.17.1",
	"stacky":                        "PolymerElements/stacky#1.3.2",
	"webcomponentsjs":               "WebComponents/webcomponentsjs#0.7.24",
}

// overrideComp maps a given spec, as described in fetchRepo doc comments to a
// desired component name.
var overrideComp = map[string]string{
	"googlecodelabs/codelab-components": "google-codelab-elements",
	"google/code-prettify":              "google-prettify",
}

// fetchRepo downloads a repo from github and unpacks it into basedir/dest,
// calling itself recursively for every dependency found in the unpacked bower.json.
// If the basedir/dest directory already exists, fetchRepo does nothing.
//
// The spec has the following format: "githubuser/repo#version".
// Any non-alphanumeric characters are stripped from the version.
// If version is missing, "master" is used instead.
// The bower registry is not used for naming resolution.
func fetchRepo(basedir, spec string) error {
	var user, repo, path, comp, ver string
	s := strings.Split(spec, "#")
	path = s[0]
	if len(s) > 1 {
		ver = s[1]
	}
	ver = strings.Trim(ver, "^~ ")
	if ver == "" {
		ver = "master"
	}
	s = strings.Split(path, "/")
	user = s[0]
	if len(s) > 1 {
		repo = s[1]
	}
	// Check exception map to see if we should override the component name.
	comp = repo
	if c, ok := overrideComp[path]; ok {
		comp = c
	}
	// if repo already exists locally, return immediately, we're done.
	if _, err := os.Stat(basedir + "/" + comp); err == nil {
		return nil
	}
	zipFile := basedir + "/" + comp + ".zip"
	url := "https://github.com/" + user + "/" + repo + "/archive/v" + ver + ".zip"
	err := downloadFile(zipFile, url)
	if err != nil {
		return err
	}
	// If get fails, it will download a file containing only "404: Not Found".
	// We check for that case by looking for an unusually small file.
	var st os.FileInfo
	if st, err = os.Stat(zipFile); err != nil {
		return err
	}
	if st.Size() < 20 {
		os.Remove(zipFile)
		url = "https://github.com/" + user + "/" + repo + "/archive/" + ver + ".zip"
		err = downloadFile(zipFile, url)
		if err != nil {
			return err
		}
	}
	err = unzip(zipFile, basedir)
	if err != nil {
		return err
	}
	os.Remove(zipFile)
	os.Rename(basedir+"/"+repo+"-"+ver, basedir+"/"+comp)
	// if unzipped archive contains a bower.json, parse it, and for each dependency therein,
	// recursively fetch the corresponding repo.
	bowerFile := basedir + "/" + comp + "/bower.json"
	if _, err := os.Stat(bowerFile); os.IsNotExist(err) {
		return nil
	}
	raw, err := ioutil.ReadFile(bowerFile)
	if err != nil {
		return err
	}
	var b struct {
		Dependencies map[string]string
	}
	err = json.Unmarshal(raw, &b)
	if err != nil {
		return err
	}
	// Check exception map to see if we should overide spec.
	for c, s := range b.Dependencies {
		if spec, ok := overrideSpec[c]; ok {
			s = spec
		}
		err = fetchRepo(basedir, s)
		if err != nil {
			return err
		}
	}
	return nil
}

// unzip a file to a dest directory.
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()
	os.MkdirAll(dest, 0755)
	// Closure to address file descriptors issue with all the deferred .Close() methods
	extractAndWriteFile := func(f *zip.File) error {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()
		path := filepath.Join(dest, f.Name)
		if f.FileInfo().IsDir() {
			return os.MkdirAll(path, f.Mode())
		}
		os.MkdirAll(filepath.Dir(path), f.Mode())
		f2, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return err
		}
		_, err = io.Copy(f2, rc)
		// Catch both io.Copy and file.Close errors but prefer returning the former.
		if err1 := f2.Close(); err1 != nil && err == nil {
			err = err1
		}
		return err
	}
	for _, f := range r.File {
		err := extractAndWriteFile(f)
		if err != nil {
			return err
		}
	}
	return nil
}

// openBrowser tries to open the URL in a browser.
func openBrowser(url string) error {
	var args []string
	switch runtime.GOOS {
	case "darwin":
		args = []string{"open"}
	case "windows":
		args = []string{"cmd", "/c", "start"}
	default:
		args = []string{"xdg-open"}
	}
	cmd := exec.Command(args[0], append(args[1:], url)...)
	return cmd.Start()
}
<|endoftext|>


<commit_before>// Copyright © 2017 Sascha Andres <sascha.andres@outlook.com>
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package helper

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

// Environ is a type alias for environment variables
type Environ []string

// Unset removes an environment key for a new process
func (e *Environ) Unset(key string) {
	for i := range *e {
		if strings.HasPrefix((*e)[i], key+"=") {
			(*e)[i] = (*e)[len(*e)-1]
			*e = (*e)[:len(*e)-1]
			break
		}
	}
}

// StartAndWait calls the command and returns the result
func StartAndWait(command *exec.Cmd) (int, error) {
	if err := command.Start(); err != nil {
		return -1, fmt.Errorf("Error running bash: %#v", err)
	}
	if err := command.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				return status.ExitStatus(), fmt.Errorf("Error waiting for bash: %#v", err)
			}
		} else {
			return -1, fmt.Errorf("Error waiting for bash: %#v", err)
		}
	}
	return 0, nil
}

// Exists returns whether the given file or directory exists or not
func Exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

// GetEnvironmentVariables returns environment variables as map
func GetEnvironmentVariables() map[string]string {
	var (
		env = os.Environ()
		m   = make(map[string]string, len(env))
	)

	for _, e := range env {
		keyVal := strings.SplitN(e, "=", 2)
		key, val := keyVal[0], keyVal[1]
		m[key] = val
	}
	return m
}
func GetCommand(commandName string, env Environ, path string, arguments ...string) (*exec.Cmd, error) {
	command := exec.Command(commandName, arguments...)
	command.Dir = path
	command.Env = env
	command.Stdout = os.Stdout
	command.Stdin = os.Stdin
	command.Stderr = os.Stderr
	return command, nil
}
<commit_msg>If Stderr is nil assume no error happended<commit_after>// Copyright © 2017 Sascha Andres <sascha.andres@outlook.com>
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package helper

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

// Environ is a type alias for environment variables
type Environ []string

// Unset removes an environment key for a new process
func (e *Environ) Unset(key string) {
	for i := range *e {
		if strings.HasPrefix((*e)[i], key+"=") {
			(*e)[i] = (*e)[len(*e)-1]
			*e = (*e)[:len(*e)-1]
			break
		}
	}
}

// StartAndWait calls the command and returns the result
func StartAndWait(command *exec.Cmd) (int, error) {
	if err := command.Start(); err != nil {
		return -1, fmt.Errorf("Error running bash: %#v", err)
	}
	if err := command.Wait(); err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				if err.(*exec.ExitError).Stderr == nil {
					return 0, nil
				}
				return status.ExitStatus(), fmt.Errorf("Error waiting for bash: %#v", err)
			}
		} else {
			return -1, fmt.Errorf("Error waiting for bash: %#v", err)
		}
	}
	return 0, nil
}

// Exists returns whether the given file or directory exists or not
func Exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

// GetEnvironmentVariables returns environment variables as map
func GetEnvironmentVariables() map[string]string {
	var (
		env = os.Environ()
		m   = make(map[string]string, len(env))
	)

	for _, e := range env {
		keyVal := strings.SplitN(e, "=", 2)
		key, val := keyVal[0], keyVal[1]
		m[key] = val
	}
	return m
}
func GetCommand(commandName string, env Environ, path string, arguments ...string) (*exec.Cmd, error) {
	command := exec.Command(commandName, arguments...)
	command.Dir = path
	command.Env = env
	command.Stdout = os.Stdout
	command.Stdin = os.Stdin
	command.Stderr = os.Stderr
	return command, nil
}
<|endoftext|>


<commit_before>package cli

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise-cli/bitrise"
	"github.com/codegangsta/cli"
)

const (
	minEnvmanVersion  = "0.9.1"
	minStepmanVersion = "0.9.2"
)

// PrintBitriseHeaderASCIIArt ...
func PrintBitriseHeaderASCIIArt() {
	// generated here: http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Bitrise
	fmt.Println(`
  ██████╗ ██╗████████╗██████╗ ██╗███████╗███████╗
  ██╔══██╗██║╚══██╔══╝██╔══██╗██║██╔════╝██╔════╝
  ██████╔╝██║   ██║   ██████╔╝██║███████╗█████╗
  ██╔══██╗██║   ██║   ██╔══██╗██║╚════██║██╔══╝
  ██████╔╝██║   ██║   ██║  ██║██║███████║███████╗
  ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝╚═╝╚══════╝╚══════╝`)
	fmt.Println()
}

func doSetup(c *cli.Context) {
	PrintBitriseHeaderASCIIArt()
	log.Infoln("[BITRISE_CLI] - Setup")
	log.Infoln("Detected OS:", runtime.GOOS)
	switch runtime.GOOS {
	case "darwin":
		if err := doSetupOnOSX(); err != nil {
			log.Fatalln("Setup failed:", err)
		}
	case "linux":
		if err := doSetupOnLinux(); err != nil {
			log.Fatalln("Setup failed:", err)
		}
	default:
		log.Fatalln("Sorry, unsupported platform :(")
	}

	// guide
	fmt.Println()
	log.Infoln("We're ready to rock!!")
	fmt.Println()
	log.Infoln("To start using bitrise-cli:")
	log.Infoln("* cd into your project's directory (if you're not there already)")
	log.Infoln("* call: bitrise-cli init")
	log.Infoln("* follow the guide")
	fmt.Println()
	log.Infoln("That's all :)")
}

func checkProgramInstalledPath(clcommand string) (string, error) {
	cmd := exec.Command("which", clcommand)
	cmd.Stderr = os.Stderr
	outBytes, err := cmd.Output()
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

func checkIsHomebrewInstalled() error {
	brewRubyInstallCmdString := `$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
	officialSiteURL := "http://brew.sh/"

	progInstallPth, err := checkProgramInstalledPath("brew")
	if err != nil {
		fmt.Println()
		log.Warn("It seems that Homebrew is not installed on your system.")
		log.Infoln("Homebrew (short: brew) is required in order to be able to auto-install all the bitrise dependencies.")
		log.Infoln("You should be able to install brew by copying this command and running it in your Terminal:")
		log.Infoln(brewRubyInstallCmdString)
		log.Infoln("You can find more information about Homebrew on it's official site at:", officialSiteURL)
		log.Warn("Once the installation of brew is finished you should call the bitrise setup again.")
		return err
	}
	verStr, err := bitrise.RunCommandAndReturnStdout("brew", "--version")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get version")
	}
	log.Infoln(" * [OK] Homebrew :", progInstallPth)
	log.Infoln("        version :", verStr)
	return nil
}

func checkIsXcodeCLTInstalled() error {
	progInstallPth, err := checkProgramInstalledPath("xcodebuild")
	if err != nil {
		fmt.Println()
		log.Warn("It seems that the Xcode Command Line Tools are not installed on your system.")
		log.Infoln("You can install it by running the following command in your Terminal:")
		log.Infoln("xcode-select --install")
		log.Warn("Once the installation is finished you should call the bitrise setup again.")
		return err
	}
	verStr, err := bitrise.RunCommandAndReturnStdout("xcodebuild", "-version")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get version")
	}
	xcodeSelectPth, err := bitrise.RunCommandAndReturnStdout("xcode-select", "-p")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get Xcode path")
	}
	log.Infoln(" * [OK] xcodebuild path :", progInstallPth)
	log.Infoln("        active Xcode (Command Line Tools) path :", xcodeSelectPth)
	log.Infoln("        version :", strings.Join(strings.Split(verStr, "\n"), " | "))
	return nil
}

//
// install with brew example
//
// func checkIsAnsibleInstalled() error {
// 	progInstallPth, err := checkProgramInstalledPath("ansible")
// 	if err != nil {
// 		officialSiteURL := "http://www.ansible.com/home"
// 		officialGitHubURL := "https://github.com/ansible/ansible"
// 		log.Infoln("")
// 		log.Infoln("Ansible was not found.")
// 		log.Infoln("Ansible is used for system provisioning.")
// 		log.Infoln("You can find more information on Ansible's official website:", officialSiteURL)
// 		log.Infoln(" or on it's GitHub page:", officialGitHubURL)
// 		log.Infoln("You can install Ansible through brew:")
// 		log.Infoln("$ brew update && brew install ansible")
// 		isInstall, err := goinp.AskForBool("Would you like to install Ansible right now?")
// 		if err != nil {
// 			return err
// 		}
// 		if !isInstall {
// 			return errors.New("Ansible not found and install was not initiated.")
// 		}
//
// 		// Install
// 		log.Infoln("$ brew update --verbose")
// 		if err := bitrise.RunCommand("brew", "update", "--verbose"); err != nil {
// 			return err
// 		}
// 		log.Infoln("$ brew install ansible")
// 		if err := bitrise.RunCommand("brew", "install", "ansible"); err != nil {
// 			return err
// 		}
//
// 		// just check again
// 		return checkIsAnsibleInstalled()
// 	}
// 	log.Infoln(" * [OK] Ansible :", progInstallPth)
// 	return nil
// }

func checkIsBitriseToolInstalled(toolname, minVersion string, isInstall bool) error {
	doInstall := func() error {
		installCmdLines := []string{
			"curl -L https://github.com/bitrise-io/" + toolname + "/releases/download/" + minVersion + "/" + toolname + "-$(uname -s)-$(uname -m) > /usr/local/bin/" + toolname,
			"chmod +x /usr/local/bin/" + toolname,
		}
		officialGithub := "https://github.com/bitrise-io/" + toolname
		fmt.Println()
		log.Warnln("No supported " + toolname + " version found.")
		log.Infoln("You can find more information about "+toolname+" on it's official GitHub page:", officialGithub)
		fmt.Println()

		// Install
		log.Infoln("Installing...")
		fmt.Println(strings.Join(installCmdLines, "\n"))
		if err := bitrise.RunBashCommandLines(installCmdLines); err != nil {
			return err
		}

		// check again
		return checkIsBitriseToolInstalled(toolname, minVersion, false)
	}

	// check whether installed
	progInstallPth, err := checkProgramInstalledPath(toolname)
	if err != nil {
		if !isInstall {
			return err
		}

		return doInstall()
	}
	verStr, err := bitrise.RunCommandAndReturnStdout(toolname, "-version")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get version")
	}

	// version check
	isVersionOk, err := bitrise.IsVersionGreaterOrEqual(verStr, minVersion)
	if err != nil {
		log.Error("Failed to validate installed version")
		return err
	}
	if !isVersionOk {
		log.Warn("Installed "+toolname+" found, but not a supported version: ", verStr)
		if !isInstall {
			return errors.New("Failed to install required version.")
		}
		log.Warn("Updating...")
		return doInstall()
	}

	log.Infoln(" * [OK] "+toolname+" :", progInstallPth)
	log.Infoln("        version :", verStr)
	return nil
}

func checkIsEnvmanInstalled() error {
	toolname := "envman"
	minVersion := minEnvmanVersion
	if err := checkIsBitriseToolInstalled(toolname, minVersion, true); err != nil {
		return err
	}
	return nil
}

func checkIsStepmanInstalled() error {
	toolname := "stepman"
	minVersion := minStepmanVersion
	if err := checkIsBitriseToolInstalled(toolname, minVersion, true); err != nil {
		return err
	}
	return nil
}

func doSetupOnOSX() error {
	log.Infoln("Doing OS X specific setup")
	log.Infoln("Checking required tools...")
	if err := checkIsXcodeCLTInstalled(); err != nil {
		return errors.New(fmt.Sprint("Xcode Command Line Tools not installed. Err:", err))
	}
	if err := checkIsHomebrewInstalled(); err != nil {
		return errors.New(fmt.Sprint("Homebrew not installed. Err:", err))
	}
	// if err := checkIsAnsibleInstalled(); err != nil {
	// 	return errors.New("Ansible failed to install")
	// }
	if err := checkIsEnvmanInstalled(); err != nil {
		return errors.New(fmt.Sprint("Envman failed to install:", err))
	}
	if err := checkIsStepmanInstalled(); err != nil {
		return errors.New(fmt.Sprint("Stepman failed to install:", err))
	}
	log.Infoln("All the required tools are installed!")
	return nil
}

func doSetupOnLinux() error {
	return errors.New("doSetupOnLinux -- Coming soon")
}
<commit_msg>requires stepman 0.9.3<commit_after>package cli

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise-cli/bitrise"
	"github.com/codegangsta/cli"
)

const (
	minEnvmanVersion  = "0.9.1"
	minStepmanVersion = "0.9.3"
)

// PrintBitriseHeaderASCIIArt ...
func PrintBitriseHeaderASCIIArt() {
	// generated here: http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Bitrise
	fmt.Println(`
  ██████╗ ██╗████████╗██████╗ ██╗███████╗███████╗
  ██╔══██╗██║╚══██╔══╝██╔══██╗██║██╔════╝██╔════╝
  ██████╔╝██║   ██║   ██████╔╝██║███████╗█████╗
  ██╔══██╗██║   ██║   ██╔══██╗██║╚════██║██╔══╝
  ██████╔╝██║   ██║   ██║  ██║██║███████║███████╗
  ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝╚═╝╚══════╝╚══════╝`)
	fmt.Println()
}

func doSetup(c *cli.Context) {
	PrintBitriseHeaderASCIIArt()
	log.Infoln("[BITRISE_CLI] - Setup")
	log.Infoln("Detected OS:", runtime.GOOS)
	switch runtime.GOOS {
	case "darwin":
		if err := doSetupOnOSX(); err != nil {
			log.Fatalln("Setup failed:", err)
		}
	case "linux":
		if err := doSetupOnLinux(); err != nil {
			log.Fatalln("Setup failed:", err)
		}
	default:
		log.Fatalln("Sorry, unsupported platform :(")
	}

	// guide
	fmt.Println()
	log.Infoln("We're ready to rock!!")
	fmt.Println()
	log.Infoln("To start using bitrise-cli:")
	log.Infoln("* cd into your project's directory (if you're not there already)")
	log.Infoln("* call: bitrise-cli init")
	log.Infoln("* follow the guide")
	fmt.Println()
	log.Infoln("That's all :)")
}

func checkProgramInstalledPath(clcommand string) (string, error) {
	cmd := exec.Command("which", clcommand)
	cmd.Stderr = os.Stderr
	outBytes, err := cmd.Output()
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

func checkIsHomebrewInstalled() error {
	brewRubyInstallCmdString := `$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
	officialSiteURL := "http://brew.sh/"

	progInstallPth, err := checkProgramInstalledPath("brew")
	if err != nil {
		fmt.Println()
		log.Warn("It seems that Homebrew is not installed on your system.")
		log.Infoln("Homebrew (short: brew) is required in order to be able to auto-install all the bitrise dependencies.")
		log.Infoln("You should be able to install brew by copying this command and running it in your Terminal:")
		log.Infoln(brewRubyInstallCmdString)
		log.Infoln("You can find more information about Homebrew on it's official site at:", officialSiteURL)
		log.Warn("Once the installation of brew is finished you should call the bitrise setup again.")
		return err
	}
	verStr, err := bitrise.RunCommandAndReturnStdout("brew", "--version")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get version")
	}
	log.Infoln(" * [OK] Homebrew :", progInstallPth)
	log.Infoln("        version :", verStr)
	return nil
}

func checkIsXcodeCLTInstalled() error {
	progInstallPth, err := checkProgramInstalledPath("xcodebuild")
	if err != nil {
		fmt.Println()
		log.Warn("It seems that the Xcode Command Line Tools are not installed on your system.")
		log.Infoln("You can install it by running the following command in your Terminal:")
		log.Infoln("xcode-select --install")
		log.Warn("Once the installation is finished you should call the bitrise setup again.")
		return err
	}
	verStr, err := bitrise.RunCommandAndReturnStdout("xcodebuild", "-version")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get version")
	}
	xcodeSelectPth, err := bitrise.RunCommandAndReturnStdout("xcode-select", "-p")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get Xcode path")
	}
	log.Infoln(" * [OK] xcodebuild path :", progInstallPth)
	log.Infoln("        active Xcode (Command Line Tools) path :", xcodeSelectPth)
	log.Infoln("        version :", strings.Join(strings.Split(verStr, "\n"), " | "))
	return nil
}

//
// install with brew example
//
// func checkIsAnsibleInstalled() error {
// 	progInstallPth, err := checkProgramInstalledPath("ansible")
// 	if err != nil {
// 		officialSiteURL := "http://www.ansible.com/home"
// 		officialGitHubURL := "https://github.com/ansible/ansible"
// 		log.Infoln("")
// 		log.Infoln("Ansible was not found.")
// 		log.Infoln("Ansible is used for system provisioning.")
// 		log.Infoln("You can find more information on Ansible's official website:", officialSiteURL)
// 		log.Infoln(" or on it's GitHub page:", officialGitHubURL)
// 		log.Infoln("You can install Ansible through brew:")
// 		log.Infoln("$ brew update && brew install ansible")
// 		isInstall, err := goinp.AskForBool("Would you like to install Ansible right now?")
// 		if err != nil {
// 			return err
// 		}
// 		if !isInstall {
// 			return errors.New("Ansible not found and install was not initiated.")
// 		}
//
// 		// Install
// 		log.Infoln("$ brew update --verbose")
// 		if err := bitrise.RunCommand("brew", "update", "--verbose"); err != nil {
// 			return err
// 		}
// 		log.Infoln("$ brew install ansible")
// 		if err := bitrise.RunCommand("brew", "install", "ansible"); err != nil {
// 			return err
// 		}
//
// 		// just check again
// 		return checkIsAnsibleInstalled()
// 	}
// 	log.Infoln(" * [OK] Ansible :", progInstallPth)
// 	return nil
// }

func checkIsBitriseToolInstalled(toolname, minVersion string, isInstall bool) error {
	doInstall := func() error {
		installCmdLines := []string{
			"curl -L https://github.com/bitrise-io/" + toolname + "/releases/download/" + minVersion + "/" + toolname + "-$(uname -s)-$(uname -m) > /usr/local/bin/" + toolname,
			"chmod +x /usr/local/bin/" + toolname,
		}
		officialGithub := "https://github.com/bitrise-io/" + toolname
		fmt.Println()
		log.Warnln("No supported " + toolname + " version found.")
		log.Infoln("You can find more information about "+toolname+" on it's official GitHub page:", officialGithub)
		fmt.Println()

		// Install
		log.Infoln("Installing...")
		fmt.Println(strings.Join(installCmdLines, "\n"))
		if err := bitrise.RunBashCommandLines(installCmdLines); err != nil {
			return err
		}

		// check again
		return checkIsBitriseToolInstalled(toolname, minVersion, false)
	}

	// check whether installed
	progInstallPth, err := checkProgramInstalledPath(toolname)
	if err != nil {
		if !isInstall {
			return err
		}

		return doInstall()
	}
	verStr, err := bitrise.RunCommandAndReturnStdout(toolname, "-version")
	if err != nil {
		log.Infoln("")
		return errors.New("Failed to get version")
	}

	// version check
	isVersionOk, err := bitrise.IsVersionGreaterOrEqual(verStr, minVersion)
	if err != nil {
		log.Error("Failed to validate installed version")
		return err
	}
	if !isVersionOk {
		log.Warn("Installed "+toolname+" found, but not a supported version: ", verStr)
		if !isInstall {
			return errors.New("Failed to install required version.")
		}
		log.Warn("Updating...")
		return doInstall()
	}

	log.Infoln(" * [OK] "+toolname+" :", progInstallPth)
	log.Infoln("        version :", verStr)
	return nil
}

func checkIsEnvmanInstalled() error {
	toolname := "envman"
	minVersion := minEnvmanVersion
	if err := checkIsBitriseToolInstalled(toolname, minVersion, true); err != nil {
		return err
	}
	return nil
}

func checkIsStepmanInstalled() error {
	toolname := "stepman"
	minVersion := minStepmanVersion
	if err := checkIsBitriseToolInstalled(toolname, minVersion, true); err != nil {
		return err
	}
	return nil
}

func doSetupOnOSX() error {
	log.Infoln("Doing OS X specific setup")
	log.Infoln("Checking required tools...")
	if err := checkIsXcodeCLTInstalled(); err != nil {
		return errors.New(fmt.Sprint("Xcode Command Line Tools not installed. Err:", err))
	}
	if err := checkIsHomebrewInstalled(); err != nil {
		return errors.New(fmt.Sprint("Homebrew not installed. Err:", err))
	}
	// if err := checkIsAnsibleInstalled(); err != nil {
	// 	return errors.New("Ansible failed to install")
	// }
	if err := checkIsEnvmanInstalled(); err != nil {
		return errors.New(fmt.Sprint("Envman failed to install:", err))
	}
	if err := checkIsStepmanInstalled(); err != nil {
		return errors.New(fmt.Sprint("Stepman failed to install:", err))
	}
	log.Infoln("All the required tools are installed!")
	return nil
}

func doSetupOnLinux() error {
	return errors.New("doSetupOnLinux -- Coming soon")
}
<|endoftext|>


<commit_before>package cli

import (
	"encoding/base64"
	"fmt"
	"github.com/brightbox/gobrightbox"
	"gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"os"
	"strings"
)

type ServersCommand struct {
	App          *CliApp
	All          bool
	Id           string
	IdList       []string
	ImageId      string
	Name         string
	ServerType   string
	Zone         string
	Groups       string
	UserData     string
	UserDataFile *os.File
	Base64       bool
}

func (l *ServersCommand) list(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}

	w := tabWriter()
	defer w.Flush()
	servers, err := l.App.Client.Servers()
	if err != nil {
		return err
	}
	listRec(w, "ID", "STATUS", "TYPE", "ZONE", "CREATED", "IMAGE", "CLOUDIPS", "NAME")
	for _, s := range *servers {
		listRec(
			w, s.Id, s.Status, s.ServerType.Handle,
			s.Zone.Handle, s.CreatedAt.Format("2006-01-02"),
			s.Image.Id, collectById(s.CloudIPs), s.Name)
	}
	return nil
}

func (l *ServersCommand) show(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	w := tabWriterRight()
	defer w.Flush()
	s, err := l.App.Client.Server(l.Id)
	if err != nil {
		l.App.Fatalf(err.Error())
	}

	drawShow(w, []interface{}{
		"id", s.Id,
		"status", s.Status,
		"locked", s.Locked,
		"name", s.Name,
		"created_at", s.CreatedAt,
		"deleted_at", s.DeletedAt,
		"zone", s.Zone.Handle,
		"type", s.ServerType.Id,
		"type_name", s.ServerType.Name,
		"type_handle", s.ServerType.Handle,
		"ram", s.ServerType.Ram,
		"cores", s.ServerType.Cores,
		"disk", s.ServerType.DiskSize,
		"compatability_mode", s.CompatabilityMode,
		"image", s.Image.Id,
		"image_name", s.Image.Name,
		"arch", s.Image.Arch,
		"private_ips", collectByField(s.Interfaces, "IPv4Address"),
		"cloud_ips", collectByField(s.CloudIPs, "PublicIP"),
		"ipv6_ips", collectByField(s.Interfaces, "IPv6Address"),
		"cloud_ip_ids", collectByField(s.CloudIPs, "Id"),
		"hostname", s.Hostname,
		"fqdn", s.Fqdn,
		"public_hostname", "public." + s.Fqdn,
		"ipv6_hostname", "ipv6." + s.Fqdn,
		"snapshots", collectById(s.Snapshots),
		"server_groups", collectById(s.ServerGroups),
	})
	return nil

}

func (l *ServersCommand) create(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	w := tabWriter()
	defer w.Flush()
	newServer := brightbox.CreateServerOptions{
		Image: l.ImageId,
		Name:  l.Name,
	}

	if l.Zone != "" {
		zoneId, err := l.App.Client.resolveZoneId(l.Zone)
		if err != nil {
			return err
		}
		newServer.Zone = zoneId
	}

	if l.ServerType != "" {
		typeId, err := l.App.Client.resolveServerTypeId(l.ServerType)
		if err != nil {
			return err
		}
		newServer.ServerType = typeId
	}

	if l.Groups != "" {
		groups := strings.Split(l.Groups, ",")
		if len(groups) > 1 || (len(groups) == 1 && groups[0] != "") {
			newServer.ServerGroups = groups
		}
	}

	var userData []byte
	if l.UserData != "" {
		userData = []byte(l.UserData)
	}
	if l.UserDataFile != nil {
		defer l.UserDataFile.Close()
		fi, err := l.UserDataFile.Stat()
		if err != nil {
			return err
		}
		if fi.Size() > 2<<13 {
			return fmt.Errorf("User data file cannot exceed 16k")
		}
		userData, err = ioutil.ReadAll(l.UserDataFile)
		if err != nil {
			return err
		}
	}

	if len(userData) > 0 && l.Base64 {
		newServer.UserData = base64.StdEncoding.EncodeToString(userData)
	} else if len(userData) > 0 {
		newServer.UserData = string(userData)
	}
	if len(newServer.UserData) > 2<<13 {
		return fmt.Errorf("User data cannot exceed 16k")
	}

	server, err := l.App.Client.CreateServer(&newServer)
	if err != nil {
		return err
	}
	listRec(w, "ID", "STATUS", "TYPE", "ZONE", "CREATED", "IMAGE", "CLOUDIPS", "NAME")
	s := server
	listRec(
		w, s.Id, s.Status, s.ServerType.Handle,
		s.Zone.Handle, s.CreatedAt.Format("2006-01-02"),
		s.Image.Id, collectById(s.CloudIPs), s.Name)
	return nil

}

func (l *ServersCommand) destroy(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Destroying server %s\n", id)
		err := l.App.Client.DestroyServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func ConfigureServersCommand(app *CliApp) {
	cmd := ServersCommand{App: app}
	servers := app.Command("servers", "Manage cloud servers")
	servers.Command("list", "List cloud servers").Action(cmd.list)
	show := servers.Command("show", "View details on a cloud server").Action(cmd.show)
	show.Arg("identifier", "Identifier of server to show").Required().StringVar(&cmd.Id)
	create := servers.Command("create", "Create a new cloud server").Action(cmd.create)
	create.Arg("image identifier", "Identifier of image with which to create the server").Required().StringVar(&cmd.ImageId)
	create.Flag("name", "Name to give the new server").Short('n').StringVar(&cmd.Name)
	create.Flag("type", "Server type for the new server").Short('t').StringVar(&cmd.ServerType)
	create.Flag("zone", "Availability zone in which to place the new server").Short('z').StringVar(&cmd.Zone)
	create.Flag("groups", "The server groups to which the new server should belong. Comma separate multiple groups.").Short('g').StringVar(&cmd.Groups)
	create.Flag("user-data", "Specify the user data as a string").PlaceHolder("USERDATA").StringVar(&cmd.UserData)
	create.Flag("user-data-file", "Specify the user data from local file").PlaceHolder("FILENAME").OpenFileVar(&cmd.UserDataFile, 0, 0)
	create.Flag("base64", "Base64 encode the user data (default: true)").Default("true").BoolVar(&cmd.Base64)
	destroy := servers.Command("destroy", "Destroy a cloud server").Action(cmd.destroy)
	destroy.Arg("identifier", "Identifier of server to destroy").Required().StringsVar(&cmd.IdList)
}
<commit_msg>More server commands<commit_after>package cli

import (
	"encoding/base64"
	"fmt"
	"github.com/brightbox/gobrightbox"
	"gopkg.in/alecthomas/kingpin.v2"
	"io/ioutil"
	"os"
	"strings"
)

type ServersCommand struct {
	App          *CliApp
	All          bool
	Id           string
	IdList       []string
	ImageId      string
	Name         string
	ServerType   string
	Zone         string
	Groups       string
	UserData     string
	UserDataFile *os.File
	Base64       bool
}

func (l *ServersCommand) list(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}

	w := tabWriter()
	defer w.Flush()
	servers, err := l.App.Client.Servers()
	if err != nil {
		return err
	}
	listRec(w, "ID", "STATUS", "TYPE", "ZONE", "CREATED", "IMAGE", "CLOUDIPS", "NAME")
	for _, s := range *servers {
		listRec(
			w, s.Id, s.Status, s.ServerType.Handle,
			s.Zone.Handle, s.CreatedAt.Format("2006-01-02"),
			s.Image.Id, collectById(s.CloudIPs), s.Name)
	}
	return nil
}

func (l *ServersCommand) show(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	w := tabWriterRight()
	defer w.Flush()
	s, err := l.App.Client.Server(l.Id)
	if err != nil {
		l.App.Fatalf(err.Error())
	}

	drawShow(w, []interface{}{
		"id", s.Id,
		"status", s.Status,
		"locked", s.Locked,
		"name", s.Name,
		"created_at", s.CreatedAt,
		"deleted_at", s.DeletedAt,
		"zone", s.Zone.Handle,
		"type", s.ServerType.Id,
		"type_name", s.ServerType.Name,
		"type_handle", s.ServerType.Handle,
		"ram", s.ServerType.Ram,
		"cores", s.ServerType.Cores,
		"disk", s.ServerType.DiskSize,
		"compatability_mode", s.CompatabilityMode,
		"image", s.Image.Id,
		"image_name", s.Image.Name,
		"arch", s.Image.Arch,
		"private_ips", collectByField(s.Interfaces, "IPv4Address"),
		"cloud_ips", collectByField(s.CloudIPs, "PublicIP"),
		"ipv6_ips", collectByField(s.Interfaces, "IPv6Address"),
		"cloud_ip_ids", collectByField(s.CloudIPs, "Id"),
		"hostname", s.Hostname,
		"fqdn", s.Fqdn,
		"public_hostname", "public." + s.Fqdn,
		"ipv6_hostname", "ipv6." + s.Fqdn,
		"snapshots", collectById(s.Snapshots),
		"server_groups", collectById(s.ServerGroups),
	})
	return nil

}

func (l *ServersCommand) create(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	w := tabWriter()
	defer w.Flush()
	newServer := brightbox.CreateServerOptions{
		Image: l.ImageId,
		Name:  l.Name,
	}

	if l.Zone != "" {
		zoneId, err := l.App.Client.resolveZoneId(l.Zone)
		if err != nil {
			return err
		}
		newServer.Zone = zoneId
	}

	if l.ServerType != "" {
		typeId, err := l.App.Client.resolveServerTypeId(l.ServerType)
		if err != nil {
			return err
		}
		newServer.ServerType = typeId
	}

	if l.Groups != "" {
		groups := strings.Split(l.Groups, ",")
		if len(groups) > 1 || (len(groups) == 1 && groups[0] != "") {
			newServer.ServerGroups = groups
		}
	}

	var userData []byte
	if l.UserData != "" {
		userData = []byte(l.UserData)
	}
	if l.UserDataFile != nil {
		defer l.UserDataFile.Close()
		fi, err := l.UserDataFile.Stat()
		if err != nil {
			return err
		}
		if fi.Size() > 2<<13 {
			return fmt.Errorf("User data file cannot exceed 16k")
		}
		userData, err = ioutil.ReadAll(l.UserDataFile)
		if err != nil {
			return err
		}
	}

	if len(userData) > 0 && l.Base64 {
		newServer.UserData = base64.StdEncoding.EncodeToString(userData)
	} else if len(userData) > 0 {
		newServer.UserData = string(userData)
	}
	if len(newServer.UserData) > 2<<13 {
		return fmt.Errorf("User data cannot exceed 16k")
	}

	server, err := l.App.Client.CreateServer(&newServer)
	if err != nil {
		return err
	}
	listRec(w, "ID", "STATUS", "TYPE", "ZONE", "CREATED", "IMAGE", "CLOUDIPS", "NAME")
	s := server
	listRec(
		w, s.Id, s.Status, s.ServerType.Handle,
		s.Zone.Handle, s.CreatedAt.Format("2006-01-02"),
		s.Image.Id, collectById(s.CloudIPs), s.Name)
	return nil

}

func (l *ServersCommand) destroy(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Destroying server %s\n", id)
		err := l.App.Client.DestroyServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) stop(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Stopping server %s\n", id)
		err := l.App.Client.StopServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) start(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Starting server %s\n", id)
		err := l.App.Client.StartServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) reboot(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Rebooting server %s\n", id)
		err := l.App.Client.RebootServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) reset(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Resetting server %s\n", id)
		err := l.App.Client.ResetServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) shutdown(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Shutting down server %s\n", id)
		err := l.App.Client.ShutdownServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) lock(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Locking server %s\n", id)
		err := l.App.Client.LockServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func (l *ServersCommand) unlock(pc *kingpin.ParseContext) error {
	err := l.App.Configure()
	if err != nil {
		return err
	}
	for _, id := range l.IdList {
		fmt.Printf("Unlocking server %s\n", id)
		err := l.App.Client.UnlockServer(id)
		if err != nil {
			l.App.Errorf("%s: %s", err.Error(), id)
		}
	}
	return nil
}

func ConfigureServersCommand(app *CliApp) {
	cmd := ServersCommand{App: app}
	servers := app.Command("servers", "Manage cloud servers")
	servers.Command("list", "List cloud servers").Action(cmd.list)

	show := servers.Command("show", "View details on a cloud server").Action(cmd.show)
	show.Arg("identifier", "Identifier of server to show").Required().StringVar(&cmd.Id)

	create := servers.Command("create", "Create a new cloud server").Action(cmd.create)
	create.Arg("image identifier", "Identifier of image with which to create the server").Required().StringVar(&cmd.ImageId)
	create.Flag("name", "Name to give the new server").Short('n').StringVar(&cmd.Name)
	create.Flag("type", "Server type for the new server").Short('t').StringVar(&cmd.ServerType)
	create.Flag("zone", "Availability zone in which to place the new server").Short('z').StringVar(&cmd.Zone)
	create.Flag("groups", "The server groups to which the new server should belong. Comma separate multiple groups.").Short('g').StringVar(&cmd.Groups)
	create.Flag("user-data", "Specify the user data as a string").PlaceHolder("USERDATA").StringVar(&cmd.UserData)
	create.Flag("user-data-file", "Specify the user data from local file").PlaceHolder("FILENAME").OpenFileVar(&cmd.UserDataFile, 0, 0)
	create.Flag("base64", "Base64 encode the user data (default: true)").Default("true").BoolVar(&cmd.Base64)

	destroy := servers.Command("destroy", "Destroy a cloud server").Action(cmd.destroy)
	destroy.Arg("identifier", "Identifier of server to destroy").Required().StringsVar(&cmd.IdList)

	stop := servers.Command("stop", "Stop a cloud server").Action(cmd.stop)
	stop.Arg("identifier", "Identifier of servers to stop").Required().StringsVar(&cmd.IdList)

	start := servers.Command("start", "Start a cloud server").Action(cmd.start)
	start.Arg("identifier", "Identifier of servers to start").Required().StringsVar(&cmd.IdList)

	reboot := servers.Command("reboot", "Reboot a cloud server").Action(cmd.reboot)
	reboot.Arg("identifier", "Identifier of servers to reboot").Required().StringsVar(&cmd.IdList)

	reset := servers.Command("reset", "Reset a cloud server").Action(cmd.reset)
	reset.Arg("identifier", "Identifier of servers to reset").Required().StringsVar(&cmd.IdList)

	shutdown := servers.Command("shutdown", "Shutdown a cloud server").Action(cmd.shutdown)
	shutdown.Arg("identifier", "Identifier of servers to shut down").Required().StringsVar(&cmd.IdList)

	lock := servers.Command("lock", "Lock a cloud server").Action(cmd.lock)
	lock.Arg("identifier", "Identifier of servers to lock").Required().StringsVar(&cmd.IdList)

	unlock := servers.Command("unlock", "Unlock a cloud server").Action(cmd.unlock)
	unlock.Arg("identifier", "Identifier of servers to unlock").Required().StringsVar(&cmd.IdList)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"os"

	functions "github.com/funcy/functions_go"
	"github.com/urfave/cli"
)

// Version of Functions CLI
var Version = "0.3.36"

func version() cli.Command {
	r := versionCmd{VersionApi: functions.NewVersionApi()}
	return cli.Command{
		Name:   "version",
		Usage:  "displays fn and functions daemon versions",
		Action: r.version,
	}
}

type versionCmd struct {
	*functions.VersionApi
}

func (r *versionCmd) version(c *cli.Context) error {
	apiURL := os.Getenv("API_URL")
	if apiURL == "" {
		apiURL = "http://localhost:8080"
	}

	u, err := url.Parse(apiURL)
	if err != nil {
		return err
	}
	r.Configuration.BasePath = u.String()

	fmt.Println("Client version:", Version)
	v, _, err := r.VersionGet()
	if err != nil {
		return err
	}
	fmt.Println("Server version", v.Version)
	return nil
}
<commit_msg>fn tool: 0.3.37 release [skip ci]<commit_after>package main

import (
	"fmt"
	"net/url"
	"os"

	functions "github.com/funcy/functions_go"
	"github.com/urfave/cli"
)

// Version of Functions CLI
var Version = "0.3.37"

func version() cli.Command {
	r := versionCmd{VersionApi: functions.NewVersionApi()}
	return cli.Command{
		Name:   "version",
		Usage:  "displays fn and functions daemon versions",
		Action: r.version,
	}
}

type versionCmd struct {
	*functions.VersionApi
}

func (r *versionCmd) version(c *cli.Context) error {
	apiURL := os.Getenv("API_URL")
	if apiURL == "" {
		apiURL = "http://localhost:8080"
	}

	u, err := url.Parse(apiURL)
	if err != nil {
		return err
	}
	r.Configuration.BasePath = u.String()

	fmt.Println("Client version:", Version)
	v, _, err := r.VersionGet()
	if err != nil {
		return err
	}
	fmt.Println("Server version", v.Version)
	return nil
}
<|endoftext|>


<commit_before>package powermux

import (
	"bytes"
	"context"
	"net/http"
	"strings"
)

// ServeMux is the multiplexer for http requests
type ServeMux struct {
	baseRoute     *Route
	hostRoutes    map[string]*Route
	executionPool *executionPool
}

// ctxKey is the key type used for path parameters in the request context
type ctxKey string

var (
	routeKey = ctxKey("route_name")
	paramKey = ctxKey("params")
)

// PathParam gets named path parameters and their values from the request
//
// the path '/users/:name' given '/users/andrew' will have `PathParam(r, "name")` => `"andrew"`
// unset values return an empty stringRoutes
func PathParam(req *http.Request, name string) (value string) {
	value = req.Context().Value(paramKey).(map[string]string)[name]
	return
}

// PathParams returns the map of all path parameters and their values from the request.
//
// Altering the values of this map will not affect future calls to PathParam and PathParams.
func PathParams(req *http.Request) (params map[string]string) {
	params = make(map[string]string)
	for k, v := range req.Context().Value(paramKey).(map[string]string) {
		params[k] = v
	}
	return
}

// RequestPath returns the path definition that the router used to serve this request,
// without any parameter substitution.
func RequestPath(req *http.Request) (value string) {
	value, _ = req.Context().Value(routeKey).(string)
	return value
}

// NewServeMux creates a new multiplexer, and sets up a default not found handler
func NewServeMux() *ServeMux {
	s := &ServeMux{
		baseRoute:     newRoute(),
		hostRoutes:    make(map[string]*Route),
		executionPool: newExecutionPool(),
	}
	s.NotFound(http.NotFoundHandler())
	return s
}

func (s *ServeMux) getAll(r *http.Request) (http.Handler, []Middleware, string, map[string]string) {
	path := r.URL.EscapedPath()

	// Check for redirect
	if path != "/" && strings.HasSuffix(path, "/") {
		r.URL.Path = strings.TrimRight(path, "/")
		redirect := http.RedirectHandler(r.URL.RequestURI(), http.StatusPermanentRedirect)
		return redirect, make([]Middleware, 0), r.URL.EscapedPath(), nil
	}

	// Get a route execution from the pool
	ex := s.executionPool.Get()
	defer s.executionPool.Put(ex)

	// fill it
	if route, ok := s.hostRoutes[r.URL.Host]; ok {
		route.execute(ex, r.Method, r.URL.EscapedPath())
	} else {
		s.baseRoute.execute(ex, r.Method, r.URL.EscapedPath())
	}

	// fall back on not found handler if necessary
	if ex.handler == nil {
		ex.handler = ex.notFound
	}

	return ex.handler, ex.middleware, ex.pattern, ex.params
}

// ServeHTTP dispatches the request to the handler whose pattern most closely matches the request URL.
func (s *ServeMux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

	handler, middleware, pattern, params := s.getAll(req)

	// Save the route path
	ctx := context.WithValue(req.Context(), routeKey, pattern)

	// set all the path params
	ctx = context.WithValue(ctx, paramKey, params)

	// Save context into request
	req = req.WithContext(ctx)

	// Run a middleware/handler closure to nest all middleware
	f := getNextMiddleware(middleware, handler)
	f(rw, req)
}

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern it is overwritten.
func (s *ServeMux) Handle(path string, handler http.Handler) {
	s.Route(path).Any(handler)
}

// HandleHost registers the handler for the given pattern and host.
// If a handler already exists for pattern it is overwritten.
func (s *ServeMux) HandleHost(host, path string, handler http.Handler) {
	s.RouteHost(host, path).Any(handler)
}

// Middleware adds middleware for the given pattern.
func (s *ServeMux) Middleware(path string, middleware Middleware) {
	s.Route(path).Middleware(middleware)
}

// MiddlewareFunc registers a plain function as a middleware.
func (s *ServeMux) MiddlewareFunc(path string, m MiddlewareFunc) *Route {
	return s.Route(path).MiddlewareFunc(m)
}

// MiddlewareHost adds middleware for the given pattern.
func (s *ServeMux) MiddlewareHost(host, path string, middleware Middleware) {
	s.RouteHost(host, path).Middleware(middleware)
}

// HandleFunc registers the handler function for the given pattern.
func (s *ServeMux) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
	s.Handle(path, http.HandlerFunc(handler))
}

// Handler returns the handler to use for the given request, consulting r.Method, r.Host, and r.URL.Path.
// It always returns a non-nil handler. If the path is not in its canonical form, the handler will be an
// internally-generated handler that redirects to the canonical path.
//
// Handler also returns the registered pattern that matches the request or, in the case of internally-generated
// redirects, the pattern that will match after following the redirect.
//
// If there is no registered handler that applies to the request, Handler returns a “page not found” handler
// and an empty pattern.
func (s *ServeMux) Handler(r *http.Request) (http.Handler, string) {
	handler, _, pattern := s.HandlerAndMiddleware(r)
	return handler, pattern
}

// HandlerAndMiddleware returns the same as Handler, but with the addition of an array of middleware, in the order
// they would have been executed
func (s *ServeMux) HandlerAndMiddleware(r *http.Request) (http.Handler, []Middleware, string) {
	handler, middlewares, pattern, _ := s.getAll(r)
	return handler, middlewares, pattern
}

// Route returns the route from the root of the domain to the given pattern
func (s *ServeMux) Route(path string) *Route {
	return s.baseRoute.Route(path)
}

// RouteHost returns the route from the root of the domain to the given pattern on a specific domain
func (s *ServeMux) RouteHost(host, path string) *Route {
	r, ok := s.hostRoutes[host]
	if !ok {
		r = newRoute()
		s.hostRoutes[host] = r
	}
	return r.Route(path)
}

// NotFound sets the default not found handler for the server
func (s *ServeMux) NotFound(handler http.Handler) {
	s.baseRoute.NotFound(handler)
}

// String returns a list of all routes registered with this server
func (s *ServeMux) String() string {
	routes := make([]string, 0, 1)
	s.baseRoute.stringRoutes(&routes)

	buf := bytes.Buffer{}

	for _, route := range routes {
		buf.WriteString(route + "\n")
	}

	for host, baseRoute := range s.hostRoutes {
		routes = routes[0:0]
		baseRoute.stringRoutes(&routes)
		for _, route := range routes {
			buf.WriteString(host + route + "\n")
		}
	}

	return buf.String()
}
<commit_msg>Better pool handling<commit_after>package powermux

import (
	"bytes"
	"context"
	"net/http"
	"strings"
)

// ServeMux is the multiplexer for http requests
type ServeMux struct {
	baseRoute     *Route
	hostRoutes    map[string]*Route
	executionPool *executionPool
}

// ctxKey is the key type used for path parameters in the request context
type ctxKey string

var (
	routeKey = ctxKey("route_name")
	paramKey = ctxKey("params")
)

// PathParam gets named path parameters and their values from the request
//
// the path '/users/:name' given '/users/andrew' will have `PathParam(r, "name")` => `"andrew"`
// unset values return an empty stringRoutes
func PathParam(req *http.Request, name string) (value string) {
	value = req.Context().Value(paramKey).(map[string]string)[name]
	return
}

// PathParams returns the map of all path parameters and their values from the request.
//
// Altering the values of this map will not affect future calls to PathParam and PathParams.
func PathParams(req *http.Request) (params map[string]string) {
	params = make(map[string]string)
	for k, v := range req.Context().Value(paramKey).(map[string]string) {
		params[k] = v
	}
	return
}

// RequestPath returns the path definition that the router used to serve this request,
// without any parameter substitution.
func RequestPath(req *http.Request) (value string) {
	value, _ = req.Context().Value(routeKey).(string)
	return value
}

// NewServeMux creates a new multiplexer, and sets up a default not found handler
func NewServeMux() *ServeMux {
	s := &ServeMux{
		baseRoute:     newRoute(),
		hostRoutes:    make(map[string]*Route),
		executionPool: newExecutionPool(),
	}
	s.NotFound(http.NotFoundHandler())
	return s
}

func (s *ServeMux) getAll(r *http.Request, ex *routeExecution) {
	path := r.URL.EscapedPath()

	// Check for redirect
	if path != "/" && strings.HasSuffix(path, "/") {
		r.URL.Path = strings.TrimRight(path, "/")
		ex.handler = http.RedirectHandler(r.URL.RequestURI(), http.StatusPermanentRedirect)
		ex.pattern = r.URL.EscapedPath()
		return
	}

	// fill it
	if route, ok := s.hostRoutes[r.URL.Host]; ok {
		route.execute(ex, r.Method, r.URL.EscapedPath())
	} else {
		s.baseRoute.execute(ex, r.Method, r.URL.EscapedPath())
	}

	// fall back on not found handler if necessary
	if ex.handler == nil {
		ex.handler = ex.notFound
	}

	return
}

// ServeHTTP dispatches the request to the handler whose pattern most closely matches the request URL.
func (s *ServeMux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	// Get a route execution from the pool
	ex := s.executionPool.Get()
	defer s.executionPool.Put(ex)

	s.getAll(req, ex)

	// Save the route path
	ctx := context.WithValue(req.Context(), routeKey, ex.pattern)

	// set all the path params
	ctx = context.WithValue(ctx, paramKey, ex.params)

	// Save context into request
	req = req.WithContext(ctx)

	// Run a middleware/handler closure to nest all middleware
	f := getNextMiddleware(ex.middleware, ex.handler)
	f(rw, req)
}

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern it is overwritten.
func (s *ServeMux) Handle(path string, handler http.Handler) {
	s.Route(path).Any(handler)
}

// HandleHost registers the handler for the given pattern and host.
// If a handler already exists for pattern it is overwritten.
func (s *ServeMux) HandleHost(host, path string, handler http.Handler) {
	s.RouteHost(host, path).Any(handler)
}

// Middleware adds middleware for the given pattern.
func (s *ServeMux) Middleware(path string, middleware Middleware) {
	s.Route(path).Middleware(middleware)
}

// MiddlewareFunc registers a plain function as a middleware.
func (s *ServeMux) MiddlewareFunc(path string, m MiddlewareFunc) *Route {
	return s.Route(path).MiddlewareFunc(m)
}

// MiddlewareHost adds middleware for the given pattern.
func (s *ServeMux) MiddlewareHost(host, path string, middleware Middleware) {
	s.RouteHost(host, path).Middleware(middleware)
}

// HandleFunc registers the handler function for the given pattern.
func (s *ServeMux) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
	s.Handle(path, http.HandlerFunc(handler))
}

// Handler returns the handler to use for the given request, consulting r.Method, r.Host, and r.URL.Path.
// It always returns a non-nil handler. If the path is not in its canonical form, the handler will be an
// internally-generated handler that redirects to the canonical path.
//
// Handler also returns the registered pattern that matches the request or, in the case of internally-generated
// redirects, the pattern that will match after following the redirect.
//
// If there is no registered handler that applies to the request, Handler returns a “page not found” handler
// and an empty pattern.
func (s *ServeMux) Handler(r *http.Request) (http.Handler, string) {
	handler, _, pattern := s.HandlerAndMiddleware(r)
	return handler, pattern
}

// HandlerAndMiddleware returns the same as Handler, but with the addition of an array of middleware, in the order
// they would have been executed
func (s *ServeMux) HandlerAndMiddleware(r *http.Request) (http.Handler, []Middleware, string) {
	// create a new execution so fields will live outside of this function
	ex := &routeExecution{
		middleware: make([]Middleware, 0),
		params:     make(map[string]string),
	}
	s.getAll(r, ex)
	return ex.handler, ex.middleware, ex.pattern
}

// Route returns the route from the root of the domain to the given pattern
func (s *ServeMux) Route(path string) *Route {
	return s.baseRoute.Route(path)
}

// RouteHost returns the route from the root of the domain to the given pattern on a specific domain
func (s *ServeMux) RouteHost(host, path string) *Route {
	r, ok := s.hostRoutes[host]
	if !ok {
		r = newRoute()
		s.hostRoutes[host] = r
	}
	return r.Route(path)
}

// NotFound sets the default not found handler for the server
func (s *ServeMux) NotFound(handler http.Handler) {
	s.baseRoute.NotFound(handler)
}

// String returns a list of all routes registered with this server
func (s *ServeMux) String() string {
	routes := make([]string, 0, 1)
	s.baseRoute.stringRoutes(&routes)

	buf := bytes.Buffer{}

	for _, route := range routes {
		buf.WriteString(route + "\n")
	}

	for host, baseRoute := range s.hostRoutes {
		routes = routes[0:0]
		baseRoute.stringRoutes(&routes)
		for _, route := range routes {
			buf.WriteString(host + route + "\n")
		}
	}

	return buf.String()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"

	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/autopilot"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

// chanController is an implementation of the autopilot.ChannelController
// interface that's backed by a running lnd instance.
type chanController struct {
	server *server
}

// OpenChannel opens a channel to a target peer, with a capacity of the
// specified amount. This function should un-block immediately after the
// funding transaction that marks the channel open has been broadcast.
func (c *chanController) OpenChannel(target *btcec.PublicKey,
	amt btcutil.Amount, addrs []net.Addr) error {

	// We can't establish a channel if no addresses were provided for the
	// peer.
	if len(addrs) == 0 {
		return fmt.Errorf("Unable to create channel w/o an active " +
			"address")
	}

	// First, we'll check if we're already connected to the target peer. If
	// not, then we'll need to establish a connection.
	if _, err := c.server.FindPeer(target); err != nil {
		// TODO(roasbeef): try teach addr

		atplLog.Tracef("Connecting to %x to auto-create channel: ",
			target.SerializeCompressed())

		lnAddr := &lnwire.NetAddress{
			IdentityKey: target,
			ChainNet:    activeNetParams.Net,
		}

		// We'll attempt to successively connect to each of the
		// advertised IP addresses until we've either exhausted the
		// advertised IP addresses, or have made a connection.
		var connected bool
		for _, addr := range addrs {
			// If the address doesn't already have a port, then
			// we'll assume the current default port.
			tcpAddr, ok := addr.(*net.TCPAddr)
			if !ok {
				return fmt.Errorf("TCP address required instead "+
					"have %T", addr)
			}
			if tcpAddr.Port == 0 {
				tcpAddr.Port = defaultPeerPort
			}

			lnAddr.Address = tcpAddr

			// TODO(roasbeef): make perm connection in server after
			// chan open?
			err := c.server.ConnectToPeer(lnAddr, false)
			if err != nil {
				// If we weren't able to connect to the peer,
				// then we'll move onto the next.
				continue
			}

			connected = true
			break
		}

		// If we weren't able to establish a connection at all, then
		// we'll error out.
		if !connected {
			return fmt.Errorf("Unable to connect to %x",
				target.SerializeCompressed())
		}
	}

	// With the connection established, we'll now establish our connection
	// to the target peer, waiting for the first update before we exit.
	feePerWeight, err := c.server.cc.feeEstimator.EstimateFeePerWeight(3)
	if err != nil {
		return err
	}

	// TODO(halseth): make configurable?
	minHtlc := lnwire.NewMSatFromSatoshis(1)

	updateStream, errChan := c.server.OpenChannel(target, amt, 0,
		minHtlc, feePerWeight, false)

	select {
	case err := <-errChan:
		return err
	case <-updateStream:
		return nil
	case <-c.server.quit:
		return nil
	}
}

func (c *chanController) CloseChannel(chanPoint *wire.OutPoint) error {
	return nil
}
func (c *chanController) SpliceIn(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}
func (c *chanController) SpliceOut(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}

// A compile time assertion to ensure chanController meets the
// autopilot.ChannelController interface.
var _ autopilot.ChannelController = (*chanController)(nil)

// initAutoPilot initializes a new autopilot.Agent instance based on the passed
// configuration struct. All interfaces needed to drive the pilot will be
// registered and launched.
func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error) {
	atplLog.Infof("Instantiating autopilot with cfg: %v", spew.Sdump(cfg))

	// First, we'll create the preferential attachment heuristic,
	// initialized with the passed auto pilot configuration parameters.
	//
	// TODO(roasbeef): switch here to dispatch specified heuristic
	minChanSize := svr.cc.wallet.Cfg.DefaultConstraints.DustLimit * 5
	prefAttachment := autopilot.NewConstrainedPrefAttachment(
		minChanSize, maxFundingAmount,
		uint16(cfg.MaxChannels), cfg.Allocation,
	)

	// With the heuristic itself created, we can now populate the remainder
	// of the items that the autopilot agent needs to perform its duties.
	self := svr.identityPriv.PubKey()
	pilotCfg := autopilot.Config{
		Self:           self,
		Heuristic:      prefAttachment,
		ChanController: &chanController{svr},
		WalletBalance: func() (btcutil.Amount, error) {
			return svr.cc.wallet.ConfirmedBalance(1, true)
		},
		Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()),
	}

	// Next, we'll fetch the current state of open channels from the
	// database to use as initial state for the auto-pilot agent.
	activeChannels, err := svr.chanDB.FetchAllChannels()
	if err != nil {
		return nil, err
	}
	initialChanState := make([]autopilot.Channel, len(activeChannels))
	for i, channel := range activeChannels {
		initialChanState[i] = autopilot.Channel{
			ChanID:   channel.ShortChanID,
			Capacity: channel.Capacity,
			Node:     autopilot.NewNodeID(channel.IdentityPub),
		}
	}

	// Now that we have all the initial dependencies, we can create the
	// auto-pilot instance itself.
	pilot, err := autopilot.New(pilotCfg, initialChanState)
	if err != nil {
		return nil, err
	}

	// Finally, we'll need to subscribe to two things: incoming
	// transactions that modify the wallet's balance, and also any graph
	// topology updates.
	txnSubscription, err := svr.cc.wallet.SubscribeTransactions()
	if err != nil {
		return nil, err
	}
	graphSubscription, err := svr.chanRouter.SubscribeTopology()
	if err != nil {
		return nil, err
	}

	// We'll launch a goroutine to provide the agent with notifications
	// whenever the balance of the wallet changes.
	svr.wg.Add(2)
	go func() {
		defer txnSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case txnUpdate := <-txnSubscription.ConfirmedTransactions():
				pilot.OnBalanceChange(txnUpdate.Value)
			case <-svr.quit:
				return
			}
		}

	}()
	go func() {
		defer svr.wg.Done()

		for {
			select {
			// We won't act upon new unconfirmed transaction, as
			// we'll only use confirmed outputs when funding.
			// However, we will still drain this request in order
			// to avoid goroutine leaks, and ensure we promptly
			// read from the channel if available.
			case <-txnSubscription.UnconfirmedTransactions():
			case <-svr.quit:
				return
			}
		}

	}()

	// We'll also launch a goroutine to provide the agent with
	// notifications for when the graph topology controlled by the node
	// changes.
	svr.wg.Add(1)
	go func() {
		defer graphSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case topChange, ok := <-graphSubscription.TopologyChanges:
				// If the router is shutting down, then we will
				// as well.
				if !ok {
					return
				}

				for _, edgeUpdate := range topChange.ChannelEdgeUpdates {
					// If this isn't an advertisement by
					// the backing lnd node, then we'll
					// continue as we only want to add
					// channels that we've created
					// ourselves.
					if !edgeUpdate.AdvertisingNode.IsEqual(self) {
						continue
					}

					// If this is indeed a channel we
					// opened, then we'll convert it to the
					// autopilot.Channel format, and notify
					// the pilot of the new channel.
					chanNode := autopilot.NewNodeID(
						edgeUpdate.ConnectingNode,
					)
					chanID := lnwire.NewShortChanIDFromInt(
						edgeUpdate.ChanID,
					)
					edge := autopilot.Channel{
						ChanID:   chanID,
						Capacity: edgeUpdate.Capacity,
						Node:     chanNode,
					}
					pilot.OnChannelOpen(edge)
				}

				// For each closed closed channel, we'll obtain
				// the chanID of the closed channel and send it
				// to the pilot.
				for _, chanClose := range topChange.ClosedChannels {
					chanID := lnwire.NewShortChanIDFromInt(
						chanClose.ChanID,
					)

					pilot.OnChannelClose(chanID)
				}

			case <-svr.quit:
				return
			}
		}
	}()

	return pilot, nil
}
<commit_msg>pilot: express fee rates using types, use EstimateFeePerVSize<commit_after>package main

import (
	"fmt"
	"net"

	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/autopilot"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

// chanController is an implementation of the autopilot.ChannelController
// interface that's backed by a running lnd instance.
type chanController struct {
	server *server
}

// OpenChannel opens a channel to a target peer, with a capacity of the
// specified amount. This function should un-block immediately after the
// funding transaction that marks the channel open has been broadcast.
func (c *chanController) OpenChannel(target *btcec.PublicKey,
	amt btcutil.Amount, addrs []net.Addr) error {

	// We can't establish a channel if no addresses were provided for the
	// peer.
	if len(addrs) == 0 {
		return fmt.Errorf("Unable to create channel w/o an active " +
			"address")
	}

	// First, we'll check if we're already connected to the target peer. If
	// not, then we'll need to establish a connection.
	if _, err := c.server.FindPeer(target); err != nil {
		// TODO(roasbeef): try teach addr

		atplLog.Tracef("Connecting to %x to auto-create channel: ",
			target.SerializeCompressed())

		lnAddr := &lnwire.NetAddress{
			IdentityKey: target,
			ChainNet:    activeNetParams.Net,
		}

		// We'll attempt to successively connect to each of the
		// advertised IP addresses until we've either exhausted the
		// advertised IP addresses, or have made a connection.
		var connected bool
		for _, addr := range addrs {
			// If the address doesn't already have a port, then
			// we'll assume the current default port.
			tcpAddr, ok := addr.(*net.TCPAddr)
			if !ok {
				return fmt.Errorf("TCP address required instead "+
					"have %T", addr)
			}
			if tcpAddr.Port == 0 {
				tcpAddr.Port = defaultPeerPort
			}

			lnAddr.Address = tcpAddr

			// TODO(roasbeef): make perm connection in server after
			// chan open?
			err := c.server.ConnectToPeer(lnAddr, false)
			if err != nil {
				// If we weren't able to connect to the peer,
				// then we'll move onto the next.
				continue
			}

			connected = true
			break
		}

		// If we weren't able to establish a connection at all, then
		// we'll error out.
		if !connected {
			return fmt.Errorf("Unable to connect to %x",
				target.SerializeCompressed())
		}
	}

	// With the connection established, we'll now establish our connection
	// to the target peer, waiting for the first update before we exit.
	feePerVSize, err := c.server.cc.feeEstimator.EstimateFeePerVSize(3)
	if err != nil {
		return err
	}

	// TODO(halseth): make configurable?
	minHtlc := lnwire.NewMSatFromSatoshis(1)

	updateStream, errChan := c.server.OpenChannel(target, amt, 0,
		minHtlc, feePerVSize, false)

	select {
	case err := <-errChan:
		return err
	case <-updateStream:
		return nil
	case <-c.server.quit:
		return nil
	}
}

func (c *chanController) CloseChannel(chanPoint *wire.OutPoint) error {
	return nil
}
func (c *chanController) SpliceIn(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}
func (c *chanController) SpliceOut(chanPoint *wire.OutPoint,
	amt btcutil.Amount) (*autopilot.Channel, error) {
	return nil, nil
}

// A compile time assertion to ensure chanController meets the
// autopilot.ChannelController interface.
var _ autopilot.ChannelController = (*chanController)(nil)

// initAutoPilot initializes a new autopilot.Agent instance based on the passed
// configuration struct. All interfaces needed to drive the pilot will be
// registered and launched.
func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error) {
	atplLog.Infof("Instantiating autopilot with cfg: %v", spew.Sdump(cfg))

	// First, we'll create the preferential attachment heuristic,
	// initialized with the passed auto pilot configuration parameters.
	//
	// TODO(roasbeef): switch here to dispatch specified heuristic
	minChanSize := svr.cc.wallet.Cfg.DefaultConstraints.DustLimit * 5
	prefAttachment := autopilot.NewConstrainedPrefAttachment(
		minChanSize, maxFundingAmount,
		uint16(cfg.MaxChannels), cfg.Allocation,
	)

	// With the heuristic itself created, we can now populate the remainder
	// of the items that the autopilot agent needs to perform its duties.
	self := svr.identityPriv.PubKey()
	pilotCfg := autopilot.Config{
		Self:           self,
		Heuristic:      prefAttachment,
		ChanController: &chanController{svr},
		WalletBalance: func() (btcutil.Amount, error) {
			return svr.cc.wallet.ConfirmedBalance(1, true)
		},
		Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()),
	}

	// Next, we'll fetch the current state of open channels from the
	// database to use as initial state for the auto-pilot agent.
	activeChannels, err := svr.chanDB.FetchAllChannels()
	if err != nil {
		return nil, err
	}
	initialChanState := make([]autopilot.Channel, len(activeChannels))
	for i, channel := range activeChannels {
		initialChanState[i] = autopilot.Channel{
			ChanID:   channel.ShortChanID,
			Capacity: channel.Capacity,
			Node:     autopilot.NewNodeID(channel.IdentityPub),
		}
	}

	// Now that we have all the initial dependencies, we can create the
	// auto-pilot instance itself.
	pilot, err := autopilot.New(pilotCfg, initialChanState)
	if err != nil {
		return nil, err
	}

	// Finally, we'll need to subscribe to two things: incoming
	// transactions that modify the wallet's balance, and also any graph
	// topology updates.
	txnSubscription, err := svr.cc.wallet.SubscribeTransactions()
	if err != nil {
		return nil, err
	}
	graphSubscription, err := svr.chanRouter.SubscribeTopology()
	if err != nil {
		return nil, err
	}

	// We'll launch a goroutine to provide the agent with notifications
	// whenever the balance of the wallet changes.
	svr.wg.Add(2)
	go func() {
		defer txnSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case txnUpdate := <-txnSubscription.ConfirmedTransactions():
				pilot.OnBalanceChange(txnUpdate.Value)
			case <-svr.quit:
				return
			}
		}

	}()
	go func() {
		defer svr.wg.Done()

		for {
			select {
			// We won't act upon new unconfirmed transaction, as
			// we'll only use confirmed outputs when funding.
			// However, we will still drain this request in order
			// to avoid goroutine leaks, and ensure we promptly
			// read from the channel if available.
			case <-txnSubscription.UnconfirmedTransactions():
			case <-svr.quit:
				return
			}
		}

	}()

	// We'll also launch a goroutine to provide the agent with
	// notifications for when the graph topology controlled by the node
	// changes.
	svr.wg.Add(1)
	go func() {
		defer graphSubscription.Cancel()
		defer svr.wg.Done()

		for {
			select {
			case topChange, ok := <-graphSubscription.TopologyChanges:
				// If the router is shutting down, then we will
				// as well.
				if !ok {
					return
				}

				for _, edgeUpdate := range topChange.ChannelEdgeUpdates {
					// If this isn't an advertisement by
					// the backing lnd node, then we'll
					// continue as we only want to add
					// channels that we've created
					// ourselves.
					if !edgeUpdate.AdvertisingNode.IsEqual(self) {
						continue
					}

					// If this is indeed a channel we
					// opened, then we'll convert it to the
					// autopilot.Channel format, and notify
					// the pilot of the new channel.
					chanNode := autopilot.NewNodeID(
						edgeUpdate.ConnectingNode,
					)
					chanID := lnwire.NewShortChanIDFromInt(
						edgeUpdate.ChanID,
					)
					edge := autopilot.Channel{
						ChanID:   chanID,
						Capacity: edgeUpdate.Capacity,
						Node:     chanNode,
					}
					pilot.OnChannelOpen(edge)
				}

				// For each closed closed channel, we'll obtain
				// the chanID of the closed channel and send it
				// to the pilot.
				for _, chanClose := range topChange.ClosedChannels {
					chanID := lnwire.NewShortChanIDFromInt(
						chanClose.ChanID,
					)

					pilot.OnChannelClose(chanID)
				}

			case <-svr.quit:
				return
			}
		}
	}()

	return pilot, nil
}
<|endoftext|>


<commit_before>/*
 * Mini Object Storage, (C) 2014,2015 Minio, Inc.
 *
 * 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 (
	"fmt"
	"log"
	"path"
	"sort"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/minio-io/mc/pkg/s3"
)

const (
	recvFormat  = "2006-01-02T15:04:05.000Z"
	printFormat = "2006-01-02 15:04:05"
)

func parseTime(t string) string {
	ti, _ := time.Parse(recvFormat, t)
	return ti.Format(printFormat)
}

func parseLastModified(t string) string {
	ti, _ := time.Parse(time.RFC1123, t)
	return ti.Format(printFormat)
}

func printBuckets(v []*s3.Bucket) {
	for _, b := range v {
		fmt.Printf("%s %s\n", parseTime(b.CreationDate), b.Name)
	}
}

func printObjects(v []*s3.Item) {
	sort.Sort(s3.BySize(v))
	for _, b := range v {
		fmt.Printf("%s   %d %s\n", parseTime(b.LastModified), b.Size, b.Key)
	}
}

func printObject(v int64, date, key string) {
	fmt.Printf("%s   %d %s\n", parseLastModified(date), v, key)
}

func getBucketAndObject(p string) (bucket, object string) {
	parts := strings.Split(path.Clean(p), "/")
	switch true {
	case len(parts) == 2:
		bucket = parts[0]
		object = parts[1]
	case len(parts) < 2:
		bucket = parts[0]
		object = ""
	case len(parts) > 2:
		bucket = parts[0]
		for _, v := range parts[1 : len(parts)-1] {
			object = object + "/" + v
		}
	}
	return
}

func doFsList(c *cli.Context) {
	var err error
	var auth *s3.Auth
	var items []*s3.Item
	auth, err = getAWSEnvironment()
	if err != nil {
		log.Fatal(err)
	}

	var buckets []*s3.Bucket
	s3c := s3.NewS3Client(auth)
	switch len(c.Args()) {
	case 1:
		input := c.Args().Get(0)
		if path.IsAbs(input) {
			log.Fatal("Invalid bucket style")
		}
		bucket, object := getBucketAndObject(input)
		if object == "" {
			items, err = s3c.GetBucket(bucket, "", s3.MAX_OBJECT_LIST)
			if err != nil {
				log.Fatal(err)
			}
			printObjects(items)
		} else {
			var date string
			var size int64
			size, date, err = s3c.Stat(object, bucket)
			if err != nil {
				log.Fatal(err)
			}
			printObject(size, date, object)
		}
	default:
		buckets, err = s3c.Buckets()
		printBuckets(buckets)
	}
}
<commit_msg>Add missing error check after Buckets() call<commit_after>/*
 * Mini Object Storage, (C) 2014,2015 Minio, Inc.
 *
 * 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 (
	"fmt"
	"log"
	"path"
	"sort"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/minio-io/mc/pkg/s3"
)

const (
	recvFormat  = "2006-01-02T15:04:05.000Z"
	printFormat = "2006-01-02 15:04:05"
)

func parseTime(t string) string {
	ti, _ := time.Parse(recvFormat, t)
	return ti.Format(printFormat)
}

func parseLastModified(t string) string {
	ti, _ := time.Parse(time.RFC1123, t)
	return ti.Format(printFormat)
}

func printBuckets(v []*s3.Bucket) {
	for _, b := range v {
		fmt.Printf("%s %s\n", parseTime(b.CreationDate), b.Name)
	}
}

func printObjects(v []*s3.Item) {
	sort.Sort(s3.BySize(v))
	for _, b := range v {
		fmt.Printf("%s   %d %s\n", parseTime(b.LastModified), b.Size, b.Key)
	}
}

func printObject(v int64, date, key string) {
	fmt.Printf("%s   %d %s\n", parseLastModified(date), v, key)
}

func getBucketAndObject(p string) (bucket, object string) {
	parts := strings.Split(path.Clean(p), "/")
	switch true {
	case len(parts) == 2:
		bucket = parts[0]
		object = parts[1]
	case len(parts) < 2:
		bucket = parts[0]
		object = ""
	case len(parts) > 2:
		bucket = parts[0]
		for _, v := range parts[1 : len(parts)-1] {
			object = object + "/" + v
		}
	}
	return
}

func doFsList(c *cli.Context) {
	var err error
	var auth *s3.Auth
	var items []*s3.Item
	auth, err = getAWSEnvironment()
	if err != nil {
		log.Fatal(err)
	}

	var buckets []*s3.Bucket
	s3c := s3.NewS3Client(auth)
	switch len(c.Args()) {
	case 1:
		input := c.Args().Get(0)
		if path.IsAbs(input) {
			log.Fatal("Invalid bucket style")
		}
		bucket, object := getBucketAndObject(input)
		if object == "" {
			items, err = s3c.GetBucket(bucket, "", s3.MAX_OBJECT_LIST)
			if err != nil {
				log.Fatal(err)
			}
			printObjects(items)
		} else {
			var date string
			var size int64
			size, date, err = s3c.Stat(object, bucket)
			if err != nil {
				log.Fatal(err)
			}
			printObject(size, date, object)
		}
	default:
		buckets, err = s3c.Buckets()
		if err != nil {
			log.Fatal(err)
		}
		printBuckets(buckets)
	}
}
<|endoftext|>