<commit_before>/*
 * Mini Copy, (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 (
	"io"

	"github.com/cheggaaa/pb"
	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/console"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/utils/log"
)

type clientManager interface {
	getSourceReader(urlStr string) (reader io.ReadCloser, length int64, md5hex string, err error)
	getTargetWriter(urlStr string, md5Hex string, length int64) (io.WriteCloser, error)
}

type mcClientManager struct{}

func (manager mcClientManager) getSourceReader(urlStr string) (reader io.ReadCloser, length int64, md5hex string, err error) {
	sourceClnt, err := getNewClient(urlStr, globalDebugFlag)
	if err != nil {
		return nil, 0, "", iodine.New(err, map[string]string{"sourceURL": urlStr})
	}
	// Get a reader for the source object
	bucket, object, err := url2Object(urlStr)
	if err != nil {
		return nil, 0, "", iodine.New(err, map[string]string{"sourceURL": urlStr})
	}

	// check if the bucket is valid
	if err := sourceClnt.StatBucket(bucket); err != nil {
		return nil, 0, "", iodine.New(err, map[string]string{"sourceURL": urlStr})
	}
	return sourceClnt.Get(bucket, object)
}

func (manager mcClientManager) getTargetWriter(urlStr string, md5Hex string, length int64) (io.WriteCloser, error) {
	targetClnt, err := getNewClient(urlStr, globalDebugFlag)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	bucket, object, err := url2Object(urlStr)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	// check if bucket is valid
	if err := targetClnt.StatBucket(bucket); err != nil {
		return nil, iodine.New(err, map[string]string{"failedURL": urlStr})
	}
	return targetClnt.Put(bucket, object, md5Hex, length)
}

func getTargetWriters(manager clientManager, urls []string, md5Hex string, length int64) ([]io.WriteCloser, error) {
	var targetWriters []io.WriteCloser
	for _, u := range urls {
		writer, err := manager.getTargetWriter(u, md5Hex, length)
		if err != nil {
			// close all writers
			for _, targetWriter := range targetWriters {
				targetWriter.Close()
			}
			return nil, iodine.New(errInvalidURL{url: u}, nil)
		}
		targetWriters = append(targetWriters, writer)
	}
	return targetWriters, nil
}

// doCopyCmd copies objects into and from a bucket or between buckets
func runCopyCmd(ctx *cli.Context) {
	if len(ctx.Args()) < 2 {
		cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code
	}

	config, err := loadMcConfig()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to load config file")
	}

	// Convert arguments to URLs: expand alias, fix format...
	urls, err := parseURLs(ctx.Args(), config.Aliases)
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to parse URL")
	}
	sourceURL := urls[0]   // First arg is source
	targetURLs := urls[1:] // 1 or more targets

	// perform copy
	if ctx.Bool("recursive") {
		doCopyCmdRecursive(ctx)
	} else {
		humanReadableError, err := doCopyCmd(mcClientManager{}, sourceURL, targetURLs)
		err = iodine.New(err, nil)
		if err != nil {
			if humanReadableError == "" {
				humanReadableError = "No error message present, please rerun with --debug and report a bug."
			}
			log.Debug.Println(err)
			console.Errorln("mc: " + humanReadableError)
		}
	}
}

func doCopyCmd(manager clientManager, sourceURL string, targetURLs []string) (string, error) {
	reader, length, hexMd5, err := manager.getSourceReader(sourceURL)
	if err != nil {
		return "Unable to read from source", iodine.New(err, nil)
		//		console.Errorln("mc: Unable to read source")
	}
	defer reader.Close()

	writeClosers, err := getTargetWriters(manager, targetURLs, hexMd5, length)
	if err != nil {
		return "Unable to write to target", iodine.New(err, nil)
	}

	var writers []io.Writer
	for _, writer := range writeClosers {
		writers = append(writers, writer)
	}

	// set up progress bar
	var bar *pb.ProgressBar
	if !globalQuietFlag {
		bar = startBar(length)
		bar.Start()
		writers = append(writers, bar)
	}

	// write progress bar
	multiWriter := io.MultiWriter(writers...)

	// copy data to writers
	_, err = io.CopyN(multiWriter, reader, length)
	if err != nil {
		return "Copying data from source to target(s) failed", iodine.New(err, nil)
	}

	// close writers
	for _, writer := range writeClosers {
		err := writer.Close()
		if err != nil {
			err = iodine.New(err, nil)
		}
	}
	if err != nil {
		return "Unable to close all connections, write may of failed.", iodine.New(err, nil)
	}
	return "", nil
}
<commit_msg>Minor cleanup<commit_after>/*
 * Mini Copy, (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 (
	"io"

	"github.com/cheggaaa/pb"
	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/console"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/utils/log"
)

type clientManager interface {
	getSourceReader(urlStr string) (reader io.ReadCloser, length int64, md5hex string, err error)
	getTargetWriter(urlStr string, md5Hex string, length int64) (io.WriteCloser, error)
}

type mcClientManager struct{}

func (manager mcClientManager) getSourceReader(urlStr string) (reader io.ReadCloser, length int64, md5hex string, err error) {
	sourceClnt, err := getNewClient(urlStr, globalDebugFlag)
	if err != nil {
		return nil, 0, "", iodine.New(err, map[string]string{"sourceURL": urlStr})
	}
	// Get a reader for the source object
	bucket, object, err := url2Object(urlStr)
	if err != nil {
		return nil, 0, "", iodine.New(err, map[string]string{"sourceURL": urlStr})
	}

	// check if the bucket is valid
	if err := sourceClnt.StatBucket(bucket); err != nil {
		return nil, 0, "", iodine.New(err, map[string]string{"sourceURL": urlStr})
	}
	return sourceClnt.Get(bucket, object)
}

func (manager mcClientManager) getTargetWriter(urlStr string, md5Hex string, length int64) (io.WriteCloser, error) {
	targetClnt, err := getNewClient(urlStr, globalDebugFlag)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	bucket, object, err := url2Object(urlStr)
	if err != nil {
		return nil, iodine.New(err, nil)
	}

	// check if bucket is valid
	if err := targetClnt.StatBucket(bucket); err != nil {
		return nil, iodine.New(err, map[string]string{"failedURL": urlStr})
	}
	return targetClnt.Put(bucket, object, md5Hex, length)
}

func getTargetWriters(manager clientManager, urls []string, md5Hex string, length int64) ([]io.WriteCloser, error) {
	var targetWriters []io.WriteCloser
	for _, u := range urls {
		writer, err := manager.getTargetWriter(u, md5Hex, length)
		if err != nil {
			// close all writers
			for _, targetWriter := range targetWriters {
				targetWriter.Close()
			}
			return nil, iodine.New(errInvalidURL{url: u}, nil)
		}
		targetWriters = append(targetWriters, writer)
	}
	return targetWriters, nil
}

// doCopyCmd copies objects into and from a bucket or between buckets
func runCopyCmd(ctx *cli.Context) {
	if len(ctx.Args()) < 2 {
		cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code
	}

	config, err := loadMcConfig()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to load config file")
	}

	// Convert arguments to URLs: expand alias, fix format...
	urls, err := parseURLs(ctx.Args(), config.Aliases)
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to parse URL")
	}
	sourceURL := urls[0]   // First arg is source
	targetURLs := urls[1:] // 1 or more targets

	// perform copy
	if ctx.Bool("recursive") {
		doCopyCmdRecursive(ctx)
	} else {
		humanReadableError, err := doCopyCmd(mcClientManager{}, sourceURL, targetURLs)
		err = iodine.New(err, nil)
		if err != nil {
			if humanReadableError == "" {
				humanReadableError = "No error message present, please rerun with --debug and report a bug."
			}
			log.Debug.Println(err)
			console.Errorln("mc: " + humanReadableError)
		}
	}
}

func doCopyCmd(manager clientManager, sourceURL string, targetURLs []string) (string, error) {
	reader, length, hexMd5, err := manager.getSourceReader(sourceURL)
	if err != nil {
		return "Unable to read from source", iodine.New(err, nil)
	}
	defer reader.Close()

	writeClosers, err := getTargetWriters(manager, targetURLs, hexMd5, length)
	if err != nil {
		return "Unable to write to target", iodine.New(err, nil)
	}

	var writers []io.Writer
	for _, writer := range writeClosers {
		writers = append(writers, writer)
	}

	// set up progress bar
	var bar *pb.ProgressBar
	if !globalQuietFlag {
		bar = startBar(length)
		bar.Start()
		writers = append(writers, bar)
	}

	// write progress bar
	multiWriter := io.MultiWriter(writers...)

	// copy data to writers
	_, err = io.CopyN(multiWriter, reader, length)
	if err != nil {
		return "Copying data from source to target(s) failed", iodine.New(err, nil)
	}

	// close writers
	for _, writer := range writeClosers {
		err := writer.Close()
		if err != nil {
			err = iodine.New(err, nil)
		}
	}
	if err != nil {
		return "Unable to close all connections, write may of failed.", iodine.New(err, nil)
	}
	return "", nil
}
<|endoftext|>


<commit_before>//  Copyright (c) 2017 Couchbase, 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 levenshtein

import (
	"encoding/binary"
	"fmt"
	"unicode"

	unicode_utf8 "unicode/utf8"

	"github.com/couchbase/vellum/utf8"
)

type dfa struct {
	states statesStack
}

type state struct {
	next  []int
	match bool
}

func (s *state) String() string {
	rv := "  |"
	for i := 0; i < 16; i++ {
		rv += fmt.Sprintf("% 5x", i)
	}
	rv += "\n"
	for i := 0; i < len(s.next); i++ {
		if i%16 == 0 {
			rv += fmt.Sprintf("%x |", i/16)
		}
		if s.next[i] != 0 {
			rv += fmt.Sprintf("% 5d", s.next[i])
		} else {
			rv += "    -"
		}
		if i%16 == 15 {
			rv += "\n"
		}
	}
	return rv
}

type dfaBuilder struct {
	dfa    *dfa
	lev    *dynamicLevenshtein
	cache  map[string]int
	keyBuf []byte

	sequences  utf8.Sequences
	rangeStack utf8.RangeStack
	startBytes []byte
	endBytes   []byte
}

func newDfaBuilder(lev *dynamicLevenshtein) *dfaBuilder {
	dfab := &dfaBuilder{
		dfa: &dfa{
			states: make([]state, 0, 16),
		},
		lev:        lev,
		cache:      make(map[string]int, 1024),
		startBytes: make([]byte, unicode_utf8.UTFMax),
		endBytes:   make([]byte, unicode_utf8.UTFMax),
	}
	dfab.newState(false) // create state 0, invalid
	return dfab
}

func (b *dfaBuilder) build() (*dfa, error) {
	var stack intsStack
	stack = stack.Push(b.lev.start())
	seen := make(map[int]struct{})

	var levState []int
	stack, levState = stack.Pop()
	for levState != nil {
		dfaSi := b.cachedState(levState)
		mmToSi, mmMismatchState, err := b.addMismatchUtf8States(dfaSi, levState)
		if err != nil {
			return nil, err
		}
		if mmToSi != 0 {
			if _, ok := seen[mmToSi]; !ok {
				seen[mmToSi] = struct{}{}
				stack = stack.Push(mmMismatchState)
			}
		}

		i := 0
		for _, r := range b.lev.query {
			if uint(levState[i]) > b.lev.distance {
				i++
				continue
			}
			levNext := b.lev.accept(levState, &r)
			nextSi := b.cachedState(levNext)
			if nextSi != 0 {
				err = b.addUtf8Sequences(true, dfaSi, nextSi, r, r)
				if err != nil {
					return nil, err
				}
				if _, ok := seen[nextSi]; !ok {
					seen[nextSi] = struct{}{}
					stack = stack.Push(levNext)
				}
			}
			i++
		}

		if len(b.dfa.states) > StateLimit {
			return nil, ErrTooManyStates
		}

		stack, levState = stack.Pop()
	}

	return b.dfa, nil
}

func (b *dfaBuilder) cachedState(levState []int) int {
	rv, _ := b.cached(levState)
	return rv
}

func levStateKey(levState []int, buf []byte) []byte {
	if cap(buf) < 8*len(levState) {
		buf = make([]byte, 8*len(levState))
	} else {
		buf = buf[0 : 8*len(levState)]
	}
	for i, state := range levState {
		binary.LittleEndian.PutUint64(buf[i*8:], uint64(state))
	}
	return buf
}

func (b *dfaBuilder) cached(levState []int) (int, bool) {
	if !b.lev.canMatch(levState) {
		return 0, true
	}
	b.keyBuf = levStateKey(levState, b.keyBuf)
	v, ok := b.cache[string(b.keyBuf)]
	if ok {
		return v, true
	}
	match := b.lev.isMatch(levState)
	b.dfa.states = append(b.dfa.states, state{
		next:  make([]int, 256),
		match: match,
	})
	newV := len(b.dfa.states) - 1
	b.cache[string(b.keyBuf)] = newV
	return newV, false
}

func (b *dfaBuilder) addMismatchUtf8States(fromSi int, levState []int) (int, []int, error) {
	mmState := b.lev.accept(levState, nil)
	toSi, _ := b.cached(mmState)
	if toSi == 0 {
		return 0, nil, nil
	}
	err := b.addUtf8Sequences(false, fromSi, toSi, 0, unicode.MaxRune)
	if err != nil {
		return 0, nil, err
	}
	return toSi, mmState, nil
}

func (b *dfaBuilder) addUtf8Sequences(overwrite bool, fromSi, toSi int, fromChar, toChar rune) (
	err error) {
	b.sequences, b.rangeStack, err = utf8.NewSequencesPrealloc(fromChar, toChar,
		b.sequences, b.rangeStack, b.startBytes, b.endBytes)
	if err != nil {
		return err
	}
	for _, seq := range b.sequences {
		fsi := fromSi
		for _, utf8r := range seq[:len(seq)-1] {
			tsi := b.newState(false)
			b.addUtf8Range(overwrite, fsi, tsi, utf8r)
			fsi = tsi
		}
		b.addUtf8Range(overwrite, fsi, toSi, seq[len(seq)-1])
	}
	return nil
}

func (b *dfaBuilder) addUtf8Range(overwrite bool, from, to int, rang utf8.Range) {
	for by := rang.Start; by <= rang.End; by++ {
		if overwrite || b.dfa.states[from].next[by] == 0 {
			b.dfa.states[from].next[by] = to
		}
	}
}

func (b *dfaBuilder) newState(match bool) int {
	b.dfa.states = append(b.dfa.states, state{
		next:  make([]int, 256),
		match: match,
	})
	return len(b.dfa.states) - 1
}
<commit_msg>optimize levenshtein dfa builder nextState() with prealloced slices<commit_after>//  Copyright (c) 2017 Couchbase, 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 levenshtein

import (
	"encoding/binary"
	"fmt"
	"unicode"

	unicode_utf8 "unicode/utf8"

	"github.com/couchbase/vellum/utf8"
)

type dfa struct {
	states statesStack
}

type state struct {
	next  []int
	match bool
}

func (s *state) String() string {
	rv := "  |"
	for i := 0; i < 16; i++ {
		rv += fmt.Sprintf("% 5x", i)
	}
	rv += "\n"
	for i := 0; i < len(s.next); i++ {
		if i%16 == 0 {
			rv += fmt.Sprintf("%x |", i/16)
		}
		if s.next[i] != 0 {
			rv += fmt.Sprintf("% 5d", s.next[i])
		} else {
			rv += "    -"
		}
		if i%16 == 15 {
			rv += "\n"
		}
	}
	return rv
}

type dfaBuilder struct {
	dfa    *dfa
	lev    *dynamicLevenshtein
	cache  map[string]int
	keyBuf []byte

	sequences  utf8.Sequences
	rangeStack utf8.RangeStack
	startBytes []byte
	endBytes   []byte
	nexts      []int
}

func newDfaBuilder(lev *dynamicLevenshtein) *dfaBuilder {
	dfab := &dfaBuilder{
		dfa: &dfa{
			states: make([]state, 0, 16),
		},
		lev:        lev,
		cache:      make(map[string]int, 1024),
		startBytes: make([]byte, unicode_utf8.UTFMax),
		endBytes:   make([]byte, unicode_utf8.UTFMax),
	}
	_, dfab.nexts = dfab.newState(false, nil) // create state 0, invalid
	return dfab
}

func (b *dfaBuilder) build() (*dfa, error) {
	var stack intsStack
	stack = stack.Push(b.lev.start())
	seen := make(map[int]struct{})

	var levState []int
	stack, levState = stack.Pop()
	for levState != nil {
		dfaSi := b.cachedState(levState)
		mmToSi, mmMismatchState, err := b.addMismatchUtf8States(dfaSi, levState)
		if err != nil {
			return nil, err
		}
		if mmToSi != 0 {
			if _, ok := seen[mmToSi]; !ok {
				seen[mmToSi] = struct{}{}
				stack = stack.Push(mmMismatchState)
			}
		}

		i := 0
		for _, r := range b.lev.query {
			if uint(levState[i]) > b.lev.distance {
				i++
				continue
			}
			levNext := b.lev.accept(levState, &r)
			nextSi := b.cachedState(levNext)
			if nextSi != 0 {
				err = b.addUtf8Sequences(true, dfaSi, nextSi, r, r)
				if err != nil {
					return nil, err
				}
				if _, ok := seen[nextSi]; !ok {
					seen[nextSi] = struct{}{}
					stack = stack.Push(levNext)
				}
			}
			i++
		}

		if len(b.dfa.states) > StateLimit {
			return nil, ErrTooManyStates
		}

		stack, levState = stack.Pop()
	}

	return b.dfa, nil
}

func (b *dfaBuilder) cachedState(levState []int) int {
	rv, _ := b.cached(levState)
	return rv
}

func levStateKey(levState []int, buf []byte) []byte {
	if cap(buf) < 8*len(levState) {
		buf = make([]byte, 8*len(levState))
	} else {
		buf = buf[0 : 8*len(levState)]
	}
	for i, state := range levState {
		binary.LittleEndian.PutUint64(buf[i*8:], uint64(state))
	}
	return buf
}

func (b *dfaBuilder) cached(levState []int) (int, bool) {
	if !b.lev.canMatch(levState) {
		return 0, true
	}
	b.keyBuf = levStateKey(levState, b.keyBuf)
	v, ok := b.cache[string(b.keyBuf)]
	if ok {
		return v, true
	}
	match := b.lev.isMatch(levState)
	b.dfa.states = append(b.dfa.states, state{
		next:  make([]int, 256),
		match: match,
	})
	newV := len(b.dfa.states) - 1
	b.cache[string(b.keyBuf)] = newV
	return newV, false
}

func (b *dfaBuilder) addMismatchUtf8States(fromSi int, levState []int) (int, []int, error) {
	mmState := b.lev.accept(levState, nil)
	toSi, _ := b.cached(mmState)
	if toSi == 0 {
		return 0, nil, nil
	}
	err := b.addUtf8Sequences(false, fromSi, toSi, 0, unicode.MaxRune)
	if err != nil {
		return 0, nil, err
	}
	return toSi, mmState, nil
}

func (b *dfaBuilder) addUtf8Sequences(overwrite bool, fromSi, toSi int, fromChar, toChar rune) (
	err error) {
	b.sequences, b.rangeStack, err = utf8.NewSequencesPrealloc(fromChar, toChar,
		b.sequences, b.rangeStack, b.startBytes, b.endBytes)
	if err != nil {
		return err
	}
	for _, seq := range b.sequences {
		fsi := fromSi
		for _, utf8r := range seq[:len(seq)-1] {
			var tsi int
			tsi, b.nexts = b.newState(false, b.nexts)
			b.addUtf8Range(overwrite, fsi, tsi, utf8r)
			fsi = tsi
		}
		b.addUtf8Range(overwrite, fsi, toSi, seq[len(seq)-1])
	}
	return nil
}

func (b *dfaBuilder) addUtf8Range(overwrite bool, from, to int, rang utf8.Range) {
	for by := rang.Start; by <= rang.End; by++ {
		if overwrite || b.dfa.states[from].next[by] == 0 {
			b.dfa.states[from].next[by] = to
		}
	}
}

func (b *dfaBuilder) newState(match bool, prealloc []int) (int, []int) {
	if len(prealloc) < 256 {
		prealloc = make([]int, 16384)
	}
	next := prealloc[0:256]
	prealloc = prealloc[256:]

	b.dfa.states = append(b.dfa.states, state{
		next:  next,
		match: match,
	})

	return len(b.dfa.states) - 1, prealloc
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Huawei Technologies Co., Ltd. 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 lvm

import (
	"errors"
	"fmt"
	"os/exec"
	"path"
	"strconv"
	"strings"

	log "github.com/golang/glog"
	"github.com/opensds/opensds/contrib/drivers/lvm/targets"
	. "github.com/opensds/opensds/contrib/drivers/utils/config"
	pb "github.com/opensds/opensds/pkg/dock/proto"
	"github.com/opensds/opensds/pkg/model"
	"github.com/opensds/opensds/pkg/utils/config"
	"github.com/satori/go.uuid"
)

const (
	defaultConfPath = "/etc/opensds/driver/lvm.yaml"
)

type LVMConfig struct {
	TgtBindIp string                    `yaml:"tgtBindIp"`
	Pool      map[string]PoolProperties `yaml:"pool,flow"`
}

type Driver struct {
	conf *LVMConfig

	handler func(script string, cmd []string) (string, error)
}

func (d *Driver) Setup() error {
	// Read lvm config file
	d.conf = &LVMConfig{TgtBindIp: "127.0.0.1"}
	p := config.CONF.OsdsDock.Backends.LVM.ConfigPath
	if "" == p {
		p = defaultConfPath
	}
	if _, err := Parse(d.conf, p); err != nil {
		return err
	}
	d.handler = execCmd

	return nil
}

func (*Driver) Unset() error { return nil }

func (d *Driver) CreateVolume(opt *pb.CreateVolumeOpts) (*model.VolumeSpec, error) {
	var size = fmt.Sprint(opt.GetSize()) + "G"
	var polName = opt.GetPoolName()

	if _, err := d.handler("lvcreate", []string{
		"-Z", "n",
		"-n", opt.GetName(),
		"-L", size,
		polName,
	}); err != nil {
		log.Error("Failed to create logic volume:", err)
		return nil, err
	}

	var lvPath, lvStatus string
	// Display and parse some metadata in logic volume returned.
	lvPath = path.Join("/dev", polName, opt.GetName())
	lv, err := d.handler("lvdisplay", []string{lvPath})
	if err != nil {
		log.Error("Failed to display logic volume:", err)
		return nil, err
	}

	for _, line := range strings.Split(lv, "\n") {
		if strings.Contains(line, "LV Path") {
			lvPath = strings.Fields(line)[2]
		}
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSpec{
		BaseModel: &model.BaseModel{
			Id: uuid.NewV4().String(),
		},
		Name:        opt.GetName(),
		Size:        opt.GetSize(),
		Description: opt.GetDescription(),
		Status:      lvStatus,
		Metadata: map[string]string{
			"lvPath": lvPath,
		},
	}, nil
}

func (d *Driver) PullVolume(volIdentifier string) (*model.VolumeSpec, error) {
	// Display and parse some metadata in logic volume returned.
	lv, err := d.handler("lvdisplay", []string{volIdentifier})
	if err != nil {
		log.Error("Failed to display logic volume:", err)
		return nil, err
	}
	var lvStatus string
	for _, line := range strings.Split(lv, "\n") {
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSpec{
		Status: lvStatus,
	}, nil
}

func (d *Driver) DeleteVolume(opt *pb.DeleteVolumeOpts) error {
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume metadata!")
		log.Error(err)
		return err
	}
	if _, err := d.handler("lvremove", []string{
		"-f", lvPath,
	}); err != nil {
		log.Error("Failed to remove logic volume:", err)
		return err
	}

	return nil
}

// ExtendVolume ...
func (d *Driver) ExtendVolume(opt *pb.ExtendVolumeOpts) (*model.VolumeSpec, error) {
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("failed to find logic volume path in volume metadata")
		log.Error(err)
		return nil, err
	}

	var size = fmt.Sprint(opt.GetSize()) + "G"

	if _, err := d.handler("lvresize", []string{
		"-L", size,
		lvPath,
	}); err != nil {
		log.Error("Failed to extend logic volume:", err)
		return nil, err
	}

	return &model.VolumeSpec{
		BaseModel: &model.BaseModel{
			Id: opt.GetId(),
		},
		Name:        opt.GetName(),
		Size:        opt.GetSize(),
		Description: opt.GetDescription(),
		Metadata:    opt.GetMetadata(),
	}, nil
}

func (d *Driver) InitializeConnection(opt *pb.CreateAttachmentOpts) (*model.ConnectionInfo, error) {
	var initiator string
	if initiator = opt.HostInfo.GetInitiator(); initiator == "" {
		initiator = "ALL"
	}
	// TODO	Add lvm path in Metadata field.
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume attachment metadata!")
		log.Error(err)
		return nil, err
	}

	t := targets.NewTarget(d.conf.TgtBindIp)
	expt, err := t.CreateExport(lvPath, initiator)
	if err != nil {
		log.Error("Failed to initialize connection of logic volume:", err)
		return nil, err
	}

	return &model.ConnectionInfo{
		DriverVolumeType: "iscsi",
		ConnectionData:   expt,
	}, nil
}

func (d *Driver) TerminateConnection(opt *pb.DeleteAttachmentOpts) error {
	var initiator string
	if initiator = opt.HostInfo.GetInitiator(); initiator == "" {
		initiator = "ALL"
	}
	// TODO	Add lvm path in Metadata field.
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume attachment metadata!")
		log.Error(err)
		return err
	}

	t := targets.NewTarget(d.conf.TgtBindIp)
	if err := t.RemoveExport(lvPath, initiator); err != nil {
		log.Error("Failed to initialize connection of logic volume:", err)
		return err
	}

	return nil
}

func (d *Driver) CreateSnapshot(opt *pb.CreateVolumeSnapshotOpts) (*model.VolumeSnapshotSpec, error) {
	var size = fmt.Sprint(opt.GetSize()) + "G"
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume snapshot metadata!")
		log.Error(err)
		return nil, err
	}

	if _, err := d.handler("lvcreate", []string{
		"-n", opt.GetName(),
		"-L", size,
		"-p", "r",
		"-s", lvPath,
	}); err != nil {
		log.Error("Failed to create logic volume snapshot:", err)
		return nil, err
	}

	var lvsDir, lvsPath string
	lvsDir, _ = path.Split(lvPath)
	lvsPath = path.Join(lvsDir, opt.GetName())
	// Display and parse some metadata in logic volume snapshot returned.
	lvs, err := d.handler("lvdisplay", []string{lvsPath})
	if err != nil {
		log.Error("Failed to display logic volume snapshot:", err)
		return nil, err
	}
	var lvStatus string
	for _, line := range strings.Split(lvs, "\n") {
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSnapshotSpec{
		BaseModel: &model.BaseModel{
			Id: uuid.NewV4().String(),
		},
		Name:        opt.GetName(),
		Size:        opt.GetSize(),
		Description: opt.GetDescription(),
		Status:      lvStatus,
		VolumeId:    opt.GetVolumeId(),
		Metadata: map[string]string{
			"lvsPath": lvsPath,
		},
	}, nil
}

func (d *Driver) PullSnapshot(snapIdentifier string) (*model.VolumeSnapshotSpec, error) {
	// Display and parse some metadata in logic volume snapshot returned.
	lv, err := d.handler("lvdisplay", []string{snapIdentifier})
	if err != nil {
		log.Error("Failed to display logic volume snapshot:", err)
		return nil, err
	}
	var lvStatus string
	for _, line := range strings.Split(lv, "\n") {
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSnapshotSpec{
		Status: lvStatus,
	}, nil
}

func (d *Driver) DeleteSnapshot(opt *pb.DeleteVolumeSnapshotOpts) error {
	lvsPath, ok := opt.GetMetadata()["lvsPath"]
	if !ok {
		err := errors.New("Failed to find logic volume snapshot path in volume snapshot metadata!")
		log.Error(err)
		return err
	}
	if _, err := d.handler("lvremove", []string{
		"-f", lvsPath,
	}); err != nil {
		log.Error("Failed to remove logic volume:", err)
		return err
	}

	return nil
}

type VolumeGroup struct {
	Name          string
	TotalCapacity int64
	FreeCapacity  int64
}

func (d *Driver) getVGList() (*[]VolumeGroup, error) {
	const vgInfoLineCount = 10
	info, err := d.handler("vgdisplay", []string{})
	if err != nil {
		return nil, err
	}

	lines := strings.Split(info, "\n")
	vgs := make([]VolumeGroup, len(lines)/vgInfoLineCount)

	var vgIdx = -1
	for _, line := range lines {
		if strings.Contains(line, "--- Volume group ---") {
			vgIdx++
			continue
		}
		if strings.Contains(line, "VG Name") {
			slice := strings.Fields(line)
			vgs[vgIdx].Name = slice[2]
		}
		if strings.Contains(line, "VG Size") {
			slice := strings.Fields(line)
			capa, _ := strconv.ParseFloat(slice[2], 64)
			vgs[vgIdx].TotalCapacity = int64(capa)
		}
		if strings.Contains(line, "Free  PE / Size") {
			slice := strings.Fields(line)
			capa, _ := strconv.ParseFloat(slice[len(slice)-2], 64)
			vgs[vgIdx].FreeCapacity = int64(capa)
		}
	}
	return &vgs, nil
}

func (d *Driver) ListPools() ([]*model.StoragePoolSpec, error) {

	vgs, err := d.getVGList()
	if err != nil {
		return nil, err
	}

	var pols []*model.StoragePoolSpec
	for _, vg := range *vgs {
		if _, ok := d.conf.Pool[vg.Name]; !ok {
			continue
		}

		pol := &model.StoragePoolSpec{
			BaseModel: &model.BaseModel{
				Id: uuid.NewV5(uuid.NamespaceOID, vg.Name).String(),
			},
			Name:             vg.Name,
			TotalCapacity:    vg.TotalCapacity,
			FreeCapacity:     vg.FreeCapacity,
			Extras:           BuildDefaultPoolParam(d.conf.Pool[vg.Name]),
			AvailabilityZone: d.conf.Pool[vg.Name].AZ,
		}
		if pol.AvailabilityZone == "" {
			pol.AvailabilityZone = "default"
		}
		pols = append(pols, pol)
	}
	return pols, nil
}

func execCmd(script string, cmd []string) (string, error) {
	log.Infof("Command: %s %s", script, strings.Join(cmd, " "))
	ret, err := exec.Command(script, cmd...).Output()
	if err != nil {
		log.Error(err.Error())
		return "", err
	}
	log.Infof("Command Result:\n%s", string(ret))
	return string(ret), nil
}
<commit_msg>Fixed a bugs that lvm does not support repetition volume/snapshot name<commit_after>// Copyright (c) 2017 Huawei Technologies Co., Ltd. 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 lvm

import (
	"errors"
	"fmt"
	"os/exec"
	"path"
	"strconv"
	"strings"

	log "github.com/golang/glog"
	"github.com/opensds/opensds/contrib/drivers/lvm/targets"
	. "github.com/opensds/opensds/contrib/drivers/utils/config"
	pb "github.com/opensds/opensds/pkg/dock/proto"
	"github.com/opensds/opensds/pkg/model"
	"github.com/opensds/opensds/pkg/utils/config"
	"github.com/satori/go.uuid"
)

const (
	defaultConfPath = "/etc/opensds/driver/lvm.yaml"
	volumePrefix    = "volume-"
	snapshotPrefix  = "_snapshot-"
)

type LVMConfig struct {
	TgtBindIp string                    `yaml:"tgtBindIp"`
	Pool      map[string]PoolProperties `yaml:"pool,flow"`
}

type Driver struct {
	conf *LVMConfig

	handler func(script string, cmd []string) (string, error)
}

func (d *Driver) Setup() error {
	// Read lvm config file
	d.conf = &LVMConfig{TgtBindIp: "127.0.0.1"}
	p := config.CONF.OsdsDock.Backends.LVM.ConfigPath
	if "" == p {
		p = defaultConfPath
	}
	if _, err := Parse(d.conf, p); err != nil {
		return err
	}
	d.handler = execCmd

	return nil
}

func (*Driver) Unset() error { return nil }

func (d *Driver) CreateVolume(opt *pb.CreateVolumeOpts) (*model.VolumeSpec, error) {
	var size = fmt.Sprint(opt.GetSize()) + "G"
	var polName = opt.GetPoolName()
	var id = uuid.NewV4().String()
	var name = volumePrefix + id

	if _, err := d.handler("lvcreate", []string{
		"-Z", "n",
		"-n", name, // use uuid instead of name.
		"-L", size,
		polName,
	}); err != nil {
		log.Error("Failed to create logic volume:", err)
		return nil, err
	}

	var lvPath, lvStatus string
	// Display and parse some metadata in logic volume returned.
	lvPath = path.Join("/dev", polName, name)
	lv, err := d.handler("lvdisplay", []string{lvPath})
	if err != nil {
		log.Error("Failed to display logic volume:", err)
		return nil, err
	}

	for _, line := range strings.Split(lv, "\n") {
		if strings.Contains(line, "LV Path") {
			lvPath = strings.Fields(line)[2]
		}
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSpec{
		BaseModel: &model.BaseModel{
			Id: id,
		},
		Name:        opt.GetName(),
		Size:        opt.GetSize(),
		Description: opt.GetDescription(),
		Status:      lvStatus,
		Metadata: map[string]string{
			"lvPath": lvPath,
		},
	}, nil
}

func (d *Driver) PullVolume(volIdentifier string) (*model.VolumeSpec, error) {
	// Display and parse some metadata in logic volume returned.
	lv, err := d.handler("lvdisplay", []string{volIdentifier})
	if err != nil {
		log.Error("Failed to display logic volume:", err)
		return nil, err
	}
	var lvStatus string
	for _, line := range strings.Split(lv, "\n") {
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSpec{
		Status: lvStatus,
	}, nil
}

func (d *Driver) DeleteVolume(opt *pb.DeleteVolumeOpts) error {
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume metadata!")
		log.Error(err)
		return err
	}
	if _, err := d.handler("lvremove", []string{
		"-f", lvPath,
	}); err != nil {
		log.Error("Failed to remove logic volume:", err)
		return err
	}

	return nil
}

// ExtendVolume ...
func (d *Driver) ExtendVolume(opt *pb.ExtendVolumeOpts) (*model.VolumeSpec, error) {
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("failed to find logic volume path in volume metadata")
		log.Error(err)
		return nil, err
	}

	var size = fmt.Sprint(opt.GetSize()) + "G"

	if _, err := d.handler("lvresize", []string{
		"-L", size,
		lvPath,
	}); err != nil {
		log.Error("Failed to extend logic volume:", err)
		return nil, err
	}

	return &model.VolumeSpec{
		BaseModel: &model.BaseModel{
			Id: opt.GetId(),
		},
		Name:        opt.GetName(),
		Size:        opt.GetSize(),
		Description: opt.GetDescription(),
		Metadata:    opt.GetMetadata(),
	}, nil
}

func (d *Driver) InitializeConnection(opt *pb.CreateAttachmentOpts) (*model.ConnectionInfo, error) {
	var initiator string
	if initiator = opt.HostInfo.GetInitiator(); initiator == "" {
		initiator = "ALL"
	}
	// TODO	Add lvm path in Metadata field.
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume attachment metadata!")
		log.Error(err)
		return nil, err
	}

	t := targets.NewTarget(d.conf.TgtBindIp)
	expt, err := t.CreateExport(lvPath, initiator)
	if err != nil {
		log.Error("Failed to initialize connection of logic volume:", err)
		return nil, err
	}

	return &model.ConnectionInfo{
		DriverVolumeType: "iscsi",
		ConnectionData:   expt,
	}, nil
}

func (d *Driver) TerminateConnection(opt *pb.DeleteAttachmentOpts) error {
	var initiator string
	if initiator = opt.HostInfo.GetInitiator(); initiator == "" {
		initiator = "ALL"
	}
	// TODO	Add lvm path in Metadata field.
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume attachment metadata!")
		log.Error(err)
		return err
	}

	t := targets.NewTarget(d.conf.TgtBindIp)
	if err := t.RemoveExport(lvPath, initiator); err != nil {
		log.Error("Failed to initialize connection of logic volume:", err)
		return err
	}

	return nil
}

func (d *Driver) CreateSnapshot(opt *pb.CreateVolumeSnapshotOpts) (*model.VolumeSnapshotSpec, error) {
	var size = fmt.Sprint(opt.GetSize()) + "G"
	var id = uuid.NewV4().String()
	var snapName = snapshotPrefix + id
	lvPath, ok := opt.GetMetadata()["lvPath"]
	if !ok {
		err := errors.New("Failed to find logic volume path in volume snapshot metadata!")
		log.Error(err)
		return nil, err
	}

	if _, err := d.handler("lvcreate", []string{
		"-n", snapName,
		"-L", size,
		"-p", "r",
		"-s", lvPath,
	}); err != nil {
		log.Error("Failed to create logic volume snapshot:", err)
		return nil, err
	}

	var lvsDir, lvsPath string
	lvsDir, _ = path.Split(lvPath)
	lvsPath = path.Join(lvsDir, snapName)
	// Display and parse some metadata in logic volume snapshot returned.
	lvs, err := d.handler("lvdisplay", []string{lvsPath})
	if err != nil {
		log.Error("Failed to display logic volume snapshot:", err)
		return nil, err
	}
	var lvStatus string
	for _, line := range strings.Split(lvs, "\n") {
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSnapshotSpec{
		BaseModel: &model.BaseModel{
			Id: id,
		},
		Name:        opt.GetName(),
		Size:        opt.GetSize(),
		Description: opt.GetDescription(),
		Status:      lvStatus,
		VolumeId:    opt.GetVolumeId(),
		Metadata: map[string]string{
			"lvsPath": lvsPath,
		},
	}, nil
}

func (d *Driver) PullSnapshot(snapIdentifier string) (*model.VolumeSnapshotSpec, error) {
	// Display and parse some metadata in logic volume snapshot returned.
	lv, err := d.handler("lvdisplay", []string{snapIdentifier})
	if err != nil {
		log.Error("Failed to display logic volume snapshot:", err)
		return nil, err
	}
	var lvStatus string
	for _, line := range strings.Split(lv, "\n") {
		if strings.Contains(line, "LV Status") {
			lvStatus = strings.Fields(line)[2]
		}
	}

	return &model.VolumeSnapshotSpec{
		Status: lvStatus,
	}, nil
}

func (d *Driver) DeleteSnapshot(opt *pb.DeleteVolumeSnapshotOpts) error {
	lvsPath, ok := opt.GetMetadata()["lvsPath"]
	if !ok {
		err := errors.New("Failed to find logic volume snapshot path in volume snapshot metadata!")
		log.Error(err)
		return err
	}
	if _, err := d.handler("lvremove", []string{
		"-f", lvsPath,
	}); err != nil {
		log.Error("Failed to remove logic volume:", err)
		return err
	}

	return nil
}

type VolumeGroup struct {
	Name          string
	TotalCapacity int64
	FreeCapacity  int64
}

func (d *Driver) getVGList() (*[]VolumeGroup, error) {
	const vgInfoLineCount = 10
	info, err := d.handler("vgdisplay", []string{})
	if err != nil {
		return nil, err
	}

	lines := strings.Split(info, "\n")
	vgs := make([]VolumeGroup, len(lines)/vgInfoLineCount)

	var vgIdx = -1
	for _, line := range lines {
		if strings.Contains(line, "--- Volume group ---") {
			vgIdx++
			continue
		}
		if strings.Contains(line, "VG Name") {
			slice := strings.Fields(line)
			vgs[vgIdx].Name = slice[2]
		}
		if strings.Contains(line, "VG Size") {
			slice := strings.Fields(line)
			capa, _ := strconv.ParseFloat(slice[2], 64)
			vgs[vgIdx].TotalCapacity = int64(capa)
		}
		if strings.Contains(line, "Free  PE / Size") {
			slice := strings.Fields(line)
			capa, _ := strconv.ParseFloat(slice[len(slice)-2], 64)
			vgs[vgIdx].FreeCapacity = int64(capa)
		}
	}
	return &vgs, nil
}

func (d *Driver) ListPools() ([]*model.StoragePoolSpec, error) {

	vgs, err := d.getVGList()
	if err != nil {
		return nil, err
	}

	var pols []*model.StoragePoolSpec
	for _, vg := range *vgs {
		if _, ok := d.conf.Pool[vg.Name]; !ok {
			continue
		}

		pol := &model.StoragePoolSpec{
			BaseModel: &model.BaseModel{
				Id: uuid.NewV5(uuid.NamespaceOID, vg.Name).String(),
			},
			Name:             vg.Name,
			TotalCapacity:    vg.TotalCapacity,
			FreeCapacity:     vg.FreeCapacity,
			Extras:           BuildDefaultPoolParam(d.conf.Pool[vg.Name]),
			AvailabilityZone: d.conf.Pool[vg.Name].AZ,
		}
		if pol.AvailabilityZone == "" {
			pol.AvailabilityZone = "default"
		}
		pols = append(pols, pol)
	}
	return pols, nil
}

func execCmd(script string, cmd []string) (string, error) {
	log.Infof("Command: %s %s", script, strings.Join(cmd, " "))
	ret, err := exec.Command(script, cmd...).Output()
	if err != nil {
		log.Error(err.Error())
		return "", err
	}
	log.Infof("Command Result:\n%s", string(ret))
	return string(ret), nil
}
<|endoftext|>


<commit_before>package lfs

import (
	"bytes"
	"fmt"
	"net/url"
	"os/exec"
	"strings"
)

type credentialFetcher interface {
	Credentials() Creds
}

type credentialFunc func(Creds, string) (credentialFetcher, error)

var execCreds credentialFunc

func credentials(u *url.URL) (Creds, error) {
	creds := Creds{"protocol": u.Scheme, "host": u.Host, "path": u.Path}
	cmd, err := execCreds(creds, "fill")
	if err != nil {
		return nil, err
	}
	return cmd.Credentials(), nil
}

type CredentialCmd struct {
	output     *bytes.Buffer
	SubCommand string
	*exec.Cmd
}

func NewCommand(input Creds, subCommand string) *CredentialCmd {
	buf1 := new(bytes.Buffer)
	cmd := exec.Command("git", "credential", subCommand)

	cmd.Stdin = input.Buffer()
	cmd.Stdout = buf1
	/*
		There is a reason we don't hook up stderr here:
		Git's credential cache daemon helper does not close its stderr, so if this
		process is the process that fires up the daemon, it will wait forever
		(until the daemon exits, really) trying to read from stderr.

		See https://github.com/github/git-lfs/issues/117 for more details.
	*/

	return &CredentialCmd{buf1, subCommand, cmd}
}

func (c *CredentialCmd) StdoutString() string {
	return c.output.String()
}

func (c *CredentialCmd) Credentials() Creds {
	creds := make(Creds)

	for _, line := range strings.Split(c.StdoutString(), "\n") {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		creds[pieces[0]] = pieces[1]
	}

	return creds
}

type Creds map[string]string

func (c Creds) Buffer() *bytes.Buffer {
	buf := new(bytes.Buffer)

	for k, v := range c {
		buf.Write([]byte(k))
		buf.Write([]byte("="))
		buf.Write([]byte(v))
		buf.Write([]byte("\n"))
	}

	return buf
}

func init() {
	execCreds = func(input Creds, subCommand string) (credentialFetcher, error) {
		cmd := NewCommand(input, subCommand)
		err := cmd.Start()
		if err == nil {
			err = cmd.Wait()
		}

		if exitErr, ok := err.(*exec.ExitError); ok {
			if exitErr.ProcessState.Success() == false && !Config.GetenvBool("GIT_TERMINAL_PROMPT", true) {
				return nil, fmt.Errorf("Change the GIT_TERMINAL_PROMPT env var to be prompted to enter your credentials for %s://%s.",
					input["protocol"], input["host"])
			}
		}

		if err != nil {
			return cmd, fmt.Errorf("'git credential %s' error: %s\n", cmd.SubCommand, err.Error())
		}

		return cmd, nil
	}
}
<commit_msg>Trim leading slash from the path passed to credential fill<commit_after>package lfs

import (
	"bytes"
	"fmt"
	"net/url"
	"os/exec"
	"strings"
)

type credentialFetcher interface {
	Credentials() Creds
}

type credentialFunc func(Creds, string) (credentialFetcher, error)

var execCreds credentialFunc

func credentials(u *url.URL) (Creds, error) {
	path := strings.TrimPrefix(u.Path, "/")
	creds := Creds{"protocol": u.Scheme, "host": u.Host, "path": path}
	cmd, err := execCreds(creds, "fill")
	if err != nil {
		return nil, err
	}
	return cmd.Credentials(), nil
}

type CredentialCmd struct {
	output     *bytes.Buffer
	SubCommand string
	*exec.Cmd
}

func NewCommand(input Creds, subCommand string) *CredentialCmd {
	buf1 := new(bytes.Buffer)
	cmd := exec.Command("git", "credential", subCommand)

	cmd.Stdin = input.Buffer()
	cmd.Stdout = buf1
	/*
		There is a reason we don't hook up stderr here:
		Git's credential cache daemon helper does not close its stderr, so if this
		process is the process that fires up the daemon, it will wait forever
		(until the daemon exits, really) trying to read from stderr.

		See https://github.com/github/git-lfs/issues/117 for more details.
	*/

	return &CredentialCmd{buf1, subCommand, cmd}
}

func (c *CredentialCmd) StdoutString() string {
	return c.output.String()
}

func (c *CredentialCmd) Credentials() Creds {
	creds := make(Creds)

	for _, line := range strings.Split(c.StdoutString(), "\n") {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		creds[pieces[0]] = pieces[1]
	}

	return creds
}

type Creds map[string]string

func (c Creds) Buffer() *bytes.Buffer {
	buf := new(bytes.Buffer)

	for k, v := range c {
		buf.Write([]byte(k))
		buf.Write([]byte("="))
		buf.Write([]byte(v))
		buf.Write([]byte("\n"))
	}

	return buf
}

func init() {
	execCreds = func(input Creds, subCommand string) (credentialFetcher, error) {
		cmd := NewCommand(input, subCommand)
		err := cmd.Start()
		if err == nil {
			err = cmd.Wait()
		}

		if exitErr, ok := err.(*exec.ExitError); ok {
			if exitErr.ProcessState.Success() == false && !Config.GetenvBool("GIT_TERMINAL_PROMPT", true) {
				return nil, fmt.Errorf("Change the GIT_TERMINAL_PROMPT env var to be prompted to enter your credentials for %s://%s.",
					input["protocol"], input["host"])
			}
		}

		if err != nil {
			return cmd, fmt.Errorf("'git credential %s' error: %s\n", cmd.SubCommand, err.Error())
		}

		return cmd, nil
	}
}
<|endoftext|>


<commit_before>package common

import (
	"crypto/ecdsa"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/golang/protobuf/proto"

	"github.com/status-im/markdown"
	"github.com/status-im/markdown/ast"

	"github.com/status-im/status-go/protocol/protobuf"
)

// QuotedMessage contains the original text of the message replied to
type QuotedMessage struct {
	// From is a public key of the author of the message.
	From       string          `json:"from"`
	Text       string          `json:"text"`
	ParsedText json.RawMessage `json:"parsedText,omitempty"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioDurationMs is the audio duration in milliseconds
	AudioDurationMs uint64 `json:"audioDurationMs,omitempty"`
}

type CommandState int

const (
	CommandStateRequestAddressForTransaction CommandState = iota + 1
	CommandStateRequestAddressForTransactionDeclined
	CommandStateRequestAddressForTransactionAccepted
	CommandStateRequestTransaction
	CommandStateRequestTransactionDeclined
	CommandStateTransactionPending
	CommandStateTransactionSent
)

type CommandParameters struct {
	// ID is the ID of the initial message
	ID string `json:"id"`
	// From is the address we are sending the command from
	From string `json:"from"`
	// Address is the address sent with the command
	Address string `json:"address"`
	// Contract is the contract address for ERC20 tokens
	Contract string `json:"contract"`
	// Value is the value as a string sent
	Value string `json:"value"`
	// TransactionHash is the hash of the transaction
	TransactionHash string `json:"transactionHash"`
	// CommandState is the state of the command
	CommandState CommandState `json:"commandState"`
	// The Signature of the pk-bytes+transaction-hash from the wallet
	// address originating
	Signature []byte `json:"signature"`
}

func (c *CommandParameters) IsTokenTransfer() bool {
	return len(c.Contract) != 0
}

const (
	OutgoingStatusSending = "sending"
	OutgoingStatusSent    = "sent"
)

// Message represents a message record in the database,
// more specifically in user_messages table.
type Message struct {
	protobuf.ChatMessage

	// ID calculated as keccak256(compressedAuthorPubKey, data) where data is unencrypted payload.
	ID string `json:"id"`
	// WhisperTimestamp is a timestamp of a Whisper envelope.
	WhisperTimestamp uint64 `json:"whisperTimestamp"`
	// From is a public key of the author of the message.
	From string `json:"from"`
	// Random 3 words name
	Alias string `json:"alias"`
	// Identicon of the author
	Identicon string `json:"identicon"`
	// The chat id to be stored locally
	LocalChatID string `json:"localChatId"`

	Seen           bool   `json:"seen"`
	OutgoingStatus string `json:"outgoingStatus,omitempty"`

	QuotedMessage *QuotedMessage `json:"quotedMessage"`

	// CommandParameters is the parameters sent with the message
	CommandParameters *CommandParameters `json:"commandParameters"`

	// Computed fields
	// RTL is whether this is a right-to-left message (arabic/hebrew script etc)
	RTL bool `json:"rtl"`
	// ParsedText is the parsed markdown for displaying
	ParsedText []byte `json:"parsedText,omitempty"`
	// LineCount is the count of newlines in the message
	LineCount int `json:"lineCount"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// ImagePath is the path of the image to be sent
	ImagePath string `json:"imagePath,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioPath is the path of the audio to be sent
	AudioPath string `json:"audioPath,omitempty"`

	// Replace indicates that this is a replacement of a message
	// that has been updated
	Replace string `json:"replace,omitempty"`
	New     bool   `json:"new,omitempty"`

	SigPubKey *ecdsa.PublicKey `json:"-"`

	// Mentions is an array of mentions for a given message
	Mentions []string

	// Links is an array of links within given message
	Links []string
}

func (m *Message) MarshalJSON() ([]byte, error) {
	type StickerAlias struct {
		Hash string `json:"hash"`
		Pack int32  `json:"pack"`
	}
	item := struct {
		ID                string                           `json:"id"`
		WhisperTimestamp  uint64                           `json:"whisperTimestamp"`
		From              string                           `json:"from"`
		Alias             string                           `json:"alias"`
		Identicon         string                           `json:"identicon"`
		Seen              bool                             `json:"seen"`
		OutgoingStatus    string                           `json:"outgoingStatus,omitempty"`
		QuotedMessage     *QuotedMessage                   `json:"quotedMessage"`
		RTL               bool                             `json:"rtl"`
		ParsedText        json.RawMessage                  `json:"parsedText,omitempty"`
		LineCount         int                              `json:"lineCount"`
		Text              string                           `json:"text"`
		ChatID            string                           `json:"chatId"`
		LocalChatID       string                           `json:"localChatId"`
		Clock             uint64                           `json:"clock"`
		Replace           string                           `json:"replace"`
		ResponseTo        string                           `json:"responseTo"`
		New               bool                             `json:"new,omitempty"`
		EnsName           string                           `json:"ensName"`
		Image             string                           `json:"image,omitempty"`
		Audio             string                           `json:"audio,omitempty"`
		AudioDurationMs   uint64                           `json:"audioDurationMs,omitempty"`
		Sticker           *StickerAlias                    `json:"sticker"`
		CommandParameters *CommandParameters               `json:"commandParameters"`
		Timestamp         uint64                           `json:"timestamp"`
		ContentType       protobuf.ChatMessage_ContentType `json:"contentType"`
		MessageType       protobuf.MessageType             `json:"messageType"`
		Mentions          []string                         `json:"mentions,omitempty"`
		Links             []string                         `json:"links,omitempty"`
	}{
		ID:                m.ID,
		WhisperTimestamp:  m.WhisperTimestamp,
		From:              m.From,
		Alias:             m.Alias,
		Identicon:         m.Identicon,
		Seen:              m.Seen,
		OutgoingStatus:    m.OutgoingStatus,
		QuotedMessage:     m.QuotedMessage,
		RTL:               m.RTL,
		ParsedText:        m.ParsedText,
		LineCount:         m.LineCount,
		Text:              m.Text,
		Replace:           m.Replace,
		ChatID:            m.ChatId,
		LocalChatID:       m.LocalChatID,
		Clock:             m.Clock,
		ResponseTo:        m.ResponseTo,
		New:               m.New,
		EnsName:           m.EnsName,
		Image:             m.Base64Image,
		Audio:             m.Base64Audio,
		Timestamp:         m.Timestamp,
		ContentType:       m.ContentType,
		Mentions:          m.Mentions,
		Links:             m.Links,
		MessageType:       m.MessageType,
		CommandParameters: m.CommandParameters,
	}
	if sticker := m.GetSticker(); sticker != nil {
		item.Sticker = &StickerAlias{
			Pack: sticker.Pack,
			Hash: sticker.Hash,
		}
	}

	if audio := m.GetAudio(); audio != nil {
		item.AudioDurationMs = audio.DurationMs
	}

	return json.Marshal(item)
}

func (m *Message) UnmarshalJSON(data []byte) error {
	type Alias Message
	aux := struct {
		*Alias
		ResponseTo      string                           `json:"responseTo"`
		EnsName         string                           `json:"ensName"`
		ChatID          string                           `json:"chatId"`
		Sticker         *protobuf.StickerMessage         `json:"sticker"`
		AudioDurationMs uint64                           `json:"audioDurationMs"`
		ParsedText      json.RawMessage                  `json:"parsedText"`
		ContentType     protobuf.ChatMessage_ContentType `json:"contentType"`
	}{
		Alias: (*Alias)(m),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	if aux.ContentType == protobuf.ChatMessage_STICKER {
		m.Payload = &protobuf.ChatMessage_Sticker{Sticker: aux.Sticker}
	}
	if aux.ContentType == protobuf.ChatMessage_AUDIO {
		m.Payload = &protobuf.ChatMessage_Audio{
			Audio: &protobuf.AudioMessage{DurationMs: aux.AudioDurationMs},
		}
	}
	m.ResponseTo = aux.ResponseTo
	m.EnsName = aux.EnsName
	m.ChatId = aux.ChatID
	m.ContentType = aux.ContentType
	m.ParsedText = aux.ParsedText
	return nil
}

// Check if the first character is Hebrew or Arabic or the RTL character
func isRTL(s string) bool {
	first, _ := utf8.DecodeRuneInString(s)
	return unicode.Is(unicode.Hebrew, first) ||
		unicode.Is(unicode.Arabic, first) ||
		// RTL character
		first == '\u200f'
}

// parseImage check the message contains an image, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseImage() error {
	if m.ContentType != protobuf.ChatMessage_IMAGE {
		return nil
	}
	image := m.GetImage()
	if image == nil {
		return errors.New("image empty")
	}

	payload := image.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getImageMessageMIME(image)

	if err != nil {
		return err
	}

	m.Base64Image = fmt.Sprintf("data:image/%s;base64,%s", mime, encBuf)

	return nil
}

// parseAudio check the message contains an audio, and if so
// it creates a base64 encoded version of it.
func (m *Message) parseAudio() error {
	if m.ContentType != protobuf.ChatMessage_AUDIO {
		return nil
	}
	audio := m.GetAudio()
	if audio == nil {
		return errors.New("audio empty")
	}

	payload := audio.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getAudioMessageMIME(audio)

	if err != nil {
		return err
	}

	m.Base64Audio = fmt.Sprintf("data:audio/%s;base64,%s", mime, encBuf)

	return nil
}

// implement interface of https://github.com/status-im/markdown/blob/b9fe921681227b1dace4b56364e15edb3b698308/ast/node.go#L701
type NodeVisitor struct {
	mentions []string
	links    []string
}

func (v *NodeVisitor) Visit(node ast.Node, entering bool) ast.WalkStatus {
	// only on entering we fetch, otherwise we go on
	if !entering {
		return ast.GoToNext
	}
	switch n := node.(type) {
	case *ast.Mention:
		v.mentions = append(v.mentions, string(n.Literal))
	case *ast.Link:
		v.links = append(v.links, string(n.Destination))
	}

	return ast.GoToNext
}

func extractMentionsAndLinks(parsedText ast.Node) ([]string, []string) {
	visitor := &NodeVisitor{}
	ast.Walk(parsedText, visitor)
	return visitor.mentions, visitor.links
}

// PrepareContent return the parsed content of the message, the line-count and whether
// is a right-to-left message
func (m *Message) PrepareContent() error {
	parsedText := markdown.Parse([]byte(m.Text), nil)
	m.Mentions, m.Links = extractMentionsAndLinks(parsedText)
	jsonParsedText, err := json.Marshal(parsedText)
	if err != nil {
		return err
	}
	m.ParsedText = jsonParsedText
	m.LineCount = strings.Count(m.Text, "\n")
	m.RTL = isRTL(m.Text)
	if err := m.parseImage(); err != nil {
		return err
	}
	return m.parseAudio()
}

func getImageMessageMIME(i *protobuf.ImageMessage) (string, error) {
	switch i.Type {
	case protobuf.ImageMessage_PNG:
		return "png", nil
	case protobuf.ImageMessage_JPEG:
		return "jpeg", nil
	case protobuf.ImageMessage_WEBP:
		return "webp", nil
	case protobuf.ImageMessage_GIF:
		return "gif", nil
	}
	return "", errors.New("image format not supported")
}

func getAudioMessageMIME(i *protobuf.AudioMessage) (string, error) {
	switch i.Type {
	case protobuf.AudioMessage_AAC:
		return "aac", nil
	case protobuf.AudioMessage_AMR:
		return "amr", nil
	}

	return "", errors.New("audio format not supported")
}

// GetSigPubKey returns an ecdsa encoded public key
// this function is required to implement the ChatEntity interface
func (m Message) GetSigPubKey() *ecdsa.PublicKey {
	return m.SigPubKey
}

// GetProtoBuf returns the struct's embedded protobuf struct
// this function is required to implement the ChatEntity interface
func (m *Message) GetProtobuf() proto.Message {
	return &m.ChatMessage
}

// SetMessageType a setter for the MessageType field
// this function is required to implement the ChatEntity interface
func (m *Message) SetMessageType(messageType protobuf.MessageType) {
	m.MessageType = messageType
}
<commit_msg>Fix getImageMessageMIME() bug<commit_after>package common

import (
	"crypto/ecdsa"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/golang/protobuf/proto"

	"github.com/status-im/markdown"
	"github.com/status-im/markdown/ast"

	"github.com/status-im/status-go/protocol/protobuf"
)

// QuotedMessage contains the original text of the message replied to
type QuotedMessage struct {
	// From is a public key of the author of the message.
	From       string          `json:"from"`
	Text       string          `json:"text"`
	ParsedText json.RawMessage `json:"parsedText,omitempty"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioDurationMs is the audio duration in milliseconds
	AudioDurationMs uint64 `json:"audioDurationMs,omitempty"`
}

type CommandState int

const (
	CommandStateRequestAddressForTransaction CommandState = iota + 1
	CommandStateRequestAddressForTransactionDeclined
	CommandStateRequestAddressForTransactionAccepted
	CommandStateRequestTransaction
	CommandStateRequestTransactionDeclined
	CommandStateTransactionPending
	CommandStateTransactionSent
)

type CommandParameters struct {
	// ID is the ID of the initial message
	ID string `json:"id"`
	// From is the address we are sending the command from
	From string `json:"from"`
	// Address is the address sent with the command
	Address string `json:"address"`
	// Contract is the contract address for ERC20 tokens
	Contract string `json:"contract"`
	// Value is the value as a string sent
	Value string `json:"value"`
	// TransactionHash is the hash of the transaction
	TransactionHash string `json:"transactionHash"`
	// CommandState is the state of the command
	CommandState CommandState `json:"commandState"`
	// The Signature of the pk-bytes+transaction-hash from the wallet
	// address originating
	Signature []byte `json:"signature"`
}

func (c *CommandParameters) IsTokenTransfer() bool {
	return len(c.Contract) != 0
}

const (
	OutgoingStatusSending = "sending"
	OutgoingStatusSent    = "sent"
)

// Message represents a message record in the database,
// more specifically in user_messages table.
type Message struct {
	protobuf.ChatMessage

	// ID calculated as keccak256(compressedAuthorPubKey, data) where data is unencrypted payload.
	ID string `json:"id"`
	// WhisperTimestamp is a timestamp of a Whisper envelope.
	WhisperTimestamp uint64 `json:"whisperTimestamp"`
	// From is a public key of the author of the message.
	From string `json:"from"`
	// Random 3 words name
	Alias string `json:"alias"`
	// Identicon of the author
	Identicon string `json:"identicon"`
	// The chat id to be stored locally
	LocalChatID string `json:"localChatId"`

	Seen           bool   `json:"seen"`
	OutgoingStatus string `json:"outgoingStatus,omitempty"`

	QuotedMessage *QuotedMessage `json:"quotedMessage"`

	// CommandParameters is the parameters sent with the message
	CommandParameters *CommandParameters `json:"commandParameters"`

	// Computed fields
	// RTL is whether this is a right-to-left message (arabic/hebrew script etc)
	RTL bool `json:"rtl"`
	// ParsedText is the parsed markdown for displaying
	ParsedText []byte `json:"parsedText,omitempty"`
	// LineCount is the count of newlines in the message
	LineCount int `json:"lineCount"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// ImagePath is the path of the image to be sent
	ImagePath string `json:"imagePath,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioPath is the path of the audio to be sent
	AudioPath string `json:"audioPath,omitempty"`

	// Replace indicates that this is a replacement of a message
	// that has been updated
	Replace string `json:"replace,omitempty"`
	New     bool   `json:"new,omitempty"`

	SigPubKey *ecdsa.PublicKey `json:"-"`

	// Mentions is an array of mentions for a given message
	Mentions []string

	// Links is an array of links within given message
	Links []string
}

func (m *Message) MarshalJSON() ([]byte, error) {
	type StickerAlias struct {
		Hash string `json:"hash"`
		Pack int32  `json:"pack"`
	}
	item := struct {
		ID                string                           `json:"id"`
		WhisperTimestamp  uint64                           `json:"whisperTimestamp"`
		From              string                           `json:"from"`
		Alias             string                           `json:"alias"`
		Identicon         string                           `json:"identicon"`
		Seen              bool                             `json:"seen"`
		OutgoingStatus    string                           `json:"outgoingStatus,omitempty"`
		QuotedMessage     *QuotedMessage                   `json:"quotedMessage"`
		RTL               bool                             `json:"rtl"`
		ParsedText        json.RawMessage                  `json:"parsedText,omitempty"`
		LineCount         int                              `json:"lineCount"`
		Text              string                           `json:"text"`
		ChatID            string                           `json:"chatId"`
		LocalChatID       string                           `json:"localChatId"`
		Clock             uint64                           `json:"clock"`
		Replace           string                           `json:"replace"`
		ResponseTo        string                           `json:"responseTo"`
		New               bool                             `json:"new,omitempty"`
		EnsName           string                           `json:"ensName"`
		Image             string                           `json:"image,omitempty"`
		Audio             string                           `json:"audio,omitempty"`
		AudioDurationMs   uint64                           `json:"audioDurationMs,omitempty"`
		Sticker           *StickerAlias                    `json:"sticker"`
		CommandParameters *CommandParameters               `json:"commandParameters"`
		Timestamp         uint64                           `json:"timestamp"`
		ContentType       protobuf.ChatMessage_ContentType `json:"contentType"`
		MessageType       protobuf.MessageType             `json:"messageType"`
		Mentions          []string                         `json:"mentions,omitempty"`
		Links             []string                         `json:"links,omitempty"`
	}{
		ID:                m.ID,
		WhisperTimestamp:  m.WhisperTimestamp,
		From:              m.From,
		Alias:             m.Alias,
		Identicon:         m.Identicon,
		Seen:              m.Seen,
		OutgoingStatus:    m.OutgoingStatus,
		QuotedMessage:     m.QuotedMessage,
		RTL:               m.RTL,
		ParsedText:        m.ParsedText,
		LineCount:         m.LineCount,
		Text:              m.Text,
		Replace:           m.Replace,
		ChatID:            m.ChatId,
		LocalChatID:       m.LocalChatID,
		Clock:             m.Clock,
		ResponseTo:        m.ResponseTo,
		New:               m.New,
		EnsName:           m.EnsName,
		Image:             m.Base64Image,
		Audio:             m.Base64Audio,
		Timestamp:         m.Timestamp,
		ContentType:       m.ContentType,
		Mentions:          m.Mentions,
		Links:             m.Links,
		MessageType:       m.MessageType,
		CommandParameters: m.CommandParameters,
	}
	if sticker := m.GetSticker(); sticker != nil {
		item.Sticker = &StickerAlias{
			Pack: sticker.Pack,
			Hash: sticker.Hash,
		}
	}

	if audio := m.GetAudio(); audio != nil {
		item.AudioDurationMs = audio.DurationMs
	}

	return json.Marshal(item)
}

func (m *Message) UnmarshalJSON(data []byte) error {
	type Alias Message
	aux := struct {
		*Alias
		ResponseTo      string                           `json:"responseTo"`
		EnsName         string                           `json:"ensName"`
		ChatID          string                           `json:"chatId"`
		Sticker         *protobuf.StickerMessage         `json:"sticker"`
		AudioDurationMs uint64                           `json:"audioDurationMs"`
		ParsedText      json.RawMessage                  `json:"parsedText"`
		ContentType     protobuf.ChatMessage_ContentType `json:"contentType"`
	}{
		Alias: (*Alias)(m),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	if aux.ContentType == protobuf.ChatMessage_STICKER {
		m.Payload = &protobuf.ChatMessage_Sticker{Sticker: aux.Sticker}
	}
	if aux.ContentType == protobuf.ChatMessage_AUDIO {
		m.Payload = &protobuf.ChatMessage_Audio{
			Audio: &protobuf.AudioMessage{DurationMs: aux.AudioDurationMs},
		}
	}
	m.ResponseTo = aux.ResponseTo
	m.EnsName = aux.EnsName
	m.ChatId = aux.ChatID
	m.ContentType = aux.ContentType
	m.ParsedText = aux.ParsedText
	return nil
}

// Check if the first character is Hebrew or Arabic or the RTL character
func isRTL(s string) bool {
	first, _ := utf8.DecodeRuneInString(s)
	return unicode.Is(unicode.Hebrew, first) ||
		unicode.Is(unicode.Arabic, first) ||
		// RTL character
		first == '\u200f'
}

// parseImage check the message contains an image, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseImage() error {
	if m.ContentType != protobuf.ChatMessage_IMAGE {
		return nil
	}
	image := m.GetImage()
	if image == nil {
		return errors.New("image empty")
	}

	payload := image.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getImageMessageMIME(image)

	if err != nil {
		return err
	}

	m.Base64Image = fmt.Sprintf("data:image/%s;base64,%s", mime, encBuf)

	return nil
}

// parseAudio check the message contains an audio, and if so
// it creates a base64 encoded version of it.
func (m *Message) parseAudio() error {
	if m.ContentType != protobuf.ChatMessage_AUDIO {
		return nil
	}
	audio := m.GetAudio()
	if audio == nil {
		return errors.New("audio empty")
	}

	payload := audio.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getAudioMessageMIME(audio)

	if err != nil {
		return err
	}

	m.Base64Audio = fmt.Sprintf("data:audio/%s;base64,%s", mime, encBuf)

	return nil
}

// implement interface of https://github.com/status-im/markdown/blob/b9fe921681227b1dace4b56364e15edb3b698308/ast/node.go#L701
type NodeVisitor struct {
	mentions []string
	links    []string
}

func (v *NodeVisitor) Visit(node ast.Node, entering bool) ast.WalkStatus {
	// only on entering we fetch, otherwise we go on
	if !entering {
		return ast.GoToNext
	}
	switch n := node.(type) {
	case *ast.Mention:
		v.mentions = append(v.mentions, string(n.Literal))
	case *ast.Link:
		v.links = append(v.links, string(n.Destination))
	}

	return ast.GoToNext
}

func extractMentionsAndLinks(parsedText ast.Node) ([]string, []string) {
	visitor := &NodeVisitor{}
	ast.Walk(parsedText, visitor)
	return visitor.mentions, visitor.links
}

// PrepareContent return the parsed content of the message, the line-count and whether
// is a right-to-left message
func (m *Message) PrepareContent() error {
	parsedText := markdown.Parse([]byte(m.Text), nil)
	m.Mentions, m.Links = extractMentionsAndLinks(parsedText)
	jsonParsedText, err := json.Marshal(parsedText)
	if err != nil {
		return err
	}
	m.ParsedText = jsonParsedText
	m.LineCount = strings.Count(m.Text, "\n")
	m.RTL = isRTL(m.Text)
	if err := m.parseImage(); err != nil {
		return err
	}
	return m.parseAudio()
}

func getImageMessageMIME(i *protobuf.ImageMessage) (string, error) {
	switch i.Type {
	case protobuf.ImageType_PNG:
		return "png", nil
	case protobuf.ImageType_JPEG:
		return "jpeg", nil
	case protobuf.ImageType_WEBP:
		return "webp", nil
	case protobuf.ImageType_GIF:
		return "gif", nil
	}
	return "", errors.New("image format not supported")
}

func getAudioMessageMIME(i *protobuf.AudioMessage) (string, error) {
	switch i.Type {
	case protobuf.AudioMessage_AAC:
		return "aac", nil
	case protobuf.AudioMessage_AMR:
		return "amr", nil
	}

	return "", errors.New("audio format not supported")
}

// GetSigPubKey returns an ecdsa encoded public key
// this function is required to implement the ChatEntity interface
func (m Message) GetSigPubKey() *ecdsa.PublicKey {
	return m.SigPubKey
}

// GetProtoBuf returns the struct's embedded protobuf struct
// this function is required to implement the ChatEntity interface
func (m *Message) GetProtobuf() proto.Message {
	return &m.ChatMessage
}

// SetMessageType a setter for the MessageType field
// this function is required to implement the ChatEntity interface
func (m *Message) SetMessageType(messageType protobuf.MessageType) {
	m.MessageType = messageType
}
<|endoftext|>


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

package docker

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/dotcloud/docker"
	dclient "github.com/fsouza/go-dockerclient"
	"github.com/globocom/config"
	"github.com/globocom/docker-cluster/cluster"
	"github.com/globocom/tsuru/action"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"io"
	"labix.org/v2/mgo/bson"
	"strings"
	"sync"
)

var (
	dCluster *cluster.Cluster
	cmutext  sync.Mutex
	fsystem  fs.Fs
)

func dockerCluster() *cluster.Cluster {
	cmutext.Lock()
	defer cmutext.Unlock()
	if dCluster == nil {
		servers, _ := config.GetList("docker:servers")
		nodes := []cluster.Node{}
		for index, server := range servers {
			node := cluster.Node{
				ID:      fmt.Sprintf("server%d", index),
				Address: server,
			}
			nodes = append(nodes, node)
		}
		dCluster, _ = cluster.New(nodes...)
	}
	return dCluster
}

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) (string, error) {
	out := bytes.Buffer{}
	err := executor().Execute(cmd, args, nil, &out, &out)
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	if err != nil {
		return "", &cmdError{cmd: cmd, args: args, err: err, out: out.String()}
	}
	return out.String(), nil
}

func getPort() (string, error) {
	return config.GetString("docker:run-cmd:port")
}

type container struct {
	ID       string `bson:"_id"`
	AppName  string
	Type     string
	IP       string
	Port     string
	HostPort string
	Status   string
	Version  string
	Image    string
}

func (c *container) getAddress() string {
	hostAddr, err := config.Get("docker:host-address")
	if err != nil {
		log.Printf("Failed to obtain container address: %s", err.Error())
		return ""
	}
	return fmt.Sprintf("http://%s:%s", hostAddr, c.HostPort)
}

// newContainer creates a new container in Docker and stores it in the database.
func newContainer(app provision.App, imageId string, cmds []string) (container, error) {
	cont := container{
		AppName: app.GetName(),
		Type:    app.GetPlatform(),
	}
	port, err := getPort()
	if err != nil {
		log.Printf("error on getting port for container %s - %s", cont.AppName, port)
		return container{}, err
	}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		log.Printf("error on getting user for container %s - %s", cont.AppName, user)
		return container{}, err
	}
	config := docker.Config{
		Image:        imageId,
		Cmd:          cmds,
		PortSpecs:    []string{port},
		User:         user,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
	}
	_, c, err := dockerCluster().CreateContainer(&config)
	if err != nil {
		log.Printf("error on creating container in docker %s - %s", cont.AppName, err.Error())
		return container{}, err
	}
	cont.ID = c.ID
	cont.Port = port
	return cont, nil
}

// hostPort returns the host port mapped for the container.
func (c *container) hostPort() (string, error) {
	if c.Port == "" {
		return "", errors.New("Container does not contain any mapped port")
	}
	dockerContainer, err := dockerCluster().InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings != nil {
		mappedPorts := dockerContainer.NetworkSettings.PortMapping
		if port, ok := mappedPorts[c.Port]; ok {
			return port, nil
		}
	}
	return "", fmt.Errorf("Container port %s is not mapped to any host port", c.Port)
}

// ip returns the ip for the container.
func (c *container) ip() (string, error) {
	dockerContainer, err := dockerCluster().InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings == nil {
		msg := "Error when getting container information. NetworkSettings is missing."
		log.Print(msg)
		return "", errors.New(msg)
	}
	instanceIP := dockerContainer.NetworkSettings.IPAddress
	if instanceIP == "" {
		msg := "error: Can't get ipaddress..."
		log.Print(msg)
		return "", errors.New(msg)
	}
	log.Printf("Instance IpAddress: %s", instanceIP)
	return instanceIP, nil
}

func (c *container) setStatus(status string) error {
	c.Status = status
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func (c *container) setImage(imageId string) error {
	c.Image = imageId
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func deploy(app provision.App, version string, w io.Writer) (string, error) {
	commands, err := deployCmds(app, version)
	if err != nil {
		return "", err
	}
	imageId := getImage(app)
	actions := []*action.Action{&createContainer, &insertContainer}
	pipeline := action.NewPipeline(actions...)
	err = pipeline.Execute(app, imageId, commands)
	if err != nil {
		log.Printf("error on execute deploy pipeline for app %s - %s", app.GetName(), err.Error())
		return "", err
	}
	c := pipeline.Result().(container)
	log.Printf("pipeline succefully executed - %s", c.ID)
	for {
		log.Printf("waiting the container be stopped - %s", c.ID)
		result, err := c.stopped()
		if err != nil {
			log.Printf("error on stopped for container %s - %s", c.ID, err.Error())
			return "", err
		}
		if result {
			break
		}
	}
	log.Printf("getting container logs - %s", c.ID)
	err = c.logs(w)
	if err != nil {
		log.Printf("error on get logs for container %s - %s", c.ID, err.Error())
		return "", err
	}
	log.Printf("commiting container - %s", c.ID)
	imageId, err = c.commit()
	if err != nil {
		log.Printf("error on commit container %s - %s", c.ID, err.Error())
		return "", err
	}
	log.Printf("removing container - %s", c.ID)
	c.remove()
	// if err != nil {
	// 	return "", err
	// }
	return imageId, nil
}

func start(app provision.App, imageId string, w io.Writer) (*container, error) {
	commands, err := runCmds()
	if err != nil {
		return nil, err
	}
	actions := []*action.Action{&createContainer, &setIp, &setHostPort, &insertContainer, &addRoute}
	pipeline := action.NewPipeline(actions...)
	err = pipeline.Execute(app, imageId, commands)
	if err != nil {
		return nil, err
	}
	c := pipeline.Result().(container)
	err = c.setImage(imageId)
	if err != nil {
		return nil, err
	}
	err = c.setStatus("running")
	if err != nil {
		return nil, err
	}
	return &c, nil
}

// remove removes a docker container.
func (c *container) remove() error {
	address := c.getAddress()
	log.Printf("Removing container %s from docker", c.ID)
	err := dockerCluster().RemoveContainer(c.ID)
	if err != nil {
		log.Printf("Failed to remove container from docker: %s", err)
		return err
	}
	runCmd("ssh-keygen", "-R", c.IP)
	log.Printf("Removing container %s from database", c.ID)
	coll := collection()
	defer coll.Database.Session.Close()
	if err := coll.RemoveId(c.ID); err != nil {
		log.Printf("Failed to remove container from database: %s", err.Error())
		return err
	}
	r, err := getRouter()
	if err != nil {
		log.Printf("Failed to obtain router: %s", err.Error())
		return err
	}
	if err := r.RemoveRoute(c.AppName, address); err != nil {
		log.Printf("Failed to remove route: %s", err.Error())
		return err
	}
	return nil
}

func (c *container) ssh(stdout, stderr io.Writer, cmd string, args ...string) error {
	stderr = &filter{w: stderr, content: []byte("unable to resolve host")}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		return err
	}
	sshArgs := []string{c.IP, "-l", user, "-o", "StrictHostKeyChecking no"}
	if keyFile, err := config.GetString("docker:ssh:private-key"); err == nil {
		sshArgs = append(sshArgs, "-i", keyFile)
	}
	sshArgs = append(sshArgs, "--", cmd)
	sshArgs = append(sshArgs, args...)
	return executor().Execute("ssh", sshArgs, nil, stdout, stderr)
}

// commit commits an image in docker based in the container
func (c *container) commit() (string, error) {
	log.Printf("commiting container %s", c.ID)
	opts := dclient.CommitContainerOptions{Container: c.ID}
	image, err := dockerCluster().CommitContainer(opts)
	if err != nil {
		log.Printf("Could not commit docker image: %s", err.Error())
		return "", err
	}
	return image.ID, nil
}

// stopped returns true if the container is stopped.
func (c *container) stopped() (bool, error) {
	dockerContainer, err := dockerCluster().InspectContainer(c.ID)
	if err != nil {
		log.Printf("error on get log for container %s", c.ID)
		return false, err
	}
	running := dockerContainer.State.Running
	return !running, nil
}

// logs returns logs for the container.
func (c *container) logs(w io.Writer) error {
	opts := dclient.AttachToContainerOptions{
		Container:    c.ID,
		Logs:         true,
		OutputStream: w,
	}
	return dockerCluster().AttachToContainer(opts)
}

func getContainer(id string) (*container, error) {
	var c container
	coll := collection()
	defer coll.Database.Session.Close()
	err := coll.Find(bson.M{"_id": id}).One(&c)
	if err != nil {
		return nil, err
	}
	return &c, nil
}

func listAppContainers(appName string) ([]container, error) {
	var containers []container
	err := collection().Find(bson.M{"appname": appName}).All(&containers)
	return containers, err
}

// getImage returns the image name or id from an app.
func getImage(app provision.App) string {
	var c container
	collection().Find(bson.M{"appname": app.GetName()}).One(&c)
	if c.Image != "" {
		return c.Image
	}
	repoNamespace, err := config.GetString("docker:repository-namespace")
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%s/%s", repoNamespace, app.GetPlatform())
}

// removeImage removes an image from docker registry
func removeImage(imageId string) error {
	return dockerCluster().RemoveImage(imageId)
}

type cmdError struct {
	cmd  string
	args []string
	err  error
	out  string
}

func (e *cmdError) Error() string {
	command := e.cmd + " " + strings.Join(e.args, " ")
	return fmt.Sprintf("Failed to run command %q (%s): %s.", command, e.err, e.out)
}
<commit_msg>docker: added logs after commit.<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package docker

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/dotcloud/docker"
	dclient "github.com/fsouza/go-dockerclient"
	"github.com/globocom/config"
	"github.com/globocom/docker-cluster/cluster"
	"github.com/globocom/tsuru/action"
	"github.com/globocom/tsuru/fs"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"io"
	"labix.org/v2/mgo/bson"
	"strings"
	"sync"
)

var (
	dCluster *cluster.Cluster
	cmutext  sync.Mutex
	fsystem  fs.Fs
)

func dockerCluster() *cluster.Cluster {
	cmutext.Lock()
	defer cmutext.Unlock()
	if dCluster == nil {
		servers, _ := config.GetList("docker:servers")
		nodes := []cluster.Node{}
		for index, server := range servers {
			node := cluster.Node{
				ID:      fmt.Sprintf("server%d", index),
				Address: server,
			}
			nodes = append(nodes, node)
		}
		dCluster, _ = cluster.New(nodes...)
	}
	return dCluster
}

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}

// runCmd executes commands and log the given stdout and stderror.
func runCmd(cmd string, args ...string) (string, error) {
	out := bytes.Buffer{}
	err := executor().Execute(cmd, args, nil, &out, &out)
	log.Printf("running the cmd: %s with the args: %s", cmd, args)
	if err != nil {
		return "", &cmdError{cmd: cmd, args: args, err: err, out: out.String()}
	}
	return out.String(), nil
}

func getPort() (string, error) {
	return config.GetString("docker:run-cmd:port")
}

type container struct {
	ID       string `bson:"_id"`
	AppName  string
	Type     string
	IP       string
	Port     string
	HostPort string
	Status   string
	Version  string
	Image    string
}

func (c *container) getAddress() string {
	hostAddr, err := config.Get("docker:host-address")
	if err != nil {
		log.Printf("Failed to obtain container address: %s", err.Error())
		return ""
	}
	return fmt.Sprintf("http://%s:%s", hostAddr, c.HostPort)
}

// newContainer creates a new container in Docker and stores it in the database.
func newContainer(app provision.App, imageId string, cmds []string) (container, error) {
	cont := container{
		AppName: app.GetName(),
		Type:    app.GetPlatform(),
	}
	port, err := getPort()
	if err != nil {
		log.Printf("error on getting port for container %s - %s", cont.AppName, port)
		return container{}, err
	}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		log.Printf("error on getting user for container %s - %s", cont.AppName, user)
		return container{}, err
	}
	config := docker.Config{
		Image:        imageId,
		Cmd:          cmds,
		PortSpecs:    []string{port},
		User:         user,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
	}
	_, c, err := dockerCluster().CreateContainer(&config)
	if err != nil {
		log.Printf("error on creating container in docker %s - %s", cont.AppName, err.Error())
		return container{}, err
	}
	cont.ID = c.ID
	cont.Port = port
	return cont, nil
}

// hostPort returns the host port mapped for the container.
func (c *container) hostPort() (string, error) {
	if c.Port == "" {
		return "", errors.New("Container does not contain any mapped port")
	}
	dockerContainer, err := dockerCluster().InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings != nil {
		mappedPorts := dockerContainer.NetworkSettings.PortMapping
		if port, ok := mappedPorts[c.Port]; ok {
			return port, nil
		}
	}
	return "", fmt.Errorf("Container port %s is not mapped to any host port", c.Port)
}

// ip returns the ip for the container.
func (c *container) ip() (string, error) {
	dockerContainer, err := dockerCluster().InspectContainer(c.ID)
	if err != nil {
		return "", err
	}
	if dockerContainer.NetworkSettings == nil {
		msg := "Error when getting container information. NetworkSettings is missing."
		log.Print(msg)
		return "", errors.New(msg)
	}
	instanceIP := dockerContainer.NetworkSettings.IPAddress
	if instanceIP == "" {
		msg := "error: Can't get ipaddress..."
		log.Print(msg)
		return "", errors.New(msg)
	}
	log.Printf("Instance IpAddress: %s", instanceIP)
	return instanceIP, nil
}

func (c *container) setStatus(status string) error {
	c.Status = status
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func (c *container) setImage(imageId string) error {
	c.Image = imageId
	coll := collection()
	defer coll.Database.Session.Close()
	return coll.UpdateId(c.ID, c)
}

func deploy(app provision.App, version string, w io.Writer) (string, error) {
	commands, err := deployCmds(app, version)
	if err != nil {
		return "", err
	}
	imageId := getImage(app)
	actions := []*action.Action{&createContainer, &insertContainer}
	pipeline := action.NewPipeline(actions...)
	err = pipeline.Execute(app, imageId, commands)
	if err != nil {
		log.Printf("error on execute deploy pipeline for app %s - %s", app.GetName(), err.Error())
		return "", err
	}
	c := pipeline.Result().(container)
	log.Printf("pipeline succefully executed - %s", c.ID)
	for {
		log.Printf("waiting the container be stopped - %s", c.ID)
		result, err := c.stopped()
		if err != nil {
			log.Printf("error on stopped for container %s - %s", c.ID, err.Error())
			return "", err
		}
		if result {
			break
		}
	}
	log.Printf("getting container logs - %s", c.ID)
	err = c.logs(w)
	if err != nil {
		log.Printf("error on get logs for container %s - %s", c.ID, err.Error())
		return "", err
	}
	log.Printf("commiting container - %s", c.ID)
	imageId, err = c.commit()
	if err != nil {
		log.Printf("error on commit container %s - %s", c.ID, err.Error())
		return "", err
	}
	log.Printf("removing container - %s", c.ID)
	c.remove()
	// if err != nil {
	// 	return "", err
	// }
	return imageId, nil
}

func start(app provision.App, imageId string, w io.Writer) (*container, error) {
	commands, err := runCmds()
	if err != nil {
		return nil, err
	}
	actions := []*action.Action{&createContainer, &setIp, &setHostPort, &insertContainer, &addRoute}
	pipeline := action.NewPipeline(actions...)
	err = pipeline.Execute(app, imageId, commands)
	if err != nil {
		return nil, err
	}
	c := pipeline.Result().(container)
	err = c.setImage(imageId)
	if err != nil {
		return nil, err
	}
	err = c.setStatus("running")
	if err != nil {
		return nil, err
	}
	return &c, nil
}

// remove removes a docker container.
func (c *container) remove() error {
	address := c.getAddress()
	log.Printf("Removing container %s from docker", c.ID)
	err := dockerCluster().RemoveContainer(c.ID)
	if err != nil {
		log.Printf("Failed to remove container from docker: %s", err)
		return err
	}
	runCmd("ssh-keygen", "-R", c.IP)
	log.Printf("Removing container %s from database", c.ID)
	coll := collection()
	defer coll.Database.Session.Close()
	if err := coll.RemoveId(c.ID); err != nil {
		log.Printf("Failed to remove container from database: %s", err.Error())
		return err
	}
	r, err := getRouter()
	if err != nil {
		log.Printf("Failed to obtain router: %s", err.Error())
		return err
	}
	if err := r.RemoveRoute(c.AppName, address); err != nil {
		log.Printf("Failed to remove route: %s", err.Error())
		return err
	}
	return nil
}

func (c *container) ssh(stdout, stderr io.Writer, cmd string, args ...string) error {
	stderr = &filter{w: stderr, content: []byte("unable to resolve host")}
	user, err := config.GetString("docker:ssh:user")
	if err != nil {
		return err
	}
	sshArgs := []string{c.IP, "-l", user, "-o", "StrictHostKeyChecking no"}
	if keyFile, err := config.GetString("docker:ssh:private-key"); err == nil {
		sshArgs = append(sshArgs, "-i", keyFile)
	}
	sshArgs = append(sshArgs, "--", cmd)
	sshArgs = append(sshArgs, args...)
	return executor().Execute("ssh", sshArgs, nil, stdout, stderr)
}

// commit commits an image in docker based in the container
func (c *container) commit() (string, error) {
	log.Printf("commiting container %s", c.ID)
	opts := dclient.CommitContainerOptions{Container: c.ID}
	image, err := dockerCluster().CommitContainer(opts)
	if err != nil {
		log.Printf("Could not commit docker image: %s", err.Error())
		return "", err
	}
	log.Printf("image %s gerenated from container %s", image.ID, c.ID)
	return image.ID, nil
}

// stopped returns true if the container is stopped.
func (c *container) stopped() (bool, error) {
	dockerContainer, err := dockerCluster().InspectContainer(c.ID)
	if err != nil {
		log.Printf("error on get log for container %s", c.ID)
		return false, err
	}
	running := dockerContainer.State.Running
	return !running, nil
}

// logs returns logs for the container.
func (c *container) logs(w io.Writer) error {
	opts := dclient.AttachToContainerOptions{
		Container:    c.ID,
		Logs:         true,
		OutputStream: w,
	}
	return dockerCluster().AttachToContainer(opts)
}

func getContainer(id string) (*container, error) {
	var c container
	coll := collection()
	defer coll.Database.Session.Close()
	err := coll.Find(bson.M{"_id": id}).One(&c)
	if err != nil {
		return nil, err
	}
	return &c, nil
}

func listAppContainers(appName string) ([]container, error) {
	var containers []container
	err := collection().Find(bson.M{"appname": appName}).All(&containers)
	return containers, err
}

// getImage returns the image name or id from an app.
func getImage(app provision.App) string {
	var c container
	collection().Find(bson.M{"appname": app.GetName()}).One(&c)
	if c.Image != "" {
		return c.Image
	}
	repoNamespace, err := config.GetString("docker:repository-namespace")
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%s/%s", repoNamespace, app.GetPlatform())
}

// removeImage removes an image from docker registry
func removeImage(imageId string) error {
	return dockerCluster().RemoveImage(imageId)
}

type cmdError struct {
	cmd  string
	args []string
	err  error
	out  string
}

func (e *cmdError) Error() string {
	command := e.cmd + " " + strings.Join(e.args, " ")
	return fmt.Sprintf("Failed to run command %q (%s): %s.", command, e.err, e.out)
}
<|endoftext|>


<commit_before>package blocks

type Msg struct {
	Msg   interface{}
	Route string
}

type AddChanMsg struct {
	Route   string
	Channel chan *Msg
}

type QueryMsg struct {
	Route    string
	RespChan chan interface{}
}

type BlockChans struct {
	InChan    chan *Msg
	QueryChan chan *QueryMsg
	AddChan   chan *AddChanMsg
	DelChan   chan *Msg
	ErrChan   chan error
	QuitChan  chan bool
}

type Block struct {
	Name        string // the name of the block specifed by the user (like MyBlock)
	Kind        string // the kind of block this is (like count, toFile, fromSQS)
	inRoutes    map[string]chan interface{}
	queryRoutes map[string]chan chan interface{}
	broadcast   chan interface{}
	BlockChans
}

type BlockInterface interface {
	Setup()
	Run()
	Quit()
	Error(error)
	Build(BlockChans)
	Broadcast() chan interface{}
	InRoute(string) chan interface{}
	QueryRoute(string) chan chan interface{}
	GetBlock() *Block
}

func (b *Block) Build(c BlockChans) {
	// fuck can I do this all in one?
	b.InChan = c.InChan
	b.QueryChan = c.QueryChan
	b.AddChan = c.AddChan
	b.DelChan = c.DelChan
	b.ErrChan = c.ErrChan
	b.QuitChan = c.QuitChan
	// route maps
	b.inRoutes = make(map[string]chan interface{})
	b.queryRoutes = make(map[string]chan chan interface{})
}

func (b *Block) InRoute(routeName string) chan interface{} {
	route := make(chan interface{})
	b.inRoutes[routeName] = route
	return route
}

func (b *Block) QueryRoute(routeName string) chan chan interface{} {
	route := make(chan chan interface{})
	b.queryRoutes[routeName] = route
	return route
}

func (b *Block) Broadcast() chan interface{} {
	return b.broadcast
}

func (b *Block) GetBlock() *Block {
	return b
}

func (b *Block) Quit() {
	b.inRoutes["quit"] <- true
	for route := range b.inRoutes {
		close(b.inRoutes[route])
	}
	for route := range b.queryRoutes {
		close(b.queryRoutes[route])
	}
	close(b.InChan)
	close(b.QueryChan)
	close(b.AddChan)
	close(b.DelChan)
	close(b.ErrChan)
	close(b.QuitChan)
}

func (b *Block) Error(e error) {
	b.ErrChan <- e
}

func BlockRoutine(bi BlockInterface) {
	outChans := make(map[string]chan *Msg)

	b := bi.GetBlock()

	bi.Setup()
	go bi.Run()

	for {
		select {
		case msg := <-b.InChan:
			b.inRoutes[msg.Route] <- msg.Msg
		case msg := <-b.QueryChan:
			b.queryRoutes[msg.Route] <- msg.RespChan
		case msg := <-b.AddChan:
			outChans[msg.Route] = msg.Channel
		case msg := <-b.DelChan:
			delete(outChans, msg.Route)
		case msg := <-b.broadcast:
			for _, v := range outChans {
				v <- &Msg{
					Msg:   msg,
					Route: "",
				}
			}
		case <-b.QuitChan:
			b.Quit()
			return
		}
	}
}
<commit_msg>Create the broadcast channel along with the others<commit_after>package blocks

type Msg struct {
	Msg   interface{}
	Route string
}

type AddChanMsg struct {
	Route   string
	Channel chan *Msg
}

type QueryMsg struct {
	Route    string
	RespChan chan interface{}
}

type BlockChans struct {
	InChan    chan *Msg
	QueryChan chan *QueryMsg
	AddChan   chan *AddChanMsg
	DelChan   chan *Msg
	ErrChan   chan error
	QuitChan  chan bool
}

type Block struct {
	Name        string // the name of the block specifed by the user (like MyBlock)
	Kind        string // the kind of block this is (like count, toFile, fromSQS)
	inRoutes    map[string]chan interface{}
	queryRoutes map[string]chan chan interface{}
	broadcast   chan interface{}
	BlockChans
}

type BlockInterface interface {
	Setup()
	Run()
	Quit()
	Error(error)
	Build(BlockChans)
	Broadcast() chan interface{}
	InRoute(string) chan interface{}
	QueryRoute(string) chan chan interface{}
	GetBlock() *Block
}

func (b *Block) Build(c BlockChans) {
	// fuck can I do this all in one?
	b.InChan = c.InChan
	b.QueryChan = c.QueryChan
	b.AddChan = c.AddChan
	b.DelChan = c.DelChan
	b.ErrChan = c.ErrChan
	b.QuitChan = c.QuitChan

	// route maps
	b.inRoutes = make(map[string]chan interface{})
	b.queryRoutes = make(map[string]chan chan interface{})

	// broadcast channel
	b.broadcast = make(chan interface{})
}

func (b *Block) InRoute(routeName string) chan interface{} {
	route := make(chan interface{})
	b.inRoutes[routeName] = route
	return route
}

func (b *Block) QueryRoute(routeName string) chan chan interface{} {
	route := make(chan chan interface{})
	b.queryRoutes[routeName] = route
	return route
}

func (b *Block) Broadcast() chan interface{} {
	return b.broadcast
}

func (b *Block) GetBlock() *Block {
	return b
}

func (b *Block) Quit() {
	b.inRoutes["quit"] <- true
	for route := range b.inRoutes {
		close(b.inRoutes[route])
	}
	for route := range b.queryRoutes {
		close(b.queryRoutes[route])
	}
	close(b.InChan)
	close(b.QueryChan)
	close(b.AddChan)
	close(b.DelChan)
	close(b.ErrChan)
	close(b.QuitChan)
	close(b.broadcast)
}

func (b *Block) Error(e error) {
	b.ErrChan <- e
}

func BlockRoutine(bi BlockInterface) {
	outChans := make(map[string]chan *Msg)

	b := bi.GetBlock()

	bi.Setup()
	go bi.Run()

	for {
		select {
		case msg := <-b.InChan:
			b.inRoutes[msg.Route] <- msg.Msg
		case msg := <-b.QueryChan:
			b.queryRoutes[msg.Route] <- msg.RespChan
		case msg := <-b.AddChan:
			outChans[msg.Route] = msg.Channel
		case msg := <-b.DelChan:
			delete(outChans, msg.Route)
		case msg := <-b.broadcast:
			for _, v := range outChans {
				v <- &Msg{
					Msg:   msg,
					Route: "",
				}
			}
		case <-b.QuitChan:
			b.Quit()
			return
		}
	}
}
<|endoftext|>