<commit_before>/*
Copyright 2015-2016, RadiantBlue Technologies, 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 utils provides various utilities that are used throughout the project.

Provide functions to return canned responses: StatusOK, StatusBadRequest, and StatusInternalServerError.
*/
package utils

import (
	"net/http"
	"os"

	"github.com/venicegeo/pzsvc-sdk-go/job"
	"github.com/venicegeo/pzsvc-sdk-go/s3"
)

// FunctionFunc defines the signature of our function creator.
type FunctionFunc func(
	http.ResponseWriter,
	*http.Request,
	*job.OutputMsg,
	job.InputMsg,
)

// MakeFunction wraps the individual PDAL functions.
// Parse the input and output filenames, creating files as needed. Download the
// input data and upload the output data.
func MakeFunction(
	fn func(
		http.ResponseWriter,
		*http.Request,
		*job.OutputMsg,
		job.InputMsg,
		string,
		string,
	),
) FunctionFunc {
	return func(
		w http.ResponseWriter,
		r *http.Request,
		res *job.OutputMsg,
		msg job.InputMsg,
	) {
		var inputName, outputName string
		var fileIn, fileOut *os.File

		// Split the source S3 key string, interpreting the last element as the
		// input filename. Create the input file, throwing 500 on error.
		inputName = s3.ParseFilenameFromKey(msg.Source.Key)
		fileIn, err := os.Create(inputName)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer fileIn.Close()

		// If provided, split the destination S3 key string, interpreting the last
		// element as the output filename. Create the output file, throwing 500 on
		// error.
		if len(msg.Destination.Key) > 0 {
			outputName = s3.ParseFilenameFromKey(msg.Destination.Key)
		}

		// Download the source data from S3, throwing 500 on error.
		err = s3.Download(fileIn, msg.Source.Bucket, msg.Source.Key)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// Run the PDAL function.
		fn(w, r, res, msg, inputName, outputName)

		// If an output has been created, upload the destination data to S3,
		// throwing 500 on error.
		if len(msg.Destination.Key) > 0 {
			fileOut, err = os.Open(outputName)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			defer fileOut.Close()
			err = s3.Upload(fileOut, msg.Destination.Bucket, msg.Destination.Key)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	}
}
<commit_msg>Print output file info<commit_after>/*
Copyright 2015-2016, RadiantBlue Technologies, 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 utils provides various utilities that are used throughout the project.

Provide functions to return canned responses: StatusOK, StatusBadRequest, and StatusInternalServerError.
*/
package utils

import (
	"fmt"
	"net/http"
	"os"

	"github.com/venicegeo/pzsvc-sdk-go/job"
	"github.com/venicegeo/pzsvc-sdk-go/s3"
)

// FunctionFunc defines the signature of our function creator.
type FunctionFunc func(
	http.ResponseWriter,
	*http.Request,
	*job.OutputMsg,
	job.InputMsg,
)

// MakeFunction wraps the individual PDAL functions.
// Parse the input and output filenames, creating files as needed. Download the
// input data and upload the output data.
func MakeFunction(
	fn func(
		http.ResponseWriter,
		*http.Request,
		*job.OutputMsg,
		job.InputMsg,
		string,
		string,
	),
) FunctionFunc {
	return func(
		w http.ResponseWriter,
		r *http.Request,
		res *job.OutputMsg,
		msg job.InputMsg,
	) {
		var inputName, outputName string
		var fileIn, fileOut *os.File

		// Split the source S3 key string, interpreting the last element as the
		// input filename. Create the input file, throwing 500 on error.
		inputName = s3.ParseFilenameFromKey(msg.Source.Key)
		fileIn, err := os.Create(inputName)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer fileIn.Close()

		// If provided, split the destination S3 key string, interpreting the last
		// element as the output filename. Create the output file, throwing 500 on
		// error.
		if len(msg.Destination.Key) > 0 {
			outputName = s3.ParseFilenameFromKey(msg.Destination.Key)
		}

		// Download the source data from S3, throwing 500 on error.
		err = s3.Download(fileIn, msg.Source.Bucket, msg.Source.Key)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		info, err := os.Stat(outputName)
		if err != nil {
			fmt.Println(info)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// Run the PDAL function.
		fn(w, r, res, msg, inputName, outputName)

		// If an output has been created, upload the destination data to S3,
		// throwing 500 on error.
		if len(msg.Destination.Key) > 0 {
			fileOut, err = os.Open(outputName)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			defer fileOut.Close()
			err = s3.Upload(fileOut, msg.Destination.Bucket, msg.Destination.Key)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package vault

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"hash"
	"io"
	"io/ioutil"

	"github.com/kopia/kopia/blob"
	"github.com/kopia/kopia/repo"

	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/pbkdf2"
)

const (
	formatBlock           = "format"
	checksumBlock         = "checksum"
	repositoryConfigBlock = "repo"

	minPasswordLength = 12
	minKeyLength      = 16
)

var (
	purposeAESKey         = []byte("AES")
	purposeChecksumSecret = []byte("CHECKSUM")
)

type Vault struct {
	Storage   blob.Storage
	MasterKey []byte
	Format    Format
}

func (v *Vault) writeEncryptedBlock(name string, content []byte) error {
	blk, err := v.newCipher()
	if err != nil {
		return err
	}

	hash, err := v.newChecksum()
	if err != nil {
		return err
	}

	ivLength := blk.BlockSize()
	ivPlusContentLength := ivLength + len(content)
	cipherText := make([]byte, ivPlusContentLength+hash.Size())

	// Store IV at the beginning of ciphertext.
	iv := cipherText[0:ivLength]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return err
	}

	ctr := cipher.NewCTR(blk, iv)
	ctr.XORKeyStream(cipherText[ivLength:], content)
	hash.Write(cipherText[0:ivPlusContentLength])
	copy(cipherText[ivPlusContentLength:], hash.Sum(nil))

	return v.Storage.PutBlock(name, ioutil.NopCloser(bytes.NewBuffer(cipherText)), blob.PutOptions{
		Overwrite: true,
	})
}

func (v *Vault) readEncryptedBlock(name string) ([]byte, error) {
	cipherText, err := v.Storage.GetBlock(name)
	if err != nil {
		return nil, err
	}

	hash, err := v.newChecksum()
	if err != nil {
		return nil, err
	}

	p := len(cipherText) - hash.Size()
	hash.Write(cipherText[0:p])
	expectedChecksum := hash.Sum(nil)
	actualChecksum := cipherText[p:]
	if !hmac.Equal(expectedChecksum, actualChecksum) {
		return nil, fmt.Errorf("cannot read encrypted block: incorrect checksum")
	}

	blk, err := v.newCipher()
	if err != nil {
		return nil, err
	}

	ivLength := blk.BlockSize()

	plainText := make([]byte, len(cipherText)-ivLength-hash.Size())
	iv := cipherText[0:blk.BlockSize()]

	ctr := cipher.NewCTR(blk, iv)
	ctr.XORKeyStream(plainText, cipherText[ivLength:len(cipherText)-hash.Size()])
	return plainText, nil
}

func (v *Vault) newChecksum() (hash.Hash, error) {
	switch v.Format.Checksum {
	case "hmac-sha-256":
		key := make([]byte, 32)
		v.deriveKey(purposeChecksumSecret, key)
		return hmac.New(sha256.New, key), nil

	default:
		return nil, fmt.Errorf("unsupported checksum format: %v", v.Format.Checksum)
	}

}

func (v *Vault) newCipher() (cipher.Block, error) {
	switch v.Format.Encryption {
	case "aes-256":
		k := make([]byte, 32)
		v.deriveKey(purposeAESKey, k)
		return aes.NewCipher(k)
	default:
		return nil, fmt.Errorf("unsupported encryption format: %v", v.Format.Encryption)
	}

}

func (v *Vault) deriveKey(purpose []byte, key []byte) error {
	k := hkdf.New(sha256.New, v.MasterKey, v.Format.UniqueID, purpose)
	_, err := io.ReadFull(k, key)
	return err
}

func (v *Vault) SetRepository(rc RepositoryConfig) error {
	b, err := json.Marshal(&rc)
	if err != nil {
		return err
	}

	return v.writeEncryptedBlock(repositoryConfigBlock, b)
}

func (v *Vault) RepositoryConfig() (*RepositoryConfig, error) {
	var rc RepositoryConfig

	b, err := v.readEncryptedBlock(repositoryConfigBlock)
	if err != nil {
		return nil, fmt.Errorf("unable to read repository: %v", err)
	}

	err = json.Unmarshal(b, &rc)
	if err != nil {
		return nil, err
	}

	return &rc, nil
}

func (v *Vault) OpenRepository() (repo.Repository, error) {
	rc, err := v.RepositoryConfig()
	if err != nil {
		return nil, err
	}

	storage, err := blob.NewStorage(rc.Storage)
	if err != nil {
		return nil, fmt.Errorf("unable to open repository: %v", err)
	}

	return repo.NewRepository(storage, rc.Format)
}

func (v *Vault) Get(id string, content interface{}) error {
	j, err := v.Storage.GetBlock(id)
	if err != nil {
		return nil
	}

	return json.Unmarshal(j, content)
}

func (v *Vault) Put(id string, content interface{}) error {
	j, err := json.Marshal(content)
	if err != nil {
		return err
	}
	return v.Storage.PutBlock(id, ioutil.NopCloser(bytes.NewBuffer(j)), blob.PutOptions{Overwrite: true})
}

func (v *Vault) List(prefix string) ([]string, error) {
	var result []string

	for b := range v.Storage.ListBlocks(prefix) {
		if b.Error != nil {
			return result, b.Error
		}
		result = append(result, b.BlockID)
	}
	return result, nil
}

func CreateWithPassword(storage blob.Storage, format *Format, password string) (*Vault, error) {
	if err := format.ensureUniqueID(); err != nil {
		return nil, err
	}

	if len(password) < minPasswordLength {
		return nil, fmt.Errorf("password too short, must be at least %v characters, got %v", minPasswordLength, len(password))
	}
	masterKey := pbkdf2.Key([]byte(password), format.UniqueID, pbkdf2Rounds, masterKeySize, sha256.New)
	return CreateWithKey(storage, format, masterKey)
}

func CreateWithKey(storage blob.Storage, format *Format, masterKey []byte) (*Vault, error) {
	if len(masterKey) < minKeyLength {
		return nil, fmt.Errorf("key too short, must be at least %v bytes, got %v", minKeyLength, len(masterKey))
	}

	v := Vault{
		Storage:   storage,
		MasterKey: masterKey,
		Format:    *format,
	}
	v.Format.Version = "1"
	if err := v.Format.ensureUniqueID(); err != nil {
		return nil, err
	}

	formatBytes, err := json.Marshal(&v.Format)
	if err != nil {
		return nil, err
	}

	storage.PutBlock(formatBlock, ioutil.NopCloser(bytes.NewBuffer(formatBytes)), blob.PutOptions{
		Overwrite: true,
	})

	vv := make([]byte, 512)
	if _, err := io.ReadFull(rand.Reader, vv); err != nil {
		return nil, err
	}

	err = v.writeEncryptedBlock(checksumBlock, vv)
	if err != nil {
		return nil, err
	}

	return OpenWithKey(storage, masterKey)
}

func OpenWithPassword(storage blob.Storage, password string) (*Vault, error) {
	v := Vault{
		Storage: storage,
	}

	f, err := storage.GetBlock(formatBlock)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(f, &v.Format)
	if err != nil {
		return nil, err
	}

	v.MasterKey = pbkdf2.Key([]byte(password), v.Format.UniqueID, pbkdf2Rounds, masterKeySize, sha256.New)

	if _, err := v.readEncryptedBlock(checksumBlock); err != nil {
		return nil, err
	}

	return &v, nil
}

func OpenWithKey(storage blob.Storage, masterKey []byte) (*Vault, error) {
	v := Vault{
		Storage:   storage,
		MasterKey: masterKey,
	}

	f, err := storage.GetBlock(formatBlock)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(f, &v.Format)
	if err != nil {
		return nil, err
	}

	if _, err := v.readEncryptedBlock(checksumBlock); err != nil {
		return nil, err
	}

	return &v, nil
}
<commit_msg>make vault encrypted again<commit_after>package vault

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"hash"
	"io"
	"io/ioutil"

	"github.com/kopia/kopia/blob"
	"github.com/kopia/kopia/repo"

	"golang.org/x/crypto/hkdf"
	"golang.org/x/crypto/pbkdf2"
)

const (
	formatBlock           = "format"
	checksumBlock         = "checksum"
	repositoryConfigBlock = "repo"

	minPasswordLength = 12
	minKeyLength      = 16
)

var (
	purposeAESKey         = []byte("AES")
	purposeChecksumSecret = []byte("CHECKSUM")
)

type Vault struct {
	Storage   blob.Storage
	MasterKey []byte
	Format    Format
}

func (v *Vault) writeEncryptedBlock(name string, content []byte) error {
	blk, err := v.newCipher()
	if err != nil {
		return err
	}

	hash, err := v.newChecksum()
	if err != nil {
		return err
	}

	ivLength := blk.BlockSize()
	ivPlusContentLength := ivLength + len(content)
	cipherText := make([]byte, ivPlusContentLength+hash.Size())

	// Store IV at the beginning of ciphertext.
	iv := cipherText[0:ivLength]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return err
	}

	ctr := cipher.NewCTR(blk, iv)
	ctr.XORKeyStream(cipherText[ivLength:], content)
	hash.Write(cipherText[0:ivPlusContentLength])
	copy(cipherText[ivPlusContentLength:], hash.Sum(nil))

	return v.Storage.PutBlock(name, ioutil.NopCloser(bytes.NewBuffer(cipherText)), blob.PutOptions{
		Overwrite: true,
	})
}

func (v *Vault) readEncryptedBlock(name string) ([]byte, error) {
	cipherText, err := v.Storage.GetBlock(name)
	if err != nil {
		return nil, err
	}

	hash, err := v.newChecksum()
	if err != nil {
		return nil, err
	}

	p := len(cipherText) - hash.Size()
	hash.Write(cipherText[0:p])
	expectedChecksum := hash.Sum(nil)
	actualChecksum := cipherText[p:]
	if !hmac.Equal(expectedChecksum, actualChecksum) {
		return nil, fmt.Errorf("cannot read encrypted block: incorrect checksum")
	}

	blk, err := v.newCipher()
	if err != nil {
		return nil, err
	}

	ivLength := blk.BlockSize()

	plainText := make([]byte, len(cipherText)-ivLength-hash.Size())
	iv := cipherText[0:blk.BlockSize()]

	ctr := cipher.NewCTR(blk, iv)
	ctr.XORKeyStream(plainText, cipherText[ivLength:len(cipherText)-hash.Size()])
	return plainText, nil
}

func (v *Vault) newChecksum() (hash.Hash, error) {
	switch v.Format.Checksum {
	case "hmac-sha-256":
		key := make([]byte, 32)
		v.deriveKey(purposeChecksumSecret, key)
		return hmac.New(sha256.New, key), nil

	default:
		return nil, fmt.Errorf("unsupported checksum format: %v", v.Format.Checksum)
	}

}

func (v *Vault) newCipher() (cipher.Block, error) {
	switch v.Format.Encryption {
	case "aes-256":
		k := make([]byte, 32)
		v.deriveKey(purposeAESKey, k)
		return aes.NewCipher(k)
	default:
		return nil, fmt.Errorf("unsupported encryption format: %v", v.Format.Encryption)
	}

}

func (v *Vault) deriveKey(purpose []byte, key []byte) error {
	k := hkdf.New(sha256.New, v.MasterKey, v.Format.UniqueID, purpose)
	_, err := io.ReadFull(k, key)
	return err
}

func (v *Vault) SetRepository(rc RepositoryConfig) error {
	b, err := json.Marshal(&rc)
	if err != nil {
		return err
	}

	return v.writeEncryptedBlock(repositoryConfigBlock, b)
}

func (v *Vault) RepositoryConfig() (*RepositoryConfig, error) {
	var rc RepositoryConfig

	b, err := v.readEncryptedBlock(repositoryConfigBlock)
	if err != nil {
		return nil, fmt.Errorf("unable to read repository: %v", err)
	}

	err = json.Unmarshal(b, &rc)
	if err != nil {
		return nil, err
	}

	return &rc, nil
}

func (v *Vault) OpenRepository() (repo.Repository, error) {
	rc, err := v.RepositoryConfig()
	if err != nil {
		return nil, err
	}

	storage, err := blob.NewStorage(rc.Storage)
	if err != nil {
		return nil, fmt.Errorf("unable to open repository: %v", err)
	}

	return repo.NewRepository(storage, rc.Format)
}

func (v *Vault) Get(id string, content interface{}) error {
	j, err := v.readEncryptedBlock(id)
	if err != nil {
		return nil
	}

	return json.Unmarshal(j, content)
}

func (v *Vault) Put(id string, content interface{}) error {
	j, err := json.Marshal(content)
	if err != nil {
		return err
	}
	return v.writeEncryptedBlock(id, j)
}

func (v *Vault) List(prefix string) ([]string, error) {
	var result []string

	for b := range v.Storage.ListBlocks(prefix) {
		if b.Error != nil {
			return result, b.Error
		}
		result = append(result, b.BlockID)
	}
	return result, nil
}

func CreateWithPassword(storage blob.Storage, format *Format, password string) (*Vault, error) {
	if err := format.ensureUniqueID(); err != nil {
		return nil, err
	}

	if len(password) < minPasswordLength {
		return nil, fmt.Errorf("password too short, must be at least %v characters, got %v", minPasswordLength, len(password))
	}
	masterKey := pbkdf2.Key([]byte(password), format.UniqueID, pbkdf2Rounds, masterKeySize, sha256.New)
	return CreateWithKey(storage, format, masterKey)
}

func CreateWithKey(storage blob.Storage, format *Format, masterKey []byte) (*Vault, error) {
	if len(masterKey) < minKeyLength {
		return nil, fmt.Errorf("key too short, must be at least %v bytes, got %v", minKeyLength, len(masterKey))
	}

	v := Vault{
		Storage:   storage,
		MasterKey: masterKey,
		Format:    *format,
	}
	v.Format.Version = "1"
	if err := v.Format.ensureUniqueID(); err != nil {
		return nil, err
	}

	formatBytes, err := json.Marshal(&v.Format)
	if err != nil {
		return nil, err
	}

	storage.PutBlock(formatBlock, ioutil.NopCloser(bytes.NewBuffer(formatBytes)), blob.PutOptions{
		Overwrite: true,
	})

	vv := make([]byte, 512)
	if _, err := io.ReadFull(rand.Reader, vv); err != nil {
		return nil, err
	}

	err = v.writeEncryptedBlock(checksumBlock, vv)
	if err != nil {
		return nil, err
	}

	return OpenWithKey(storage, masterKey)
}

func OpenWithPassword(storage blob.Storage, password string) (*Vault, error) {
	v := Vault{
		Storage: storage,
	}

	f, err := storage.GetBlock(formatBlock)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(f, &v.Format)
	if err != nil {
		return nil, err
	}

	v.MasterKey = pbkdf2.Key([]byte(password), v.Format.UniqueID, pbkdf2Rounds, masterKeySize, sha256.New)

	if _, err := v.readEncryptedBlock(checksumBlock); err != nil {
		return nil, err
	}

	return &v, nil
}

func OpenWithKey(storage blob.Storage, masterKey []byte) (*Vault, error) {
	v := Vault{
		Storage:   storage,
		MasterKey: masterKey,
	}

	f, err := storage.GetBlock(formatBlock)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(f, &v.Format)
	if err != nil {
		return nil, err
	}

	if _, err := v.readEncryptedBlock(checksumBlock); err != nil {
		return nil, err
	}

	return &v, nil
}
<|endoftext|>


<commit_before>package vimeo

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	libraryVersion   = "2.1.0"
	defaultBaseURL   = "https://api.vimeo.com/"
	defaultUserAgent = "go-vimeo/" + libraryVersion

	mediaTypeVersion = "application/vnd.vimeo.*+json;version=3.4"

	headerRateLimit     = "X-RateLimit-Limit"
	headerRateRemaining = "X-RateLimit-Remaining"
	headerRateReset     = "X-RateLimit-Reset"
)

// Client manages communication with Vimeo API.
type Client struct {
	client *http.Client

	BaseURL *url.URL

	UserAgent string

	// Config
	Config *Config

	// Services used for communicating with the API
	Categories      *CategoriesService
	Channels        *ChannelsService
	ContentRatings  *ContentRatingsService
	CreativeCommons *CreativeCommonsService
	Groups          *GroupsService
	Languages       *LanguagesService
	Tags            *TagsService
	Videos          *VideosService
	Users           *UsersService
}

type service struct {
	client *Client
}

// NewClient returns a new Vimeo API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// authentication, provide an http.Client that will perform the authentication
// for you (such as that provided by the golang.org/x/oauth2 library).
func NewClient(httpClient *http.Client, config *Config) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	if config == nil {
		config = DefaultConfig()
	}
	baseURL, _ := url.Parse(defaultBaseURL)

	c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: defaultUserAgent}
	c.Config = config
	c.Categories = &CategoriesService{client: c}
	c.Channels = &ChannelsService{client: c}
	c.ContentRatings = &ContentRatingsService{client: c}
	c.CreativeCommons = &CreativeCommonsService{client: c}
	c.Groups = &GroupsService{client: c}
	c.Languages = &LanguagesService{client: c}
	c.Tags = &TagsService{client: c}
	c.Videos = &VideosService{client: c}
	c.Users = &UsersService{client: c}
	return c
}

// Client returns the HTTP client configured for this client.
func (c *Client) Client() *http.Client {
	return c.client
}

// NewRequest creates an API request.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err = json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	req.Header.Set("Accept", mediaTypeVersion)

	if c.UserAgent != "" {
		req.Header.Set("User-Agent", c.UserAgent)
	}

	return req, nil
}

// Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value
// pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface,
// the raw response will be written to v, without attempting to decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer func() {
		io.CopyN(ioutil.Discard, resp.Body, 512)
		resp.Body.Close()
	}()

	response := newResponse(resp)

	err = CheckResponse(resp)
	if err != nil {
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return nil, err
			}
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
			if err == io.EOF {
				err = nil
			}
		}
	}

	return response, err
}

type paginator interface {
	GetPage() int
	GetTotal() int
	GetPaging() (string, string, string, string)
}

type paging struct {
	Next  string `json:"next,omitempty"`
	Prev  string `json:"previous,omitempty"`
	First string `json:"first,omitempty"`
	Last  string `json:"last,omitempty"`
}

type pagination struct {
	Total  int    `json:"total,omitempty"`
	Page   int    `json:"page,omitempty"`
	Paging paging `json:"paging,omitempty"`
}

// GetPage returns the current page number.
func (p pagination) GetPage() int {
	return p.Page
}

// GetTotal returns the total number of pages.
func (p pagination) GetTotal() int {
	return p.Total
}

// GetPaging returns the data pagination presented as relative references.
// In the following procedure: next, previous, first, last page.
func (p pagination) GetPaging() (string, string, string, string) {
	return p.Paging.Next, p.Paging.Prev, p.Paging.First, p.Paging.Last
}

// Response is a Vimeo response. This wraps the standard http.Response.
// Provides access pagination links.
type Response struct {
	*http.Response
	// Pagination
	Page       int
	TotalPages int
	NextPage   string
	PrevPage   string
	FirstPage  string
	LastPage   string
}

func (r *Response) setPaging(p paginator) {
	r.Page = p.GetPage()
	r.TotalPages = p.GetTotal()
	r.NextPage, r.PrevPage, r.FirstPage, r.LastPage = p.GetPaging()
}

// ErrorResponse is a Vimeo error response. This wraps the standard http.Response.
// Provides access error message returned Vimeo.
type ErrorResponse struct {
	Response *http.Response
	Message  string `json:"error"`
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d %v",
		r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
		r.Response.StatusCode, r.Message)
}

// Rate represents the rate limit for the current client.
type Rate struct {
	Limit     int
	Remaining int
	Reset     time.Time
}

// RateLimitError occurs when API response with a rate limit remaining value of 0.
type RateLimitError struct {
	Rate     Rate
	Response *http.Response
	Message  string
}

func (r *RateLimitError) Error() string {
	return fmt.Sprintf("%v %v: %d %v Reset in %v.",
		r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
		r.Response.StatusCode, r.Message, r.Rate.Reset)
}

// parseRate parses the rate related headers.
func parseRate(r *http.Response) Rate {
	var rate Rate

	if reset := r.Header.Get(headerRateReset); reset != "" {
		t, err := time.Parse(time.RFC3339, reset)
		if err == nil {
			rate.Reset = t
		}
	}
	if limit := r.Header.Get(headerRateLimit); limit != "" {
		rate.Limit, _ = strconv.Atoi(limit)
	}
	if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
		rate.Remaining, _ = strconv.Atoi(remaining)
	}

	return rate
}

func sanitizeURL(uri *url.URL) *url.URL {
	if uri == nil {
		return nil
	}
	params := uri.Query()
	if len(params.Get("client_secret")) > 0 {
		params.Set("client_secret", "REDACTED")
		uri.RawQuery = params.Encode()
	}
	return uri
}

func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	return response
}

// CheckResponse checks the API response for errors, and returns them if
// present.  A response is considered an error if it has a status code outside
// the 200 range.  API error responses are expected to have either no response
// body, or a JSON response body that maps to ErrorResponse.  Any other
// response body will be silently ignored.
func CheckResponse(r *http.Response) error {
	if code := r.StatusCode; 200 <= code && code <= 299 || code == 308 {
		return nil
	}

	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)

	if err == nil && data != nil {
		json.Unmarshal(data, errorResponse)
	}

	if r.StatusCode == http.StatusTooManyRequests && r.Header.Get(headerRateRemaining) == "0" {
		return &RateLimitError{
			Rate:     parseRate(r),
			Response: errorResponse.Response,
			Message:  errorResponse.Message,
		}
	}

	return errorResponse
}

// CallOption is an optional argument to an API call.
// A CallOption is something that configures an API call in a way that is not specific to that API: page, filter and etc
type CallOption interface {
	Get() (key, value string)
}

// OptPage is an optional argument to an API call
type OptPage int

// Get return key/value for make query
func (o OptPage) Get() (string, string) {
	return "page", fmt.Sprint(o)
}

// OptPerPage is an optional argument to an API call
type OptPerPage int

// Get return key/value for make query
func (o OptPerPage) Get() (string, string) {
	return "per_page", fmt.Sprint(o)
}

// OptSort is an optional argument to an API call
type OptSort string

// Get key/value for make query
func (o OptSort) Get() (string, string) {
	return "sort", fmt.Sprint(o)
}

// OptDirection is an optional argument to an API call
// All sortable resources accept the direction parameter which must be either asc or desc.
type OptDirection string

// Get key/value for make query
func (o OptDirection) Get() (string, string) {
	return "direction", fmt.Sprint(o)
}

// OptFilter is an optional argument to an API call
type OptFilter string

// Get key/value for make query
func (o OptFilter) Get() (string, string) {
	return "filter", fmt.Sprint(o)
}

// OptFilterEmbeddable is an optional argument to an API call
type OptFilterEmbeddable string

// Get key/value for make query
func (o OptFilterEmbeddable) Get() (string, string) {
	return "filter_embeddable", fmt.Sprint(o)
}

// OptFilterPlayable is an optional argument to an API call
type OptFilterPlayable string

// Get key/value for make query
func (o OptFilterPlayable) Get() (string, string) {
	return "filter_playable", fmt.Sprint(o)
}

// OptQuery is an optional argument to an API call. Search query.
type OptQuery string

// Get key/value for make query
func (o OptQuery) Get() (string, string) {
	return "query", fmt.Sprint(o)
}

// OptFilterContentRating is an optional argument to an API call
// Content filter is a specific type of resource filter, available on all video resources.
// Any videos that do not match one of the provided ratings will be excluded from the list of videos.
// Valid ratings include: language/drugs/violence/nudity/safe/unrated
type OptFilterContentRating []string

// Get key/value for make query
func (o OptFilterContentRating) Get() (string, string) {
	return "filter_content_rating", strings.Join(o, ",")
}

// OptFields is an optional argument to an API call.
// With a simple parameter you can reduce the size of the responses,
// and dramatically increase the performance of your API requests.
type OptFields []string

// Get key/value for make query
func (o OptFields) Get() (string, string) {
	return "fields", strings.Join(o, ",")
}

func addOptions(s string, opts ...CallOption) (string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}

	qs := u.Query()
	for _, o := range opts {
		qs.Set(o.Get())
	}

	u.RawQuery = qs.Encode()
	return u.String(), nil
}
<commit_msg>Prepare 2.2.1<commit_after>package vimeo

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	libraryVersion   = "2.2.1"
	defaultBaseURL   = "https://api.vimeo.com/"
	defaultUserAgent = "go-vimeo/" + libraryVersion

	mediaTypeVersion = "application/vnd.vimeo.*+json;version=3.4"

	headerRateLimit     = "X-RateLimit-Limit"
	headerRateRemaining = "X-RateLimit-Remaining"
	headerRateReset     = "X-RateLimit-Reset"
)

// Client manages communication with Vimeo API.
type Client struct {
	client *http.Client

	BaseURL *url.URL

	UserAgent string

	// Config
	Config *Config

	// Services used for communicating with the API
	Categories      *CategoriesService
	Channels        *ChannelsService
	ContentRatings  *ContentRatingsService
	CreativeCommons *CreativeCommonsService
	Groups          *GroupsService
	Languages       *LanguagesService
	Tags            *TagsService
	Videos          *VideosService
	Users           *UsersService
}

type service struct {
	client *Client
}

// NewClient returns a new Vimeo API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// authentication, provide an http.Client that will perform the authentication
// for you (such as that provided by the golang.org/x/oauth2 library).
func NewClient(httpClient *http.Client, config *Config) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}

	if config == nil {
		config = DefaultConfig()
	}
	baseURL, _ := url.Parse(defaultBaseURL)

	c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: defaultUserAgent}
	c.Config = config
	c.Categories = &CategoriesService{client: c}
	c.Channels = &ChannelsService{client: c}
	c.ContentRatings = &ContentRatingsService{client: c}
	c.CreativeCommons = &CreativeCommonsService{client: c}
	c.Groups = &GroupsService{client: c}
	c.Languages = &LanguagesService{client: c}
	c.Tags = &TagsService{client: c}
	c.Videos = &VideosService{client: c}
	c.Users = &UsersService{client: c}
	return c
}

// Client returns the HTTP client configured for this client.
func (c *Client) Client() *http.Client {
	return c.client
}

// NewRequest creates an API request.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err = json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	req.Header.Set("Accept", mediaTypeVersion)

	if c.UserAgent != "" {
		req.Header.Set("User-Agent", c.UserAgent)
	}

	return req, nil
}

// Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value
// pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface,
// the raw response will be written to v, without attempting to decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer func() {
		io.CopyN(ioutil.Discard, resp.Body, 512)
		resp.Body.Close()
	}()

	response := newResponse(resp)

	err = CheckResponse(resp)
	if err != nil {
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			_, err = io.Copy(w, resp.Body)
			if err != nil {
				return nil, err
			}
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
			if err == io.EOF {
				err = nil
			}
		}
	}

	return response, err
}

type paginator interface {
	GetPage() int
	GetTotal() int
	GetPaging() (string, string, string, string)
}

type paging struct {
	Next  string `json:"next,omitempty"`
	Prev  string `json:"previous,omitempty"`
	First string `json:"first,omitempty"`
	Last  string `json:"last,omitempty"`
}

type pagination struct {
	Total  int    `json:"total,omitempty"`
	Page   int    `json:"page,omitempty"`
	Paging paging `json:"paging,omitempty"`
}

// GetPage returns the current page number.
func (p pagination) GetPage() int {
	return p.Page
}

// GetTotal returns the total number of pages.
func (p pagination) GetTotal() int {
	return p.Total
}

// GetPaging returns the data pagination presented as relative references.
// In the following procedure: next, previous, first, last page.
func (p pagination) GetPaging() (string, string, string, string) {
	return p.Paging.Next, p.Paging.Prev, p.Paging.First, p.Paging.Last
}

// Response is a Vimeo response. This wraps the standard http.Response.
// Provides access pagination links.
type Response struct {
	*http.Response
	// Pagination
	Page       int
	TotalPages int
	NextPage   string
	PrevPage   string
	FirstPage  string
	LastPage   string
}

func (r *Response) setPaging(p paginator) {
	r.Page = p.GetPage()
	r.TotalPages = p.GetTotal()
	r.NextPage, r.PrevPage, r.FirstPage, r.LastPage = p.GetPaging()
}

// ErrorResponse is a Vimeo error response. This wraps the standard http.Response.
// Provides access error message returned Vimeo.
type ErrorResponse struct {
	Response *http.Response
	Message  string `json:"error"`
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d %v",
		r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
		r.Response.StatusCode, r.Message)
}

// Rate represents the rate limit for the current client.
type Rate struct {
	Limit     int
	Remaining int
	Reset     time.Time
}

// RateLimitError occurs when API response with a rate limit remaining value of 0.
type RateLimitError struct {
	Rate     Rate
	Response *http.Response
	Message  string
}

func (r *RateLimitError) Error() string {
	return fmt.Sprintf("%v %v: %d %v Reset in %v.",
		r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
		r.Response.StatusCode, r.Message, r.Rate.Reset)
}

// parseRate parses the rate related headers.
func parseRate(r *http.Response) Rate {
	var rate Rate

	if reset := r.Header.Get(headerRateReset); reset != "" {
		t, err := time.Parse(time.RFC3339, reset)
		if err == nil {
			rate.Reset = t
		}
	}
	if limit := r.Header.Get(headerRateLimit); limit != "" {
		rate.Limit, _ = strconv.Atoi(limit)
	}
	if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
		rate.Remaining, _ = strconv.Atoi(remaining)
	}

	return rate
}

func sanitizeURL(uri *url.URL) *url.URL {
	if uri == nil {
		return nil
	}
	params := uri.Query()
	if len(params.Get("client_secret")) > 0 {
		params.Set("client_secret", "REDACTED")
		uri.RawQuery = params.Encode()
	}
	return uri
}

func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	return response
}

// CheckResponse checks the API response for errors, and returns them if
// present.  A response is considered an error if it has a status code outside
// the 200 range.  API error responses are expected to have either no response
// body, or a JSON response body that maps to ErrorResponse.  Any other
// response body will be silently ignored.
func CheckResponse(r *http.Response) error {
	if code := r.StatusCode; 200 <= code && code <= 299 || code == 308 {
		return nil
	}

	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)

	if err == nil && data != nil {
		json.Unmarshal(data, errorResponse)
	}

	if r.StatusCode == http.StatusTooManyRequests && r.Header.Get(headerRateRemaining) == "0" {
		return &RateLimitError{
			Rate:     parseRate(r),
			Response: errorResponse.Response,
			Message:  errorResponse.Message,
		}
	}

	return errorResponse
}

// CallOption is an optional argument to an API call.
// A CallOption is something that configures an API call in a way that is not specific to that API: page, filter and etc
type CallOption interface {
	Get() (key, value string)
}

// OptPage is an optional argument to an API call
type OptPage int

// Get return key/value for make query
func (o OptPage) Get() (string, string) {
	return "page", fmt.Sprint(o)
}

// OptPerPage is an optional argument to an API call
type OptPerPage int

// Get return key/value for make query
func (o OptPerPage) Get() (string, string) {
	return "per_page", fmt.Sprint(o)
}

// OptSort is an optional argument to an API call
type OptSort string

// Get key/value for make query
func (o OptSort) Get() (string, string) {
	return "sort", fmt.Sprint(o)
}

// OptDirection is an optional argument to an API call
// All sortable resources accept the direction parameter which must be either asc or desc.
type OptDirection string

// Get key/value for make query
func (o OptDirection) Get() (string, string) {
	return "direction", fmt.Sprint(o)
}

// OptFilter is an optional argument to an API call
type OptFilter string

// Get key/value for make query
func (o OptFilter) Get() (string, string) {
	return "filter", fmt.Sprint(o)
}

// OptFilterEmbeddable is an optional argument to an API call
type OptFilterEmbeddable string

// Get key/value for make query
func (o OptFilterEmbeddable) Get() (string, string) {
	return "filter_embeddable", fmt.Sprint(o)
}

// OptFilterPlayable is an optional argument to an API call
type OptFilterPlayable string

// Get key/value for make query
func (o OptFilterPlayable) Get() (string, string) {
	return "filter_playable", fmt.Sprint(o)
}

// OptQuery is an optional argument to an API call. Search query.
type OptQuery string

// Get key/value for make query
func (o OptQuery) Get() (string, string) {
	return "query", fmt.Sprint(o)
}

// OptFilterContentRating is an optional argument to an API call
// Content filter is a specific type of resource filter, available on all video resources.
// Any videos that do not match one of the provided ratings will be excluded from the list of videos.
// Valid ratings include: language/drugs/violence/nudity/safe/unrated
type OptFilterContentRating []string

// Get key/value for make query
func (o OptFilterContentRating) Get() (string, string) {
	return "filter_content_rating", strings.Join(o, ",")
}

// OptFields is an optional argument to an API call.
// With a simple parameter you can reduce the size of the responses,
// and dramatically increase the performance of your API requests.
type OptFields []string

// Get key/value for make query
func (o OptFields) Get() (string, string) {
	return "fields", strings.Join(o, ",")
}

func addOptions(s string, opts ...CallOption) (string, error) {
	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}

	qs := u.Query()
	for _, o := range opts {
		qs.Set(o.Get())
	}

	u.RawQuery = qs.Encode()
	return u.String(), nil
}
<|endoftext|>


<commit_before>package volume

import (
	"testing"
)

func TestGetVolume(t *testing.T) {
	_, err := GetVolume()
	if err != nil {
		t.Errorf("get volume failed: %+v", err)
	}
}

func TestSetVolume(t *testing.T) {
	vol, err := GetVolume()
	defer SetVolume(vol)
	if err != nil {
		t.Errorf("get volume failed: %+v", err)
	}
	for _, vol := range []int{0, 37, 54, 20, 10} {
		err = SetVolume(vol)
		if err != nil {
			t.Errorf("set volume failed: %+v", err)
		}
		v, err := GetVolume()
		if err != nil {
			t.Errorf("set volume failed: %+v", err)
		}
		if vol != v {
			t.Errorf("set volume failed: (got: %+v, expected: %+v)", v, vol)
		}
	}
}
<commit_msg>add tests for Mute/Unmute<commit_after>package volume

import (
	"testing"
)

func TestGetVolume(t *testing.T) {
	_, err := GetVolume()
	if err != nil {
		t.Errorf("get volume failed: %+v", err)
	}
}

func TestSetVolume(t *testing.T) {
	vol, err := GetVolume()
	defer SetVolume(vol)
	if err != nil {
		t.Errorf("get volume failed: %+v", err)
	}
	for _, vol := range []int{0, 37, 54, 20, 10} {
		err = SetVolume(vol)
		if err != nil {
			t.Errorf("set volume failed: %+v", err)
		}
		v, err := GetVolume()
		if err != nil {
			t.Errorf("set volume failed: %+v", err)
		}
		if vol != v {
			t.Errorf("set volume failed: (got: %+v, expected: %+v)", v, vol)
		}
	}
}

func TestMute(t *testing.T) {
	origMuted, err := GetMuted()
	defer func() {
		if origMuted {
			Mute()
		} else {
			Unmute()
		}
	}()
	if err != nil {
		t.Errorf("get muted failed: %+v", err)
	}
	err = Mute()
	if err != nil {
		t.Errorf("mute failed: %+v", err)
	}
	muted, _ := GetMuted()
	if !muted {
		t.Errorf("mute failed: %t", muted)
	}
	err = Unmute()
	if err != nil {
		t.Errorf("unmute failed: %+v", err)
	}
	muted, _ = GetMuted()
	if muted {
		t.Errorf("unmute failed: %t", muted)
	}
}
<|endoftext|>


<commit_before>package builder

import (
	"context"
	"fmt"
	"strings"

	"github.com/docker/cli/cli"
	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/opts"
	"github.com/docker/docker/api/types"
	units "github.com/docker/go-units"
	"github.com/spf13/cobra"
)

type pruneOptions struct {
	force       bool
	all         bool
	filter      opts.FilterOpt
	keepStorage opts.MemBytes
}

// NewPruneCommand returns a new cobra prune command for images
func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
	options := pruneOptions{filter: opts.NewFilterOpt()}

	cmd := &cobra.Command{
		Use:   "prune",
		Short: "Remove build cache",
		Args:  cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			spaceReclaimed, output, err := runPrune(dockerCli, options)
			if err != nil {
				return err
			}
			if output != "" {
				fmt.Fprintln(dockerCli.Out(), output)
			}
			fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
			return nil
		},
		Annotations: map[string]string{"version": "1.39"},
	}

	flags := cmd.Flags()
	flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
	flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images, not just dangling ones")
	flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=24h')")
	flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache")

	return cmd
}

const (
	normalWarning   = `WARNING! This will remove all dangling build cache. Are you sure you want to continue?`
	allCacheWarning = `WARNING! This will remove all build cache. Are you sure you want to continue?`
)

func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
	pruneFilters := options.filter.Value()
	pruneFilters = command.PruneFilters(dockerCli, pruneFilters)

	warning := normalWarning
	if options.all {
		warning = allCacheWarning
	}
	if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
		return 0, "", nil
	}

	report, err := dockerCli.Client().BuildCachePrune(context.Background(), types.BuildCachePruneOptions{
		All:         options.all,
		KeepStorage: options.keepStorage.Value(),
		Filters:     pruneFilters,
	})
	if err != nil {
		return 0, "", err
	}

	if len(report.CachesDeleted) > 0 {
		var sb strings.Builder
		sb.WriteString("Deleted build cache objects:\n")
		for _, id := range report.CachesDeleted {
			sb.WriteString(id)
			sb.WriteByte('\n')
		}
		output = sb.String()
	}

	return report.SpaceReclaimed, output, nil
}

// CachePrune executes a prune command for build cache
func CachePrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
	return runPrune(dockerCli, pruneOptions{force: true, all: all, filter: filter})
}
<commit_msg>Fix builder prune -a/--all flag description<commit_after>package builder

import (
	"context"
	"fmt"
	"strings"

	"github.com/docker/cli/cli"
	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/opts"
	"github.com/docker/docker/api/types"
	units "github.com/docker/go-units"
	"github.com/spf13/cobra"
)

type pruneOptions struct {
	force       bool
	all         bool
	filter      opts.FilterOpt
	keepStorage opts.MemBytes
}

// NewPruneCommand returns a new cobra prune command for images
func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
	options := pruneOptions{filter: opts.NewFilterOpt()}

	cmd := &cobra.Command{
		Use:   "prune",
		Short: "Remove build cache",
		Args:  cli.NoArgs,
		RunE: func(cmd *cobra.Command, args []string) error {
			spaceReclaimed, output, err := runPrune(dockerCli, options)
			if err != nil {
				return err
			}
			if output != "" {
				fmt.Fprintln(dockerCli.Out(), output)
			}
			fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
			return nil
		},
		Annotations: map[string]string{"version": "1.39"},
	}

	flags := cmd.Flags()
	flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
	flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused build cache, not just dangling ones")
	flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=24h')")
	flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache")

	return cmd
}

const (
	normalWarning   = `WARNING! This will remove all dangling build cache. Are you sure you want to continue?`
	allCacheWarning = `WARNING! This will remove all build cache. Are you sure you want to continue?`
)

func runPrune(dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
	pruneFilters := options.filter.Value()
	pruneFilters = command.PruneFilters(dockerCli, pruneFilters)

	warning := normalWarning
	if options.all {
		warning = allCacheWarning
	}
	if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
		return 0, "", nil
	}

	report, err := dockerCli.Client().BuildCachePrune(context.Background(), types.BuildCachePruneOptions{
		All:         options.all,
		KeepStorage: options.keepStorage.Value(),
		Filters:     pruneFilters,
	})
	if err != nil {
		return 0, "", err
	}

	if len(report.CachesDeleted) > 0 {
		var sb strings.Builder
		sb.WriteString("Deleted build cache objects:\n")
		for _, id := range report.CachesDeleted {
			sb.WriteString(id)
			sb.WriteByte('\n')
		}
		output = sb.String()
	}

	return report.SpaceReclaimed, output, nil
}

// CachePrune executes a prune command for build cache
func CachePrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) {
	return runPrune(dockerCli, pruneOptions{force: true, all: all, filter: filter})
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 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 dockercontroller

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"

	"bufio"

	"github.com/fsouza/go-dockerclient"
	container "github.com/hyperledger/fabric/core/container/api"
	"github.com/hyperledger/fabric/core/container/ccintf"
	cutil "github.com/hyperledger/fabric/core/container/util"
	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"golang.org/x/net/context"
)

var (
	dockerLogger = logging.MustGetLogger("dockercontroller")
	hostConfig   *docker.HostConfig
)

//DockerVM is a vm. It is identified by an image id
type DockerVM struct {
	id string
}

func getDockerHostConfig() *docker.HostConfig {
	if hostConfig != nil {
		return hostConfig
	}
	dockerKey := func(key string) string {
		return "vm.docker.hostConfig." + key
	}
	getInt64 := func(key string) int64 {
		defer func() {
			if err := recover(); err != nil {
				dockerLogger.Warningf("load vm.docker.hostConfig.%s failed, error: %v", key, err)
			}
		}()
		n := viper.GetInt(dockerKey(key))
		return int64(n)
	}

	var logConfig docker.LogConfig
	err := viper.UnmarshalKey(dockerKey("LogConfig"), &logConfig)
	if err != nil {
		dockerLogger.Warningf("load docker HostConfig.LogConfig failed, error: %s", err.Error())
	}
	networkMode := viper.GetString(dockerKey("NetworkMode"))
	if networkMode == "" {
		networkMode = "host"
	}
	dockerLogger.Debugf("docker container hostconfig NetworkMode: %s", networkMode)

	hostConfig = &docker.HostConfig{
		CapAdd:  viper.GetStringSlice(dockerKey("CapAdd")),
		CapDrop: viper.GetStringSlice(dockerKey("CapDrop")),

		DNS:         viper.GetStringSlice(dockerKey("Dns")),
		DNSSearch:   viper.GetStringSlice(dockerKey("DnsSearch")),
		ExtraHosts:  viper.GetStringSlice(dockerKey("ExtraHosts")),
		NetworkMode: networkMode,
		IpcMode:     viper.GetString(dockerKey("IpcMode")),
		PidMode:     viper.GetString(dockerKey("PidMode")),
		UTSMode:     viper.GetString(dockerKey("UTSMode")),
		LogConfig:   logConfig,

		ReadonlyRootfs:   viper.GetBool(dockerKey("ReadonlyRootfs")),
		SecurityOpt:      viper.GetStringSlice(dockerKey("SecurityOpt")),
		CgroupParent:     viper.GetString(dockerKey("CgroupParent")),
		Memory:           getInt64("Memory"),
		MemorySwap:       getInt64("MemorySwap"),
		MemorySwappiness: getInt64("MemorySwappiness"),
		OOMKillDisable:   viper.GetBool(dockerKey("OomKillDisable")),
		CPUShares:        getInt64("CpuShares"),
		CPUSet:           viper.GetString(dockerKey("Cpuset")),
		CPUSetCPUs:       viper.GetString(dockerKey("CpusetCPUs")),
		CPUSetMEMs:       viper.GetString(dockerKey("CpusetMEMs")),
		CPUQuota:         getInt64("CpuQuota"),
		CPUPeriod:        getInt64("CpuPeriod"),
		BlkioWeight:      getInt64("BlkioWeight"),
	}

	return hostConfig
}

func (vm *DockerVM) createContainer(ctxt context.Context, client *docker.Client, imageID string, containerID string, args []string, env []string, attachStdout bool) error {
	config := docker.Config{Cmd: args, Image: imageID, Env: env, AttachStdout: attachStdout, AttachStderr: attachStdout}
	copts := docker.CreateContainerOptions{Name: containerID, Config: &config, HostConfig: getDockerHostConfig()}
	dockerLogger.Debugf("Create container: %s", containerID)
	_, err := client.CreateContainer(copts)
	if err != nil {
		return err
	}
	dockerLogger.Debugf("Created container: %s", imageID)
	return nil
}

func (vm *DockerVM) deployImage(client *docker.Client, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
	id, _ := vm.GetVMName(ccid)
	outputbuf := bytes.NewBuffer(nil)
	opts := docker.BuildImageOptions{
		Name:         id,
		Pull:         false,
		InputStream:  reader,
		OutputStream: outputbuf,
	}

	if err := client.BuildImage(opts); err != nil {
		dockerLogger.Errorf("Error building images: %s", err)
		dockerLogger.Errorf("Image Output:\n********************\n%s\n********************", outputbuf.String())
		return err
	}

	dockerLogger.Debugf("Created image: %s", id)

	return nil
}

//Deploy use the reader containing targz to create a docker image
//for docker inputbuf is tar reader ready for use by docker.Client
//the stream from end client to peer could directly be this tar stream
//talk to docker daemon using docker Client and build the image
func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
	client, err := cutil.NewDockerClient()
	switch err {
	case nil:
		if err = vm.deployImage(client, ccid, args, env, reader); err != nil {
			return err
		}
	default:
		return fmt.Errorf("Error creating docker client: %s", err)
	}
	return nil
}

//Start starts a container using a previously created docker image
func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory) error {
	imageID, _ := vm.GetVMName(ccid)
	client, err := cutil.NewDockerClient()
	if err != nil {
		dockerLogger.Debugf("start - cannot create client %s", err)
		return err
	}

	containerID := strings.Replace(imageID, ":", "_", -1)
	attachStdout := viper.GetBool("vm.docker.attachStdout")

	//stop,force remove if necessary
	dockerLogger.Debugf("Cleanup container %s", containerID)
	vm.stopInternal(ctxt, client, containerID, 0, false, false)

	dockerLogger.Debugf("Start container %s", containerID)
	err = vm.createContainer(ctxt, client, imageID, containerID, args, env, attachStdout)
	if err != nil {
		//if image not found try to create image and retry
		if err == docker.ErrNoSuchImage {
			if builder != nil {
				dockerLogger.Debugf("start-could not find image ...attempt to recreate image %s", err)

				reader, err := builder()
				if err != nil {
					dockerLogger.Errorf("Error creating image builder: %s", err)
				}

				if err = vm.deployImage(client, ccid, args, env, reader); err != nil {
					return err
				}

				dockerLogger.Debug("start-recreated image successfully")
				if err = vm.createContainer(ctxt, client, imageID, containerID, args, env, attachStdout); err != nil {
					dockerLogger.Errorf("start-could not recreate container post recreate image: %s", err)
					return err
				}
			} else {
				dockerLogger.Errorf("start-could not find image: %s", err)
				return err
			}
		} else {
			dockerLogger.Errorf("start-could not recreate container %s", err)
			return err
		}
	}

	if attachStdout {
		// Launch a few go-threads to manage output streams from the container.
		// They will be automatically destroyed when the container exits
		attached := make(chan struct{})
		r, w := io.Pipe()

		go func() {
			// AttachToContainer will fire off a message on the "attached" channel once the
			// attachment completes, and then block until the container is terminated.
			err = client.AttachToContainer(docker.AttachToContainerOptions{
				Container:    containerID,
				OutputStream: w,
				ErrorStream:  w,
				Logs:         true,
				Stdout:       true,
				Stderr:       true,
				Stream:       true,
				Success:      attached,
			})

			// If we get here, the container has terminated.  Send a signal on the pipe
			// so that downstream may clean up appropriately
			_ = w.CloseWithError(err)
		}()

		go func() {
			// Block here until the attachment completes or we timeout
			select {
			case <-attached:
				// successful attach
			case <-time.After(10 * time.Second):
				dockerLogger.Errorf("Timeout while attaching to IO channel in container %s", containerID)
				return
			}

			// Acknowledge the attachment?  This was included in the gist I followed
			// (http://bit.ly/2jBrCtM).  Not sure it's actually needed but it doesn't
			// appear to hurt anything.
			attached <- struct{}{}

			// Establish a buffer for our IO channel so that we may do readline-style
			// ingestion of the IO, one log entry per line
			is := bufio.NewReader(r)

			// Acquire a custom logger for our chaincode, inheriting the level from the peer
			containerLogger := logging.MustGetLogger(containerID)
			logging.SetLevel(logging.GetLevel("peer"), containerID)

			for {
				// Loop forever dumping lines of text into the containerLogger
				// until the pipe is closed
				line, err := is.ReadString('\n')
				if err != nil {
					switch err {
					case io.EOF:
						dockerLogger.Infof("Container %s has closed its IO channel", containerID)
					default:
						dockerLogger.Errorf("Error reading container output: %s", err)
					}

					return
				}

				containerLogger.Info(line)
			}
		}()
	}

	// start container with HostConfig was deprecated since v1.10 and removed in v1.2
	err = client.StartContainer(containerID, nil)
	if err != nil {
		dockerLogger.Errorf("start-could not start container %s", err)
		return err
	}

	dockerLogger.Debugf("Started container %s", containerID)
	return nil
}

//Stop stops a running chaincode
func (vm *DockerVM) Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error {
	id, _ := vm.GetVMName(ccid)
	client, err := cutil.NewDockerClient()
	if err != nil {
		dockerLogger.Debugf("stop - cannot create client %s", err)
		return err
	}
	id = strings.Replace(id, ":", "_", -1)

	err = vm.stopInternal(ctxt, client, id, timeout, dontkill, dontremove)

	return err
}

func (vm *DockerVM) stopInternal(ctxt context.Context, client *docker.Client, id string, timeout uint, dontkill bool, dontremove bool) error {
	err := client.StopContainer(id, timeout)
	if err != nil {
		dockerLogger.Debugf("Stop container %s(%s)", id, err)
	} else {
		dockerLogger.Debugf("Stopped container %s", id)
	}
	if !dontkill {
		err = client.KillContainer(docker.KillContainerOptions{ID: id})
		if err != nil {
			dockerLogger.Debugf("Kill container %s (%s)", id, err)
		} else {
			dockerLogger.Debugf("Killed container %s", id)
		}
	}
	if !dontremove {
		err = client.RemoveContainer(docker.RemoveContainerOptions{ID: id, Force: true})
		if err != nil {
			dockerLogger.Debugf("Remove container %s (%s)", id, err)
		} else {
			dockerLogger.Debugf("Removed container %s", id)
		}
	}
	return err
}

//Destroy destroys an image
func (vm *DockerVM) Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error {
	id, _ := vm.GetVMName(ccid)
	client, err := cutil.NewDockerClient()
	if err != nil {
		dockerLogger.Errorf("destroy-cannot create client %s", err)
		return err
	}
	id = strings.Replace(id, ":", "_", -1)

	err = client.RemoveImageExtended(id, docker.RemoveImageOptions{Force: force, NoPrune: noprune})

	if err != nil {
		dockerLogger.Errorf("error while destroying image: %s", err)
	} else {
		dockerLogger.Debug("Destroyed image %s", id)
	}

	return err
}

//GetVMName generates the docker image from peer information given the hashcode. This is needed to
//keep image name's unique in a single host, multi-peer environment (such as a development environment)
func (vm *DockerVM) GetVMName(ccid ccintf.CCID) (string, error) {
	name := ccid.GetName()

	if ccid.NetworkID != "" {
		return fmt.Sprintf("%s-%s-%s", ccid.NetworkID, ccid.PeerID, name), nil
	} else if ccid.PeerID != "" {
		return fmt.Sprintf("%s-%s", ccid.PeerID, name), nil
	} else {
		return name, nil
	}
}
<commit_msg>[FAB-2929] Docker repository tags not sanitised<commit_after>/*
Copyright IBM Corp. 2016 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 dockercontroller

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"

	"bufio"

	"regexp"

	"github.com/fsouza/go-dockerclient"
	container "github.com/hyperledger/fabric/core/container/api"
	"github.com/hyperledger/fabric/core/container/ccintf"
	cutil "github.com/hyperledger/fabric/core/container/util"
	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"golang.org/x/net/context"
)

var (
	dockerLogger = logging.MustGetLogger("dockercontroller")
	hostConfig   *docker.HostConfig
)

//DockerVM is a vm. It is identified by an image id
type DockerVM struct {
	id string
}

func getDockerHostConfig() *docker.HostConfig {
	if hostConfig != nil {
		return hostConfig
	}
	dockerKey := func(key string) string {
		return "vm.docker.hostConfig." + key
	}
	getInt64 := func(key string) int64 {
		defer func() {
			if err := recover(); err != nil {
				dockerLogger.Warningf("load vm.docker.hostConfig.%s failed, error: %v", key, err)
			}
		}()
		n := viper.GetInt(dockerKey(key))
		return int64(n)
	}

	var logConfig docker.LogConfig
	err := viper.UnmarshalKey(dockerKey("LogConfig"), &logConfig)
	if err != nil {
		dockerLogger.Warningf("load docker HostConfig.LogConfig failed, error: %s", err.Error())
	}
	networkMode := viper.GetString(dockerKey("NetworkMode"))
	if networkMode == "" {
		networkMode = "host"
	}
	dockerLogger.Debugf("docker container hostconfig NetworkMode: %s", networkMode)

	hostConfig = &docker.HostConfig{
		CapAdd:  viper.GetStringSlice(dockerKey("CapAdd")),
		CapDrop: viper.GetStringSlice(dockerKey("CapDrop")),

		DNS:         viper.GetStringSlice(dockerKey("Dns")),
		DNSSearch:   viper.GetStringSlice(dockerKey("DnsSearch")),
		ExtraHosts:  viper.GetStringSlice(dockerKey("ExtraHosts")),
		NetworkMode: networkMode,
		IpcMode:     viper.GetString(dockerKey("IpcMode")),
		PidMode:     viper.GetString(dockerKey("PidMode")),
		UTSMode:     viper.GetString(dockerKey("UTSMode")),
		LogConfig:   logConfig,

		ReadonlyRootfs:   viper.GetBool(dockerKey("ReadonlyRootfs")),
		SecurityOpt:      viper.GetStringSlice(dockerKey("SecurityOpt")),
		CgroupParent:     viper.GetString(dockerKey("CgroupParent")),
		Memory:           getInt64("Memory"),
		MemorySwap:       getInt64("MemorySwap"),
		MemorySwappiness: getInt64("MemorySwappiness"),
		OOMKillDisable:   viper.GetBool(dockerKey("OomKillDisable")),
		CPUShares:        getInt64("CpuShares"),
		CPUSet:           viper.GetString(dockerKey("Cpuset")),
		CPUSetCPUs:       viper.GetString(dockerKey("CpusetCPUs")),
		CPUSetMEMs:       viper.GetString(dockerKey("CpusetMEMs")),
		CPUQuota:         getInt64("CpuQuota"),
		CPUPeriod:        getInt64("CpuPeriod"),
		BlkioWeight:      getInt64("BlkioWeight"),
	}

	return hostConfig
}

func (vm *DockerVM) createContainer(ctxt context.Context, client *docker.Client, imageID string, containerID string, args []string, env []string, attachStdout bool) error {
	config := docker.Config{Cmd: args, Image: imageID, Env: env, AttachStdout: attachStdout, AttachStderr: attachStdout}
	copts := docker.CreateContainerOptions{Name: containerID, Config: &config, HostConfig: getDockerHostConfig()}
	dockerLogger.Debugf("Create container: %s", containerID)
	_, err := client.CreateContainer(copts)
	if err != nil {
		return err
	}
	dockerLogger.Debugf("Created container: %s", imageID)
	return nil
}

func (vm *DockerVM) deployImage(client *docker.Client, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
	id, err := vm.GetVMName(ccid)
	if err != nil {
		return err
	}
	outputbuf := bytes.NewBuffer(nil)
	opts := docker.BuildImageOptions{
		Name:         id,
		Pull:         false,
		InputStream:  reader,
		OutputStream: outputbuf,
	}

	if err := client.BuildImage(opts); err != nil {
		dockerLogger.Errorf("Error building images: %s", err)
		dockerLogger.Errorf("Image Output:\n********************\n%s\n********************", outputbuf.String())
		return err
	}

	dockerLogger.Debugf("Created image: %s", id)

	return nil
}

//Deploy use the reader containing targz to create a docker image
//for docker inputbuf is tar reader ready for use by docker.Client
//the stream from end client to peer could directly be this tar stream
//talk to docker daemon using docker Client and build the image
func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
	client, err := cutil.NewDockerClient()
	switch err {
	case nil:
		if err = vm.deployImage(client, ccid, args, env, reader); err != nil {
			return err
		}
	default:
		return fmt.Errorf("Error creating docker client: %s", err)
	}
	return nil
}

//Start starts a container using a previously created docker image
func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory) error {
	imageID, err := vm.GetVMName(ccid)
	if err != nil {
		return err
	}
	client, err := cutil.NewDockerClient()
	if err != nil {
		dockerLogger.Debugf("start - cannot create client %s", err)
		return err
	}

	containerID := strings.Replace(imageID, ":", "_", -1)
	attachStdout := viper.GetBool("vm.docker.attachStdout")

	//stop,force remove if necessary
	dockerLogger.Debugf("Cleanup container %s", containerID)
	vm.stopInternal(ctxt, client, containerID, 0, false, false)

	dockerLogger.Debugf("Start container %s", containerID)
	err = vm.createContainer(ctxt, client, imageID, containerID, args, env, attachStdout)
	if err != nil {
		//if image not found try to create image and retry
		if err == docker.ErrNoSuchImage {
			if builder != nil {
				dockerLogger.Debugf("start-could not find image ...attempt to recreate image %s", err)

				reader, err := builder()
				if err != nil {
					dockerLogger.Errorf("Error creating image builder: %s", err)
				}

				if err = vm.deployImage(client, ccid, args, env, reader); err != nil {
					return err
				}

				dockerLogger.Debug("start-recreated image successfully")
				if err = vm.createContainer(ctxt, client, imageID, containerID, args, env, attachStdout); err != nil {
					dockerLogger.Errorf("start-could not recreate container post recreate image: %s", err)
					return err
				}
			} else {
				dockerLogger.Errorf("start-could not find image: %s", err)
				return err
			}
		} else {
			dockerLogger.Errorf("start-could not recreate container %s", err)
			return err
		}
	}

	if attachStdout {
		// Launch a few go-threads to manage output streams from the container.
		// They will be automatically destroyed when the container exits
		attached := make(chan struct{})
		r, w := io.Pipe()

		go func() {
			// AttachToContainer will fire off a message on the "attached" channel once the
			// attachment completes, and then block until the container is terminated.
			err = client.AttachToContainer(docker.AttachToContainerOptions{
				Container:    containerID,
				OutputStream: w,
				ErrorStream:  w,
				Logs:         true,
				Stdout:       true,
				Stderr:       true,
				Stream:       true,
				Success:      attached,
			})

			// If we get here, the container has terminated.  Send a signal on the pipe
			// so that downstream may clean up appropriately
			_ = w.CloseWithError(err)
		}()

		go func() {
			// Block here until the attachment completes or we timeout
			select {
			case <-attached:
				// successful attach
			case <-time.After(10 * time.Second):
				dockerLogger.Errorf("Timeout while attaching to IO channel in container %s", containerID)
				return
			}

			// Acknowledge the attachment?  This was included in the gist I followed
			// (http://bit.ly/2jBrCtM).  Not sure it's actually needed but it doesn't
			// appear to hurt anything.
			attached <- struct{}{}

			// Establish a buffer for our IO channel so that we may do readline-style
			// ingestion of the IO, one log entry per line
			is := bufio.NewReader(r)

			// Acquire a custom logger for our chaincode, inheriting the level from the peer
			containerLogger := logging.MustGetLogger(containerID)
			logging.SetLevel(logging.GetLevel("peer"), containerID)

			for {
				// Loop forever dumping lines of text into the containerLogger
				// until the pipe is closed
				line, err := is.ReadString('\n')
				if err != nil {
					switch err {
					case io.EOF:
						dockerLogger.Infof("Container %s has closed its IO channel", containerID)
					default:
						dockerLogger.Errorf("Error reading container output: %s", err)
					}

					return
				}

				containerLogger.Info(line)
			}
		}()
	}

	// start container with HostConfig was deprecated since v1.10 and removed in v1.2
	err = client.StartContainer(containerID, nil)
	if err != nil {
		dockerLogger.Errorf("start-could not start container %s", err)
		return err
	}

	dockerLogger.Debugf("Started container %s", containerID)
	return nil
}

//Stop stops a running chaincode
func (vm *DockerVM) Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error {
	id, err := vm.GetVMName(ccid)
	if err != nil {
		return err
	}
	client, err := cutil.NewDockerClient()
	if err != nil {
		dockerLogger.Debugf("stop - cannot create client %s", err)
		return err
	}
	id = strings.Replace(id, ":", "_", -1)

	err = vm.stopInternal(ctxt, client, id, timeout, dontkill, dontremove)

	return err
}

func (vm *DockerVM) stopInternal(ctxt context.Context, client *docker.Client, id string, timeout uint, dontkill bool, dontremove bool) error {
	err := client.StopContainer(id, timeout)
	if err != nil {
		dockerLogger.Debugf("Stop container %s(%s)", id, err)
	} else {
		dockerLogger.Debugf("Stopped container %s", id)
	}
	if !dontkill {
		err = client.KillContainer(docker.KillContainerOptions{ID: id})
		if err != nil {
			dockerLogger.Debugf("Kill container %s (%s)", id, err)
		} else {
			dockerLogger.Debugf("Killed container %s", id)
		}
	}
	if !dontremove {
		err = client.RemoveContainer(docker.RemoveContainerOptions{ID: id, Force: true})
		if err != nil {
			dockerLogger.Debugf("Remove container %s (%s)", id, err)
		} else {
			dockerLogger.Debugf("Removed container %s", id)
		}
	}
	return err
}

//Destroy destroys an image
func (vm *DockerVM) Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error {
	id, err := vm.GetVMName(ccid)
	if err != nil {
		return err
	}
	client, err := cutil.NewDockerClient()
	if err != nil {
		dockerLogger.Errorf("destroy-cannot create client %s", err)
		return err
	}
	id = strings.Replace(id, ":", "_", -1)

	err = client.RemoveImageExtended(id, docker.RemoveImageOptions{Force: force, NoPrune: noprune})

	if err != nil {
		dockerLogger.Errorf("error while destroying image: %s", err)
	} else {
		dockerLogger.Debug("Destroyed image %s", id)
	}

	return err
}

//GetVMName generates the docker image from peer information given the hashcode. This is needed to
//keep image name's unique in a single host, multi-peer environment (such as a development environment)
func (vm *DockerVM) GetVMName(ccid ccintf.CCID) (string, error) {
	name := ccid.GetName()

	//Convert to lowercase and replace any invalid characters with "-"
	r := regexp.MustCompile("[^a-zA-Z0-9-_.]")

	if ccid.NetworkID != "" && ccid.PeerID != "" {
		name = strings.ToLower(
			r.ReplaceAllString(
				fmt.Sprintf("%s-%s-%s", ccid.NetworkID, ccid.PeerID, name), "-"))
	} else if ccid.NetworkID != "" {
		name = strings.ToLower(
			r.ReplaceAllString(
				fmt.Sprintf("%s-%s", ccid.NetworkID, name), "-"))
	} else if ccid.PeerID != "" {
		name = strings.ToLower(
			r.ReplaceAllString(
				fmt.Sprintf("%s-%s", ccid.PeerID, name), "-"))
	}

	// Check name complies with Docker's repository naming rules
	r = regexp.MustCompile("^[a-z0-9]+(([._-][a-z0-9]+)+)?$")

	if !r.MatchString(name) {
		dockerLogger.Errorf("Error constructing Docker VM Name. '%s' breaks Docker's repository naming rules", name)
		return name, fmt.Errorf("Error constructing Docker VM Name. '%s' breaks Docker's repository naming rules", name)
	}
	return name, nil
}
<|endoftext|>


<commit_before>package main

import (
  "strconv"
  "runtime"
  "os"
  "log"
)

// Version is the version number of the application.
const version = "Apollo v1.0.0"

// Create a configuration directory for Unis systems.
func makeUnixConfigDir() {
  err := os.Mkdir(os.Getenv("HOME")+"/.config/apollo", 0755)
	if err != nil {
		log.Print(err)
	}
}

// Returns the path of the database file.
func databasePath() string {
  if runtime.GOOS == "windows" {
    return "database.json"
  } else {
    makeUnixConfigDir()
    return os.Getenv("HOME") + "/.config/apollo/database.json"
  }
}

// Returns the path of the configuration file.
func configurationPath() string {
  if runtime.GOOS == "windows" {
    return "configuration.json"
  } else {
    makeUnixConfigDir()
    return os.Getenv("HOME") + "/.config/apollo/configuration.json"
  }
}

// PrintHelp prints out the help guide to the logs.
func (a *Apollo) printHelp() {
	s := []string{
		"{b}*───( Main Help Guide )───*",
		"{b}│ {d}List of key-bindings:",
		"{b}│ {d}Ctrl+C.........Close this software",
		"{b}│ {d}Alt+Num........Go to the [num]th tab",
		"{b}│ {d}Enter..........Send, or toggle between input bar and tab",
		"{b}│",
		"{b}│ {d}List of commands:",
		"{b}│ {d}(For more details, use /help <command name>)",
		"{b}│ {d}/help..........Show this help guide",
		"{b}│ {d}/quit..........Close this software",
		"{b}│ {d}/open..........Create a new tab",
		"{b}│ {d}/close.........Close the current tab",
		"{b}│ {d}/set...........Set a configuration option",
		"{b}│ {d}/config........Show the current configuration",
		"{b}│ {d}/stats.........Prints some stats about the entries",
		"{b}*───*",
	}

	for i := 0; i < len(s); i++ {
		a.log(s[i])
	}
}

// PrintDetailedHelp prints out the detailed help of a function to the logs.
func (a *Apollo) printDetailedHelp(subject string) {
	var s []string
	switch subject {
	case "help":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/help",
			"{b}│ {d}/help <command name>",
			"{b}│",
			"{b}│ {d}Displays a help guide.",
			"{b}*───*",
		}
	case "quit":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/quit",
			"{b}│",
			"{b}│ {d}Closes this software.",
			"{b}*───*",
		}
	case "open":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/open <tab name>",
			"{b}│",
			"{b}│ {d}Opens a given tab or, if it already exists, selects it.",
			"{b}│ {d}Tabs available: anime, books, games, movies, series.",
			"{b}│ {d}For keybindings, use '/help tabs'",
			"{b}*───*",
		}
	case "close":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/close",
			"{b}│",
			"{b}│ {d}Close the current tab.",
			"{b}*───*",
		}
	case "set":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/set <option> <value>",
			"{b}│",
			"{b}│ {d}Sets a configuration option to a specific value.",
			"{b}*───*",
		}
	case "config":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/config",
			"{b}│",
			"{b}│ {d}Shows the configuration options and their values.",
			"{b}*───*",
		}
	case "stats":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/stats",
			"{b}│",
			"{b}│ {d}Prints statistics about the database entries.",
			"{b}*───*",
		}
	case "tabs":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}List of key-bindings:",
			"{b}│ {d}1..............Switch to passive view",
			"{b}│ {d}2..............Switch to active view",
			"{b}│ {d}3..............Switch to inactive view",
			"{b}│ {d}4..............Switch to all view",
			"{b}│ {d}s..............Sort the entries",
			"{b}│ {d}D..............Delete the current entry",
			"{b}│ {d}e..............Edit the current entry",
			"{b}│ {d}t..............Tag the current entry",
			"{b}│ {d}r..............Toggle ratings",
			"{b}│ {d}a..............Toggle the current entry's state",
			"{b}│ {d}z/x............Change the rating of the current entry",
			"{b}│ {d}c/v............Change the current episode of an entry",
			"{b}│ {d}p..............Print the current entries to a file",
			"{b}*───*",
		}
	default:
		s = []string{
			"{b}│ {d}Detailed help does not exist for this command.",
		}
	}

	for i := 0; i < len(s); i++ {
		a.log(s[i])
	}
}

// PrintWelcome prints out the welcome message to the logs.
func (a *Apollo) printWelcome() {
	a.log("{b}*───( " + version + " )───*")
	a.log("{b}│ {d}This software is licensed under the MIT License.")
	a.log("{b}│ {d}To get started, use /help.")
	a.log("{b}*───*")
}

// PrintConfig prints out the list of configuration options to the logs.
func (a *Apollo) printConfig() {
	a.log("{b}*───( Current Configuration )───*")
	for _, value := range a.c.config() {
		a.log("{b}│ {d}" + value)
	}
	a.log("{b}*───*")
}

// PrintStats prints out relevant statistics about the database.
func (a *Apollo) printStats() {
	totalMovies := 0
	watchedMovies := 0
	for i := range a.d.Movies {
		totalMovies++
		if a.d.Movies[i].State == "passive" {
			watchedMovies++
		}
	}

	totalAnime := 0
	watchedAnime := 0
	totalAnimeEp := 0
	watchedAnimeEp := 0
	for i := range a.d.Anime {
		totalAnime++
		if a.d.Anime[i].State == "passive" {
			watchedAnime++
		}
		totalAnimeEp += a.d.Anime[i].EpisodeTotal
		watchedAnimeEp += a.d.Anime[i].EpisodeDone
	}

	totalGames := 0
	playedGames := 0
	for i := range a.d.Games {
		totalGames++
		if a.d.Games[i].State == "passive" {
			playedGames++
		}
	}

	totalBooks := 0
	readBooks := 0
	for i := range a.d.Books {
		totalBooks++
		if a.d.Books[i].State == "passive" {
			readBooks++
		}
	}

	sTotalMovies := strconv.Itoa(totalMovies)
	sWatchedMovies := strconv.Itoa(watchedMovies)
	sTotalAnime := strconv.Itoa(totalAnime)
	sWatchedAnime := strconv.Itoa(watchedAnime)
	sTotalAnimeEp := strconv.Itoa(totalAnimeEp)
	sWatchedAnimeEp := strconv.Itoa(watchedAnimeEp)
	sTotalGames := strconv.Itoa(totalGames)
	sPlayedGames := strconv.Itoa(playedGames)
	sTotalBooks := strconv.Itoa(totalBooks)
	sReadBooks := strconv.Itoa(readBooks)

	a.log("{b}*───( Statistics )───*")
	a.log("{b}│ {d}Movies watched: " + sWatchedMovies + "/" + sTotalMovies)
	a.log("{b}│ {d}Anime seasons watched: " + sWatchedAnime + "/" + sTotalAnime)
	a.log("{b}│ {d} Episodes watched: " + sWatchedAnimeEp + "/" + sTotalAnimeEp)
	a.log("{b}│ {d}Games played: " + sPlayedGames + "/" + sTotalGames)
	a.log("{b}│ {d}Books read: " + sReadBooks + "/" + sTotalBooks)
	a.log("{b}*───*")
}
<commit_msg>Run gofmt to make file consistent<commit_after>package main

import (
	"log"
	"os"
	"runtime"
	"strconv"
)

// Version is the version number of the application.
const version = "Apollo v1.0.0"

// Create a configuration directory for Unis systems.
func makeUnixConfigDir() {
	err := os.Mkdir(os.Getenv("HOME")+"/.config/apollo", 0755)
	if err != nil {
		log.Print(err)
	}
}

// Returns the path of the database file.
func databasePath() string {
	if runtime.GOOS == "windows" {
		return "database.json"
	} else {
		makeUnixConfigDir()
		return os.Getenv("HOME") + "/.config/apollo/database.json"
	}
}

// Returns the path of the configuration file.
func configurationPath() string {
	if runtime.GOOS == "windows" {
		return "configuration.json"
	} else {
		makeUnixConfigDir()
		return os.Getenv("HOME") + "/.config/apollo/configuration.json"
	}
}

// PrintHelp prints out the help guide to the logs.
func (a *Apollo) printHelp() {
	s := []string{
		"{b}*───( Main Help Guide )───*",
		"{b}│ {d}List of key-bindings:",
		"{b}│ {d}Ctrl+C.........Close this software",
		"{b}│ {d}Alt+Num........Go to the [num]th tab",
		"{b}│ {d}Enter..........Send, or toggle between input bar and tab",
		"{b}│",
		"{b}│ {d}List of commands:",
		"{b}│ {d}(For more details, use /help <command name>)",
		"{b}│ {d}/help..........Show this help guide",
		"{b}│ {d}/quit..........Close this software",
		"{b}│ {d}/open..........Create a new tab",
		"{b}│ {d}/close.........Close the current tab",
		"{b}│ {d}/set...........Set a configuration option",
		"{b}│ {d}/config........Show the current configuration",
		"{b}│ {d}/stats.........Prints some stats about the entries",
		"{b}*───*",
	}

	for i := 0; i < len(s); i++ {
		a.log(s[i])
	}
}

// PrintDetailedHelp prints out the detailed help of a function to the logs.
func (a *Apollo) printDetailedHelp(subject string) {
	var s []string
	switch subject {
	case "help":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/help",
			"{b}│ {d}/help <command name>",
			"{b}│",
			"{b}│ {d}Displays a help guide.",
			"{b}*───*",
		}
	case "quit":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/quit",
			"{b}│",
			"{b}│ {d}Closes this software.",
			"{b}*───*",
		}
	case "open":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/open <tab name>",
			"{b}│",
			"{b}│ {d}Opens a given tab or, if it already exists, selects it.",
			"{b}│ {d}Tabs available: anime, books, games, movies, series.",
			"{b}│ {d}For keybindings, use '/help tabs'",
			"{b}*───*",
		}
	case "close":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/close",
			"{b}│",
			"{b}│ {d}Close the current tab.",
			"{b}*───*",
		}
	case "set":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/set <option> <value>",
			"{b}│",
			"{b}│ {d}Sets a configuration option to a specific value.",
			"{b}*───*",
		}
	case "config":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/config",
			"{b}│",
			"{b}│ {d}Shows the configuration options and their values.",
			"{b}*───*",
		}
	case "stats":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}/stats",
			"{b}│",
			"{b}│ {d}Prints statistics about the database entries.",
			"{b}*───*",
		}
	case "tabs":
		s = []string{
			"{b}*───( Detailed Help Guide )───*",
			"{b}│ {d}List of key-bindings:",
			"{b}│ {d}1..............Switch to passive view",
			"{b}│ {d}2..............Switch to active view",
			"{b}│ {d}3..............Switch to inactive view",
			"{b}│ {d}4..............Switch to all view",
			"{b}│ {d}s..............Sort the entries",
			"{b}│ {d}D..............Delete the current entry",
			"{b}│ {d}e..............Edit the current entry",
			"{b}│ {d}t..............Tag the current entry",
			"{b}│ {d}r..............Toggle ratings",
			"{b}│ {d}a..............Toggle the current entry's state",
			"{b}│ {d}z/x............Change the rating of the current entry",
			"{b}│ {d}c/v............Change the current episode of an entry",
			"{b}│ {d}p..............Print the current entries to a file",
			"{b}*───*",
		}
	default:
		s = []string{
			"{b}│ {d}Detailed help does not exist for this command.",
		}
	}

	for i := 0; i < len(s); i++ {
		a.log(s[i])
	}
}

// PrintWelcome prints out the welcome message to the logs.
func (a *Apollo) printWelcome() {
	a.log("{b}*───( " + version + " )───*")
	a.log("{b}│ {d}This software is licensed under the MIT License.")
	a.log("{b}│ {d}To get started, use /help.")
	a.log("{b}*───*")
}

// PrintConfig prints out the list of configuration options to the logs.
func (a *Apollo) printConfig() {
	a.log("{b}*───( Current Configuration )───*")
	for _, value := range a.c.config() {
		a.log("{b}│ {d}" + value)
	}
	a.log("{b}*───*")
}

// PrintStats prints out relevant statistics about the database.
func (a *Apollo) printStats() {
	totalMovies := 0
	watchedMovies := 0
	for i := range a.d.Movies {
		totalMovies++
		if a.d.Movies[i].State == "passive" {
			watchedMovies++
		}
	}

	totalAnime := 0
	watchedAnime := 0
	totalAnimeEp := 0
	watchedAnimeEp := 0
	for i := range a.d.Anime {
		totalAnime++
		if a.d.Anime[i].State == "passive" {
			watchedAnime++
		}
		totalAnimeEp += a.d.Anime[i].EpisodeTotal
		watchedAnimeEp += a.d.Anime[i].EpisodeDone
	}

	totalGames := 0
	playedGames := 0
	for i := range a.d.Games {
		totalGames++
		if a.d.Games[i].State == "passive" {
			playedGames++
		}
	}

	totalBooks := 0
	readBooks := 0
	for i := range a.d.Books {
		totalBooks++
		if a.d.Books[i].State == "passive" {
			readBooks++
		}
	}

	sTotalMovies := strconv.Itoa(totalMovies)
	sWatchedMovies := strconv.Itoa(watchedMovies)
	sTotalAnime := strconv.Itoa(totalAnime)
	sWatchedAnime := strconv.Itoa(watchedAnime)
	sTotalAnimeEp := strconv.Itoa(totalAnimeEp)
	sWatchedAnimeEp := strconv.Itoa(watchedAnimeEp)
	sTotalGames := strconv.Itoa(totalGames)
	sPlayedGames := strconv.Itoa(playedGames)
	sTotalBooks := strconv.Itoa(totalBooks)
	sReadBooks := strconv.Itoa(readBooks)

	a.log("{b}*───( Statistics )───*")
	a.log("{b}│ {d}Movies watched: " + sWatchedMovies + "/" + sTotalMovies)
	a.log("{b}│ {d}Anime seasons watched: " + sWatchedAnime + "/" + sTotalAnime)
	a.log("{b}│ {d} Episodes watched: " + sWatchedAnimeEp + "/" + sTotalAnimeEp)
	a.log("{b}│ {d}Games played: " + sPlayedGames + "/" + sTotalGames)
	a.log("{b}│ {d}Books read: " + sReadBooks + "/" + sTotalBooks)
	a.log("{b}*───*")
}
<|endoftext|>