<commit_before>package bip38

import (
	"bytes"
	"code.google.com/p/go.crypto/scrypt"
	"crypto/aes"
	"crypto/sha256"
	"encoding/hex"
	"github.com/piotrnar/gocoin/btc"
	"log"
	"math/big"
)

func sha256Twice(b []byte) []byte {
	h := sha256.New()
	h.Write(b)
	hashedOnce := h.Sum(nil)
	h.Reset()
	h.Write(hashedOnce)
	return h.Sum(nil)
}

func DecryptWithPassphrase(encryptedKey string, passphrase string) string {
	dec := btc.Decodeb58(encryptedKey)[:39] // trim to length 39 (not sure why needed)
	if dec == nil {
		log.Fatal("Cannot decode base58 string " + encryptedKey)
	}

	if dec[0] == 0x01 && dec[1] == 0x42 {
		log.Fatal("TODO: implement decryption when EC multiply mode not used")
	} else if dec[0] == 0x01 && dec[1] == 0x43 {
		compress := dec[2]&0x20 == 0x20
		hasLotSequence := dec[2]&0x04 == 0x04

		var ownerSalt, ownerEntropy []byte
		if hasLotSequence {
			ownerSalt = dec[7:11]
			ownerEntropy = dec[7:15]
		} else {
			ownerSalt = dec[7:15]
			ownerEntropy = ownerSalt
		}

		prefactorA, err := scrypt.Key([]byte(passphrase), ownerSalt, 16384, 8, 8, 32)
		if prefactorA == nil {
			log.Fatal(err)
		}

		var passFactor []byte
		if hasLotSequence {
			prefactorB := bytes.Join([][]byte{prefactorA, ownerEntropy}, nil)
			passFactor = sha256Twice(prefactorB)
		} else {
			passFactor = prefactorA
		}

		passpoint, err := btc.PublicFromPrivate(passFactor, true)
		if passpoint == nil {
			log.Fatal(err)
		}

		encryptedpart1 := dec[15:23]
		encryptedpart2 := dec[23:39]

		derived, err := scrypt.Key(passpoint, bytes.Join([][]byte{dec[3:7], ownerEntropy}, nil), 1024, 1, 1, 64)
		if derived == nil {
			log.Fatal(err)
		}

		h, err := aes.NewCipher(derived[32:])
		if h == nil {
			log.Fatal(err)
		}

		unencryptedpart2 := make([]byte, 16)
		h.Decrypt(unencryptedpart2, encryptedpart2)
		for i := range unencryptedpart2 {
			unencryptedpart2[i] ^= derived[i+16]
		}

		encryptedpart1 = bytes.Join([][]byte{encryptedpart1, unencryptedpart2[:8]}, nil)

		unencryptedpart1 := make([]byte, 16)
		h.Decrypt(unencryptedpart1, encryptedpart1)
		for i := range unencryptedpart1 {
			unencryptedpart1[i] ^= derived[i]
		}

		seeddb := bytes.Join([][]byte{unencryptedpart1[:16], unencryptedpart2[8:]}, nil)
		factorb := sha256Twice(seeddb)

		bigN, success := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
		if !success {
			log.Fatal("Failed to create Int for N")
		}

		passFactorBig := new(big.Int).SetBytes(passFactor)
		factorbBig := new(big.Int).SetBytes(factorb)

		privKey := new(big.Int)
		privKey.Mul(passFactorBig, factorbBig)
		privKey.Mod(privKey, bigN)

		pubKey, err := btc.PublicFromPrivate(privKey.Bytes(), compress)
		if pubKey == nil {
			log.Fatal(err)
		}

		addr := btc.NewAddrFromPubkey(pubKey, 0).String()

		addrHashed := sha256Twice([]byte(addr))

		if addrHashed[0] != dec[3] || addrHashed[1] != dec[4] || addrHashed[2] != dec[5] || addrHashed[3] != dec[6] {
			return ""
		}

		return hex.EncodeToString(privKey.Bytes())
	}

	log.Fatal("Malformed byte slice")
	return ""
}
<commit_msg>Fixed it to work again.  It got a bit stale and stopped working.  Should run.<commit_after>package bip38

import (
	"bytes"
	"golang.org/x/crypto/scrypt"
	"crypto/aes"
	"crypto/sha256"
	"encoding/hex"
	"github.com/cculianu/gocoin/btc"
	"log"
	"math/big"
)

func sha256Twice(b []byte) []byte {
	h := sha256.New()
	h.Write(b)
	hashedOnce := h.Sum(nil)
	h.Reset()
	h.Write(hashedOnce)
	return h.Sum(nil)
}

func DecryptWithPassphrase(encryptedKey string, passphrase string) string {
	dec := btc.Decodeb58(encryptedKey)[:39] // trim to length 39 (not sure why needed)
	if dec == nil {
		log.Fatal("Cannot decode base58 string " + encryptedKey)
	}

	if dec[0] == 0x01 && dec[1] == 0x42 {
		log.Fatal("TODO: implement decryption when EC multiply mode not used")
	} else if dec[0] == 0x01 && dec[1] == 0x43 {
		compress := dec[2]&0x20 == 0x20
		hasLotSequence := dec[2]&0x04 == 0x04

		var ownerSalt, ownerEntropy []byte
		if hasLotSequence {
			ownerSalt = dec[7:11]
			ownerEntropy = dec[7:15]
		} else {
			ownerSalt = dec[7:15]
			ownerEntropy = ownerSalt
		}

		prefactorA, err := scrypt.Key([]byte(passphrase), ownerSalt, 16384, 8, 8, 32)
		if prefactorA == nil {
			log.Fatal(err)
		}

		var passFactor []byte
		if hasLotSequence {
			prefactorB := bytes.Join([][]byte{prefactorA, ownerEntropy}, nil)
			passFactor = sha256Twice(prefactorB)
		} else {
			passFactor = prefactorA
		}

		passpoint, err := btc.PublicFromPrivate(passFactor, true)
		if passpoint == nil {
			log.Fatal(err)
		}

		encryptedpart1 := dec[15:23]
		encryptedpart2 := dec[23:39]

		derived, err := scrypt.Key(passpoint, bytes.Join([][]byte{dec[3:7], ownerEntropy}, nil), 1024, 1, 1, 64)
		if derived == nil {
			log.Fatal(err)
		}

		h, err := aes.NewCipher(derived[32:])
		if h == nil {
			log.Fatal(err)
		}

		unencryptedpart2 := make([]byte, 16)
		h.Decrypt(unencryptedpart2, encryptedpart2)
		for i := range unencryptedpart2 {
			unencryptedpart2[i] ^= derived[i+16]
		}

		encryptedpart1 = bytes.Join([][]byte{encryptedpart1, unencryptedpart2[:8]}, nil)

		unencryptedpart1 := make([]byte, 16)
		h.Decrypt(unencryptedpart1, encryptedpart1)
		for i := range unencryptedpart1 {
			unencryptedpart1[i] ^= derived[i]
		}

		seeddb := bytes.Join([][]byte{unencryptedpart1[:16], unencryptedpart2[8:]}, nil)
		factorb := sha256Twice(seeddb)

		bigN, success := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
		if !success {
			log.Fatal("Failed to create Int for N")
		}

		passFactorBig := new(big.Int).SetBytes(passFactor)
		factorbBig := new(big.Int).SetBytes(factorb)

		privKey := new(big.Int)
		privKey.Mul(passFactorBig, factorbBig)
		privKey.Mod(privKey, bigN)

		pubKey, err := btc.PublicFromPrivate(privKey.Bytes(), compress)
		if pubKey == nil {
			log.Fatal(err)
		}

		addr := btc.NewAddrFromPubkey(pubKey, 0).String()

		addrHashed := sha256Twice([]byte(addr))

		if addrHashed[0] != dec[3] || addrHashed[1] != dec[4] || addrHashed[2] != dec[5] || addrHashed[3] != dec[6] {
			return ""
		}

		return hex.EncodeToString(privKey.Bytes())
	}

	log.Fatal("Malformed byte slice")
	return ""
}
<|endoftext|>


<commit_before>package boolconv

import "testing"

func TestNewBoolByBytes(t *testing.T) {

	ret := NewBoolByBytes([]byte{0})
	if ret == True {
		t.Error("expected boolconv.Flase got boolconv.True")
	}

	ret = NewBoolByBytes([]byte{1})
	if ret == False {
		t.Error("expected boolconv.True got boolconv.False")
	}

}

func TestNewBoolBybool(t *testing.T) {

	ret := NewBoolBybool(false)
	if ret == True {
		t.Error("expected boolconv.Flase got boolconv.True")
	}

	ret = NewBoolBybool(true)
	if ret == False {
		t.Error("expected boolconv.True got boolconv.False")
	}

}

func TestNewBoolByInterface(t *testing.T) {

	_, err := NewBoolByInterface(true)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(false)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(True)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(False)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(True.Bytes())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(False.Bytes())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(True.String())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(False.String())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(byte(1))
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(byte(0))
	if err != nil {
		t.Error("error should not be nil")
	}

	// should err
	_, err = NewBoolByInterface([]string{"test"})
	if err == nil {
		t.Error("error is occurred")
	}

}

func TestTob(t *testing.T) {

	if False.Tob() {
		t.Error("expected false got true")
	}

	if !True.Tob() {
		t.Error("expected true got false")
	}

}

func TestBytes(t *testing.T) {

	if False.Bytes()[0] == byte(True) {
		t.Error("expected boolconv.Flase got boolconv.True")
	}

	if True.Bytes()[0] == byte(False) {
		t.Error("expected boolconv.True got boolconv.False")
	}

}

func TestString(t *testing.T) {

	if False.String() != "false" {
		t.Error("expected \"false\" got ", False.String())
	}

	if True.String() != "true" {
		t.Error("expected \"true\" got ", True.String())
	}

}
<commit_msg>Fix test<commit_after>package boolconv

import "testing"

func TestNewBoolByBytes(t *testing.T) {

	ret := NewBoolByBytes([]byte{0})
	if ret == True {
		t.Error("expected boolconv.Flase got boolconv.True")
	}

	ret = NewBoolByBytes([]byte{1})
	if ret == False {
		t.Error("expected boolconv.True got boolconv.False")
	}

}

func TestNewBoolBybool(t *testing.T) {

	ret := NewBoolBybool(false)
	if ret == True {
		t.Error("expected boolconv.Flase got boolconv.True")
	}

	ret = NewBoolBybool(true)
	if ret == False {
		t.Error("expected boolconv.True got boolconv.False")
	}

}

func TestNewBoolByInterface(t *testing.T) {

	_, err := NewBoolByInterface(true)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(false)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(True)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(False)
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(True.Bytes())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(False.Bytes())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(True.String())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(False.String())
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(byte(1))
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface(byte(0))
	if err != nil {
		t.Error("error should not be nil")
	}

	_, err = NewBoolByInterface([]string{"test"})
	if err == nil {
		t.Error("error is occurred")
	}

}

func TestTob(t *testing.T) {

	if False.Tob() {
		t.Error("expected false got true")
	}

	if !True.Tob() {
		t.Error("expected true got false")
	}

}

func TestBytes(t *testing.T) {

	if False.Bytes()[0] == byte(True) {
		t.Error("expected boolconv.Flase got boolconv.True")
	}

	if True.Bytes()[0] == byte(False) {
		t.Error("expected boolconv.True got boolconv.False")
	}

}

func TestString(t *testing.T) {

	if False.String() != "false" {
		t.Error("expected \"false\" got ", False.String())
	}

	if True.String() != "true" {
		t.Error("expected \"true\" got ", True.String())
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Rook Authors. 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 integration

import (
	"fmt"
	"strings"
	"testing"
	"time"

	cassandrav1alpha1 "github.com/rook/rook/pkg/apis/cassandra.rook.io/v1alpha1"
	"github.com/rook/rook/tests/framework/installer"
	"github.com/rook/rook/tests/framework/utils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
)

// ************************************************
// *** Major scenarios tested by the CassandraSuite ***
// Setup
// - via the cluster CRD with very simple properties
//   - 1 replica
//     - 1 CPU
//     - 2GB memory
//     - 5Gi volume from default provider
// ************************************************

type CassandraSuite struct {
	suite.Suite
	k8sHelper       *utils.K8sHelper
	installer       *installer.CassandraInstaller
	namespace       string
	systemNamespace string
	instanceCount   int
}

// TestCassandraSuite initiates the CassandraSuite
func TestCassandraSuite(t *testing.T) {
	if installer.SkipTestSuite(installer.CassandraTestSuite) {
		t.Skip()
	}

	s := new(CassandraSuite)
	defer func(s *CassandraSuite) {
		r := recover()
		if r != nil {
			logger.Infof("unexpected panic occurred during test %s, --> %v", t.Name(), r)
			t.Fail()
			s.Teardown()
			t.FailNow()
		}
	}(s)
	suite.Run(t, s)
}

// SetupSuite runs once at the beginning of the suite,
// before any tests are run.
func (s *CassandraSuite) SetupSuite() {

	s.namespace = "cassandra-ns"
	s.systemNamespace = installer.SystemNamespace(s.namespace)
	s.instanceCount = 1

	k8sHelper, err := utils.CreateK8sHelper(s.T)
	require.NoError(s.T(), err)
	s.k8sHelper = k8sHelper

	k8sVersion := s.k8sHelper.GetK8sServerVersion()
	logger.Infof("Installing Cassandra on K8s %s", k8sVersion)

	s.installer = installer.NewCassandraInstaller(s.k8sHelper, s.T)

	if err = s.installer.InstallCassandra(s.systemNamespace, s.namespace, s.instanceCount, cassandrav1alpha1.ClusterModeCassandra); err != nil {
		logger.Errorf("Cassandra was not installed successfully: %s", err.Error())
		s.T().Fail()
		s.Teardown()
		s.T().FailNow()
	}
}

// BeforeTest runs before every test in the CassandraSuite.
func (s *CassandraSuite) TeardownSuite() {
	s.Teardown()
}

///////////
// Tests //
///////////

// TestCassandraClusterCreation tests the creation of a Cassandra cluster.
func (s *CassandraSuite) TestCassandraClusterCreation() {
	s.CheckClusterHealth()
}

// TestScyllaClusterCreation tests the creation of a Scylla cluster.
// func (s *CassandraSuite) TestScyllaClusterCreation() {
// 	s.CheckClusterHealth()
// }

//////////////////////
// Helper Functions //
//////////////////////

// Teardown gathers logs and other helping info and then uninstalls
// everything installed by the CassandraSuite
func (s *CassandraSuite) Teardown() {
	s.installer.GatherAllCassandraLogs(s.systemNamespace, s.namespace, s.T().Name())
	s.installer.UninstallCassandra(s.systemNamespace, s.namespace)
}

// CheckClusterHealth checks if all Pods in the cluster are ready
// and CQL is working.
func (s *CassandraSuite) CheckClusterHealth() {
	// Verify that cassandra-operator is running
	logger.Infof("Verifying that all expected pods in cassandra cluster %s are running", s.namespace)
	assert.True(s.T(), s.k8sHelper.CheckPodCountAndState("rook-cassandra-operator", s.systemNamespace, 1, "Running"), "rook-cassandra-operator must be in Running state")

	// Give the StatefulSet a head start
	// CheckPodCountAndState timeout might be too fast and the test may fail
	time.Sleep(30 * time.Second)
	// Verify cassandra cluster instances are running OK
	assert.True(s.T(), s.k8sHelper.CheckPodCountAndState("rook-cassandra", s.namespace, s.instanceCount, "Running"), fmt.Sprintf("%d rook-cassandra pods must be in running state", s.instanceCount))

	// Determine a pod name for the cluster
	podName := "cassandra-ns-us-east-1-us-east-1a-0"

	// Get the Pod's IP address
	command := "hostname"
	commandArgs := []string{"-i"}
	podIP, err := s.k8sHelper.Exec(s.namespace, podName, command, commandArgs)
	assert.NoError(s.T(), err)

	command = "cqlsh"
	commandArgs = []string{
		"-e",
		`
CREATE KEYSPACE IF NOT EXISTS test WITH REPLICATION = {
'class': 'SimpleStrategy',
'replication_factor': 1
};
USE test;
CREATE TABLE IF NOT EXISTS map (key text, value text, PRIMARY KEY(key));
INSERT INTO map (key, value) VALUES('test_key', 'test_value');
SELECT key,value FROM map WHERE key='test_key';`,
		podIP,
	}

	time.Sleep(30 * time.Second)
	var result string
	for i := 0; i < utils.RetryLoop; i++ {
		result, err = s.k8sHelper.Exec(s.namespace, podName, command, commandArgs)
		logger.Infof("cassandra cql command exited, err: %v. result: %s", err, result)
		if err == nil {
			break
		}
		logger.Warning("cassandra cql command failed, will try again")
		time.Sleep(utils.RetryInterval * time.Second)
	}

	assert.NoError(s.T(), err)
	assert.True(s.T(), strings.Contains(result, "test_key"))
	assert.True(s.T(), strings.Contains(result, "test_value"))
}
<commit_msg>cassandra: fix integration test waiting logic<commit_after>/*
Copyright 2018 The Rook Authors. 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 integration

import (
	"context"
	"strings"
	"testing"
	"time"

	cassandrav1alpha1 "github.com/rook/rook/pkg/apis/cassandra.rook.io/v1alpha1"
	"github.com/rook/rook/tests/framework/installer"
	"github.com/rook/rook/tests/framework/utils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/stretchr/testify/suite"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ************************************************
// *** Major scenarios tested by the CassandraSuite ***
// Setup
// - via the cluster CRD with very simple properties
//   - 1 replica
//     - 1 CPU
//     - 2GB memory
//     - 5Gi volume from default provider
// ************************************************

type CassandraSuite struct {
	suite.Suite
	k8sHelper       *utils.K8sHelper
	installer       *installer.CassandraInstaller
	namespace       string
	systemNamespace string
	instanceCount   int
}

// TestCassandraSuite initiates the CassandraSuite
func TestCassandraSuite(t *testing.T) {
	if installer.SkipTestSuite(installer.CassandraTestSuite) {
		t.Skip()
	}

	s := new(CassandraSuite)
	defer func(s *CassandraSuite) {
		r := recover()
		if r != nil {
			logger.Infof("unexpected panic occurred during test %s, --> %v", t.Name(), r)
			t.Fail()
			s.Teardown()
			t.FailNow()
		}
	}(s)
	suite.Run(t, s)
}

// SetupSuite runs once at the beginning of the suite,
// before any tests are run.
func (s *CassandraSuite) SetupSuite() {

	s.namespace = "cassandra-ns"
	s.systemNamespace = installer.SystemNamespace(s.namespace)
	s.instanceCount = 1

	k8sHelper, err := utils.CreateK8sHelper(s.T)
	require.NoError(s.T(), err)
	s.k8sHelper = k8sHelper

	k8sVersion := s.k8sHelper.GetK8sServerVersion()
	logger.Infof("Installing Cassandra on K8s %s", k8sVersion)

	s.installer = installer.NewCassandraInstaller(s.k8sHelper, s.T)

	if err = s.installer.InstallCassandra(s.systemNamespace, s.namespace, s.instanceCount, cassandrav1alpha1.ClusterModeCassandra); err != nil {
		logger.Errorf("Cassandra was not installed successfully: %s", err.Error())
		s.T().Fail()
		s.Teardown()
		s.T().FailNow()
	}
}

// BeforeTest runs before every test in the CassandraSuite.
func (s *CassandraSuite) TeardownSuite() {
	s.Teardown()
}

///////////
// Tests //
///////////

// TestCassandraClusterCreation tests the creation of a Cassandra cluster.
func (s *CassandraSuite) TestCassandraClusterCreation() {
	s.CheckClusterHealth()
}

// TestScyllaClusterCreation tests the creation of a Scylla cluster.
// func (s *CassandraSuite) TestScyllaClusterCreation() {
// 	s.CheckClusterHealth()
// }

//////////////////////
// Helper Functions //
//////////////////////

// Teardown gathers logs and other helping info and then uninstalls
// everything installed by the CassandraSuite
func (s *CassandraSuite) Teardown() {
	s.installer.GatherAllCassandraLogs(s.systemNamespace, s.namespace, s.T().Name())
	s.installer.UninstallCassandra(s.systemNamespace, s.namespace)
}

// CheckClusterHealth checks if all Pods in the cluster are ready
// and CQL is working.
func (s *CassandraSuite) CheckClusterHealth() {
	// Verify that cassandra-operator is running
	operatorName := "rook-cassandra-operator"
	logger.Infof("Verifying that all expected pods of cassandra operator are ready")
	ready := utils.Retry(10, 30*time.Second,
		"Waiting for Cassandra operator to be ready", func() bool {
			sts, err := s.k8sHelper.Clientset.AppsV1().StatefulSets(s.systemNamespace).Get(context.TODO(), operatorName, v1.GetOptions{})
			if err != nil {
				logger.Errorf("Error getting Cassandra operator `%s`", operatorName)
				return false
			}
			if sts.Generation != sts.Status.ObservedGeneration {
				logger.Infof("Operator Statefulset has not converged yet")
				return false
			}
			if sts.Status.UpdatedReplicas != *sts.Spec.Replicas {
				logger.Error("Operator StatefulSet is rolling updating")
				return false
			}
			if sts.Status.ReadyReplicas != *sts.Spec.Replicas {
				logger.Infof("Statefulset not ready. Got: %v, Want: %v",
					sts.Status.ReadyReplicas, sts.Spec.Replicas)
				return false
			}
			return true
		})
	assert.True(s.T(), ready, "Timed out waiting for Cassandra operator to become ready")

	// Verify cassandra cluster instances are running OK
	clusterName := "cassandra-ns"
	clusterNamespace := "cassandra-ns"
	ready = utils.Retry(10, 30*time.Second,
		"Waiting for Cassandra cluster to be ready", func() bool {
			c, err := s.k8sHelper.RookClientset.CassandraV1alpha1().Clusters(clusterNamespace).Get(context.TODO(), clusterName, v1.GetOptions{})
			if err != nil {
				logger.Errorf("Error getting Cassandra cluster `%s`", clusterName)
				return false
			}
			for rackName, rack := range c.Status.Racks {
				var desiredMembers int32
				for _, r := range c.Spec.Datacenter.Racks {
					if r.Name == rackName {
						desiredMembers = r.Members
						break
					}
				}
				if !(desiredMembers == rack.Members && rack.Members == rack.ReadyMembers) {
					logger.Infof("Rack `%s` is not ready yet", rackName)
					return false
				}
			}
			return true
		})
	assert.True(s.T(), ready, "Timed out waiting for Cassandra cluster to become ready")

	// Determine a pod name for the cluster
	podName := "cassandra-ns-us-east-1-us-east-1a-0"

	// Get the Pod's IP address
	command := "hostname"
	commandArgs := []string{"-i"}
	podIP, err := s.k8sHelper.Exec(s.namespace, podName, command, commandArgs)
	assert.NoError(s.T(), err)

	command = "cqlsh"
	commandArgs = []string{
		"-e",
		`
CREATE KEYSPACE IF NOT EXISTS test WITH REPLICATION = {
'class': 'SimpleStrategy',
'replication_factor': 1
};
USE test;
CREATE TABLE IF NOT EXISTS map (key text, value text, PRIMARY KEY(key));
INSERT INTO map (key, value) VALUES('test_key', 'test_value');
SELECT key,value FROM map WHERE key='test_key';`,
		podIP,
	}

	time.Sleep(30 * time.Second)
	var result string
	for i := 0; i < utils.RetryLoop; i++ {
		result, err = s.k8sHelper.Exec(s.namespace, podName, command, commandArgs)
		logger.Infof("cassandra cql command exited, err: %v. result: %s", err, result)
		if err == nil {
			break
		}
		logger.Warning("cassandra cql command failed, will try again")
		time.Sleep(utils.RetryInterval * time.Second)
	}

	assert.NoError(s.T(), err)
	assert.True(s.T(), strings.Contains(result, "test_key"))
	assert.True(s.T(), strings.Contains(result, "test_value"))
}
<|endoftext|>


<commit_before>// +build zeromq

package boomer

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

func Run(tasks ...*Task) {

	// support go version below 1.5
	runtime.GOMAXPROCS(runtime.NumCPU())

	if !flag.Parsed() {
		flag.Parse()
	}

	if *runTasks != "" {
		// Run tasks without connecting to the master.
		taskNames := strings.Split(*runTasks, ",")
		for _, task := range tasks {
			if task.Name == "" {
				continue
			} else {
				for _, name := range taskNames {
					if name == task.Name {
						log.Println("Running " + task.Name)
						task.Fn()
					}
				}
			}
		}
		return
	}

	log.Println("Boomer is built with zeromq support.")

	var message string
	var r *runner
	if *rpc == "zeromq" {
		client := newZmqClient(*masterHost, *masterPort)
		r = &runner{
			tasks:  tasks,
			client: client,
			nodeId: getNodeId(),
		}
		message = fmt.Sprintf("Boomer is connected to master(%s:%d|%d) press Ctrl+c to quit.", *masterHost, *masterPort, *masterPort+1)
	} else if *rpc == "socket" {
		client := newSocketClient(*masterHost, *masterPort)
		r = &runner{
			tasks:  tasks,
			client: client,
			nodeId: getNodeId(),
		}
		message = fmt.Sprintf("Boomer is connected to master(%s:%d) press Ctrl+c to quit.", *masterHost, *masterPort)
	} else {
		log.Fatal("Unknown rpc type:", *rpc)
	}

	Events.Subscribe("boomer:quit", r.onQuiting)

	r.getReady()

	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGINT)

	log.Println(message)

	<-c
	Events.Publish("boomer:quit")

	// wait for quit message is sent to master
	<-disconnectedFromServer
	log.Println("shut down")

}

var masterHost *string
var masterPort *int
var rpc *string
var runTasks *string

func init() {
	masterHost = flag.String("master-host", "127.0.0.1", "Host or IP address of locust master for distributed load testing. Defaults to 127.0.0.1.")
	masterPort = flag.Int("master-port", 5557, "The port to connect to that is used by the locust master for distributed load testing. Defaults to 5557.")
	rpc = flag.String("rpc", "zeromq", "Choose zeromq or tcp socket to communicate with master, don't mix them up.")
	runTasks = flag.String("run-tasks", "", "Run tasks without connecting to the master, multiply tasks is seperated by comma. Usually, it's for debug purpose.")
}
<commit_msg>Add "strings"<commit_after>// +build zeromq

package boomer

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"strings"
)

func Run(tasks ...*Task) {

	// support go version below 1.5
	runtime.GOMAXPROCS(runtime.NumCPU())

	if !flag.Parsed() {
		flag.Parse()
	}

	if *runTasks != "" {
		// Run tasks without connecting to the master.
		taskNames := strings.Split(*runTasks, ",")
		for _, task := range tasks {
			if task.Name == "" {
				continue
			} else {
				for _, name := range taskNames {
					if name == task.Name {
						log.Println("Running " + task.Name)
						task.Fn()
					}
				}
			}
		}
		return
	}

	log.Println("Boomer is built with zeromq support.")

	var message string
	var r *runner
	if *rpc == "zeromq" {
		client := newZmqClient(*masterHost, *masterPort)
		r = &runner{
			tasks:  tasks,
			client: client,
			nodeId: getNodeId(),
		}
		message = fmt.Sprintf("Boomer is connected to master(%s:%d|%d) press Ctrl+c to quit.", *masterHost, *masterPort, *masterPort+1)
	} else if *rpc == "socket" {
		client := newSocketClient(*masterHost, *masterPort)
		r = &runner{
			tasks:  tasks,
			client: client,
			nodeId: getNodeId(),
		}
		message = fmt.Sprintf("Boomer is connected to master(%s:%d) press Ctrl+c to quit.", *masterHost, *masterPort)
	} else {
		log.Fatal("Unknown rpc type:", *rpc)
	}

	Events.Subscribe("boomer:quit", r.onQuiting)

	r.getReady()

	c := make(chan os.Signal)
	signal.Notify(c, syscall.SIGINT)

	log.Println(message)

	<-c
	Events.Publish("boomer:quit")

	// wait for quit message is sent to master
	<-disconnectedFromServer
	log.Println("shut down")

}

var masterHost *string
var masterPort *int
var rpc *string
var runTasks *string

func init() {
	masterHost = flag.String("master-host", "127.0.0.1", "Host or IP address of locust master for distributed load testing. Defaults to 127.0.0.1.")
	masterPort = flag.Int("master-port", 5557, "The port to connect to that is used by the locust master for distributed load testing. Defaults to 5557.")
	rpc = flag.String("rpc", "zeromq", "Choose zeromq or tcp socket to communicate with master, don't mix them up.")
	runTasks = flag.String("run-tasks", "", "Run tasks without connecting to the master, multiply tasks is seperated by comma. Usually, it's for debug purpose.")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"

	"github.com/bpowell/brocker/container"
	"github.com/bpowell/brocker/service"
)

var services map[string]service.Service
var containers map[string]container.Container

const (
	bridgeNameBase = "brocker"
	vethNameBase   = "veth"
	MOUNT_LOC      = "/app"
	CONTAIN_DIR    = "/container"
)

func init() {
	services = make(map[string]service.Service)
	containers = make(map[string]container.Container)
}

func main() {
	ctrlC := make(chan os.Signal, 1)
	signal.Notify(ctrlC, os.Interrupt)
	go func() {
		for range ctrlC {
			for _, s := range services {
				s.Stop()
			}
			os.Exit(0)
		}
	}()

	http.HandleFunc("/api/v1/service/add", serviceAdd)
	http.HandleFunc("/api/v1/container/run", containerRun)
	http.HandleFunc("/api/v1/container/list", containerList)
	http.HandleFunc("/api/v1/container/exec", containerExec)
	http.HandleFunc("/api/v1/container/rm", containerRm)
	err := http.ListenAndServe(":3000", nil)
	if err != nil {
		fmt.Println(err)
	}
}

func serviceAdd(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	s := service.Service{
		Containers: make(map[string]container.Container),
	}

	if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if _, ok := services[s.Name]; ok {
		http.Error(w, "Service already exists", http.StatusInternalServerError)
		return
	}

	s.BridgeName = fmt.Sprintf("%s%d", bridgeNameBase, len(services)+1)

	s.LoadBalanceType = "least_conn"
	if err := serviceCreateNetwork(s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	path, err := exec.LookPath("nginx")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	c := container.Container{
		Name:        fmt.Sprintf("%s-nginx", s.Name),
		ServiceName: s.Name,
		Command:     fmt.Sprintf("%s -c %s", path, "/app/nginx.conf"),
	}

	go run(c, true)

	w.WriteHeader(http.StatusCreated)
}

func containerRun(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	var c container.Container
	if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if _, ok := services[c.ServiceName]; ok == false {
		http.Error(w, "Service does not exists", http.StatusInternalServerError)
		return
	}

	go run(c, false)

	w.WriteHeader(http.StatusCreated)
}

func containerList(w http.ResponseWriter, r *http.Request) {
	if err := json.NewEncoder(w).Encode(containers); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func containerExec(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	data := struct {
		Name string `json:"name"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	for _, c := range containers {
		if c.Name == data.Name {
			w.Write([]byte(fmt.Sprintf("%d", c.Pid)))
			return
		}
	}
}

func containerRm(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	data := struct {
		Name string `json:"name"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	c, ok := containers[data.Name]
	if !ok {
		http.Error(w, "Not a running container", http.StatusInternalServerError)
		return
	}
	c.Close()

	w.Write([]byte("Stopping container"))
}

func serviceCreateNetwork(s service.Service) error {
	createBridge := strings.Split(fmt.Sprintf("/sbin/ip link add name %s type bridge", s.BridgeName), " ")
	setBridgeUp := strings.Split(fmt.Sprintf("/sbin/ip link set %s up", s.BridgeName), " ")
	setBridgeIP := strings.Split(fmt.Sprintf("/sbin/ifconfig %s %s", s.BridgeName, s.BridgeIP), " ")

	if err := exec.Command(createBridge[0], createBridge[1:]...).Run(); err != nil {
		return err
	}

	if err := exec.Command(setBridgeUp[0], setBridgeUp[1:]...).Run(); err != nil {
		return err
	}

	if err := exec.Command(setBridgeIP[0], setBridgeIP[1:]...).Run(); err != nil {
		return err
	}

	services[s.Name] = s
	return nil
}

func run(c container.Container, isNginx bool) {
	fmt.Println("running parent")
	s := services[c.ServiceName]
	runcmd, err := exec.LookPath("brocker-run")
	if err != nil {
		fmt.Println(err)
		return
	}

	c.StartTime = time.Now()
	c.SetName()

	if err := os.Mkdir(fmt.Sprintf("%s/%s", CONTAIN_DIR, c.Name), 0644); err != nil {
		fmt.Println(err)
		return
	}

	if c.CopyFile {
		if err := exec.Command("cp", c.FileToCopy, fmt.Sprintf("%s/%s/%s", CONTAIN_DIR, c.Name, path.Base(c.FileToCopy))).Run(); err != nil {
			fmt.Println(err)
			return
		}
	}

	if isNginx {
		s.ContainterName = c.Name
		s.WriteConfig(CONTAIN_DIR)
	}

	args := strings.Split(fmt.Sprintf("%s %s %s", runcmd, c.Name, c.Command), " ")

	cmd := &exec.Cmd{
		Path: runcmd,
		Args: args,
	}
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWPID |
			syscall.CLONE_NEWNS |
			syscall.CLONE_NEWNET,
	}

	if err := cmd.Start(); err != nil {
		fmt.Println(err)
	}

	c.Pid = cmd.Process.Pid
	c.VEth = fmt.Sprintf("%s%d", vethNameBase, len(containers))
	link := strings.Split(fmt.Sprintf("/sbin/ip link add name %s type veth peer name veth1 netns %d", c.VEth, c.Pid), " ")
	if err := exec.Command(link[0], link[1:]...).Run(); err != nil {
		fmt.Println(err)
		return
	}

	uplink := strings.Split(fmt.Sprintf("/sbin/ifconfig %s up", c.VEth), " ")
	if err := exec.Command(uplink[0], uplink[1:]...).Run(); err != nil {
		fmt.Println(err)
		return
	}

	bridge := strings.Split(fmt.Sprintf("/sbin/ip link set %s master %s", c.VEth, s.BridgeName), " ")
	if err := exec.Command(bridge[0], bridge[1:]...).Run(); err != nil {
		fmt.Println(err)
		return
	}

	bridgeip := net.ParseIP(s.BridgeIP)
	lastOctet := bridgeip[15] + byte(len(s.Containers)+1)
	ip := net.IPv4(bridgeip[12], bridgeip[13], bridgeip[14], lastOctet)
	c.IP = ip.String()

	if err := c.Exec(fmt.Sprintf("/sbin/ifconfig veth1 %s", ip.String())); err != nil {
		fmt.Println(err)
		return
	}

	containers[c.Name] = c
	s.Containers[c.Name] = c

	if isNginx {
		s.Pid = c.Pid
	} else {
		s.Servers = append(s.Servers, fmt.Sprintf("%s:8080", c.IP))
		s.WriteConfig(CONTAIN_DIR)
		s.Reload()
	}
	services[c.ServiceName] = s

	fmt.Println(cmd.Process.Pid)

	cmd.Wait()

	delete(containers, c.Name)
	delete(services[c.ServiceName].Containers, c.Name)
}
<commit_msg>Now writes to own log file per container<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"time"

	"github.com/bpowell/brocker/container"
	"github.com/bpowell/brocker/service"
)

var services map[string]service.Service
var containers map[string]container.Container

const (
	bridgeNameBase = "brocker"
	vethNameBase   = "veth"
	MOUNT_LOC      = "/app"
	CONTAIN_DIR    = "/container"
)

func init() {
	services = make(map[string]service.Service)
	containers = make(map[string]container.Container)
}

func main() {
	ctrlC := make(chan os.Signal, 1)
	signal.Notify(ctrlC, os.Interrupt)
	go func() {
		for range ctrlC {
			for _, s := range services {
				s.Stop()
			}
			os.Exit(0)
		}
	}()

	http.HandleFunc("/api/v1/service/add", serviceAdd)
	http.HandleFunc("/api/v1/container/run", containerRun)
	http.HandleFunc("/api/v1/container/list", containerList)
	http.HandleFunc("/api/v1/container/exec", containerExec)
	http.HandleFunc("/api/v1/container/rm", containerRm)
	err := http.ListenAndServe(":3000", nil)
	if err != nil {
		fmt.Println(err)
	}
}

func serviceAdd(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	s := service.Service{
		Containers: make(map[string]container.Container),
	}

	if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if _, ok := services[s.Name]; ok {
		http.Error(w, "Service already exists", http.StatusInternalServerError)
		return
	}

	s.BridgeName = fmt.Sprintf("%s%d", bridgeNameBase, len(services)+1)

	s.LoadBalanceType = "least_conn"
	if err := serviceCreateNetwork(s); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	path, err := exec.LookPath("nginx")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	c := container.Container{
		Name:        fmt.Sprintf("%s-nginx", s.Name),
		ServiceName: s.Name,
		Command:     fmt.Sprintf("%s -c %s", path, "/app/nginx.conf"),
	}

	go run(c, true)

	w.WriteHeader(http.StatusCreated)
}

func containerRun(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	var c container.Container
	if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if _, ok := services[c.ServiceName]; ok == false {
		http.Error(w, "Service does not exists", http.StatusInternalServerError)
		return
	}

	go run(c, false)

	w.WriteHeader(http.StatusCreated)
}

func containerList(w http.ResponseWriter, r *http.Request) {
	if err := json.NewEncoder(w).Encode(containers); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func containerExec(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	data := struct {
		Name string `json:"name"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	for _, c := range containers {
		if c.Name == data.Name {
			w.Write([]byte(fmt.Sprintf("%d", c.Pid)))
			return
		}
	}
}

func containerRm(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Invalid Request!", http.StatusMethodNotAllowed)
		return
	}

	data := struct {
		Name string `json:"name"`
	}{}
	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	c, ok := containers[data.Name]
	if !ok {
		http.Error(w, "Not a running container", http.StatusInternalServerError)
		return
	}
	c.Close()

	w.Write([]byte("Stopping container"))
}

func serviceCreateNetwork(s service.Service) error {
	createBridge := strings.Split(fmt.Sprintf("/sbin/ip link add name %s type bridge", s.BridgeName), " ")
	setBridgeUp := strings.Split(fmt.Sprintf("/sbin/ip link set %s up", s.BridgeName), " ")
	setBridgeIP := strings.Split(fmt.Sprintf("/sbin/ifconfig %s %s", s.BridgeName, s.BridgeIP), " ")

	if err := exec.Command(createBridge[0], createBridge[1:]...).Run(); err != nil {
		return err
	}

	if err := exec.Command(setBridgeUp[0], setBridgeUp[1:]...).Run(); err != nil {
		return err
	}

	if err := exec.Command(setBridgeIP[0], setBridgeIP[1:]...).Run(); err != nil {
		return err
	}

	services[s.Name] = s
	return nil
}

func run(c container.Container, isNginx bool) {
	fmt.Println("running parent")
	s := services[c.ServiceName]
	runcmd, err := exec.LookPath("brocker-run")
	if err != nil {
		fmt.Println(err)
		return
	}

	c.StartTime = time.Now()
	c.SetName()

	if err := os.Mkdir(fmt.Sprintf("%s/%s", CONTAIN_DIR, c.Name), 0644); err != nil {
		fmt.Println(err)
		return
	}

	stdouterr, err := os.Create(fmt.Sprintf("%s/%s/out", CONTAIN_DIR, c.Name))
	if err != nil {
		fmt.Println("Cannot create out:", err)
		return
	}
	defer stdouterr.Close()

	if c.CopyFile {
		if err := exec.Command("cp", c.FileToCopy, fmt.Sprintf("%s/%s/%s", CONTAIN_DIR, c.Name, path.Base(c.FileToCopy))).Run(); err != nil {
			fmt.Println(err)
			return
		}
	}

	if isNginx {
		s.ContainterName = c.Name
		s.WriteConfig(CONTAIN_DIR)
	}

	args := strings.Split(fmt.Sprintf("%s %s %s", runcmd, c.Name, c.Command), " ")

	cmd := &exec.Cmd{
		Path: runcmd,
		Args: args,
	}
	cmd.Stdin = os.Stdin
	cmd.Stdout = stdouterr
	cmd.Stderr = stdouterr
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWPID |
			syscall.CLONE_NEWNS |
			syscall.CLONE_NEWNET,
	}

	if err := cmd.Start(); err != nil {
		fmt.Println(err)
	}

	c.Pid = cmd.Process.Pid
	c.VEth = fmt.Sprintf("%s%d", vethNameBase, len(containers))
	link := strings.Split(fmt.Sprintf("/sbin/ip link add name %s type veth peer name veth1 netns %d", c.VEth, c.Pid), " ")
	if err := exec.Command(link[0], link[1:]...).Run(); err != nil {
		fmt.Println(err)
		return
	}

	uplink := strings.Split(fmt.Sprintf("/sbin/ifconfig %s up", c.VEth), " ")
	if err := exec.Command(uplink[0], uplink[1:]...).Run(); err != nil {
		fmt.Println(err)
		return
	}

	bridge := strings.Split(fmt.Sprintf("/sbin/ip link set %s master %s", c.VEth, s.BridgeName), " ")
	if err := exec.Command(bridge[0], bridge[1:]...).Run(); err != nil {
		fmt.Println(err)
		return
	}

	bridgeip := net.ParseIP(s.BridgeIP)
	lastOctet := bridgeip[15] + byte(len(s.Containers)+1)
	ip := net.IPv4(bridgeip[12], bridgeip[13], bridgeip[14], lastOctet)
	c.IP = ip.String()

	if err := c.Exec(fmt.Sprintf("/sbin/ifconfig veth1 %s", ip.String())); err != nil {
		fmt.Println(err)
		return
	}

	containers[c.Name] = c
	s.Containers[c.Name] = c

	if isNginx {
		s.Pid = c.Pid
	} else {
		s.Servers = append(s.Servers, fmt.Sprintf("%s:8080", c.IP))
		s.WriteConfig(CONTAIN_DIR)
		s.Reload()
	}
	services[c.ServiceName] = s

	fmt.Println(cmd.Process.Pid)

	cmd.Wait()

	delete(containers, c.Name)
	delete(services[c.ServiceName].Containers, c.Name)
}
<|endoftext|>


<commit_before>package core

import (
    "fmt"
    "net/http"
    "encoding/json"
    "github.com/gorilla/mux"
    "github.com/codegangsta/negroni"
    "bsm/api/lesson"
    "bsm/api/slide"
)

type JSONHandlerFunc func(http.ResponseWriter, *http.Request) (interface{}, error)

func Init() {
    router := mux.NewRouter().StrictSlash(false)
    router.HandleFunc("/hello", handler)
    router.HandleFunc("/api/v1/lesson", JSONDecorator(lesson.List))
    router.HandleFunc("/api/v1/lesson/{id:[0-9]+}", JSONDecorator(lesson.Retrieve))
    router.HandleFunc("/api/v1/slide/{id:[0-9]+}", JSONDecorator(slide.Retrieve))
    // add controller routes
    router.PathPrefix("/").Handler(http.FileServer(http.Dir("./assets/")))

    stack := negroni.New()
    // add middleware
    stack.UseHandler(router)

    http.Handle("/", stack)
}

func JSONDecorator(handler JSONHandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        data, err := handler(w,r)
        payload := struct{
            Data    interface{}         `json:"data"`
            Err     error               `json:"err"`
        } {
            Data: data,
            Err: err,
        }

        w.Header().Set("Content-Type", "application/json")
        if bPayload, err := json.MarshalIndent(payload, "", "  "); err == nil {
            fmt.Fprint(w, string(bPayload))
        } else {
            fmt.Fprintf(w, "{\"data\":\"\",\"err\":\"Marshal error: %s\"}", err)
        }
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, world!")
}

<commit_msg>return 500 on error<commit_after>package core

import (
    "fmt"
    "net/http"
    "encoding/json"
    "github.com/gorilla/mux"
    "github.com/codegangsta/negroni"
    "bsm/api/lesson"
    "bsm/api/slide"
)

type JSONHandlerFunc func(http.ResponseWriter, *http.Request) (interface{}, error)

func Init() {
    router := mux.NewRouter().StrictSlash(false)
    router.HandleFunc("/hello", handler)
    router.HandleFunc("/api/v1/lesson", JSONDecorator(lesson.List))
    router.HandleFunc("/api/v1/lesson/{id:[0-9]+}", JSONDecorator(lesson.Retrieve))
    router.HandleFunc("/api/v1/slide/{id:[0-9]+}", JSONDecorator(slide.Retrieve))
    // add controller routes
    router.PathPrefix("/").Handler(http.FileServer(http.Dir("./assets/")))

    stack := negroni.New()
    // add middleware
    stack.UseHandler(router)

    http.Handle("/", stack)
}

func JSONDecorator(handler JSONHandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        data, err := handler(w,r)

        payload := struct{
            Data    interface{}         `json:"data"`
            Err     interface{}         `json:"err"`
        } {
            Data: data,
            Err: err,
        }

        if err != nil {
            payload.Err = err.Error()
        }

        w.Header().Set("Content-Type", "application/json")
        if bPayload, merr := json.MarshalIndent(payload, "", "  "); merr == nil {
            if err != nil {
                w.WriteHeader(500)
            }
            fmt.Fprint(w, string(bPayload))
        } else {
            w.WriteHeader(500)
            fmt.Fprintf(w, "{\"data\":\"\",\"err\":\"Marshal error: %s\"}", err)
        }
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, world!")
}

<|endoftext|>


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

// Keep in sync with ../base32/example_test.go.

package base64_test

import (
	"encoding/base64"
	"fmt"
	"os"
)

func Example() {
	msg := "Hello, 世界"
	encoded := base64.StdEncoding.EncodeToString([]byte(msg))
	fmt.Println(encoded)
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		fmt.Println("decode error:", err)
		return
	}
	fmt.Println(string(decoded))
	// Output:
	// SGVsbG8sIOS4lueVjA==
	// Hello, 世界
}

func ExampleEncoding_EncodeToString() {
	data := []byte("any + old & data")
	str := base64.StdEncoding.EncodeToString(data)
	fmt.Println(str)
	// Output:
	// YW55ICsgb2xkICYgZGF0YQ==
}

func ExampleEncoding_DecodeString() {
	str := "c29tZSBkYXRhIHdpdGggACBhbmQg77u/"
	data, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Printf("%q\n", data)
	// Output:
	// "some data with \x00 and \ufeff"
}

func ExampleNewEncoder() {
	input := []byte("foo\x00bar")
	encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
	encoder.Write(input)
	// Must close the encoder when finished to flush any partial blocks.
	// If you comment out the following line, the last partial block "r"
	// won't be encoded.
	encoder.Close()
	// Output:
	// Zm9vAGJhcg==
}
<commit_msg>encoding/base64: add examples for Encode/Decode<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Keep in sync with ../base32/example_test.go.

package base64_test

import (
	"encoding/base64"
	"fmt"
	"os"
)

func Example() {
	msg := "Hello, 世界"
	encoded := base64.StdEncoding.EncodeToString([]byte(msg))
	fmt.Println(encoded)
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		fmt.Println("decode error:", err)
		return
	}
	fmt.Println(string(decoded))
	// Output:
	// SGVsbG8sIOS4lueVjA==
	// Hello, 世界
}

func ExampleEncoding_EncodeToString() {
	data := []byte("any + old & data")
	str := base64.StdEncoding.EncodeToString(data)
	fmt.Println(str)
	// Output:
	// YW55ICsgb2xkICYgZGF0YQ==
}

func ExampleEncoding_Encode() {
	data := []byte("Hello, world!")
	dst := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
	base64.StdEncoding.Encode(dst, data)
	fmt.Println(string(dst))
	// Output:
	// SGVsbG8sIHdvcmxkIQ==
}

func ExampleEncoding_DecodeString() {
	str := "c29tZSBkYXRhIHdpdGggACBhbmQg77u/"
	data, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Printf("%q\n", data)
	// Output:
	// "some data with \x00 and \ufeff"
}

func ExampleEncoding_Decode() {
	str := "SGVsbG8sIHdvcmxkIQ=="
	dst := make([]byte, base64.StdEncoding.DecodedLen(len(str)))
	n, err := base64.StdEncoding.Decode(dst, []byte(str))
	if err != nil {
		fmt.Println("decode error:", err)
		return
	}
	dst = dst[:n]
	fmt.Printf("%q\n", dst)
	// Output:
	// "Hello, world!"
}

func ExampleNewEncoder() {
	input := []byte("foo\x00bar")
	encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
	encoder.Write(input)
	// Must close the encoder when finished to flush any partial blocks.
	// If you comment out the following line, the last partial block "r"
	// won't be encoded.
	encoder.Close()
	// Output:
	// Zm9vAGJhcg==
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/mux"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
)

func Create(router *mux.Router) {
	router.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		redirectURI := r.Host
		if r.URL.Scheme == "" {
			redirectURI = "http%3A%2F%2F" + redirectURI + "%2foauth2callback"
		} else {
			redirectURI = "https%3A%2F%2F" + redirectURI + "%2foauth2callback"
		}
		http.Redirect(w, r,
			"https://accounts.google.com/o/oauth2/auth?scope=email&redirect_uri="+
				redirectURI+"&response_type=code&client_id="+os.Getenv("GOOGLE_OAUTH2_CLIENT_ID"),
			http.StatusTemporaryRedirect)
	})
	router.HandleFunc("/oauth2callback", func(w http.ResponseWriter, r *http.Request) {
		if r.FormValue("error") != "" {
			fmt.Println("Error in OAuth", r.FormValue("error"))
			// redirect to wherever we came from with error message
		}
		redirectURI := r.Host
		if r.URL.Scheme == "" {
			redirectURI = "http://" + redirectURI + "/oauth2callback"
		} else {
			redirectURI = "https://" + redirectURI + "/oauth2callback"
		}
		// send token request
		resp, err := http.PostForm("https://www.googleapis.com/oauth2/v3/token",
			url.Values{"code": {r.FormValue("code")}, "grant_type": {"authorization_code"}, "redirect_uri": {redirectURI},
				"client_id": {os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")}, "client_secret": {os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")}})

		var result map[string]interface{}
		body, err := ioutil.ReadAll(resp.Body)
		json.Unmarshal(body, &result)
		token, _ := jwt.Parse(result["id_token"].(string), func(token *jwt.Token) (interface{}, error) {
			return result["access_token"], nil
		})
		fmt.Println(token.Claims["email"], err, resp.StatusCode)

		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
	})
}
<commit_msg>changing https detection<commit_after>package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gorilla/mux"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
)

func Create(router *mux.Router) {
	router.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		scheme := "http"
		if os.Getenv("ENVIRONMENT") != "development" {
			scheme += "s"
		}

		http.Redirect(w, r, "https://accounts.google.com/o/oauth2/auth?scope=email&redirect_uri="+
				scheme + "%3A%2F%2F" + r.Host + "%2foauth2callback"+"&response_type=code&client_id="+os.Getenv("GOOGLE_OAUTH2_CLIENT_ID"),
			http.StatusTemporaryRedirect)
	})
	router.HandleFunc("/oauth2callback", func(w http.ResponseWriter, r *http.Request) {
		if r.FormValue("error") != "" {
			fmt.Println("Error in OAuth", r.FormValue("error"))
			// redirect to wherever we came from with error message
		}
		scheme := "http"
		if os.Getenv("ENVIRONMENT") != "development" {
			scheme += "s"
		}
		// send token request
		resp, err := http.PostForm("https://www.googleapis.com/oauth2/v3/token",
			url.Values{"code": {r.FormValue("code")}, "grant_type": {"authorization_code"}, "redirect_uri": { scheme + "://" + r.Host + "/oauth2callback"},
				"client_id": {os.Getenv("GOOGLE_OAUTH2_CLIENT_ID")}, "client_secret": {os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")}})

		var result map[string]interface{}
		body, err := ioutil.ReadAll(resp.Body)
		json.Unmarshal(body, &result)
		token, _ := jwt.Parse(result["id_token"].(string), func(token *jwt.Token) (interface{}, error) {
			return result["access_token"], nil
		})
		fmt.Println(token.Claims["email"], err, resp.StatusCode)

		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
	})
}
<|endoftext|>


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

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package chaincode

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/hyperledger/fabric/core"
	"github.com/hyperledger/fabric/peer/common"
	"github.com/hyperledger/fabric/peer/util"
	pb "github.com/hyperledger/fabric/protos"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"golang.org/x/net/context"
)

func getChaincodeSpecification(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
	spec := &pb.ChaincodeSpec{}
	if err := checkChaincodeCmdParams(cmd); err != nil {
		return spec, err
	}

	// Build the spec
	input := &pb.ChaincodeInput{}
	if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
		return spec, fmt.Errorf("Chaincode argument error: %s", err)
	}

	var attributes []string
	if err := json.Unmarshal([]byte(chaincodeAttributesJSON), &attributes); err != nil {
		return spec, fmt.Errorf("Chaincode argument error: %s", err)
	}

	chaincodeLang = strings.ToUpper(chaincodeLang)
	spec = &pb.ChaincodeSpec{
		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
		ChaincodeID: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName},
		CtorMsg:     input,
		Attributes:  attributes,
	}
	// If security is enabled, add client login token
	if core.SecurityEnabled() {
		if chaincodeUsr == common.UndefinedParamValue {
			return spec, errors.New("Must supply username for chaincode when security is enabled")
		}

		// Retrieve the CLI data storage path
		// Returns /var/openchain/production/client/
		localStore := util.GetCliFilePath()

		// Check if the user is logged in before sending transaction
		if _, err := os.Stat(localStore + "loginToken_" + chaincodeUsr); err == nil {
			logger.Infof("Local user '%s' is already logged in. Retrieving login token.\n", chaincodeUsr)

			// Read in the login token
			token, err := ioutil.ReadFile(localStore + "loginToken_" + chaincodeUsr)
			if err != nil {
				panic(fmt.Errorf("Fatal error when reading client login token: %s\n", err))
			}

			// Add the login token to the chaincodeSpec
			spec.SecureContext = string(token)

			// If privacy is enabled, mark chaincode as confidential
			if viper.GetBool("security.privacy") {
				logger.Info("Set confidentiality level to CONFIDENTIAL.\n")
				spec.ConfidentialityLevel = pb.ConfidentialityLevel_CONFIDENTIAL
			}
		} else {
			// Check if the token is not there and fail
			if os.IsNotExist(err) {
				return spec, fmt.Errorf("User '%s' not logged in. Use the 'login' command to obtain a security token.", chaincodeUsr)
			}
			// Unexpected error
			panic(fmt.Errorf("Fatal error when checking for client login token: %s\n", err))
		}
	} else {
		if chaincodeUsr != common.UndefinedParamValue {
			logger.Warning("Username supplied but security is disabled.")
		}
		if viper.GetBool("security.privacy") {
			panic(errors.New("Privacy cannot be enabled as requested because security is disabled"))
		}
	}
	return spec, nil
}

// chaincodeInvokeOrQuery invokes or queries the chaincode. If successful, the
// INVOKE form prints the transaction ID on STDOUT, and the QUERY form prints
// the query result on STDOUT. A command-line flag (-r, --raw) determines
// whether the query result is output as raw bytes, or as a printable string.
// The printable form is optionally (-x, --hex) a hexadecimal representation
// of the query response. If the query response is NIL, nothing is output.
func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err error) {
	spec, err := getChaincodeSpecification(cmd)
	if err != nil {
		return err
	}

	devopsClient, err := common.GetDevopsClient(cmd)
	if err != nil {
		return fmt.Errorf("Error building %s: %s", chainFuncName, err)
	}

	// Build the ChaincodeInvocationSpec message
	invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
	if customIDGenAlg != common.UndefinedParamValue {
		invocation.IdGenerationAlg = customIDGenAlg
	}

	var resp *pb.Response
	if invoke {
		resp, err = devopsClient.Invoke(context.Background(), invocation)
	} else {
		resp, err = devopsClient.Query(context.Background(), invocation)
	}

	if err != nil {
		if invoke {
			err = fmt.Errorf("Error invoking %s: %s\n", chainFuncName, err)
		} else {
			err = fmt.Errorf("Error querying %s: %s\n", chainFuncName, err)
		}
		return
	}
	if invoke {
		transactionID := string(resp.Msg)
		logger.Infof("Successfully invoked transaction: %s(%s)", invocation, transactionID)
	} else {
		logger.Infof("Successfully queried transaction: %s", invocation)
		if resp != nil {
			if chaincodeQueryRaw {
				if chaincodeQueryHex {
					err = errors.New("Options --raw (-r) and --hex (-x) are not compatible\n")
					return
				}
				fmt.Print("Query Result (Raw): ")
				os.Stdout.Write(resp.Msg)
			} else {
				if chaincodeQueryHex {
					fmt.Printf("Query Result: %x\n", resp.Msg)
				} else {
					fmt.Printf("Query Result: %s\n", string(resp.Msg))
				}
			}
		}
	}
	return nil
}

func checkChaincodeCmdParams(cmd *cobra.Command) error {

	if chaincodeName == common.UndefinedParamValue {
		if chaincodePath == common.UndefinedParamValue {
			return fmt.Errorf("Must supply value for %s path parameter.\n", chainFuncName)
		}
	}

	// Check that non-empty chaincode parameters contain only Args as a key.
	// Type checking is done later when the JSON is actually unmarshaled
	// into a pb.ChaincodeInput. To better understand what's going
	// on here with JSON parsing see http://blog.golang.org/json-and-go -
	// Generic JSON with interface{}
	if chaincodeCtorJSON != "{}" {
		var f interface{}
		err := json.Unmarshal([]byte(chaincodeCtorJSON), &f)
		if err != nil {
			return fmt.Errorf("Chaincode argument error: %s", err)
		}
		m := f.(map[string]interface{})
		if len(m) != 2 {
			return fmt.Errorf("Non-empty JSON chaincode parameters must contain exactly 2 keys: 'Function' and 'Args'")
		}
		for k := range m {
			switch strings.ToLower(k) {
			case "args":
			case "function":
			default:
				return fmt.Errorf("Illegal chaincode key '%s' - must be 'Function' or 'Args'", k)
			}
		}
	} else {
		return errors.New("Empty JSON chaincode parameters must contain exactly 2 keys: 'Function' and 'Args'")
	}

	if chaincodeAttributesJSON != "[]" {
		var f interface{}
		err := json.Unmarshal([]byte(chaincodeAttributesJSON), &f)
		if err != nil {
			return fmt.Errorf("Chaincode argument error: %s", err)
		}
	}

	return nil
}
<commit_msg>Clarify "not logged in" error<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package chaincode

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/hyperledger/fabric/core"
	"github.com/hyperledger/fabric/peer/common"
	"github.com/hyperledger/fabric/peer/util"
	pb "github.com/hyperledger/fabric/protos"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"golang.org/x/net/context"
)

func getChaincodeSpecification(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
	spec := &pb.ChaincodeSpec{}
	if err := checkChaincodeCmdParams(cmd); err != nil {
		return spec, err
	}

	// Build the spec
	input := &pb.ChaincodeInput{}
	if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
		return spec, fmt.Errorf("Chaincode argument error: %s", err)
	}

	var attributes []string
	if err := json.Unmarshal([]byte(chaincodeAttributesJSON), &attributes); err != nil {
		return spec, fmt.Errorf("Chaincode argument error: %s", err)
	}

	chaincodeLang = strings.ToUpper(chaincodeLang)
	spec = &pb.ChaincodeSpec{
		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
		ChaincodeID: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName},
		CtorMsg:     input,
		Attributes:  attributes,
	}
	// If security is enabled, add client login token
	if core.SecurityEnabled() {
		if chaincodeUsr == common.UndefinedParamValue {
			return spec, errors.New("Must supply username for chaincode when security is enabled")
		}

		// Retrieve the CLI data storage path
		// Returns /var/openchain/production/client/
		localStore := util.GetCliFilePath()

		// Check if the user is logged in before sending transaction
		if _, err := os.Stat(localStore + "loginToken_" + chaincodeUsr); err == nil {
			logger.Infof("Local user '%s' is already logged in. Retrieving login token.\n", chaincodeUsr)

			// Read in the login token
			token, err := ioutil.ReadFile(localStore + "loginToken_" + chaincodeUsr)
			if err != nil {
				panic(fmt.Errorf("Fatal error when reading client login token: %s\n", err))
			}

			// Add the login token to the chaincodeSpec
			spec.SecureContext = string(token)

			// If privacy is enabled, mark chaincode as confidential
			if viper.GetBool("security.privacy") {
				logger.Info("Set confidentiality level to CONFIDENTIAL.\n")
				spec.ConfidentialityLevel = pb.ConfidentialityLevel_CONFIDENTIAL
			}
		} else {
			// Check if the token is not there and fail
			if os.IsNotExist(err) {
				return spec, fmt.Errorf("User '%s' not logged in. Use the 'peer network login' command to obtain a security token.", chaincodeUsr)
			}
			// Unexpected error
			panic(fmt.Errorf("Fatal error when checking for client login token: %s\n", err))
		}
	} else {
		if chaincodeUsr != common.UndefinedParamValue {
			logger.Warning("Username supplied but security is disabled.")
		}
		if viper.GetBool("security.privacy") {
			panic(errors.New("Privacy cannot be enabled as requested because security is disabled"))
		}
	}
	return spec, nil
}

// chaincodeInvokeOrQuery invokes or queries the chaincode. If successful, the
// INVOKE form prints the transaction ID on STDOUT, and the QUERY form prints
// the query result on STDOUT. A command-line flag (-r, --raw) determines
// whether the query result is output as raw bytes, or as a printable string.
// The printable form is optionally (-x, --hex) a hexadecimal representation
// of the query response. If the query response is NIL, nothing is output.
func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err error) {
	spec, err := getChaincodeSpecification(cmd)
	if err != nil {
		return err
	}

	devopsClient, err := common.GetDevopsClient(cmd)
	if err != nil {
		return fmt.Errorf("Error building %s: %s", chainFuncName, err)
	}

	// Build the ChaincodeInvocationSpec message
	invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
	if customIDGenAlg != common.UndefinedParamValue {
		invocation.IdGenerationAlg = customIDGenAlg
	}

	var resp *pb.Response
	if invoke {
		resp, err = devopsClient.Invoke(context.Background(), invocation)
	} else {
		resp, err = devopsClient.Query(context.Background(), invocation)
	}

	if err != nil {
		if invoke {
			err = fmt.Errorf("Error invoking %s: %s\n", chainFuncName, err)
		} else {
			err = fmt.Errorf("Error querying %s: %s\n", chainFuncName, err)
		}
		return
	}
	if invoke {
		transactionID := string(resp.Msg)
		logger.Infof("Successfully invoked transaction: %s(%s)", invocation, transactionID)
	} else {
		logger.Infof("Successfully queried transaction: %s", invocation)
		if resp != nil {
			if chaincodeQueryRaw {
				if chaincodeQueryHex {
					err = errors.New("Options --raw (-r) and --hex (-x) are not compatible\n")
					return
				}
				fmt.Print("Query Result (Raw): ")
				os.Stdout.Write(resp.Msg)
			} else {
				if chaincodeQueryHex {
					fmt.Printf("Query Result: %x\n", resp.Msg)
				} else {
					fmt.Printf("Query Result: %s\n", string(resp.Msg))
				}
			}
		}
	}
	return nil
}

func checkChaincodeCmdParams(cmd *cobra.Command) error {

	if chaincodeName == common.UndefinedParamValue {
		if chaincodePath == common.UndefinedParamValue {
			return fmt.Errorf("Must supply value for %s path parameter.\n", chainFuncName)
		}
	}

	// Check that non-empty chaincode parameters contain only Args as a key.
	// Type checking is done later when the JSON is actually unmarshaled
	// into a pb.ChaincodeInput. To better understand what's going
	// on here with JSON parsing see http://blog.golang.org/json-and-go -
	// Generic JSON with interface{}
	if chaincodeCtorJSON != "{}" {
		var f interface{}
		err := json.Unmarshal([]byte(chaincodeCtorJSON), &f)
		if err != nil {
			return fmt.Errorf("Chaincode argument error: %s", err)
		}
		m := f.(map[string]interface{})
		if len(m) != 2 {
			return fmt.Errorf("Non-empty JSON chaincode parameters must contain exactly 2 keys: 'Function' and 'Args'")
		}
		for k := range m {
			switch strings.ToLower(k) {
			case "args":
			case "function":
			default:
				return fmt.Errorf("Illegal chaincode key '%s' - must be 'Function' or 'Args'", k)
			}
		}
	} else {
		return errors.New("Empty JSON chaincode parameters must contain exactly 2 keys: 'Function' and 'Args'")
	}

	if chaincodeAttributesJSON != "[]" {
		var f interface{}
		err := json.Unmarshal([]byte(chaincodeAttributesJSON), &f)
		if err != nil {
			return fmt.Errorf("Chaincode argument error: %s", err)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package remote

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"bufio"

	"github.com/lnsp/dkvs/lib"
)

const (
	maxReconnects     = 5
	reconnectInterval = 3 * time.Second
	cmdArgSeparator   = ";"
	cmdNameSeparator  = "#"
	StatusDown        = "DOWN"
	StatusReady       = "READY"
	StatusStartup     = "STARTUP"
	StatusShutdown    = "SHUTDOWN"
	ShutdownOK        = "OK"
	ShutdownDenied    = "DENIED"
	StoreOK           = "OK"
	StoreDenied       = "DENIED"
	RoleMasterPrimary = "PRIMARY"
	RoleMaster        = "SECONDARY"
	RoleSlave         = "GENERIC"
	JoinOK            = "OK"
	JoinDenied        = "DENIED"
	JoinInUse         = "IN USE"
	RebuildOK         = "OK"
	HandshakeOK       = "OK"
	AssistOK          = "OK"
	RevisionOK        = "OK"
	MirrorOK          = "OK"
)

var (
	CommandCluster    = &Command{Name: "CLUSTER"}
	CommandReplicas   = &Command{Name: "REPLICAS"}
	CommandRole       = &Command{Name: "ROLE"}
	CommandJoin       = &Command{Name: "JOIN"}
	CommandRevision   = &Command{Name: "REVISION"}
	CommandStore      = &Command{Name: "STORE"}
	CommandLocalStore = &Command{Name: "STORE_LOCAL"}
	CommandLocalRead  = &Command{Name: "READ_LOCAL"}
	CommandRead       = &Command{Name: "READ"}
	CommandError      = &Command{Name: "ERROR"}
	CommandStatus     = &Command{Name: "STATUS"}
	CommandShutdown   = &Command{Name: "SHUTDOWN"}
	CommandAddress    = &Command{Name: "ADDRESS"}
	CommandRebuild    = &Command{Name: "REBUILD"}
	CommandHandshake  = &Command{Name: "HANDSHAKE"}
	CommandAssist     = &Command{Name: "ASSIST"}
	CommandMirror     = &Command{Name: "MIRROR"}
	CommandLocalKeys  = &Command{Name: "KEYS_LOCAL"}
	CommandKeys       = &Command{Name: "KEYS"}
)

type Node interface {
	Close()
	Queue(cmd *Command) (*Command, error)
	Poll() (*Command, error)
	Push(*Command) error
}

func Error(err error) *Command {
	return CommandError.Param(strings.ToUpper(err.Error()))
}

type Command struct {
	Name string
	Args []string
}

func (cmd Command) KindOf(kind *Command) bool {
	return cmd.Name == kind.Name
}

func (cmd Command) Arg(index int) string {
	if len(cmd.Args) <= index {
		return ""
	}
	return cmd.Args[index]
}

func (cmd Command) ArgCount() int {
	return len(cmd.Args)
}

func (cmd Command) ArgList() []string {
	return cmd.Args[:]
}

func (cmd Command) Param(params ...string) *Command {
	return &Command{
		Name: cmd.Name,
		Args: params,
	}
}

func (cmd Command) Marshal() []byte {
	return []byte(cmd.Name + cmdNameSeparator + strings.Join(cmd.Args, cmdArgSeparator) + "\n")
}

func (cmd Command) String() string {
	return fmt.Sprintf("%s (%s)", cmd.Name, strings.Join(cmd.Args, ", "))
}

func UnmarshalCommand(cmd []byte) (*Command, error) {
	cmdString := string(cmd)
	cmdTokens := strings.Split(cmdString, cmdNameSeparator)
	if len(cmdTokens) < 1 {
		return nil, fmt.Errorf("Invalid command format: missing tokens")
	}
	name := strings.ToUpper(cmdTokens[0])
	if len(cmdTokens) < 2 {
		return &Command{
			Name: name,
			Args: []string{},
		}, nil
	}
	args := cmdTokens[1]
	argTokens := strings.Split(args, cmdArgSeparator)
	return &Command{
		Name: name,
		Args: argTokens,
	}, nil
}

type Slave struct {
	Connection    net.Conn
	PublicAddress string
	Dead          bool
	reader        *bufio.Reader
}

func (slave *Slave) remoteStatus() string {
	if slave.Connection != nil {
		response, err := slave.Queue(CommandStatus)
		if err != nil {
			return StatusDown
		}
		if !response.KindOf(CommandStatus) {
			return StatusDown
		}
		return response.Arg(0)
	}
	return StatusDown
}

func (slave *Slave) Push(cmd *Command) error {
	if !slave.Dead {
		err := slave.Open()
		if err != nil {
			return err
		}
	} else {
		return errors.New("Could not push to dead connection")
	}
	slave.Log("push", cmd)
	_, err := slave.Connection.Write(cmd.Marshal())
	if err != nil {
		slave.Reset()
		return errors.New("Could not write to socket")
	}
	return nil
}

func (slave *Slave) Log(tag string, args ...interface{}) {
	fmt.Print("["+strings.ToUpper(tag)+"] ", fmt.Sprintln(args...))
}

func (slave *Slave) Reset() {
	if slave.Connection != nil {
		slave.Connection.Close()
		slave.Connection = nil
	}
}

func (slave *Slave) Poll() (*Command, error) {
	if !slave.Dead {
		err := slave.Open()
		if err != nil {
			return nil, err
		}
	} else {
		return nil, errors.New("Could not read from dead connection")
	}
	if slave.reader == nil {
		slave.reader = bufio.NewReader(slave.Connection)
	}
	data, _, err := slave.reader.ReadLine()
	if err != nil {
		slave.Reset()
		return nil, errors.New("Could not read from socket")
	}
	cmd, err := UnmarshalCommand(data)
	if err != nil {
		return nil, errors.New("Could not unmarshal command")
	}
	slave.Log("poll", cmd)
	return cmd, nil
}

func (slave *Slave) Queue(cmd *Command) (*Command, error) {
	if err := slave.Push(cmd); err != nil {
		return nil, errors.New("Could not write request to socket")
	}
	respCmd, err := slave.Poll()
	if err != nil {
		return nil, errors.New("Failed to receive response")
	}
	if respCmd.KindOf(CommandError) {
		return nil, errors.New(respCmd.Arg(0))
	}
	if !respCmd.KindOf(cmd) {
		return nil, errors.New("Unexpected command response type")
	}
	return respCmd, nil
}

func (slave *Slave) Open() error {
	if slave.Connection != nil {
		return nil
	}
	conn, err := net.Dial("tcp", slave.PublicAddress)
	if err != nil {
		return err
	}
	slave.Connection = conn
	slave.reader = bufio.NewReader(slave.Connection)
	for i := 0; slave.remoteStatus() != StatusReady; i++ {
		time.Sleep(reconnectInterval)
		if i >= maxReconnects {
			slave.Connection.Close()
			slave.Connection = nil
			return errors.New("Could not reach endpoint")
		}
	}
	return nil
}

func (slave *Slave) Address() string {
	if slave.PublicAddress != "" {
		return slave.PublicAddress
	}
	response, err := slave.Queue(CommandAddress)
	if err != nil {
		return slave.PublicAddress
	}
	slave.PublicAddress = response.Arg(0)
	return slave.PublicAddress
}

func (slave *Slave) Read(key string) (string, lib.Revision, error) {
	response, err := slave.Queue(CommandRead.Param(key))
	if err != nil {
		return key, nil, err
	}
	rev, err := lib.ToRevision(response.Arg(1))
	if err != nil {
		return key, nil, err
	}
	return response.Arg(0), rev, nil
}

func (slave *Slave) LocalRead(key string) (string, lib.Revision, error) {
	response, err := slave.Queue(CommandLocalRead.Param(key))
	if err != nil {
		return key, nil, err
	}
	rev, err := lib.ToRevision(response.Arg(1))
	if err != nil {
		return key, nil, err
	}
	return response.Arg(0), rev, nil
}

func (slave *Slave) Store(key, value string, rev lib.Revision) error {
	revString := ""
	if rev != nil {
		revString = rev.String()
	}

	response, err := slave.Queue(CommandStore.Param(key, value, revString))
	if err != nil {
		return err
	}
	if response.Arg(0) != StoreOK {
		return errors.New("Store denied by host")
	}
	return nil
}

func (slave *Slave) LocalStore(key, value string, rev lib.Revision) error {
	response, err := slave.Queue(CommandLocalStore.Param(key, value, rev.String()))
	if err != nil {
		return err
	}
	if response.Arg(0) != StoreOK {
		return errors.New("Store denied by host")
	}
	return nil
}

func (slave *Slave) Status() lib.Status {
	switch slave.remoteStatus() {
	case StatusDown:
		return lib.StatusDown
	case StatusReady:
		return lib.StatusReady
	case StatusStartup:
		return lib.StatusStartup
	case StatusShutdown:
		return lib.StatusShutdown
	default:
		return lib.StatusDown
	}
}

func (slave *Slave) Shutdown() error {
	response, err := slave.Queue(CommandShutdown)
	if err != nil {
		return err
	}
	if response.Arg(0) != ShutdownOK {
		return errors.New("Shutdown denied by host")
	}
	return nil
}

func (slave *Slave) Revision(rev lib.Revision) (lib.Revision, error) {
	cmd := CommandRevision
	if rev != nil {
		cmd = cmd.Param(rev.String())
	}
	response, err := slave.Queue(cmd)
	if err != nil {
		return nil, err
	}
	bytes, err := lib.ToRevision(response.Arg(0))
	if err != nil {
		return nil, err
	}
	return lib.Revision(bytes), nil
}

func (slave *Slave) Rebuild() error {
	response, err := slave.Queue(CommandRebuild)
	if err != nil {
		return err
	}
	if response.Arg(0) != RebuildOK {
		return errors.New("Rebuild denied by host")
	}
	return nil
}

type Master struct {
	*Slave
}

func (master *Master) Cluster() ([]lib.Node, error) {
	response, err := master.Queue(CommandCluster)
	if err != nil {
		return nil, err
	}
	nodes := make([]lib.Node, response.ArgCount())
	for i := 0; i < response.ArgCount(); i++ {
		nodes[i] = NewSlave(response.Arg(i))
	}
	return nodes, nil
}

func (master *Master) Replicas() ([]lib.Master, error) {
	response, err := master.Queue(CommandReplicas)
	if err != nil {
		return nil, err
	}
	replicas := make([]lib.Master, response.ArgCount())
	for i := 0; i < response.ArgCount(); i++ {
		replicas[i] = NewMaster(response.Arg(i))
	}
	return replicas, nil
}

func (slave *Slave) Role() (lib.Role, error) {
	response, err := slave.Queue(CommandRole)
	if err != nil {
		return lib.RoleSlave, err
	}
	switch response.Arg(0) {
	case RoleMasterPrimary:
		return lib.RoleMasterPrimary, nil
	case RoleMaster:
		return lib.RoleMaster, nil
	default:
		return lib.RoleSlave, nil
	}
}

func (slave *Slave) LocalKeys() ([]string, error) {
	response, err := slave.Queue(CommandLocalKeys.Param())
	if err != nil {
		return nil, err
	}
	return response.ArgList(), nil
}

func (slave *Slave) Keys() ([]string, error) {
	response, err := slave.Queue(CommandKeys.Param())
	if err != nil {
		return nil, err
	}
	return response.ArgList(), nil
}

func (slave *Slave) Mirror(peers []lib.Node) error {
	addrs := make([]string, len(peers))
	for i := range addrs {
		addrs[i] = peers[i].Address()
	}
	response, err := slave.Queue(CommandMirror.Param(addrs...))
	if err != nil {
		return err
	}
	if response.Arg(0) != MirrorOK {
		return errors.New("Expected OK, got " + response.Arg(0))
	}
	return nil
}

func (slave *Slave) Close() {
	if !slave.Dead {
		if slave.Connection != nil {
			slave.Connection.Close()
		}
		slave.Connection = nil
		slave.Dead = true
	}
}

func (master *Master) Join(n lib.Node) error {
	response, err := master.Queue(CommandJoin.Param(n.Address()))
	if err != nil {
		return err
	}
	if response.Arg(0) != JoinOK {
		return errors.New("Join denied by host")
	}
	return nil
}

func (master *Master) Assist(m lib.Master) error {
	response, err := master.Queue(CommandAssist.Param(m.Address()))
	if err != nil {
		return err
	}
	if response.Arg(0) != AssistOK {
		return errors.New("Assist denied by host")
	}
	return nil
}

func NewMaster(addr string) *Master {
	return &Master{NewSlave(addr)}
}

func NewSlave(addr string) *Slave {
	return &Slave{PublicAddress: addr}
}
<commit_msg>add godoc remote package comments<commit_after>// Package remote provides a remote interface for network nodes.
package remote

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"bufio"

	"github.com/lnsp/dkvs/lib"
)

// A node in the cluster can either be down (not reachable), ready (usable), in startup mode (online, but not yet usable)
// or in shutdown mode (online, soon unreachable).
const (
	StatusDown     = "DOWN"
	StatusReady    = "READY"
	StatusStartup  = "STARTUP"
	StatusShutdown = "SHUTDOWN"
)

// A nodes role in the cluster can either be primary. secondary or generic.
const (
	RoleMasterPrimary = "PRIMARY"
	RoleMaster        = "SECONDARY"
	RoleSlave         = "GENERIC"
)

// Most command respond with a binary state, most often OK or DENIED.
const (
	ShutdownOK     = "OK"
	ShutdownDenied = "DENIED"
	StoreOK        = "OK"
	StoreDenied    = "DENIED"
	JoinOK         = "OK"
	JoinDenied     = "DENIED"
	JoinInUse      = "IN USE"
	RebuildOK      = "OK"
	HandshakeOK    = "OK"
	AssistOK       = "OK"
	RevisionOK     = "OK"
	MirrorOK       = "OK"
)

const (
	maxReconnects     = 5
	reconnectInterval = 3 * time.Second
	cmdArgSeparator   = ";"
	cmdNameSeparator  = "#"
)

var (
	// CommandCluster retrieves a list of nodes in the cluster.
	CommandCluster = &Command{Name: "CLUSTER"}
	// CommandReplicas retrieves a list of masters in the cluster.
	CommandReplicas = &Command{Name: "REPLICAS"}
	// CommandRole sets and gets the nodes role.
	CommandRole = &Command{Name: "ROLE"}
	// CommandJoin tells the master that a specific node wants to join its cluster.
	CommandJoin = &Command{Name: "JOIN"}
	// CommandRevision gets the nodes local revision.
	CommandRevision = &Command{Name: "REVISION"}
	// CommandStore tells the cluster to store a key-value pair.
	CommandStore = &Command{Name: "STORE"}
	// CommandLocalStore tells the node to store a key-value pair locally.
	CommandLocalStore = &Command{Name: "STORE_LOCAL"}
	// CommandLocalRead retrieves a local key-value pair from the node.
	CommandLocalRead = &Command{Name: "READ_LOCAL"}
	// CommandRead retrieves a key-value pair from the cluster.
	CommandRead = &Command{Name: "READ"}
	// CommandError signals an error.
	CommandError = &Command{Name: "ERROR"}
	// CommandStatus sets and gets the nodes internal status.
	CommandStatus = &Command{Name: "STATUS"}
	// CommandShutdown kills the cluster node.
	CommandShutdown = &Command{Name: "SHUTDOWN"}
	// CommandAddress retrieves the nodes public address.
	CommandAddress = &Command{Name: "ADDRESS"}
	// CommandRebuild tells the node to rebuild its cluster access data.
	CommandRebuild = &Command{Name: "REBUILD"}
	// CommandHandshake does nothing than shakin dem hands.
	CommandHandshake = &Command{Name: "HANDSHAKE"}
	// CommandAssist tells the master that a node wants to assist in the cluster as a master.
	CommandAssist = &Command{Name: "ASSIST"}
	// CommandMirror tells the receiver to mirror the specified node.
	CommandMirror = &Command{Name: "MIRROR"}
	// CommandLocalKeys retrieves the list of locally stored keys.
	CommandLocalKeys = &Command{Name: "KEYS_LOCAL"}
	// CommandKeys retrieves the list of keys stored in the cluster.
	CommandKeys = &Command{Name: "KEYS"}
)

// Node is a remote node connection.
type Node interface {
	Close()
	Queue(cmd *Command) (*Command, error)
	Poll() (*Command, error)
	Push(*Command) error
}

// Error generates a parameterized error command.
func Error(err error) *Command {
	return CommandError.Param(strings.ToUpper(err.Error()))
}

// Command is a generic DKVS command that can be send via a network connection.
type Command struct {
	Name string
	Args []string
}

// KindOf checks if the command types are the same.
func (cmd Command) KindOf(kind *Command) bool {
	return cmd.Name == kind.Name
}

// Arg gets the command argument at index i.
func (cmd Command) Arg(index int) string {
	if len(cmd.Args) <= index {
		return ""
	}
	return cmd.Args[index]
}

// ArgCount returns the number of arguments.
func (cmd Command) ArgCount() int {
	return len(cmd.Args)
}

// ArgList returns a copy of the commands arguments.
func (cmd Command) ArgList() []string {
	return cmd.Args[:]
}

// Param builds a new command instance with the same type but different arguments.
func (cmd Command) Param(params ...string) *Command {
	return &Command{
		Name: cmd.Name,
		Args: params,
	}
}

// Marshal converts the command into a slice of bytes.
func (cmd Command) Marshal() []byte {
	return []byte(cmd.Name + cmdNameSeparator + strings.Join(cmd.Args, cmdArgSeparator) + "\n")
}

// String outputs the command in a human readable format.
func (cmd Command) String() string {
	return fmt.Sprintf("%s (%s)", cmd.Name, strings.Join(cmd.Args, ", "))
}

// UnmarshalCommand generates a command from a string of bytes. It may return an error while parsing.
func UnmarshalCommand(cmd []byte) (*Command, error) {
	cmdString := string(cmd)
	cmdTokens := strings.Split(cmdString, cmdNameSeparator)
	if len(cmdTokens) < 1 {
		return nil, fmt.Errorf("Invalid command format: missing tokens")
	}
	name := strings.ToUpper(cmdTokens[0])
	if len(cmdTokens) < 2 {
		return &Command{
			Name: name,
			Args: []string{},
		}, nil
	}
	args := cmdTokens[1]
	argTokens := strings.Split(args, cmdArgSeparator)
	return &Command{
		Name: name,
		Args: argTokens,
	}, nil
}

// Slave is a remotely connected cluster slave.
type Slave struct {
	Connection    net.Conn
	PublicAddress string
	Dead          bool
	reader        *bufio.Reader
}

func (slave *Slave) remoteStatus() string {
	if slave.Connection != nil {
		response, err := slave.Queue(CommandStatus)
		if err != nil {
			return StatusDown
		}
		if !response.KindOf(CommandStatus) {
			return StatusDown
		}
		return response.Arg(0)
	}
	return StatusDown
}

func (slave *Slave) Push(cmd *Command) error {
	if !slave.Dead {
		err := slave.Open()
		if err != nil {
			return err
		}
	} else {
		return errors.New("Could not push to dead connection")
	}
	slave.Log("push", cmd)
	_, err := slave.Connection.Write(cmd.Marshal())
	if err != nil {
		slave.Reset()
		return errors.New("Could not write to socket")
	}
	return nil
}

func (slave *Slave) Log(tag string, args ...interface{}) {
	fmt.Print("["+strings.ToUpper(tag)+"] ", fmt.Sprintln(args...))
}

func (slave *Slave) Reset() {
	if slave.Connection != nil {
		slave.Connection.Close()
		slave.Connection = nil
	}
}

func (slave *Slave) Poll() (*Command, error) {
	if !slave.Dead {
		err := slave.Open()
		if err != nil {
			return nil, err
		}
	} else {
		return nil, errors.New("Could not read from dead connection")
	}
	if slave.reader == nil {
		slave.reader = bufio.NewReader(slave.Connection)
	}
	data, _, err := slave.reader.ReadLine()
	if err != nil {
		slave.Reset()
		return nil, errors.New("Could not read from socket")
	}
	cmd, err := UnmarshalCommand(data)
	if err != nil {
		return nil, errors.New("Could not unmarshal command")
	}
	slave.Log("poll", cmd)
	return cmd, nil
}

func (slave *Slave) Queue(cmd *Command) (*Command, error) {
	if err := slave.Push(cmd); err != nil {
		return nil, errors.New("Could not write request to socket")
	}
	respCmd, err := slave.Poll()
	if err != nil {
		return nil, errors.New("Failed to receive response")
	}
	if respCmd.KindOf(CommandError) {
		return nil, errors.New(respCmd.Arg(0))
	}
	if !respCmd.KindOf(cmd) {
		return nil, errors.New("Unexpected command response type")
	}
	return respCmd, nil
}

func (slave *Slave) Open() error {
	if slave.Connection != nil {
		return nil
	}
	conn, err := net.Dial("tcp", slave.PublicAddress)
	if err != nil {
		return err
	}
	slave.Connection = conn
	slave.reader = bufio.NewReader(slave.Connection)
	for i := 0; slave.remoteStatus() != StatusReady; i++ {
		time.Sleep(reconnectInterval)
		if i >= maxReconnects {
			slave.Connection.Close()
			slave.Connection = nil
			return errors.New("Could not reach endpoint")
		}
	}
	return nil
}

func (slave *Slave) Address() string {
	if slave.PublicAddress != "" {
		return slave.PublicAddress
	}
	response, err := slave.Queue(CommandAddress)
	if err != nil {
		return slave.PublicAddress
	}
	slave.PublicAddress = response.Arg(0)
	return slave.PublicAddress
}

func (slave *Slave) Read(key string) (string, lib.Revision, error) {
	response, err := slave.Queue(CommandRead.Param(key))
	if err != nil {
		return key, nil, err
	}
	rev, err := lib.ToRevision(response.Arg(1))
	if err != nil {
		return key, nil, err
	}
	return response.Arg(0), rev, nil
}

func (slave *Slave) LocalRead(key string) (string, lib.Revision, error) {
	response, err := slave.Queue(CommandLocalRead.Param(key))
	if err != nil {
		return key, nil, err
	}
	rev, err := lib.ToRevision(response.Arg(1))
	if err != nil {
		return key, nil, err
	}
	return response.Arg(0), rev, nil
}

func (slave *Slave) Store(key, value string, rev lib.Revision) error {
	revString := ""
	if rev != nil {
		revString = rev.String()
	}

	response, err := slave.Queue(CommandStore.Param(key, value, revString))
	if err != nil {
		return err
	}
	if response.Arg(0) != StoreOK {
		return errors.New("Store denied by host")
	}
	return nil
}

func (slave *Slave) LocalStore(key, value string, rev lib.Revision) error {
	response, err := slave.Queue(CommandLocalStore.Param(key, value, rev.String()))
	if err != nil {
		return err
	}
	if response.Arg(0) != StoreOK {
		return errors.New("Store denied by host")
	}
	return nil
}

func (slave *Slave) Status() lib.Status {
	switch slave.remoteStatus() {
	case StatusDown:
		return lib.StatusDown
	case StatusReady:
		return lib.StatusReady
	case StatusStartup:
		return lib.StatusStartup
	case StatusShutdown:
		return lib.StatusShutdown
	default:
		return lib.StatusDown
	}
}

func (slave *Slave) Shutdown() error {
	response, err := slave.Queue(CommandShutdown)
	if err != nil {
		return err
	}
	if response.Arg(0) != ShutdownOK {
		return errors.New("Shutdown denied by host")
	}
	return nil
}

func (slave *Slave) Revision(rev lib.Revision) (lib.Revision, error) {
	cmd := CommandRevision
	if rev != nil {
		cmd = cmd.Param(rev.String())
	}
	response, err := slave.Queue(cmd)
	if err != nil {
		return nil, err
	}
	bytes, err := lib.ToRevision(response.Arg(0))
	if err != nil {
		return nil, err
	}
	return lib.Revision(bytes), nil
}

func (slave *Slave) Rebuild() error {
	response, err := slave.Queue(CommandRebuild)
	if err != nil {
		return err
	}
	if response.Arg(0) != RebuildOK {
		return errors.New("Rebuild denied by host")
	}
	return nil
}

// Master is a remotely connected master in the cluster.
type Master struct {
	*Slave
}

func (master *Master) Cluster() ([]lib.Node, error) {
	response, err := master.Queue(CommandCluster)
	if err != nil {
		return nil, err
	}
	nodes := make([]lib.Node, response.ArgCount())
	for i := 0; i < response.ArgCount(); i++ {
		nodes[i] = NewSlave(response.Arg(i))
	}
	return nodes, nil
}

func (master *Master) Replicas() ([]lib.Master, error) {
	response, err := master.Queue(CommandReplicas)
	if err != nil {
		return nil, err
	}
	replicas := make([]lib.Master, response.ArgCount())
	for i := 0; i < response.ArgCount(); i++ {
		replicas[i] = NewMaster(response.Arg(i))
	}
	return replicas, nil
}

func (slave *Slave) Role() (lib.Role, error) {
	response, err := slave.Queue(CommandRole)
	if err != nil {
		return lib.RoleSlave, err
	}
	switch response.Arg(0) {
	case RoleMasterPrimary:
		return lib.RoleMasterPrimary, nil
	case RoleMaster:
		return lib.RoleMaster, nil
	default:
		return lib.RoleSlave, nil
	}
}

func (slave *Slave) LocalKeys() ([]string, error) {
	response, err := slave.Queue(CommandLocalKeys.Param())
	if err != nil {
		return nil, err
	}
	return response.ArgList(), nil
}

func (slave *Slave) Keys() ([]string, error) {
	response, err := slave.Queue(CommandKeys.Param())
	if err != nil {
		return nil, err
	}
	return response.ArgList(), nil
}

func (slave *Slave) Mirror(peers []lib.Node) error {
	addrs := make([]string, len(peers))
	for i := range addrs {
		addrs[i] = peers[i].Address()
	}
	response, err := slave.Queue(CommandMirror.Param(addrs...))
	if err != nil {
		return err
	}
	if response.Arg(0) != MirrorOK {
		return errors.New("Expected OK, got " + response.Arg(0))
	}
	return nil
}

func (slave *Slave) Close() {
	if !slave.Dead {
		if slave.Connection != nil {
			slave.Connection.Close()
		}
		slave.Connection = nil
		slave.Dead = true
	}
}

func (master *Master) Join(n lib.Node) error {
	response, err := master.Queue(CommandJoin.Param(n.Address()))
	if err != nil {
		return err
	}
	if response.Arg(0) != JoinOK {
		return errors.New("Join denied by host")
	}
	return nil
}

func (master *Master) Assist(m lib.Master) error {
	response, err := master.Queue(CommandAssist.Param(m.Address()))
	if err != nil {
		return err
	}
	if response.Arg(0) != AssistOK {
		return errors.New("Assist denied by host")
	}
	return nil
}

// NewMaster initializes a new remote-connected master with the specified public address.
func NewMaster(addr string) *Master {
	return &Master{NewSlave(addr)}
}

// NewSlave initializes a new remote-connected slave with the specified public address.
func NewSlave(addr string) *Slave {
	return &Slave{PublicAddress: addr}
}
<|endoftext|>