<commit_before>// Copyright 2016 The Cockroach Authors.
//
// 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.
//
// Author: Radu Berinde (radu@cockroachlabs.com)
// Author: Andrei Matei (andreimatei1@gmail.com)
//
// This file provides generic interfaces that allow tests to set up test servers
// without importing the server package (avoiding circular dependencies).
// To be used, the binary needs to call
// InitTestServerFactory(server.TestServerFactory), generally from a TestMain()
// in an "foo_test" package (which can import server and is linked together with
// the other tests in package "foo").

package serverutils

import (
	gosql "database/sql"
	"net/http"
	"net/url"
	"testing"

	"github.com/gogo/protobuf/proto"

	"github.com/cockroachdb/cockroach/pkg/base"
	"github.com/cockroachdb/cockroach/pkg/gossip"
	"github.com/cockroachdb/cockroach/pkg/internal/client"
	"github.com/cockroachdb/cockroach/pkg/kv"
	"github.com/cockroachdb/cockroach/pkg/roachpb"
	"github.com/cockroachdb/cockroach/pkg/rpc"
	"github.com/cockroachdb/cockroach/pkg/security"
	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
	"github.com/cockroachdb/cockroach/pkg/util/hlc"
	"github.com/cockroachdb/cockroach/pkg/util/httputil"
	"github.com/cockroachdb/cockroach/pkg/util/stop"
)

// TestServerInterface defines test server functionality that tests need; it is
// implemented by server.TestServer.
type TestServerInterface interface {
	Stopper() *stop.Stopper

	Start(params base.TestServerArgs) error

	// ServingAddr returns the server's address.
	ServingAddr() string

	// KVClient() returns a *client.DB instance for talking to this KV server,
	// as an interface{}.
	KVClient() interface{}

	// KVDB() returns the *kv.DB instance as an interface{}.
	KVDB() interface{}

	// RPCContext returns the rpc context used by the test server.
	RPCContext() *rpc.Context

	// LeaseManager() returns the *sql.LeaseManager as an interface{}.
	LeaseManager() interface{}

	// Gossip returns the gossip used by the TestServer.
	Gossip() *gossip.Gossip

	// Clock returns the clock used by the TestServer.
	Clock() *hlc.Clock

	// DistSender returns the DistSender used by the TestServer.
	DistSender() *kv.DistSender

	// AdminURL returns the URL for the admin UI.
	AdminURL() string
	// GetHTTPClient returns an http client connected to the server. It uses the
	// context client TLS config.
	GetHTTPClient() (http.Client, error)

	// MustGetSQLCounter returns the value of a counter metric from the server's
	// SQL Executor. Runs in O(# of metrics) time, which is fine for test code.
	MustGetSQLCounter(name string) int64
	// MustGetSQLNetworkCounter returns the value of a counter metric from the
	// server's SQL server. Runs in O(# of metrics) time, which is fine for test
	// code.
	MustGetSQLNetworkCounter(name string) int64
	// WriteSummaries records summaries of time-series data, which is required for
	// any tests that query server stats.
	WriteSummaries() error

	// GetFirstStoreID is a utility function returning the StoreID of the first
	// store on this node.
	GetFirstStoreID() roachpb.StoreID
}

// TestServerFactory encompasses the actual implementation of the shim
// service.
type TestServerFactory interface {
	// New instantiates a test server.
	New(params base.TestServerArgs) interface{}
}

var srvFactoryImpl TestServerFactory

// InitTestServerFactory should be called once to provide the implementation
// of the service. It will be called from a xx_test package that can import the
// server package.
func InitTestServerFactory(impl TestServerFactory) {
	srvFactoryImpl = impl
}

// StartServer creates a test server and sets up a gosql DB connection.
// The server should be stopped by calling server.Stopper().Stop().
func StartServer(
	t testing.TB, params base.TestServerArgs,
) (TestServerInterface, *gosql.DB, *client.DB) {
	server, err := StartServerRaw(params)
	if err != nil {
		t.Fatal(err)
	}

	kvClient := server.KVClient().(*client.DB)
	pgURL, cleanupGoDB := sqlutils.PGUrl(
		t, server.ServingAddr(), "StartServer", url.User(security.RootUser))
	pgURL.Path = params.UseDatabase
	goDB, err := gosql.Open("postgres", pgURL.String())
	if err != nil {
		t.Fatal(err)
	}
	server.Stopper().AddCloser(
		stop.CloserFn(func() {
			_ = goDB.Close()
			cleanupGoDB()
		}))
	return server, goDB, kvClient
}

// StartServerRaw creates and starts a TestServer.
// Generally StartServer() should be used. However this function can be used
// directly when opening a connection to the server is not desired.
func StartServerRaw(args base.TestServerArgs) (TestServerInterface, error) {
	if srvFactoryImpl == nil {
		panic("TestServerFactory not initialized. One needs to be injected " +
			"from the package's TestMain()")
	}
	server := srvFactoryImpl.New(args).(TestServerInterface)
	if err := server.Start(args); err != nil {
		return nil, err
	}
	return server, nil
}

// GetJSONProto uses the supplied client to GET the URL specified by the parameters
// and unmarshals the result into response.
func GetJSONProto(ts TestServerInterface, path string, response proto.Message) error {
	httpClient, err := ts.GetHTTPClient()
	if err != nil {
		return err
	}
	return httputil.GetJSON(httpClient, ts.AdminURL()+path, response)
}

// PostJSONProto uses the supplied client to POST request to the URL specified by
// the parameters and unmarshals the result into response.
func PostJSONProto(ts TestServerInterface, path string, request, response proto.Message) error {
	httpClient, err := ts.GetHTTPClient()
	if err != nil {
		return err
	}
	return httputil.PostJSON(httpClient, ts.AdminURL()+path, request, response)
}
<commit_msg>testutils: add SplitRange to the server shim<commit_after>// Copyright 2016 The Cockroach Authors.
//
// 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.
//
// Author: Radu Berinde (radu@cockroachlabs.com)
// Author: Andrei Matei (andreimatei1@gmail.com)
//
// This file provides generic interfaces that allow tests to set up test servers
// without importing the server package (avoiding circular dependencies).
// To be used, the binary needs to call
// InitTestServerFactory(server.TestServerFactory), generally from a TestMain()
// in an "foo_test" package (which can import server and is linked together with
// the other tests in package "foo").

package serverutils

import (
	gosql "database/sql"
	"net/http"
	"net/url"
	"testing"

	"github.com/gogo/protobuf/proto"

	"github.com/cockroachdb/cockroach/pkg/base"
	"github.com/cockroachdb/cockroach/pkg/gossip"
	"github.com/cockroachdb/cockroach/pkg/internal/client"
	"github.com/cockroachdb/cockroach/pkg/kv"
	"github.com/cockroachdb/cockroach/pkg/roachpb"
	"github.com/cockroachdb/cockroach/pkg/rpc"
	"github.com/cockroachdb/cockroach/pkg/security"
	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
	"github.com/cockroachdb/cockroach/pkg/util/hlc"
	"github.com/cockroachdb/cockroach/pkg/util/httputil"
	"github.com/cockroachdb/cockroach/pkg/util/stop"
)

// TestServerInterface defines test server functionality that tests need; it is
// implemented by server.TestServer.
type TestServerInterface interface {
	Stopper() *stop.Stopper

	Start(params base.TestServerArgs) error

	// ServingAddr returns the server's address.
	ServingAddr() string

	// KVClient() returns a *client.DB instance for talking to this KV server,
	// as an interface{}.
	KVClient() interface{}

	// KVDB() returns the *kv.DB instance as an interface{}.
	KVDB() interface{}

	// RPCContext returns the rpc context used by the test server.
	RPCContext() *rpc.Context

	// LeaseManager() returns the *sql.LeaseManager as an interface{}.
	LeaseManager() interface{}

	// Gossip returns the gossip used by the TestServer.
	Gossip() *gossip.Gossip

	// Clock returns the clock used by the TestServer.
	Clock() *hlc.Clock

	// DistSender returns the DistSender used by the TestServer.
	DistSender() *kv.DistSender

	// AdminURL returns the URL for the admin UI.
	AdminURL() string
	// GetHTTPClient returns an http client connected to the server. It uses the
	// context client TLS config.
	GetHTTPClient() (http.Client, error)

	// MustGetSQLCounter returns the value of a counter metric from the server's
	// SQL Executor. Runs in O(# of metrics) time, which is fine for test code.
	MustGetSQLCounter(name string) int64
	// MustGetSQLNetworkCounter returns the value of a counter metric from the
	// server's SQL server. Runs in O(# of metrics) time, which is fine for test
	// code.
	MustGetSQLNetworkCounter(name string) int64
	// WriteSummaries records summaries of time-series data, which is required for
	// any tests that query server stats.
	WriteSummaries() error

	// GetFirstStoreID is a utility function returning the StoreID of the first
	// store on this node.
	GetFirstStoreID() roachpb.StoreID

	// SplitRange splits the range containing splitKey.
	SplitRange(
		splitKey roachpb.Key,
	) (left roachpb.RangeDescriptor, right roachpb.RangeDescriptor, err error)
}

// TestServerFactory encompasses the actual implementation of the shim
// service.
type TestServerFactory interface {
	// New instantiates a test server.
	New(params base.TestServerArgs) interface{}
}

var srvFactoryImpl TestServerFactory

// InitTestServerFactory should be called once to provide the implementation
// of the service. It will be called from a xx_test package that can import the
// server package.
func InitTestServerFactory(impl TestServerFactory) {
	srvFactoryImpl = impl
}

// StartServer creates a test server and sets up a gosql DB connection.
// The server should be stopped by calling server.Stopper().Stop().
func StartServer(
	t testing.TB, params base.TestServerArgs,
) (TestServerInterface, *gosql.DB, *client.DB) {
	server, err := StartServerRaw(params)
	if err != nil {
		t.Fatal(err)
	}

	kvClient := server.KVClient().(*client.DB)
	pgURL, cleanupGoDB := sqlutils.PGUrl(
		t, server.ServingAddr(), "StartServer", url.User(security.RootUser))
	pgURL.Path = params.UseDatabase
	goDB, err := gosql.Open("postgres", pgURL.String())
	if err != nil {
		t.Fatal(err)
	}
	server.Stopper().AddCloser(
		stop.CloserFn(func() {
			_ = goDB.Close()
			cleanupGoDB()
		}))
	return server, goDB, kvClient
}

// StartServerRaw creates and starts a TestServer.
// Generally StartServer() should be used. However this function can be used
// directly when opening a connection to the server is not desired.
func StartServerRaw(args base.TestServerArgs) (TestServerInterface, error) {
	if srvFactoryImpl == nil {
		panic("TestServerFactory not initialized. One needs to be injected " +
			"from the package's TestMain()")
	}
	server := srvFactoryImpl.New(args).(TestServerInterface)
	if err := server.Start(args); err != nil {
		return nil, err
	}
	return server, nil
}

// GetJSONProto uses the supplied client to GET the URL specified by the parameters
// and unmarshals the result into response.
func GetJSONProto(ts TestServerInterface, path string, response proto.Message) error {
	httpClient, err := ts.GetHTTPClient()
	if err != nil {
		return err
	}
	return httputil.GetJSON(httpClient, ts.AdminURL()+path, response)
}

// PostJSONProto uses the supplied client to POST request to the URL specified by
// the parameters and unmarshals the result into response.
func PostJSONProto(ts TestServerInterface, path string, request, response proto.Message) error {
	httpClient, err := ts.GetHTTPClient()
	if err != nil {
		return err
	}
	return httputil.PostJSON(httpClient, ts.AdminURL()+path, request, response)
}
<|endoftext|>


<commit_before>/*

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 katibclient

import (
	"context"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/config"

	experimentsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1alpha3"
	suggestionsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/suggestions/v1alpha3"
	trialsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1alpha3"
	"github.com/kubeflow/katib/pkg/controller.v1alpha3/consts"
)

type Client interface {
	InjectClient(c client.Client)
	GetClient() client.Client
	GetExperimentList(namespace ...string) (*experimentsv1alpha3.ExperimentList, error)
	CreateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error
	UpdateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error
	DeleteExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error
	GetExperiment(name string, namespace ...string) (*experimentsv1alpha3.Experiment, error)
	GetConfigMap(name string, namespace ...string) (map[string]string, error)
	GetTrialList(name string, namespace ...string) (*trialsv1alpha3.TrialList, error)
	GetTrialTemplates(namespace ...string) (map[string]string, error)
	GetSuggestion(name string, namespace ...string) (*suggestionsv1alpha3.Suggestion, error)
	UpdateTrialTemplates(newTrialTemplates map[string]string, namespace ...string) error
	GetNamespaceList() (*apiv1.NamespaceList, error)
}

type KatibClient struct {
	client client.Client
}

func NewWithGivenClient(c client.Client) Client {
	return &KatibClient{
		client: c,
	}
}

func NewClient(options client.Options) (Client, error) {
	cfg, err := config.GetConfig()
	if err != nil {
		return nil, err
	}
	experimentsv1alpha3.AddToScheme(scheme.Scheme)
	trialsv1alpha3.AddToScheme(scheme.Scheme)
	suggestionsv1alpha3.AddToScheme(scheme.Scheme)
	cl, err := client.New(cfg, options)
	return &KatibClient{
		client: cl,
	}, nil
}

func (k *KatibClient) InjectClient(c client.Client) {
	k.client = c
}

func (k *KatibClient) GetClient() client.Client {
	return k.client
}

func (k *KatibClient) GetExperimentList(namespace ...string) (*experimentsv1alpha3.ExperimentList, error) {
	ns := getNamespace(namespace...)
	expList := &experimentsv1alpha3.ExperimentList{}
	listOpt := client.InNamespace(ns)

	if err := k.client.List(context.Background(), listOpt, expList); err != nil {
		return expList, err
	}
	return expList, nil

}

func (k *KatibClient) GetSuggestion(name string, namespace ...string) (
	*suggestionsv1alpha3.Suggestion, error) {
	ns := getNamespace(namespace...)
	suggestion := &suggestionsv1alpha3.Suggestion{}

	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ns}, suggestion); err != nil {
		return nil, err
	}
	return suggestion, nil

}

func (k *KatibClient) GetTrialList(name string, namespace ...string) (*trialsv1alpha3.TrialList, error) {
	ns := getNamespace(namespace...)
	trialList := &trialsv1alpha3.TrialList{}
	labels := map[string]string{consts.LabelExperimentName: name}
	listOpt := &client.ListOptions{}
	listOpt.MatchingLabels(labels).InNamespace(ns)

	if err := k.client.List(context.Background(), listOpt, trialList); err != nil {
		return trialList, err
	}
	return trialList, nil

}

func (k *KatibClient) CreateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error {

	if err := k.client.Create(context.Background(), experiment); err != nil {
		return err
	}
	return nil
}

func (k *KatibClient) UpdateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error {

	if err := k.client.Update(context.Background(), experiment); err != nil {
		return err
	}
	return nil
}

func (k *KatibClient) DeleteExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error {

	if err := k.client.Delete(context.Background(), experiment); err != nil {
		return err
	}
	return nil
}

func (k *KatibClient) GetExperiment(name string, namespace ...string) (*experimentsv1alpha3.Experiment, error) {
	ns := getNamespace(namespace...)
	exp := &experimentsv1alpha3.Experiment{}
	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ns}, exp); err != nil {
		return nil, err
	}
	return exp, nil
}

// GetConfigMap returns the configmap for the given name and namespace.
func (k *KatibClient) GetConfigMap(name string, namespace ...string) (map[string]string, error) {
	ns := getNamespace(namespace...)
	configMap := &apiv1.ConfigMap{}
	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ns}, configMap); err != nil {
		return map[string]string{}, err
	}
	return configMap.Data, nil
}

// GetTrialTemplates returns the trial template if it exists.
func (k *KatibClient) GetTrialTemplates(namespace ...string) (map[string]string, error) {
	ns := getNamespace(namespace...)

	data, err := k.GetConfigMap(experimentsv1alpha3.DefaultTrialConfigMapName, ns)
	if err != nil && errors.IsNotFound(err) {
		return map[string]string{}, nil
	} else if err != nil {
		return nil, err
	}
	return data, nil
}

func (k *KatibClient) UpdateTrialTemplates(newTrialTemplates map[string]string, namespace ...string) error {
	ns := getNamespace(namespace...)
	trialTemplates := &apiv1.ConfigMap{}

	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: experimentsv1alpha3.DefaultTrialConfigMapName, Namespace: ns}, trialTemplates); err != nil {
		return err
	}
	trialTemplates.Data = newTrialTemplates

	if err := k.client.Update(context.Background(), trialTemplates); err != nil {
		return err
	}
	return nil
}

func getNamespace(namespace ...string) string {
	if len(namespace) == 0 {
		return consts.DefaultKatibNamespace
	}
	return namespace[0]
}

func (k *KatibClient) GetNamespaceList() (*apiv1.NamespaceList, error) {

	namespaceList := &apiv1.NamespaceList{}
	listOpt := &client.ListOptions{}

	if err := k.client.List(context.TODO(), listOpt, namespaceList); err != nil {
		return namespaceList, err
	}
	return namespaceList, nil
}
<commit_msg>Adding error propagation for K8s client creation in KatibClient (#1053)<commit_after>/*

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 katibclient

import (
	"context"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/config"

	experimentsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1alpha3"
	suggestionsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/suggestions/v1alpha3"
	trialsv1alpha3 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1alpha3"
	"github.com/kubeflow/katib/pkg/controller.v1alpha3/consts"
)

type Client interface {
	InjectClient(c client.Client)
	GetClient() client.Client
	GetExperimentList(namespace ...string) (*experimentsv1alpha3.ExperimentList, error)
	CreateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error
	UpdateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error
	DeleteExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error
	GetExperiment(name string, namespace ...string) (*experimentsv1alpha3.Experiment, error)
	GetConfigMap(name string, namespace ...string) (map[string]string, error)
	GetTrialList(name string, namespace ...string) (*trialsv1alpha3.TrialList, error)
	GetTrialTemplates(namespace ...string) (map[string]string, error)
	GetSuggestion(name string, namespace ...string) (*suggestionsv1alpha3.Suggestion, error)
	UpdateTrialTemplates(newTrialTemplates map[string]string, namespace ...string) error
	GetNamespaceList() (*apiv1.NamespaceList, error)
}

type KatibClient struct {
	client client.Client
}

func NewWithGivenClient(c client.Client) Client {
	return &KatibClient{
		client: c,
	}
}

func NewClient(options client.Options) (Client, error) {
	cfg, err := config.GetConfig()
	if err != nil {
		return nil, err
	}
	experimentsv1alpha3.AddToScheme(scheme.Scheme)
	trialsv1alpha3.AddToScheme(scheme.Scheme)
	suggestionsv1alpha3.AddToScheme(scheme.Scheme)
	cl, err := client.New(cfg, options)
	if err != nil {
		return nil, err
	}
	return &KatibClient{
		client: cl,
	}, nil
}

func (k *KatibClient) InjectClient(c client.Client) {
	k.client = c
}

func (k *KatibClient) GetClient() client.Client {
	return k.client
}

func (k *KatibClient) GetExperimentList(namespace ...string) (*experimentsv1alpha3.ExperimentList, error) {
	ns := getNamespace(namespace...)
	expList := &experimentsv1alpha3.ExperimentList{}
	listOpt := client.InNamespace(ns)

	if err := k.client.List(context.Background(), listOpt, expList); err != nil {
		return expList, err
	}
	return expList, nil

}

func (k *KatibClient) GetSuggestion(name string, namespace ...string) (
	*suggestionsv1alpha3.Suggestion, error) {
	ns := getNamespace(namespace...)
	suggestion := &suggestionsv1alpha3.Suggestion{}

	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ns}, suggestion); err != nil {
		return nil, err
	}
	return suggestion, nil

}

func (k *KatibClient) GetTrialList(name string, namespace ...string) (*trialsv1alpha3.TrialList, error) {
	ns := getNamespace(namespace...)
	trialList := &trialsv1alpha3.TrialList{}
	labels := map[string]string{consts.LabelExperimentName: name}
	listOpt := &client.ListOptions{}
	listOpt.MatchingLabels(labels).InNamespace(ns)

	if err := k.client.List(context.Background(), listOpt, trialList); err != nil {
		return trialList, err
	}
	return trialList, nil

}

func (k *KatibClient) CreateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error {

	if err := k.client.Create(context.Background(), experiment); err != nil {
		return err
	}
	return nil
}

func (k *KatibClient) UpdateExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error {

	if err := k.client.Update(context.Background(), experiment); err != nil {
		return err
	}
	return nil
}

func (k *KatibClient) DeleteExperiment(experiment *experimentsv1alpha3.Experiment, namespace ...string) error {

	if err := k.client.Delete(context.Background(), experiment); err != nil {
		return err
	}
	return nil
}

func (k *KatibClient) GetExperiment(name string, namespace ...string) (*experimentsv1alpha3.Experiment, error) {
	ns := getNamespace(namespace...)
	exp := &experimentsv1alpha3.Experiment{}
	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ns}, exp); err != nil {
		return nil, err
	}
	return exp, nil
}

// GetConfigMap returns the configmap for the given name and namespace.
func (k *KatibClient) GetConfigMap(name string, namespace ...string) (map[string]string, error) {
	ns := getNamespace(namespace...)
	configMap := &apiv1.ConfigMap{}
	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: ns}, configMap); err != nil {
		return map[string]string{}, err
	}
	return configMap.Data, nil
}

// GetTrialTemplates returns the trial template if it exists.
func (k *KatibClient) GetTrialTemplates(namespace ...string) (map[string]string, error) {
	ns := getNamespace(namespace...)

	data, err := k.GetConfigMap(experimentsv1alpha3.DefaultTrialConfigMapName, ns)
	if err != nil && errors.IsNotFound(err) {
		return map[string]string{}, nil
	} else if err != nil {
		return nil, err
	}
	return data, nil
}

func (k *KatibClient) UpdateTrialTemplates(newTrialTemplates map[string]string, namespace ...string) error {
	ns := getNamespace(namespace...)
	trialTemplates := &apiv1.ConfigMap{}

	if err := k.client.Get(context.TODO(), types.NamespacedName{Name: experimentsv1alpha3.DefaultTrialConfigMapName, Namespace: ns}, trialTemplates); err != nil {
		return err
	}
	trialTemplates.Data = newTrialTemplates

	if err := k.client.Update(context.Background(), trialTemplates); err != nil {
		return err
	}
	return nil
}

func getNamespace(namespace ...string) string {
	if len(namespace) == 0 {
		return consts.DefaultKatibNamespace
	}
	return namespace[0]
}

func (k *KatibClient) GetNamespaceList() (*apiv1.NamespaceList, error) {

	namespaceList := &apiv1.NamespaceList{}
	listOpt := &client.ListOptions{}

	if err := k.client.List(context.TODO(), listOpt, namespaceList); err != nil {
		return namespaceList, err
	}
	return namespaceList, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	rpc "github.com/conformal/btcrpcclient"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
)

// ChainServer describes the arguments necessary to connect a btcwallet
// instance to a btcd websocket RPC server.
type ChainServer struct {
	connect  string
	user     string
	pass     string
	certPath string
	keyPath  string
	cert     []byte
}

// For now, hardcode a single already-running btcd connection that is used for
// each actor. This should be changed to start a new btcd with the --simnet
// flag, and each actor can connect to the spawned btcd process.
var defaultChainServer = ChainServer{
	connect: "localhost:18556", // local simnet btcd
	user:    "rpcuser",
	pass:    "rpcpass",
}

var (
	// maxConnRetries defines the number of times to retry rpc client connections
	maxConnRetries = flag.Int("maxconnretries", 15, "Maximum retries to connect to rpc client")

	// maxActors defines the Number of actors to spawn
	maxActors = flag.Int("maxactors", 1, "Maximum number of actors")

	// maxBlocks defines how many blocks have to connect to the blockchain
	// before the simulation normally stops
	maxBlocks = flag.Int("maxblocks", 20000, "Maximum blocks to generate")

	// matureBlock defines after which block the blockchain is mature enough to start
	// controlled mining as per the tx curve
	matureBlock = flag.Int("matureblock", 16200, "Block number at blockchain maturity")

	// maxAddresses defines the number of addresses to generate per actor
	maxAddresses = flag.Int("maxaddresses", 1000, "Maximum addresses per actor")

	// txCurvePath is the path to a CSV file containing the block vs no. of transactions curve
	txCurvePath = flag.String("txcurve", "",
		"Path to the CSV File containing <block #>, <txCount> fields")
)

func init() {
	flag.Parse()

	runtime.GOMAXPROCS(runtime.NumCPU())
	rand.Seed(time.Now().UnixNano())
}

func main() {
	// txCurve is a slice of Rows, each corresponding
	// to a row in the input CSV file
	// if txCurve is not nil, we control mining so as to
	// get the same block vs tx count as the input curve
	var txCurve []*Row
	if *txCurvePath != "" {
		var err error
		txCurve, err = readCSV(*txCurvePath)
		if err != nil {
			log.Fatalf("Error reading tx curve CSV: %v", err)
			return
		}
	}

	actors := make([]*Actor, 0, *maxActors)
	com := NewCommunication()

	if txCurve != nil {
		// start - start generating tx for next block
		// txpool - tx has been accepted into miner mempool
		// both are required only for generating tx curve
		com.start = make(chan struct{})
		com.txpool = make(chan struct{})
	}

	btcdHomeDir := btcutil.AppDataDir("btcd", false)
	defaultChainServer.certPath = filepath.Join(btcdHomeDir, "rpc.cert")
	defaultChainServer.keyPath = filepath.Join(btcdHomeDir, "rpc.key")
	cert, err := ioutil.ReadFile(defaultChainServer.certPath)
	if err != nil {
		log.Fatalf("Cannot read certificate: %v", err)
	}
	defaultChainServer.cert = cert

	btcdArgs := []string{
		"--simnet",
		"-u" + defaultChainServer.user,
		"-P" + defaultChainServer.pass,
		"--rpccert=" + defaultChainServer.certPath,
		"--rpckey=" + defaultChainServer.keyPath,
		"--profile=",
	}

	log.Println("Starting btcd on simnet...")
	btcd := exec.Command("btcd", btcdArgs...)
	if err := btcd.Start(); err != nil {
		log.Fatalf("Couldn't start btcd: %v", err)
	}

	// Create and start RPC client.
	rpcConf := rpc.ConnConfig{
		Host:         defaultChainServer.connect,
		Endpoint:     "ws",
		User:         defaultChainServer.user,
		Pass:         defaultChainServer.pass,
		Certificates: defaultChainServer.cert,
	}

	ntfnHandlers := rpc.NotificationHandlers{
		OnTxAccepted: func(hash *btcwire.ShaHash, amount btcutil.Amount) {
			log.Printf("CHSR: Transaction accepted: Hash: %v, Amount: %v", hash, amount)
			com.timeReceived <- time.Now()
		},
	}

	var client *rpc.Client
	for i := 0; i < *maxConnRetries; i++ {
		if client, err = rpc.New(&rpcConf, &ntfnHandlers); err != nil {
			time.Sleep(time.Duration(i) * 50 * time.Millisecond)
			continue
		}
		break
	}
	if client == nil {
		log.Printf("Cannot start btcd rpc client: %v", err)
		if err := Exit(btcd); err != nil {
			log.Printf("Cannot kill initial btcd process: %v", err)
		}
		return
	}

	// Register for transaction notifications
	if err := client.NotifyNewTransactions(false); err != nil {
		log.Printf("Cannot register for transactions notifications: %v", err)
		if err := Exit(btcd); err != nil {
			log.Printf("Cannot kill initial btcd process: %v", err)
		}
		return
	}

	for i := 0; i < *maxActors; i++ {
		a, err := NewActor(&defaultChainServer, uint16(18557+i))
		if err != nil {
			log.Printf("Cannot create actor on %s: %v", "localhost:"+a.args.port, err)
			continue
		}
		actors = append(actors, a)
	}

	// close actors and exit btcd on interrupt
	addInterruptHandler(func() {
		close(com.interrupt)
		Close(actors)
		if err := Exit(btcd); err != nil {
			log.Printf("Cannot kill initial btcd process: %v", err)
		}
		close(com.waitForInterrupt)
	})

	// Start simulation.
	tpsChan := com.Start(actors, client, btcd, txCurve)
	com.WaitForShutdown()

	tps, ok := <-tpsChan
	if ok {
		log.Printf("Average transactions per sec: %.2f", tps)
	}
}

// Exit closes the cmd by passing SIGINT
// workaround for windows by passing SIGKILL
func Exit(cmd *exec.Cmd) (err error) {
	defer cmd.Wait()

	if runtime.GOOS == "windows" {
		err = cmd.Process.Signal(os.Kill)
	} else {
		err = cmd.Process.Signal(os.Interrupt)
	}

	return
}

// Close sends close signal to actors, waits for actor goroutines
// to exit and then shuts down all actors.
func Close(actors []*Actor) {
	// Stop actors by shuting down their rpc client and closing quit channel.
	for _, a := range actors {
		a.Stop()
		a.WaitForShutdown()
	}

	// shutdown only after all actors have stopped
	for _, a := range actors {
		a.Shutdown()
	}
}
<commit_msg>use a tempdir for chainserver datadir<commit_after>// Copyright (c) 2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	rpc "github.com/conformal/btcrpcclient"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
)

// ChainServer describes the arguments necessary to connect a btcwallet
// instance to a btcd websocket RPC server.
type ChainServer struct {
	connect  string
	user     string
	pass     string
	certPath string
	keyPath  string
	cert     []byte
}

// For now, hardcode a single already-running btcd connection that is used for
// each actor. This should be changed to start a new btcd with the --simnet
// flag, and each actor can connect to the spawned btcd process.
var defaultChainServer = ChainServer{
	connect: "localhost:18556", // local simnet btcd
	user:    "rpcuser",
	pass:    "rpcpass",
}

var (
	// maxConnRetries defines the number of times to retry rpc client connections
	maxConnRetries = flag.Int("maxconnretries", 15, "Maximum retries to connect to rpc client")

	// maxActors defines the Number of actors to spawn
	maxActors = flag.Int("maxactors", 1, "Maximum number of actors")

	// maxBlocks defines how many blocks have to connect to the blockchain
	// before the simulation normally stops
	maxBlocks = flag.Int("maxblocks", 20000, "Maximum blocks to generate")

	// matureBlock defines after which block the blockchain is mature enough to start
	// controlled mining as per the tx curve
	matureBlock = flag.Int("matureblock", 16200, "Block number at blockchain maturity")

	// maxAddresses defines the number of addresses to generate per actor
	maxAddresses = flag.Int("maxaddresses", 1000, "Maximum addresses per actor")

	// txCurvePath is the path to a CSV file containing the block vs no. of transactions curve
	txCurvePath = flag.String("txcurve", "",
		"Path to the CSV File containing <block #>, <txCount> fields")
)

func init() {
	flag.Parse()

	runtime.GOMAXPROCS(runtime.NumCPU())
	rand.Seed(time.Now().UnixNano())
}

func main() {
	// txCurve is a slice of Rows, each corresponding
	// to a row in the input CSV file
	// if txCurve is not nil, we control mining so as to
	// get the same block vs tx count as the input curve
	var txCurve []*Row
	if *txCurvePath != "" {
		var err error
		txCurve, err = readCSV(*txCurvePath)
		if err != nil {
			log.Fatalf("Error reading tx curve CSV: %v", err)
			return
		}
	}

	actors := make([]*Actor, 0, *maxActors)
	com := NewCommunication()

	if txCurve != nil {
		// start - start generating tx for next block
		// txpool - tx has been accepted into miner mempool
		// both are required only for generating tx curve
		com.start = make(chan struct{})
		com.txpool = make(chan struct{})
	}

	btcdHomeDir := btcutil.AppDataDir("btcd", false)
	defaultChainServer.certPath = filepath.Join(btcdHomeDir, "rpc.cert")
	defaultChainServer.keyPath = filepath.Join(btcdHomeDir, "rpc.key")
	cert, err := ioutil.ReadFile(defaultChainServer.certPath)
	if err != nil {
		log.Fatalf("Cannot read certificate: %v", err)
	}
	defaultChainServer.cert = cert

	datadir, err := ioutil.TempDir("", "chainServerData")
	if err != nil {
		log.Fatalf("Cannot read certificate: %v", err)
	}
	defer func(datadir string) {
		if err := os.RemoveAll(datadir); err != nil {
			log.Printf("Cannot remove mining btcd datadir: %v", err)
			return
		}
	}(datadir)

	btcdArgs := []string{
		"--simnet",
		"-u" + defaultChainServer.user,
		"-P" + defaultChainServer.pass,
		"--datadir=" + datadir,
		"--rpccert=" + defaultChainServer.certPath,
		"--rpckey=" + defaultChainServer.keyPath,
		"--profile=",
	}

	log.Println("Starting btcd on simnet...")
	btcd := exec.Command("btcd", btcdArgs...)
	if err := btcd.Start(); err != nil {
		log.Fatalf("Couldn't start btcd: %v", err)
	}

	// Create and start RPC client.
	rpcConf := rpc.ConnConfig{
		Host:         defaultChainServer.connect,
		Endpoint:     "ws",
		User:         defaultChainServer.user,
		Pass:         defaultChainServer.pass,
		Certificates: defaultChainServer.cert,
	}

	ntfnHandlers := rpc.NotificationHandlers{
		OnTxAccepted: func(hash *btcwire.ShaHash, amount btcutil.Amount) {
			log.Printf("CHSR: Transaction accepted: Hash: %v, Amount: %v", hash, amount)
			com.timeReceived <- time.Now()
		},
	}

	var client *rpc.Client
	for i := 0; i < *maxConnRetries; i++ {
		if client, err = rpc.New(&rpcConf, &ntfnHandlers); err != nil {
			time.Sleep(time.Duration(i) * 50 * time.Millisecond)
			continue
		}
		break
	}
	if client == nil {
		log.Printf("Cannot start btcd rpc client: %v", err)
		if err := Exit(btcd); err != nil {
			log.Printf("Cannot kill initial btcd process: %v", err)
		}
		return
	}

	// Register for transaction notifications
	if err := client.NotifyNewTransactions(false); err != nil {
		log.Printf("Cannot register for transactions notifications: %v", err)
		if err := Exit(btcd); err != nil {
			log.Printf("Cannot kill initial btcd process: %v", err)
		}
		return
	}

	for i := 0; i < *maxActors; i++ {
		a, err := NewActor(&defaultChainServer, uint16(18557+i))
		if err != nil {
			log.Printf("Cannot create actor on %s: %v", "localhost:"+a.args.port, err)
			continue
		}
		actors = append(actors, a)
	}

	// close actors and exit btcd on interrupt
	addInterruptHandler(func() {
		close(com.interrupt)
		Close(actors)
		if err := Exit(btcd); err != nil {
			log.Printf("Cannot kill initial btcd process: %v", err)
		}
		close(com.waitForInterrupt)
	})

	// Start simulation.
	tpsChan := com.Start(actors, client, btcd, txCurve)
	com.WaitForShutdown()

	tps, ok := <-tpsChan
	if ok {
		log.Printf("Average transactions per sec: %.2f", tps)
	}
}

// Exit closes the cmd by passing SIGINT
// workaround for windows by passing SIGKILL
func Exit(cmd *exec.Cmd) (err error) {
	defer cmd.Wait()

	if runtime.GOOS == "windows" {
		err = cmd.Process.Signal(os.Kill)
	} else {
		err = cmd.Process.Signal(os.Interrupt)
	}

	return
}

// Close sends close signal to actors, waits for actor goroutines
// to exit and then shuts down all actors.
func Close(actors []*Actor) {
	// Stop actors by shuting down their rpc client and closing quit channel.
	for _, a := range actors {
		a.Stop()
		a.WaitForShutdown()
	}

	// shutdown only after all actors have stopped
	for _, a := range actors {
		a.Shutdown()
	}
}
<|endoftext|>


<commit_before>package gerrittest

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/opalmer/dockertest"
	log "github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

// SSHClient implements an SSH client for talking to
// Gerrit.
type SSHClient struct {
	log    *log.Entry
	Client *ssh.Client
}

// Close will close the SSHPort client and session.
func (s *SSHClient) Close() error {
	return s.Client.Close()
}

// Run executes a command over ssh.
func (s *SSHClient) Run(command string) ([]byte, []byte, error) {
	logger := s.log.WithField("cmd", command)
	session, err := s.Client.NewSession()
	if err != nil {
		logger.WithError(err).Error()
		return nil, nil, err
	}
	defer session.Close() // nolint: errcheck
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	session.Stdout = &stdout
	session.Stderr = &stderr
	err = session.Run(command)
	if err != nil {
		logger.WithError(err).Error()
	} else {
		logger.Debug()
	}
	return stdout.Bytes(), stderr.Bytes(), nil
}

// Version returns the current version of Gerrit.
func (s *SSHClient) Version() (string, error) {
	stdout, _, err := s.Run("gerrit version")
	return strings.Split(strings.TrimSpace(string(stdout)), " ")[2], err
}

// NewSSHClient produces an *SSHClient struct and attempts to connect to
// Gerrrit.
func NewSSHClient(config *Config, port *dockertest.Port) (*SSHClient, error) {
	logger := log.WithFields(log.Fields{
		"svc":  "gerrittest",
		"cmp":  "SSHPort",
		"path": config.PrivateKeyPath,
	})

	_, private, err := ReadSSHKeys(config.PrivateKeyPath)
	if err != nil {
		return nil, err
	}

	sshClient, err := ssh.Dial(
		"tcp", fmt.Sprintf("%s:%d", port.Address, port.Public),
		&ssh.ClientConfig{
			User:            config.Username,
			Auth:            []ssh.AuthMethod{ssh.PublicKeys(private)},
			HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		},
	)
	if err != nil {
		logger.WithError(err).Error()
		return nil, err
	}

	client := &SSHClient{
		log:    logger,
		Client: sshClient,
	}
	version, err := client.Version()
	logger.WithField("version", version).Debug()
	return client, err
}

// GenerateRSAKey will generate and return an SSH key pair.
func GenerateRSAKey() (*rsa.PrivateKey, error) {
	private, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	return private, nil
}

// ReadSSHKeys will read the provided private key and return the public and
// private portions.
func ReadSSHKeys(path string) (ssh.PublicKey, ssh.Signer, error) {
	logger := log.WithFields(log.Fields{
		"cmp":   "ssh",
		"phase": "read-ssh-key",
		"path":  path,
	})

	logger.WithField("action", "read-file").Debug()
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, nil, err
	}

	logger.WithField("action", "parse-key").Debug()
	signer, err := ssh.ParsePrivateKey(data)
	return signer.PublicKey(), signer, err
}

// WriteRSAKey will take a private key and write out the public
// and private portions to disk.
// nolint: interfacer
func WriteRSAKey(key *rsa.PrivateKey, file *os.File) error {
	logger := log.WithFields(log.Fields{
		"cmp":   "ssh",
		"phase": "write-ssh-key",
	})

	logger.WithField("action", "validate").Debug()
	if err := key.Validate(); err != nil {
		return err
	}

	logger.WithField("action", "encode").Debug()
	defer file.Close() // nolint: errcheck
	return pem.Encode(file, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	})
}
<commit_msg>improve handling of errors in Close()<commit_after>package gerrittest

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"

	"github.com/opalmer/dockertest"
	log "github.com/sirupsen/logrus"
	"golang.org/x/crypto/ssh"
)

// SSHClient implements an SSH client for talking to
// Gerrit.
type SSHClient struct {
	log    *log.Entry
	Client *ssh.Client
}

// Close will close the SSHPort client and session.
func (s *SSHClient) Close() error {
	err := s.Client.Close()
	if err != nil {
		if operr, ok := err.(*net.OpError); ok {
			// If the error occurred due to Close() being
			// called then ignore it rather than return an
			// error. SSHClient is short lived so we should
			// expect the socket to be closed when the program
			// terminates.
			if operr.Op == "close" {
				err = nil
			}
		}
	}

	return err
}

// Run executes a command over ssh.
func (s *SSHClient) Run(command string) ([]byte, []byte, error) {
	logger := s.log.WithField("cmd", command)
	session, err := s.Client.NewSession()
	if err != nil {
		logger.WithError(err).Error()
		return nil, nil, err
	}
	defer session.Close() // nolint: errcheck
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	session.Stdout = &stdout
	session.Stderr = &stderr
	err = session.Run(command)
	if err != nil {
		logger.WithError(err).Error()
	} else {
		logger.Debug()
	}
	return stdout.Bytes(), stderr.Bytes(), nil
}

// Version returns the current version of Gerrit.
func (s *SSHClient) Version() (string, error) {
	stdout, _, err := s.Run("gerrit version")
	return strings.Split(strings.TrimSpace(string(stdout)), " ")[2], err
}

// NewSSHClient produces an *SSHClient struct and attempts to connect to
// Gerrrit.
func NewSSHClient(config *Config, port *dockertest.Port) (*SSHClient, error) {
	logger := log.WithFields(log.Fields{
		"svc":  "gerrittest",
		"cmp":  "SSHPort",
		"path": config.PrivateKeyPath,
	})

	_, private, err := ReadSSHKeys(config.PrivateKeyPath)
	if err != nil {
		return nil, err
	}

	sshClient, err := ssh.Dial(
		"tcp", fmt.Sprintf("%s:%d", port.Address, port.Public),
		&ssh.ClientConfig{
			User:            config.Username,
			Auth:            []ssh.AuthMethod{ssh.PublicKeys(private)},
			HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		},
	)
	if err != nil {
		logger.WithError(err).Error()
		return nil, err
	}

	client := &SSHClient{
		log:    logger,
		Client: sshClient,
	}
	version, err := client.Version()
	logger.WithField("version", version).Debug()
	return client, err
}

// GenerateRSAKey will generate and return an SSH key pair.
func GenerateRSAKey() (*rsa.PrivateKey, error) {
	private, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	return private, nil
}

// ReadSSHKeys will read the provided private key and return the public and
// private portions.
func ReadSSHKeys(path string) (ssh.PublicKey, ssh.Signer, error) {
	logger := log.WithFields(log.Fields{
		"cmp":   "ssh",
		"phase": "read-ssh-key",
		"path":  path,
	})

	logger.WithField("action", "read-file").Debug()
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, nil, err
	}

	logger.WithField("action", "parse-key").Debug()
	signer, err := ssh.ParsePrivateKey(data)
	return signer.PublicKey(), signer, err
}

// WriteRSAKey will take a private key and write out the public
// and private portions to disk.
// nolint: interfacer
func WriteRSAKey(key *rsa.PrivateKey, file *os.File) error {
	logger := log.WithFields(log.Fields{
		"cmp":   "ssh",
		"phase": "write-ssh-key",
	})

	logger.WithField("action", "validate").Debug()
	if err := key.Validate(); err != nil {
		return err
	}

	logger.WithField("action", "encode").Debug()
	defer file.Close() // nolint: errcheck
	return pem.Encode(file, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	})
}
<|endoftext|>


<commit_before>/*
package stm provides Software Transactional Memory operations for Go. This is
an alternative to the standard way of writing concurrent code (channels and
mutexes). STM makes it easy to perform arbitrarily complex operations in an
atomic fashion. One of its primary advantages over traditional locking is that
STM transactions are composable, whereas locking functions are not -- the
composition will either deadlock or release the lock between functions (making
it non-atomic).

To begin, create an STM object that wraps the data you want to access
concurrently.

	x := stm.NewVar(3)

You can then use the Atomically method to atomically read and/or write the the
data. This code atomically decrements x:

	stm.Atomically(func(tx *stm.Tx) {
		cur := tx.Get(x).(int)
		tx.Set(x, cur-1)
	})

An important part of STM transactions is retrying. At any point during the
transaction, you can call tx.Retry(), which will abort the transaction, but
not cancel it entirely. The call to Atomically will block until another call
to Atomically finishes, at which point the transaction will be rerun.
Specifically, one of the values read by the transaction (via tx.Get) must be
updated before the transaction will be rerun. As an example, this code will
try to decrement x, but will block as long as x is zero:

	stm.Atomically(func(tx *stm.Tx) {
		cur := tx.Get(x).(int)
		if cur == 0 {
			tx.Retry()
		}
		tx.Set(x, cur-1)
	})

Internally, tx.Retry simply calls panic(stm.Retry). Panicking with any other
value will cancel the transaction; no values will be changed. However, it is
the responsibility of the caller to catch such panics.

Multiple transactions can be composed using Select. If the first transaction
calls Retry, the next transaction will be run, and so on. If all of the
transactions call Retry, the call will block and the entire selection will be
retried. For example, this code implements the "decrement-if-nonzero"
transaction above, but for two values. It will first try to decrement x, then
y, and block if both values are zero.

	func dec(v *stm.Var) {
		return func(tx *stm.Tx) {
			cur := tx.Get(v).(int)
			if cur == 0 {
				tx.Retry()
			}
			tx.Set(v, cur-1)
		}
	}

	// Note that Select does not perform any work itself, but merely
	// returns a transaction function.
	stm.Atomically(stm.Select(dec(x), dec(y)))

An important caveat: transactions must be idempotent (they should have the
same effect every time they are invoked). This is because a transaction may be
retried several times before successfully completing, meaning its side effects
may execute more than once. This will almost certainly cause incorrect
behavior. One common way to get around this is to build up a list of impure
operations inside the transaction, and then perform them after the transaction
completes.

The stm API tries to mimic that of Haskell's Control.Concurrent.STM, but this
is not entirely possible due to Go's type system; we are forced to use
interface{} and type assertions. Furthermore, Haskell can enforce at compile
time that STM variables are not modified outside the STM monad. This is not
possible in Go, so be especially careful when using pointers in your STM code.
*/
package stm

import (
	"sync"
	"sync/atomic"
)

// Retry is a sentinel value. When thrown via panic, it indicates that a
// transaction should be retried.
const Retry = "retry"

// The globalLock serializes transaction verification/committal.
var globalLock sync.Mutex

// A Var holds an STM variable.
type Var struct {
	val atomic.Value
}

// NewVar returns a new STM variable.
func NewVar(val interface{}) *Var {
	v := new(Var)
	v.val.Store(val)
	return v
}

// A Tx represents an atomic transaction.
type Tx struct {
	reads  map[*Var]interface{}
	writes map[*Var]interface{}
}

// verify checks that none of the logged values have changed since the
// transaction began.
// TODO: is pointer equality good enough? probably not, without immutable data
func (tx *Tx) verify() bool {
	for v, val := range tx.reads {
		if v.val.Load() != val {
			return false
		}
	}
	return true
}

// commit writes the values in the transaction log to their respective Vars.
func (tx *Tx) commit() {
	for v, val := range tx.writes {
		v.val.Store(val)
	}
}

// Get returns the value of v as of the start of the transaction.
func (tx *Tx) Get(v *Var) interface{} {
	// If we previously wrote to v, it will be in the write log.
	if val, ok := tx.writes[v]; ok {
		return val
	}
	// If we previously read v, it will be in the read log.
	if val, ok := tx.reads[v]; ok {
		return val
	}
	// Otherwise, record and return its current value.
	tx.reads[v] = v.val.Load()
	return tx.reads[v]
}

// Set sets the value of a Var for the lifetime of the transaction.
func (tx *Tx) Set(v *Var, val interface{}) {
	tx.writes[v] = val
}

// Retry aborts the transaction and retries it when a Var changes.
func (tx *Tx) Retry() {
	panic(Retry)
}

// Assert is a helper function that retries a transaction if the condition is
// not satisfied.
func (tx *Tx) Assert(p bool) {
	if !p {
		tx.Retry()
	}
}

// catchRetry returns true if fn calls tx.Retry.
func catchRetry(fn func(*Tx), tx *Tx) (retry bool) {
	defer func() {
		if r := recover(); r == Retry {
			retry = true
		} else if r != nil {
			panic(r)
		}
	}()
	fn(tx)
	return
}

// Select runs the supplied functions in order. Execution stops when a
// function succeeds without calling Retry. If no functions succeed, the
// entire selection will be retried.
func Select(fns ...func(*Tx)) func(*Tx) {
	return func(tx *Tx) {
		switch len(fns) {
		case 0:
			// empty Select blocks forever
			tx.Retry()
		case 1:
			fns[0](tx)
		default:
			if catchRetry(fns[0], tx) {
				Select(fns[1:]...)(tx)
			}
		}
	}
}

// Atomically executes the atomic function fn.
func Atomically(fn func(*Tx)) {
retry:
	// run the transaction
	tx := &Tx{
		reads:  make(map[*Var]interface{}),
		writes: make(map[*Var]interface{}),
	}
	if catchRetry(fn, tx) {
		goto retry
	}
	// verify the read log
	globalLock.Lock()
	if !tx.verify() {
		globalLock.Unlock()
		goto retry
	}
	// commit the write log
	tx.commit()
	globalLock.Unlock()
}

// AtomicGet is a helper function that atomically reads a value.
func AtomicGet(v *Var) interface{} {
	// since we're only doing one operation, we don't need a full transaction
	globalLock.Lock()
	val := v.val.Load()
	globalLock.Unlock()
	return val
}

// AtomicSet is a helper function that atomically writes a value.
func AtomicSet(v *Var, val interface{}) {
	// since we're only doing one operation, we don't need a full transaction
	globalLock.Lock()
	v.val.Store(val)
	globalLock.Unlock()
}

// Compose is a helper function that composes multiple transactions into a
// single transaction.
func Compose(fns ...func(*Tx)) func(*Tx) {
	return func(tx *Tx) {
		for _, f := range fns {
			f(tx)
		}
	}
}
<commit_msg>efficient retries via sync.Cond<commit_after>/*
package stm provides Software Transactional Memory operations for Go. This is
an alternative to the standard way of writing concurrent code (channels and
mutexes). STM makes it easy to perform arbitrarily complex operations in an
atomic fashion. One of its primary advantages over traditional locking is that
STM transactions are composable, whereas locking functions are not -- the
composition will either deadlock or release the lock between functions (making
it non-atomic).

To begin, create an STM object that wraps the data you want to access
concurrently.

	x := stm.NewVar(3)

You can then use the Atomically method to atomically read and/or write the the
data. This code atomically decrements x:

	stm.Atomically(func(tx *stm.Tx) {
		cur := tx.Get(x).(int)
		tx.Set(x, cur-1)
	})

An important part of STM transactions is retrying. At any point during the
transaction, you can call tx.Retry(), which will abort the transaction, but
not cancel it entirely. The call to Atomically will block until another call
to Atomically finishes, at which point the transaction will be rerun.
Specifically, one of the values read by the transaction (via tx.Get) must be
updated before the transaction will be rerun. As an example, this code will
try to decrement x, but will block as long as x is zero:

	stm.Atomically(func(tx *stm.Tx) {
		cur := tx.Get(x).(int)
		if cur == 0 {
			tx.Retry()
		}
		tx.Set(x, cur-1)
	})

Internally, tx.Retry simply calls panic(stm.Retry). Panicking with any other
value will cancel the transaction; no values will be changed. However, it is
the responsibility of the caller to catch such panics.

Multiple transactions can be composed using Select. If the first transaction
calls Retry, the next transaction will be run, and so on. If all of the
transactions call Retry, the call will block and the entire selection will be
retried. For example, this code implements the "decrement-if-nonzero"
transaction above, but for two values. It will first try to decrement x, then
y, and block if both values are zero.

	func dec(v *stm.Var) {
		return func(tx *stm.Tx) {
			cur := tx.Get(v).(int)
			if cur == 0 {
				tx.Retry()
			}
			tx.Set(v, cur-1)
		}
	}

	// Note that Select does not perform any work itself, but merely
	// returns a transaction function.
	stm.Atomically(stm.Select(dec(x), dec(y)))

An important caveat: transactions must be idempotent (they should have the
same effect every time they are invoked). This is because a transaction may be
retried several times before successfully completing, meaning its side effects
may execute more than once. This will almost certainly cause incorrect
behavior. One common way to get around this is to build up a list of impure
operations inside the transaction, and then perform them after the transaction
completes.

The stm API tries to mimic that of Haskell's Control.Concurrent.STM, but this
is not entirely possible due to Go's type system; we are forced to use
interface{} and type assertions. Furthermore, Haskell can enforce at compile
time that STM variables are not modified outside the STM monad. This is not
possible in Go, so be especially careful when using pointers in your STM code.
*/
package stm

import (
	"sync"
	"sync/atomic"
)

// Retry is a sentinel value. When thrown via panic, it indicates that a
// transaction should be retried.
const Retry = "retry"

// The globalLock serializes transaction verification/committal.
var globalLock sync.Mutex
var globalCond = sync.NewCond(&globalLock)

// A Var holds an STM variable.
type Var struct {
	val atomic.Value
}

// NewVar returns a new STM variable.
func NewVar(val interface{}) *Var {
	v := new(Var)
	v.val.Store(val)
	return v
}

// A Tx represents an atomic transaction.
type Tx struct {
	reads  map[*Var]interface{}
	writes map[*Var]interface{}
}

// verify checks that none of the logged values have changed since the
// transaction began.
// TODO: is pointer equality good enough? probably not, without immutable data
func (tx *Tx) verify() bool {
	for v, val := range tx.reads {
		if v.val.Load() != val {
			return false
		}
	}
	return true
}

// commit writes the values in the transaction log to their respective Vars.
func (tx *Tx) commit() {
	for v, val := range tx.writes {
		v.val.Store(val)
	}
}

// wait blocks until another transaction modifies any of the Vars read by tx.
func (tx *Tx) wait() {
	globalCond.L.Lock()
	for tx.verify() {
		globalCond.Wait()
	}
	globalCond.L.Unlock()
}

// Get returns the value of v as of the start of the transaction.
func (tx *Tx) Get(v *Var) interface{} {
	// If we previously wrote to v, it will be in the write log.
	if val, ok := tx.writes[v]; ok {
		return val
	}
	// If we previously read v, it will be in the read log.
	if val, ok := tx.reads[v]; ok {
		return val
	}
	// Otherwise, record and return its current value.
	tx.reads[v] = v.val.Load()
	return tx.reads[v]
}

// Set sets the value of a Var for the lifetime of the transaction.
func (tx *Tx) Set(v *Var, val interface{}) {
	tx.writes[v] = val
}

// Retry aborts the transaction and retries it when a Var changes.
func (tx *Tx) Retry() {
	panic(Retry)
}

// Assert is a helper function that retries a transaction if the condition is
// not satisfied.
func (tx *Tx) Assert(p bool) {
	if !p {
		tx.Retry()
	}
}

// catchRetry returns true if fn calls tx.Retry.
func catchRetry(fn func(*Tx), tx *Tx) (retry bool) {
	defer func() {
		if r := recover(); r == Retry {
			retry = true
		} else if r != nil {
			panic(r)
		}
	}()
	fn(tx)
	return
}

// Select runs the supplied functions in order. Execution stops when a
// function succeeds without calling Retry. If no functions succeed, the
// entire selection will be retried.
func Select(fns ...func(*Tx)) func(*Tx) {
	return func(tx *Tx) {
		switch len(fns) {
		case 0:
			// empty Select blocks forever
			tx.Retry()
		case 1:
			fns[0](tx)
		default:
			if catchRetry(fns[0], tx) {
				Select(fns[1:]...)(tx)
			}
		}
	}
}

// Atomically executes the atomic function fn.
func Atomically(fn func(*Tx)) {
retry:
	// run the transaction
	tx := &Tx{
		reads:  make(map[*Var]interface{}),
		writes: make(map[*Var]interface{}),
	}
	if catchRetry(fn, tx) {
		tx.wait()
		goto retry
	}
	// verify the read log
	globalLock.Lock()
	if !tx.verify() {
		globalLock.Unlock()
		goto retry
	}
	// commit the write log
	tx.commit()
	globalCond.Broadcast()
	globalLock.Unlock()
}

// AtomicGet is a helper function that atomically reads a value.
func AtomicGet(v *Var) interface{} {
	// since we're only doing one operation, we don't need a full transaction
	globalLock.Lock()
	val := v.val.Load()
	globalLock.Unlock()
	return val
}

// AtomicSet is a helper function that atomically writes a value.
func AtomicSet(v *Var, val interface{}) {
	// since we're only doing one operation, we don't need a full transaction
	globalLock.Lock()
	v.val.Store(val)
	globalCond.Broadcast()
	globalLock.Unlock()
}

// Compose is a helper function that composes multiple transactions into a
// single transaction.
func Compose(fns ...func(*Tx)) func(*Tx) {
	return func(tx *Tx) {
		for _, f := range fns {
			f(tx)
		}
	}
}
<|endoftext|>


<commit_before>package id3v2

import (
	"bytes"
	"errors"
	"github.com/bogem/id3v2/frame"
	"os"
)

type Tag struct {
	Header    *TagHeader
	frames    map[string]frame.Framer
	commonIDs map[string]string

	File         *os.File
	OriginalSize uint32
}

func (t *Tag) SetAttachedPicture(pf frame.PictureFramer) {
	var f frame.APICSequencer
	id := t.commonIDs["Attached Picture"]

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = frame.NewAPICSequence()
		f.SetID(id)
	} else {
		f = existingFrame.(frame.APICSequencer)
	}

	f.AddPicture(pf)
	t.frames[id] = f
}

func (t *Tag) SetTextFrame(id string, text string) {
	var f frame.TextFramer

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = new(frame.TextFrame)
		f.SetID(id)
	} else {
		f = existingFrame.(frame.TextFramer)
	}

	f.SetText(text)
	t.frames[id] = f
}

func (t *Tag) SetTitle(title string) {
	t.SetTextFrame(t.commonIDs["Title"], title)
}

func (t *Tag) SetArtist(artist string) {
	t.SetTextFrame(t.commonIDs["Artist"], artist)
}

func (t *Tag) SetAlbum(album string) {
	t.SetTextFrame(t.commonIDs["Album"], album)
}

func (t *Tag) SetYear(year string) {
	t.SetTextFrame(t.commonIDs["Year"], year)
}

func (t *Tag) SetGenre(genre string) {
	t.SetTextFrame(t.commonIDs["Genre"], genre)
}

func NewTag(file *os.File) *Tag {
	header := &TagHeader{
		Version:    4,
		FramesSize: 0,
	}

	return &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: 0,
	}
}

func ParseTag(file *os.File) (*Tag, error) {
	header, err := ParseHeader(file)
	if err != nil {
		err = errors.New("Trying to parse tag header: " + err.Error())
		return nil, err
	}
	if header == nil {
		return NewTag(file), nil
	}
	if header.Version < 3 {
		err = errors.New("Unsupported version of ID3 tag")
		return nil, err
	}

	tag := &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: TagHeaderSize + header.FramesSize,
	}

	return tag, nil
}

func (t Tag) CalcualteSizeOfAllFrames() (size uint32) {
	for _, f := range t.frames {
		size += frame.FrameHeaderSize + f.Size()
	}
	return
}

func (t Tag) FormFrames() ([]byte, error) {
	var b bytes.Buffer
	for _, frame := range t.frames {
		formedFrame, err := frame.Form()
		if err != nil {
			return nil, err
		}
		b.Write(formedFrame)
	}
	return b.Bytes(), nil
}
<commit_msg>Add concurrency to tag.CalculateSizeOfAllFrames Also fixed a tipo in name of this method<commit_after>package id3v2

import (
	"bytes"
	"errors"
	"github.com/bogem/id3v2/frame"
	"os"
	"sync"
	"sync/atomic"
)

type Tag struct {
	Header    *TagHeader
	frames    map[string]frame.Framer
	commonIDs map[string]string

	File         *os.File
	OriginalSize uint32
}

func (t *Tag) SetAttachedPicture(pf frame.PictureFramer) {
	var f frame.APICSequencer
	id := t.commonIDs["Attached Picture"]

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = frame.NewAPICSequence()
		f.SetID(id)
	} else {
		f = existingFrame.(frame.APICSequencer)
	}

	f.AddPicture(pf)
	t.frames[id] = f
}

func (t *Tag) SetTextFrame(id string, text string) {
	var f frame.TextFramer

	existingFrame := t.frames[id]
	if existingFrame == nil {
		f = new(frame.TextFrame)
		f.SetID(id)
	} else {
		f = existingFrame.(frame.TextFramer)
	}

	f.SetText(text)
	t.frames[id] = f
}

func (t *Tag) SetTitle(title string) {
	t.SetTextFrame(t.commonIDs["Title"], title)
}

func (t *Tag) SetArtist(artist string) {
	t.SetTextFrame(t.commonIDs["Artist"], artist)
}

func (t *Tag) SetAlbum(album string) {
	t.SetTextFrame(t.commonIDs["Album"], album)
}

func (t *Tag) SetYear(year string) {
	t.SetTextFrame(t.commonIDs["Year"], year)
}

func (t *Tag) SetGenre(genre string) {
	t.SetTextFrame(t.commonIDs["Genre"], genre)
}

func NewTag(file *os.File) *Tag {
	header := &TagHeader{
		Version:    4,
		FramesSize: 0,
	}

	return &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: 0,
	}
}

func ParseTag(file *os.File) (*Tag, error) {
	header, err := ParseHeader(file)
	if err != nil {
		err = errors.New("Trying to parse tag header: " + err.Error())
		return nil, err
	}
	if header == nil {
		return NewTag(file), nil
	}
	if header.Version < 3 {
		err = errors.New("Unsupported version of ID3 tag")
		return nil, err
	}

	tag := &Tag{
		Header:    header,
		frames:    make(map[string]frame.Framer),
		commonIDs: frame.V24CommonIDs,

		File:         file,
		OriginalSize: TagHeaderSize + header.FramesSize,
	}

	return tag, nil
}

func (t Tag) CalculateSizeOfAllFrames() uint32 {
	var wg sync.WaitGroup
	wg.Add(len(t.frames))

	var size uint32
	for _, v := range t.frames {
		go func(f frame.Framer) {
			atomic.AddUint32(&size, frame.FrameHeaderSize+f.Size())
			wg.Done()
		}(v)
	}
	wg.Wait()
	return atomic.LoadUint32(&size)
}

func (t Tag) FormFrames() ([]byte, error) {
	var b bytes.Buffer
	for _, frame := range t.frames {
		formedFrame, err := frame.Form()
		if err != nil {
			return nil, err
		}
		b.Write(formedFrame)
	}
	return b.Bytes(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"time"
)

type TcpRequest struct {
	Request
	Content string `json:"content"`
}

func startTCPServer(port string, writers ...TcpRequestWriter) {
	fmt.Println("Starting TCP server on port " + port)
	server, err := net.Listen("tcp", port)

	if server == nil {
		panic(fmt.Sprintf("couldn't start listening: %s", err))
	}
	conns := clientConns(server)
	for {
		go handleConn(<-conns, writers...)
	}
}

func clientConns(listener net.Listener) chan net.Conn {
	ch := make(chan net.Conn)
	i := 0
	go func() {
		for {
			client, err := listener.Accept()
			if client == nil {
				fmt.Printf(fmt.Sprintf("couldn't accept: %s", err))
				continue
			}
			i++
			fmt.Printf("%d: %v <-> %v\n", i, client.LocalAddr(), client.RemoteAddr())
			client.SetReadDeadline(time.Now().Add(4 * time.Second))
			ch <- client
		}
	}()
	return ch
}

func handleConn(client net.Conn, writers ...TcpRequestWriter) {
	b := bufio.NewReader(client)
	var res bytes.Buffer

	buf := make([]byte, 32)
	for {
		size, err := b.Read(buf)
		res.Write(buf[:size])
		if err != nil {
			break
		}
	}
	request := TcpRequest{
		Request{
			Time:       time.Now(),
			RemoteAddr: client.RemoteAddr().String(),
		},
		res.String(),
	}
	for _, writer := range writers {
		err := writer.WriteTcpRequest(request)
		if err != nil {
			fmt.Println(err)
		}
	}

	client.Close()
}
<commit_msg>Removed debug print<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"time"
)

type TcpRequest struct {
	Request
	Content string `json:"content"`
}

func startTCPServer(port string, writers ...TcpRequestWriter) {
	fmt.Println("Starting TCP server on port " + port)
	server, err := net.Listen("tcp", port)

	if server == nil {
		panic(fmt.Sprintf("couldn't start listening: %s", err))
	}
	conns := clientConns(server)
	for {
		go handleConn(<-conns, writers...)
	}
}

func clientConns(listener net.Listener) chan net.Conn {
	ch := make(chan net.Conn)
	i := 0
	go func() {
		for {
			client, err := listener.Accept()
			if client == nil {
				fmt.Printf(fmt.Sprintf("couldn't accept: %s", err))
				continue
			}
			i++
			client.SetReadDeadline(time.Now().Add(4 * time.Second))
			ch <- client
		}
	}()
	return ch
}

func handleConn(client net.Conn, writers ...TcpRequestWriter) {
	b := bufio.NewReader(client)
	var res bytes.Buffer

	buf := make([]byte, 32)
	for {
		size, err := b.Read(buf)
		res.Write(buf[:size])
		if err != nil {
			break
		}
	}
	request := TcpRequest{
		Request{
			Time:       time.Now(),
			RemoteAddr: client.RemoteAddr().String(),
		},
		res.String(),
	}
	for _, writer := range writers {
		err := writer.WriteTcpRequest(request)
		if err != nil {
			fmt.Println(err)
		}
	}

	client.Close()
}
<|endoftext|>


<commit_before>package OSXKeyboard

/*
#cgo LDFLAGS: -framework ApplicationServices -framework Carbon
extern int listen();
*/
import "C"

import (
  "container/list"
  "errors"
)

var (
  // flag variable to check whether the c code was already called
  isListening = false
  // Channels list of all the channels where the letters will be streamed
  Channels = list.New()
)

// Listen start listening the keyboard events
func Listen() error {

  // start the c code listeners to call the go function hook
  if isListening == false {
    ok := C.listen()

    if ok == 1 {
      return errors.New("It was not possible to listen the global keyboard events, make sure to have the root permissions enabled")
    }
    // change the flag to true to avoid entering this condition again
    isListening = true
  }

  return nil

}

// AddChannel add a channel to the Channels list
func AddChannel(ch *chan string) {
  // add che channel to the list
  Channels.PushFront(ch)
}

// GoKeypressCallback hook function called in the c code
//export GoKeypressCallback
func GoKeypressCallback(key *C.char) {

  letter := C.GoString(key)

  for item := Channels.Front(); item != nil; item = item.Next() {
    item.Value.(chan string) <- letter
  }

}
<commit_msg>updated: replaced go lists with slices<commit_after>package OSXKeyboard

/*
#cgo LDFLAGS: -framework ApplicationServices -framework Carbon
extern int listen();
*/
import "C"

import (
  "errors"
)

var (
  // flag variable to check whether the c code was already called
  isListening = false
  // Channels list of all the channels where the letters will be streamed
  Channels = make([]chan string, 1)
)

// Listen start listening the keyboard events
func Listen() error {

  // start the c code listeners to call the go function hook
  if isListening == false {
    ok := C.listen()

    if ok == 1 {
      return errors.New("It was not possible to listen the global keyboard events, make sure to have the root permissions enabled")
    }
    // change the flag to true to avoid entering this condition again
    isListening = true
  }

  return nil

}

// AddChannel add a channel to the Channels list
func AddChannel(ch *chan string) {
  // add che channel to the list
  append(Channels, *ch)
}

// GoKeypressCallback hook function called in the c code
//export GoKeypressCallback
func GoKeypressCallback(key *C.char) {

  letter := C.GoString(key)

  for i := 0; i < len(Channels); i++ {
    Channels[i] <- letter
  }

}
<|endoftext|>


<commit_before>/*-
 * Copyright 2015 Square 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 (
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync/atomic"
	"time"
	"unsafe"

	certigo "github.com/square/certigo/lib"
)

var cipherSuites = map[string][]uint16{
	"AES": []uint16{
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
	},
	"CHACHA": []uint16{
		tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
		tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
	},
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// certificate wraps a TLS certificate in a reloadable way
type certificate struct {
	keystorePath, keystorePass string
	cached                     unsafe.Pointer
}

// Build reloadable certificate
func buildCertificate(keystorePath, keystorePass string) (*certificate, error) {
	if keystorePath == "" {
		return &certificate{}, nil
	}
	cert := &certificate{keystorePath, keystorePass, nil}
	err := cert.reload()
	if err != nil {
		return nil, err
	}
	return cert, nil
}

// Retrieve actual certificate
func (c *certificate) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	return (*tls.Certificate)(atomic.LoadPointer(&c.cached)), nil
}

// Reload certificate
func (c *certificate) reload() error {
	var err error
	if hasPKCS11() {
		err = c.reloadFromPKCS11()
	} else {
		err = c.reloadFromPEM()
	}

	if err == nil {
		cert, _ := c.getCertificate(nil)
		logger.Printf("loaded certificate with common name '%s'", cert.Leaf.Subject.CommonName)
	}
	return err
}

func (c *certificate) reloadFromPEM() error {
	keystore, err := os.Open(c.keystorePath)
	if err != nil {
		return err
	}

	var pemBlocks []*pem.Block
	err = certigo.ReadAsPEMFromFiles(
		[]*os.File{keystore},
		"",
		func(prompt string) string {
			return c.keystorePass
		},
		func(block *pem.Block) {
			pemBlocks = append(pemBlocks, block)
		})
	if err != nil {
		return fmt.Errorf("error during keystore read (%s)", err)
	}
	if len(pemBlocks) == 0 {
		return fmt.Error("no certificates or private key found in keystore")
	}

	var pemBytes []byte
	for _, block := range pemBlocks {
		pemBytes = append(pemBytes, pem.EncodeToMemory(block)...)
	}

	certAndKey, err := tls.X509KeyPair(pemBytes, pemBytes)
	if err != nil {
		return err
	}

	certAndKey.Leaf, err = x509.ParseCertificate(certAndKey.Certificate[0])
	if err != nil {
		return err
	}

	atomic.StorePointer(&c.cached, unsafe.Pointer(&certAndKey))
	return nil
}

func (c *certificate) reloadFromPKCS11() error {
	// Expecting keystore file to only have certificate,
	// with the private key being in an HSM/PKCS11 module.
	keystore, err := os.Open(c.keystorePath)
	if err != nil {
		return err
	}

	certAndKey := tls.Certificate{}
	err = certigo.ReadAsX509FromFiles(
		[]*os.File{keystore}, "", nil,
		func(cert *x509.Certificate, err error) {
			if err != nil {
				logger.Printf("error during keystore read: %s", err)
				return
			}
			if certAndKey.Leaf == nil {
				certAndKey.Leaf = cert
			}
			certAndKey.Certificate = append(certAndKey.Certificate, cert.Raw)
		})
	if err != nil {
		return fmt.Errorf("error during keystore read (%s)", err)
	}
	if certAndKey.Leaf == nil {
		return fmt.Error("no certificates found in keystore")
	}

	// Reuse previously loaded PKCS11 private key if we already have it. We want to
	// avoid reloading the key every time the cert reloads, as it's a potentially
	// expensive operation that calls out into a shared library.
	if c.cached != nil {
		old, _ := c.getCertificate(nil)
		certAndKey.PrivateKey = old.PrivateKey
	} else {
		privateKey, err := newPKCS11(certAndKey.Leaf.PublicKey)
		if err != nil {
			return err
		}
		certAndKey.PrivateKey = privateKey
	}

	atomic.StorePointer(&c.cached, unsafe.Pointer(&certAndKey))
	return nil
}

func caBundle(caBundlePath string) (*x509.CertPool, error) {
	if caBundlePath == "" {
		return x509.SystemCertPool()
	}

	caBundleBytes, err := ioutil.ReadFile(caBundlePath)
	if err != nil {
		return nil, err
	}

	bundle := x509.NewCertPool()
	ok := bundle.AppendCertsFromPEM(caBundleBytes)
	if !ok {
		return nil, errors.New("unable to read certificates from CA bundle")
	}

	return bundle, nil
}

// Internal copy of tls.DialWithDialer, adapter so it can work with HTTP CONNECT dialers.
// See: https://golang.org/pkg/crypto/tls/#DialWithDialer
func dialWithDialer(dialer Dialer, timeout time.Duration, network, addr string, config *tls.Config) (*tls.Conn, error) {
	errChannel := make(chan error, 2)
	time.AfterFunc(timeout, func() {
		errChannel <- timeoutError{}
	})

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	conn := tls.Client(rawConn, config)
	go func() {
		errChannel <- conn.Handshake()
	}()

	err = <-errChannel

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// buildConfig reads command-line options and builds a tls.Config
func buildConfig(enabledCipherSuites string, caBundlePath string) (*tls.Config, error) {
	ca, err := caBundle(caBundlePath)
	if err != nil {
		return nil, err
	}

	// List of cipher suite preferences:
	// * We list ECDSA ahead of RSA to prefer ECDSA for multi-cert setups.
	// * We list AES-128 ahead of AES-256 for performance reasons.

	suites := []uint16{}
	for _, suite := range strings.Split(enabledCipherSuites, ",") {
		ciphers, ok := cipherSuites[strings.TrimSpace(suite)]
		if !ok {
			return nil, fmt.Errorf("invalid cipher suite '%s' selected", suite)
		}

		suites = append(suites, ciphers...)
	}

	return &tls.Config{
		// Certificates
		RootCAs:   ca,
		ClientCAs: ca,

		PreferServerCipherSuites: true,

		ClientAuth:   tls.RequireAndVerifyClientCert,
		MinVersion:   tls.VersionTLS12,
		CipherSuites: suites,
		CurvePreferences: []tls.CurveID{
			// P-256/X25519 have an ASM implementation, others do not (at least on x86-64).
			tls.X25519,
			tls.CurveP256,
		},
	}, nil
}
<commit_msg>Fix build error, use errors.New<commit_after>/*-
 * Copyright 2015 Square 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 (
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync/atomic"
	"time"
	"unsafe"

	certigo "github.com/square/certigo/lib"
)

var cipherSuites = map[string][]uint16{
	"AES": []uint16{
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
	},
	"CHACHA": []uint16{
		tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
		tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
	},
}

type timeoutError struct{}

func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool   { return true }
func (timeoutError) Temporary() bool { return true }

// certificate wraps a TLS certificate in a reloadable way
type certificate struct {
	keystorePath, keystorePass string
	cached                     unsafe.Pointer
}

// Build reloadable certificate
func buildCertificate(keystorePath, keystorePass string) (*certificate, error) {
	if keystorePath == "" {
		return &certificate{}, nil
	}
	cert := &certificate{keystorePath, keystorePass, nil}
	err := cert.reload()
	if err != nil {
		return nil, err
	}
	return cert, nil
}

// Retrieve actual certificate
func (c *certificate) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	return (*tls.Certificate)(atomic.LoadPointer(&c.cached)), nil
}

// Reload certificate
func (c *certificate) reload() error {
	var err error
	if hasPKCS11() {
		err = c.reloadFromPKCS11()
	} else {
		err = c.reloadFromPEM()
	}

	if err == nil {
		cert, _ := c.getCertificate(nil)
		logger.Printf("loaded certificate with common name '%s'", cert.Leaf.Subject.CommonName)
	}
	return err
}

func (c *certificate) reloadFromPEM() error {
	keystore, err := os.Open(c.keystorePath)
	if err != nil {
		return err
	}

	var pemBlocks []*pem.Block
	err = certigo.ReadAsPEMFromFiles(
		[]*os.File{keystore},
		"",
		func(prompt string) string {
			return c.keystorePass
		},
		func(block *pem.Block) {
			pemBlocks = append(pemBlocks, block)
		})
	if err != nil {
		return fmt.Errorf("error during keystore read (%s)", err)
	}
	if len(pemBlocks) == 0 {
		return errors.New("no certificates or private key found in keystore")
	}

	var pemBytes []byte
	for _, block := range pemBlocks {
		pemBytes = append(pemBytes, pem.EncodeToMemory(block)...)
	}

	certAndKey, err := tls.X509KeyPair(pemBytes, pemBytes)
	if err != nil {
		return err
	}

	certAndKey.Leaf, err = x509.ParseCertificate(certAndKey.Certificate[0])
	if err != nil {
		return err
	}

	atomic.StorePointer(&c.cached, unsafe.Pointer(&certAndKey))
	return nil
}

func (c *certificate) reloadFromPKCS11() error {
	// Expecting keystore file to only have certificate,
	// with the private key being in an HSM/PKCS11 module.
	keystore, err := os.Open(c.keystorePath)
	if err != nil {
		return err
	}

	certAndKey := tls.Certificate{}
	err = certigo.ReadAsX509FromFiles(
		[]*os.File{keystore}, "", nil,
		func(cert *x509.Certificate, err error) {
			if err != nil {
				logger.Printf("error during keystore read: %s", err)
				return
			}
			if certAndKey.Leaf == nil {
				certAndKey.Leaf = cert
			}
			certAndKey.Certificate = append(certAndKey.Certificate, cert.Raw)
		})
	if err != nil {
		return fmt.Errorf("error during keystore read (%s)", err)
	}
	if certAndKey.Leaf == nil {
		return errors.New("no certificates found in keystore")
	}

	// Reuse previously loaded PKCS11 private key if we already have it. We want to
	// avoid reloading the key every time the cert reloads, as it's a potentially
	// expensive operation that calls out into a shared library.
	if c.cached != nil {
		old, _ := c.getCertificate(nil)
		certAndKey.PrivateKey = old.PrivateKey
	} else {
		privateKey, err := newPKCS11(certAndKey.Leaf.PublicKey)
		if err != nil {
			return err
		}
		certAndKey.PrivateKey = privateKey
	}

	atomic.StorePointer(&c.cached, unsafe.Pointer(&certAndKey))
	return nil
}

func caBundle(caBundlePath string) (*x509.CertPool, error) {
	if caBundlePath == "" {
		return x509.SystemCertPool()
	}

	caBundleBytes, err := ioutil.ReadFile(caBundlePath)
	if err != nil {
		return nil, err
	}

	bundle := x509.NewCertPool()
	ok := bundle.AppendCertsFromPEM(caBundleBytes)
	if !ok {
		return nil, errors.New("unable to read certificates from CA bundle")
	}

	return bundle, nil
}

// Internal copy of tls.DialWithDialer, adapter so it can work with HTTP CONNECT dialers.
// See: https://golang.org/pkg/crypto/tls/#DialWithDialer
func dialWithDialer(dialer Dialer, timeout time.Duration, network, addr string, config *tls.Config) (*tls.Conn, error) {
	errChannel := make(chan error, 2)
	time.AfterFunc(timeout, func() {
		errChannel <- timeoutError{}
	})

	rawConn, err := dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}

	conn := tls.Client(rawConn, config)
	go func() {
		errChannel <- conn.Handshake()
	}()

	err = <-errChannel

	if err != nil {
		rawConn.Close()
		return nil, err
	}

	return conn, nil
}

// buildConfig reads command-line options and builds a tls.Config
func buildConfig(enabledCipherSuites string, caBundlePath string) (*tls.Config, error) {
	ca, err := caBundle(caBundlePath)
	if err != nil {
		return nil, err
	}

	// List of cipher suite preferences:
	// * We list ECDSA ahead of RSA to prefer ECDSA for multi-cert setups.
	// * We list AES-128 ahead of AES-256 for performance reasons.

	suites := []uint16{}
	for _, suite := range strings.Split(enabledCipherSuites, ",") {
		ciphers, ok := cipherSuites[strings.TrimSpace(suite)]
		if !ok {
			return nil, fmt.Errorf("invalid cipher suite '%s' selected", suite)
		}

		suites = append(suites, ciphers...)
	}

	return &tls.Config{
		// Certificates
		RootCAs:   ca,
		ClientCAs: ca,

		PreferServerCipherSuites: true,

		ClientAuth:   tls.RequireAndVerifyClientCert,
		MinVersion:   tls.VersionTLS12,
		CipherSuites: suites,
		CurvePreferences: []tls.CurveID{
			// P-256/X25519 have an ASM implementation, others do not (at least on x86-64).
			tls.X25519,
			tls.CurveP256,
		},
	}, nil
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io"
	"log"
	"time"

	"golang.org/x/net/context"

	"github.com/golang/protobuf/ptypes/duration"

	dlp "cloud.google.com/go/dlp/apiv2"
	"google.golang.org/api/iterator"
	dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2"
)

// [START dlp_create_trigger]

// createTrigger creates a trigger with the given configuration.
func createTrigger(w io.Writer, client *dlp.Client, project string, minLikelihood dlppb.Likelihood, maxFindings int32, triggerID, displayName, description, bucketName string, scanPeriod int64, infoTypes []string) {
	// Convert the info type strings to a list of InfoTypes.
	var i []*dlppb.InfoType
	for _, it := range infoTypes {
		i = append(i, &dlppb.InfoType{Name: it})
	}

	// Create a configured request.
	req := &dlppb.CreateJobTriggerRequest{
		Parent:    "projects/" + project,
		TriggerId: triggerID,
		JobTrigger: &dlppb.JobTrigger{
			DisplayName: displayName,
			Description: description,
			Status:      dlppb.JobTrigger_HEALTHY,
			// Triggers control when the job will start.
			Triggers: []*dlppb.JobTrigger_Trigger{
				{
					&dlppb.JobTrigger_Trigger_Schedule{
						Schedule: &dlppb.Schedule{
							Option: &dlppb.Schedule_RecurrencePeriodDuration{
								RecurrencePeriodDuration: &duration.Duration{
									Seconds: scanPeriod * 60 * 60 * 24, // Trigger the scan daily
								},
							},
						},
					},
				},
			},
			// Job configures the job to run when the trigger runs.
			Job: &dlppb.JobTrigger_InspectJob{
				InspectJob: &dlppb.InspectJobConfig{
					InspectConfig: &dlppb.InspectConfig{
						InfoTypes:     i,
						MinLikelihood: minLikelihood,
						Limits: &dlppb.InspectConfig_FindingLimits{
							MaxFindingsPerRequest: maxFindings,
						},
					},
					StorageConfig: &dlppb.StorageConfig{
						Type: &dlppb.StorageConfig_CloudStorageOptions{
							CloudStorageOptions: &dlppb.CloudStorageOptions{
								FileSet: &dlppb.CloudStorageOptions_FileSet{
									Url: "gs://" + bucketName + "/*",
								},
							},
						},
					},
				},
			},
		},
	}
	// Send the request.
	resp, err := client.CreateJobTrigger(context.Background(), req)
	if err != nil {
		log.Fatalf("error creating job trigger: %v", err)
	}
	fmt.Fprintf(w, "Successfully created trigger: %v", resp.GetName())
}

// [END dlp_create_trigger]

// [START dlp_list_triggers]

// listTriggers lists the triggers for the given project.
func listTriggers(w io.Writer, client *dlp.Client, project string) {
	// Create a configured request.
	req := &dlppb.ListJobTriggersRequest{
		Parent: "projects/" + project,
	}
	// Send the request and iterate over the results.
	it := client.ListJobTriggers(context.Background(), req)
	for {
		t, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatalf("error getting jobs: %v", err)
		}
		c := t.GetCreateTime()
		u := t.GetUpdateTime()
		fmt.Fprintf(w, "Trigger %v\n", t.GetName())
		fmt.Fprintf(w, "  Created: %v\n", time.Unix(c.GetSeconds(), int64(c.GetNanos())).Format(time.RFC1123))
		fmt.Fprintf(w, "  Updated: %v\n", time.Unix(u.GetSeconds(), int64(u.GetNanos())).Format(time.RFC1123))
		fmt.Fprintf(w, "  Display Name: %q\n", t.GetDisplayName())
		fmt.Fprintf(w, "  Description: %q\n", t.GetDescription())
		fmt.Fprintf(w, "  Status: %v\n", t.GetStatus())
		fmt.Fprintf(w, "  Error Count: %v\n", len(t.GetErrors()))
	}
}

// [END dlp_list_triggers]

// [START dlp_delete_trigger]

// deleteTrigger deletes the given trigger.
func deleteTrigger(w io.Writer, client *dlp.Client, triggerID string) {
	req := &dlppb.DeleteJobTriggerRequest{
		Name: triggerID,
	}
	err := client.DeleteJobTrigger(context.Background(), req)
	if err != nil {
		log.Fatalf("error deleting job: %v", err)
	}
	fmt.Fprintf(w, "Successfully deleted trigger %v", triggerID)
}

// [END dlp_delete_trigger]
<commit_msg>dlp: add field name to struct initializer (#490)<commit_after>// Copyright 2018 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"io"
	"log"
	"time"

	"golang.org/x/net/context"

	"github.com/golang/protobuf/ptypes/duration"

	dlp "cloud.google.com/go/dlp/apiv2"
	"google.golang.org/api/iterator"
	dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2"
)

// [START dlp_create_trigger]

// createTrigger creates a trigger with the given configuration.
func createTrigger(w io.Writer, client *dlp.Client, project string, minLikelihood dlppb.Likelihood, maxFindings int32, triggerID, displayName, description, bucketName string, scanPeriod int64, infoTypes []string) {
	// Convert the info type strings to a list of InfoTypes.
	var i []*dlppb.InfoType
	for _, it := range infoTypes {
		i = append(i, &dlppb.InfoType{Name: it})
	}

	// Create a configured request.
	req := &dlppb.CreateJobTriggerRequest{
		Parent:    "projects/" + project,
		TriggerId: triggerID,
		JobTrigger: &dlppb.JobTrigger{
			DisplayName: displayName,
			Description: description,
			Status:      dlppb.JobTrigger_HEALTHY,
			// Triggers control when the job will start.
			Triggers: []*dlppb.JobTrigger_Trigger{
				{
					Trigger: &dlppb.JobTrigger_Trigger_Schedule{
						Schedule: &dlppb.Schedule{
							Option: &dlppb.Schedule_RecurrencePeriodDuration{
								RecurrencePeriodDuration: &duration.Duration{
									Seconds: scanPeriod * 60 * 60 * 24, // Trigger the scan daily
								},
							},
						},
					},
				},
			},
			// Job configures the job to run when the trigger runs.
			Job: &dlppb.JobTrigger_InspectJob{
				InspectJob: &dlppb.InspectJobConfig{
					InspectConfig: &dlppb.InspectConfig{
						InfoTypes:     i,
						MinLikelihood: minLikelihood,
						Limits: &dlppb.InspectConfig_FindingLimits{
							MaxFindingsPerRequest: maxFindings,
						},
					},
					StorageConfig: &dlppb.StorageConfig{
						Type: &dlppb.StorageConfig_CloudStorageOptions{
							CloudStorageOptions: &dlppb.CloudStorageOptions{
								FileSet: &dlppb.CloudStorageOptions_FileSet{
									Url: "gs://" + bucketName + "/*",
								},
							},
						},
					},
				},
			},
		},
	}
	// Send the request.
	resp, err := client.CreateJobTrigger(context.Background(), req)
	if err != nil {
		log.Fatalf("error creating job trigger: %v", err)
	}
	fmt.Fprintf(w, "Successfully created trigger: %v", resp.GetName())
}

// [END dlp_create_trigger]

// [START dlp_list_triggers]

// listTriggers lists the triggers for the given project.
func listTriggers(w io.Writer, client *dlp.Client, project string) {
	// Create a configured request.
	req := &dlppb.ListJobTriggersRequest{
		Parent: "projects/" + project,
	}
	// Send the request and iterate over the results.
	it := client.ListJobTriggers(context.Background(), req)
	for {
		t, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatalf("error getting jobs: %v", err)
		}
		c := t.GetCreateTime()
		u := t.GetUpdateTime()
		fmt.Fprintf(w, "Trigger %v\n", t.GetName())
		fmt.Fprintf(w, "  Created: %v\n", time.Unix(c.GetSeconds(), int64(c.GetNanos())).Format(time.RFC1123))
		fmt.Fprintf(w, "  Updated: %v\n", time.Unix(u.GetSeconds(), int64(u.GetNanos())).Format(time.RFC1123))
		fmt.Fprintf(w, "  Display Name: %q\n", t.GetDisplayName())
		fmt.Fprintf(w, "  Description: %q\n", t.GetDescription())
		fmt.Fprintf(w, "  Status: %v\n", t.GetStatus())
		fmt.Fprintf(w, "  Error Count: %v\n", len(t.GetErrors()))
	}
}

// [END dlp_list_triggers]

// [START dlp_delete_trigger]

// deleteTrigger deletes the given trigger.
func deleteTrigger(w io.Writer, client *dlp.Client, triggerID string) {
	req := &dlppb.DeleteJobTriggerRequest{
		Name: triggerID,
	}
	err := client.DeleteJobTrigger(context.Background(), req)
	if err != nil {
		log.Fatalf("error deleting job: %v", err)
	}
	fmt.Fprintf(w, "Successfully deleted trigger %v", triggerID)
}

// [END dlp_delete_trigger]
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strconv"

	api "github.com/gophergala2016/be/insightapi"
	"github.com/gophergala2016/be/tui"
	"github.com/nsf/termbox-go"
)

var (
	state api.BlockList
)

const (
	boxWidth  = 19
	boxHeight = 7
	xMargin   = 2
	yMargin   = 1
	xSpace    = 4
	ySpace    = 2
)

func tuiLatestBlocks() {
	var err error
	state, err = api.GetLatestBlocks()
	if err != nil {
		log.Fatal(err)
	}

	err = termbox.Init()
	if err != nil {
		log.Fatal(err)
	}
	defer termbox.Close()

	draw()
	tuiPoll()
}

func box(lines []string, x, y int, background termbox.Attribute) tui.Box {
	return tui.Box{
		Lines: lines,
		X:     xMargin + x*(boxWidth+xSpace), Y: yMargin + y*(boxHeight+ySpace),
		Width: boxWidth, Height: boxHeight,
		Background: background, Foreground: termbox.ColorBlack,
	}
}

func horizontalLine(x, y int) tui.Box {
	line := ""
	for i := 0; i < xSpace; i++ {
		line = line + "─"
	}

	return tui.Box{
		Lines: []string{line},
		X:     xMargin + boxWidth + (xSpace+boxWidth)*x,
		Y:     yMargin + boxHeight/2 + (ySpace+boxHeight)*y,
		Width: xSpace, Height: 1,
		Foreground: termbox.ColorWhite,
	}
}

func verticalLine(x, y int) tui.Box {
	lines := []string{}
	for i := 0; i < ySpace; i++ {
		lines = append(lines, "│")
	}

	return tui.Box{
		Lines: lines,
		X:     xMargin + boxWidth/2 + (xSpace+boxWidth)*x,
		Y:     yMargin + boxHeight + (ySpace+boxHeight)*y,
		Width: 1, Height: ySpace,
		Foreground: termbox.ColorWhite,
	}
}

func calculateFit(pad, space, boxSize, containerSize int) (boxes int) {
	for {
		if pad+boxSize*(boxes+1)+space*boxes+pad > containerSize {
			return
		}

		boxes = boxes + 1
	}
}

func blockBox(block api.BlockInfo, i int) tui.Group {
	containerWidth, _ := termbox.Size()

	xBoxes := calculateFit(xMargin, xSpace, boxWidth, containerWidth)

	y := i / xBoxes

	var x int

	if y%2 == 0 {
		x = i % xBoxes
	} else {
		x = xBoxes - 1 - (i % xBoxes)
	}

	box := box(
		[]string{
			"",
			"  #" + strconv.Itoa(block.Height),
			"",
			"  " + strconv.Itoa(block.Txlength) + "txs",
			"  " + strconv.Itoa(block.Size/1024) + "kb",
			"  " + block.PoolInfo.PoolName,
		},
		x, y, termbox.ColorBlue,
	)

	var line tui.Drawable

	if y%2 == 0 {
		if x == 0 {
			line = verticalLine(x, y-1)
		} else {
			line = horizontalLine(x-1, y)
		}
	} else {
		if x == xBoxes-1 {
			line = verticalLine(x, y-1)
		} else {
			line = horizontalLine(x, y)
		}
	}

	return tui.Group{box, line}
}

func nextBlockBox(block api.BlockInfo) tui.Box {
	return box(
		[]string{
			"",
			"  #" + strconv.Itoa(block.Height+1),
			"",
			"  next",
			"  block",
		},
		0, 0, termbox.ColorRed,
	)
}

func draw() {
	canvas := tui.Canvas{}

	group := tui.Group{}
	for i, block := range state.Blocks {
		if i == 0 { // draw unconfirmed block
			group = append(group, nextBlockBox(block))
		}

		group = append(group, blockBox(block, i+1))
	}

	canvas.Drawable = group
	canvas.Redraw()
}

func tuiPoll() {
	for {
		e := termbox.PollEvent()

		if e.Type == termbox.EventKey {
			return
		}

		if e.Type == termbox.EventResize {
			draw()
		}
	}
}
<commit_msg>Add arrows ;)<commit_after>package main

import (
	"log"
	"strconv"

	api "github.com/gophergala2016/be/insightapi"
	"github.com/gophergala2016/be/tui"
	"github.com/nsf/termbox-go"
)

var (
	state api.BlockList
)

const (
	boxWidth  = 19
	boxHeight = 7
	xMargin   = 2
	yMargin   = 1
	xSpace    = 4
	ySpace    = 2
)

func tuiLatestBlocks() {
	var err error
	state, err = api.GetLatestBlocks()
	if err != nil {
		log.Fatal(err)
	}

	err = termbox.Init()
	if err != nil {
		log.Fatal(err)
	}
	defer termbox.Close()

	draw()
	tuiPoll()
}

func box(lines []string, x, y int, background termbox.Attribute) tui.Box {
	return tui.Box{
		Lines: lines,
		X:     xMargin + x*(boxWidth+xSpace), Y: yMargin + y*(boxHeight+ySpace),
		Width: boxWidth, Height: boxHeight,
		Background: background, Foreground: termbox.ColorBlack,
	}
}

func horizontalLine(x, y int) tui.Box {
	line := ""
	if y%2 != 0 {
		line = line + "<"
	}
	for i := 0; i < xSpace-1; i++ {
		line = line + "─"
	}
	if y%2 == 0 {
		line = line + ">"
	}

	return tui.Box{
		Lines: []string{line},
		X:     xMargin + boxWidth + (xSpace+boxWidth)*x,
		Y:     yMargin + boxHeight/2 + (ySpace+boxHeight)*y,
		Width: xSpace, Height: 1,
		Foreground: termbox.ColorWhite,
	}
}

func verticalLine(x, y int) tui.Box {
	lines := []string{}
	for i := 0; i < ySpace-1; i++ {
		lines = append(lines, "│")
	}
	lines = append(lines, "v")

	return tui.Box{
		Lines: lines,
		X:     xMargin + boxWidth/2 + (xSpace+boxWidth)*x,
		Y:     yMargin + boxHeight + (ySpace+boxHeight)*y,
		Width: 1, Height: ySpace,
		Foreground: termbox.ColorWhite,
	}
}

func calculateFit(pad, space, boxSize, containerSize int) (boxes int) {
	for {
		if pad+boxSize*(boxes+1)+space*boxes+pad > containerSize {
			return
		}

		boxes = boxes + 1
	}
}

func blockBox(block api.BlockInfo, i int) tui.Group {
	containerWidth, _ := termbox.Size()

	xBoxes := calculateFit(xMargin, xSpace, boxWidth, containerWidth)

	y := i / xBoxes

	var x int

	if y%2 == 0 {
		x = i % xBoxes
	} else {
		x = xBoxes - 1 - (i % xBoxes)
	}

	box := box(
		[]string{
			"",
			"  #" + strconv.Itoa(block.Height),
			"",
			"  " + strconv.Itoa(block.Txlength) + "txs",
			"  " + strconv.Itoa(block.Size/1024) + "kb",
			"  " + block.PoolInfo.PoolName,
		},
		x, y, termbox.ColorBlue,
	)

	var line tui.Drawable

	if y%2 == 0 {
		if x == 0 {
			line = verticalLine(x, y-1)
		} else {
			line = horizontalLine(x-1, y)
		}
	} else {
		if x == xBoxes-1 {
			line = verticalLine(x, y-1)
		} else {
			line = horizontalLine(x, y)
		}
	}

	return tui.Group{box, line}
}

func nextBlockBox(block api.BlockInfo) tui.Box {
	return box(
		[]string{
			"",
			"  #" + strconv.Itoa(block.Height+1),
			"",
			"  next",
			"  block",
		},
		0, 0, termbox.ColorRed,
	)
}

func draw() {
	canvas := tui.Canvas{}

	group := tui.Group{}
	for i, block := range state.Blocks {
		if i == 0 { // draw unconfirmed block
			group = append(group, nextBlockBox(block))
		}

		group = append(group, blockBox(block, i+1))
	}

	canvas.Drawable = group
	canvas.Redraw()
}

func tuiPoll() {
	for {
		e := termbox.PollEvent()

		if e.Type == termbox.EventKey {
			return
		}

		if e.Type == termbox.EventResize {
			draw()
		}
	}
}
<|endoftext|>


<commit_before>package index

import "github.com/leeola/kala/q"

type Options map[string]interface{}

type Field struct {
	Field   string      `json:"field"`
	Value   interface{} `json:"value"`
	Options Options     `json:"options"`
}

// Index implements indexing and searching functionality for a kala store.
type Index interface {
	Index(id string, fields []Field) error
	Search(q.Query) ([][]byte, error)
}
<commit_msg>refactor: Search should return a slice of matching hashes<commit_after>package index

import "github.com/leeola/kala/q"

type Options map[string]interface{}

type Field struct {
	Field   string      `json:"field"`
	Value   interface{} `json:"value"`
	Options Options     `json:"options"`
}

// Index implements indexing and searching functionality for a kala store.
type Index interface {
	Index(id string, fields []Field) error
	Search(q.Query) ([]string, error)
}
<|endoftext|>


<commit_before><commit_msg>Fix RRCA carry handling (failed test)<commit_after><|endoftext|>