<commit_before>// Copyright 2019 Anapaya Systems
//
// 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 beaconing

import (
	"context"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"hash"
	"net"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/scionproto/scion/go/cs/beacon"
	"github.com/scionproto/scion/go/cs/beaconing/mock_beaconing"
	"github.com/scionproto/scion/go/cs/ifstate"
	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/ctrl/seg"
	"github.com/scionproto/scion/go/lib/infra/modules/itopo/itopotest"
	"github.com/scionproto/scion/go/lib/scrypto"
	"github.com/scionproto/scion/go/lib/scrypto/signed"
	"github.com/scionproto/scion/go/lib/serrors"
	cryptopb "github.com/scionproto/scion/go/pkg/proto/crypto"
)

const (
	topoCore    = "testdata/topology-core.json"
	topoNonCore = "testdata/topology.json"
)

func TestOriginatorRun(t *testing.T) {
	topoProvider := itopotest.TopoProviderFromFile(t, topoCore)
	mac, err := scrypto.InitMac(make(common.RawBytes, 16))
	require.NoError(t, err)
	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	require.NoError(t, err)
	pub := priv.Public()
	signer := testSigner(t, priv, topoProvider.Get().IA())
	t.Run("run originates ifid packets on all active interfaces", func(t *testing.T) {
		mctrl := gomock.NewController(t)
		defer mctrl.Finish()
		intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap(), ifstate.Config{})
		sender := mock_beaconing.NewMockBeaconSender(mctrl)
		o := Originator{
			Extender: &DefaultExtender{
				IA:         topoProvider.Get().IA(),
				MTU:        topoProvider.Get().MTU(),
				Signer:     signer,
				Intfs:      intfs,
				MAC:        func() hash.Hash { return mac },
				MaxExpTime: func() uint8 { return uint8(beacon.DefaultMaxExpTime) },
				StaticInfo: func() *StaticInfoCfg { return nil },
			},
			BeaconSender: sender,
			IA:           topoProvider.Get().IA(),
			Signer:       signer,
			Intfs:        intfs,
			Tick:         NewTick(time.Hour),
		}

		require.NoError(t, err)

		sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
			gomock.Any()).Times(4).DoAndReturn(

			func(_ context.Context, beacon *seg.PathSegment, dst addr.IA, egress common.IFIDType,
				nextHop *net.UDPAddr) error {

				// Check the beacon is valid and verifiable.
				assert.NoError(t, beacon.Validate(seg.ValidateBeacon))
				assert.NoError(t, beacon.VerifyASEntry(context.Background(),
					segVerifier{pubKey: pub}, beacon.MaxIdx()))

				// Extract the hop field from the current AS entry to compare.
				hopF := beacon.ASEntries[beacon.MaxIdx()].HopEntry.HopField
				// Check the interface matches.
				assert.Equal(t, hopF.ConsEgress, uint16(egress))
				// Check that the beacon is sent to the correct border router.
				br := topoProvider.Get().IFInfoMap()[egress].InternalAddr
				assert.Equal(t, br, nextHop)
				return nil
			},
		)

		// Start beacon messages.
		o.Run(nil)
		// The second run should not cause any beacons to originate.
		o.Run(nil)
	})
	t.Run("Fast recovery", func(t *testing.T) {
		mctrl := gomock.NewController(t)
		defer mctrl.Finish()
		intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap(), ifstate.Config{})
		sender := mock_beaconing.NewMockBeaconSender(mctrl)

		o := Originator{
			Extender: &DefaultExtender{
				IA:         topoProvider.Get().IA(),
				MTU:        topoProvider.Get().MTU(),
				Signer:     signer,
				Intfs:      intfs,
				MAC:        func() hash.Hash { return mac },
				MaxExpTime: func() uint8 { return uint8(beacon.DefaultMaxExpTime) },
				StaticInfo: func() *StaticInfoCfg { return nil },
			},
			BeaconSender: sender,
			IA:           topoProvider.Get().IA(),
			Signer:       signer,
			Intfs:        intfs,
			Tick:         NewTick(2 * time.Second),
		}

		// 1. Initial run where one beacon fails to send. -> 4 calls
		// 2. Second run where the beacon is delivered. -> 1 call
		// 3. Run where no beacon is sent. -> no call
		// 4. Run where beacons are sent on all interfaces. -> 4 calls

		first := sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
			gomock.Any())
		first.Return(serrors.New("fail"))

		sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
			gomock.Any()).Times(8).Return(nil)
		// Initial run. Two writes expected, one write will fail.
		o.Run(nil)
		time.Sleep(1 * time.Second)
		// Second run. One write expected.
		o.Run(nil)
		// Third run. No write expected
		o.Run(nil)
		time.Sleep(1 * time.Second)
		// Fourth run. Since period has passed, two writes are expected.
		o.Run(nil)
	})
}

type segVerifier struct {
	pubKey crypto.PublicKey
}

func (v segVerifier) Verify(_ context.Context, signedMsg *cryptopb.SignedMessage,
	associatedData ...[]byte) (*signed.Message, error) {

	return signed.Verify(signedMsg, v.pubKey, associatedData...)
}
<commit_msg>cs: fix testing race (#3920)<commit_after>// Copyright 2019 Anapaya Systems
//
// 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 beaconing

import (
	"context"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"net"
	"testing"
	"time"

	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/scionproto/scion/go/cs/beacon"
	"github.com/scionproto/scion/go/cs/beaconing/mock_beaconing"
	"github.com/scionproto/scion/go/cs/ifstate"
	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/ctrl/seg"
	"github.com/scionproto/scion/go/lib/infra/modules/itopo/itopotest"
	"github.com/scionproto/scion/go/lib/scrypto/signed"
	"github.com/scionproto/scion/go/lib/serrors"
	cryptopb "github.com/scionproto/scion/go/pkg/proto/crypto"
)

const (
	topoCore    = "testdata/topology-core.json"
	topoNonCore = "testdata/topology.json"
)

func TestOriginatorRun(t *testing.T) {
	topoProvider := itopotest.TopoProviderFromFile(t, topoCore)
	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	require.NoError(t, err)
	pub := priv.Public()
	signer := testSigner(t, priv, topoProvider.Get().IA())
	t.Run("run originates ifid packets on all active interfaces", func(t *testing.T) {
		mctrl := gomock.NewController(t)
		defer mctrl.Finish()
		intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap(), ifstate.Config{})
		sender := mock_beaconing.NewMockBeaconSender(mctrl)
		o := Originator{
			Extender: &DefaultExtender{
				IA:         topoProvider.Get().IA(),
				MTU:        topoProvider.Get().MTU(),
				Signer:     signer,
				Intfs:      intfs,
				MAC:        macFactory,
				MaxExpTime: func() uint8 { return uint8(beacon.DefaultMaxExpTime) },
				StaticInfo: func() *StaticInfoCfg { return nil },
			},
			BeaconSender: sender,
			IA:           topoProvider.Get().IA(),
			Signer:       signer,
			Intfs:        intfs,
			Tick:         NewTick(time.Hour),
		}

		require.NoError(t, err)

		sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
			gomock.Any()).Times(4).DoAndReturn(

			func(_ context.Context, beacon *seg.PathSegment, dst addr.IA, egress common.IFIDType,
				nextHop *net.UDPAddr) error {

				// Check the beacon is valid and verifiable.
				assert.NoError(t, beacon.Validate(seg.ValidateBeacon))
				assert.NoError(t, beacon.VerifyASEntry(context.Background(),
					segVerifier{pubKey: pub}, beacon.MaxIdx()))

				// Extract the hop field from the current AS entry to compare.
				hopF := beacon.ASEntries[beacon.MaxIdx()].HopEntry.HopField
				// Check the interface matches.
				assert.Equal(t, hopF.ConsEgress, uint16(egress))
				// Check that the beacon is sent to the correct border router.
				br := topoProvider.Get().IFInfoMap()[egress].InternalAddr
				assert.Equal(t, br, nextHop)
				return nil
			},
		)

		// Start beacon messages.
		o.Run(nil)
		// The second run should not cause any beacons to originate.
		o.Run(nil)
	})
	t.Run("Fast recovery", func(t *testing.T) {
		mctrl := gomock.NewController(t)
		defer mctrl.Finish()
		intfs := ifstate.NewInterfaces(topoProvider.Get().IFInfoMap(), ifstate.Config{})
		sender := mock_beaconing.NewMockBeaconSender(mctrl)

		o := Originator{
			Extender: &DefaultExtender{
				IA:         topoProvider.Get().IA(),
				MTU:        topoProvider.Get().MTU(),
				Signer:     signer,
				Intfs:      intfs,
				MAC:        macFactory,
				MaxExpTime: func() uint8 { return uint8(beacon.DefaultMaxExpTime) },
				StaticInfo: func() *StaticInfoCfg { return nil },
			},
			BeaconSender: sender,
			IA:           topoProvider.Get().IA(),
			Signer:       signer,
			Intfs:        intfs,
			Tick:         NewTick(2 * time.Second),
		}

		// 1. Initial run where one beacon fails to send. -> 4 calls
		// 2. Second run where the beacon is delivered. -> 1 call
		// 3. Run where no beacon is sent. -> no call
		// 4. Run where beacons are sent on all interfaces. -> 4 calls

		first := sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
			gomock.Any())
		first.Return(serrors.New("fail"))

		sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
			gomock.Any()).Times(8).Return(nil)
		// Initial run. Two writes expected, one write will fail.
		o.Run(nil)
		time.Sleep(1 * time.Second)
		// Second run. One write expected.
		o.Run(nil)
		// Third run. No write expected
		o.Run(nil)
		time.Sleep(1 * time.Second)
		// Fourth run. Since period has passed, two writes are expected.
		o.Run(nil)
	})
}

type segVerifier struct {
	pubKey crypto.PublicKey
}

func (v segVerifier) Verify(_ context.Context, signedMsg *cryptopb.SignedMessage,
	associatedData ...[]byte) (*signed.Message, error) {

	return signed.Verify(signedMsg, v.pubKey, associatedData...)
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"labix.org/v2/mgo/bson"
)

type Machine struct {
	ObjectId    bson.ObjectId   `bson:"_id" json:"_id"`
	Uid         string          `bson:"uid" json:"uid"`
	QueryString string          `bson:"queryString" json:"queryString"`
	IpAddress   string          `bson:"ipAddress" json:"ipAddress"`
	Domain      string          `bson:"domain" json:"domain"`
	Provider    string          `bson:"provider" json:"provider"`
	Label       string          `bson:"label" json:"label"`
	Slug        string          `bson:"slug" json:"slug"`
	Provisoners []bson.ObjectId `bson:"provisoners" json:"provisoners"`
	Credential  string          `bson:"credential" json:"credential"`
	Users       []struct {
		Id    bson.ObjectId `bson:"id" json:"id"`
		Sudo  bool          `bson:"sudo" json:"sudo"`
		Owner bool          `bson:"owner" json:"owner"`
	} `bson:"users" json:"users"`
	Groups []struct {
		Id bson.ObjectId `bson:"id" json:"id"`
	} `bson:"groups" json:"groups"`
	CreatedAt time.Time `bson:"createdAt" json:"createdAt" `
	Status    struct {
		State      string    `bson:"state" json:"state"`
		ModifiedAt time.Time `bson:"modifiedAt" json:"modifiedAt"`
	} `bson:"status" json:"status"`
	Meta     interface{} `bson:"meta" json:"meta"`
	Assignee struct {
		AssignedAt time.Time `bson:"assignedAt" json:"assignedAt"`
		InProgress bool      `bson:"inProgress" json:"inProgress"`
	} `bson:"assignee" json:"assignee"`
	UserDeleted bool `bson:"userDeleted" json:"userDeleted"`
}
<commit_msg>models: remove anonymous structs for Machine<commit_after>package models

import (
	"time"

	"labix.org/v2/mgo/bson"
)

type MachineGroup struct {
	Id bson.ObjectId `bson:"id" json:"id"`
}

type MachineStatus struct {
	State      string    `bson:"state" json:"state"`
	ModifiedAt time.Time `bson:"modifiedAt" json:"modifiedAt"`
}

type MachineUser struct {
	Id    bson.ObjectId `bson:"id" json:"id"`
	Sudo  bool          `bson:"sudo" json:"sudo"`
	Owner bool          `bson:"owner" json:"owner"`
}

type MachineAssignee struct {
	AssignedAt time.Time `bson:"assignedAt" json:"assignedAt"`
	InProgress bool      `bson:"inProgress" json:"inProgress"`
}

type Machine struct {
	ObjectId    bson.ObjectId   `bson:"_id" json:"_id"`
	Uid         string          `bson:"uid" json:"uid"`
	QueryString string          `bson:"queryString" json:"queryString"`
	IpAddress   string          `bson:"ipAddress" json:"ipAddress"`
	Domain      string          `bson:"domain" json:"domain"`
	Provider    string          `bson:"provider" json:"provider"`
	Label       string          `bson:"label" json:"label"`
	Slug        string          `bson:"slug" json:"slug"`
	Provisoners []bson.ObjectId `bson:"provisoners" json:"provisoners"`
	Credential  string          `bson:"credential" json:"credential"`
	Users       []MachineUser   `bson:"users" json:"users"`
	Groups      []MachineGroup  `bson:"groups" json:"groups"`
	CreatedAt   time.Time       `bson:"createdAt" json:"createdAt" `
	Status      MachineStatus   `bson:"status" json:"status"`
	Meta        interface{}     `bson:"meta" json:"meta"`
	Assignee    MachineAssignee `bson:"assignee" json:"assignee"`
	UserDeleted bool            `bson:"userDeleted" json:"userDeleted"`
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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.
*/

package openstack

import (
	"context"
	"fmt"
	"regexp"

	"github.com/golang/glog"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

// Instances encapsulates an implementation of Instances for OpenStack.
type Instances struct {
	compute *gophercloud.ServiceClient
	opts    MetadataOpts
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := os.NewComputeV2()
	if err != nil {
		return nil, false
	}

	glog.V(4).Info("Claiming to support Instances")

	return &Instances{
		compute: compute,
		opts:    os.metadataOpts,
	}, true
}

// CurrentNodeName implements Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error) {
	md, err := getMetadata(i.opts.SearchOrder)
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Hostname), nil
}

// AddSSHKeyToAllInstances is not implemented for OpenStack
func (i *Instances) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error {
	return cloudprovider.NotImplemented
}

// NodeAddresses implements Instances.NodeAddresses
func (i *Instances) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	addresses, err := nodeAddresses(server)
	if err != nil {
		return []v1.NodeAddress{}, err
	}

	return addresses, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(ctx context.Context, name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name, true)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (i *Instances) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) {
	instanceID, err := instanceIDFromProviderID(providerID)
	if err != nil {
		return false, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()
	if err != nil {
		if isNotFound(err) {
			return false, nil
		}
		return false, err
	}

	if server.Status != "ACTIVE" {
		glog.Warningf("the instance %s is not active", instanceID)
		return false, nil
	}

	return true, nil
}

// InstanceShutdownByProviderID returns true if the instances is in safe state to detach volumes
func (i *Instances) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
	return false, cloudprovider.NotImplemented
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	if len(os.localInstanceID) == 0 {
		id, err := readInstanceID(os.metadataOpts.SearchOrder)
		if err != nil {
			return "", err
		}
		os.localInstanceID = id
	}
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(ctx context.Context, name types.NodeName) (string, error) {
	// we should fetch instanceid from all states instead of ACTIVE
	srv, err := getServerByName(i.compute, name, false)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return "", err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return "", err
	}

	return srvInstanceType(server)
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(ctx context.Context, name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name, true)

	if err != nil {
		return "", err
	}

	return srvInstanceType(srv)
}

func srvInstanceType(srv *servers.Server) (string, error) {
	keys := []string{"name", "id", "original_name"}
	for _, key := range keys {
		val, found := srv.Flavor[key]
		if found {
			flavor, ok := val.(string)
			if ok {
				return flavor, nil
			}
		}
	}
	return "", fmt.Errorf("flavor name/id not found")
}

// instanceIDFromProviderID splits a provider's id and return instanceID.
// A providerID is build out of '${ProviderName}:///${instance-id}'which contains ':///'.
// See cloudprovider.GetInstanceProviderID and Instances.InstanceID.
func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
	// If Instances.InstanceID or cloudprovider.GetInstanceProviderID is changed, the regexp should be changed too.
	var providerIDRegexp = regexp.MustCompile(`^` + ProviderName + `:///([^/]+)$`)

	matches := providerIDRegexp.FindStringSubmatch(providerID)
	if len(matches) != 2 {
		return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"openstack:///InstanceID\"", providerID)
	}
	return matches[1], nil
}
<commit_msg>Revert "add node shutdown taint"<commit_after>/*
Copyright 2016 The Kubernetes 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.
*/

package openstack

import (
	"context"
	"fmt"
	"regexp"

	"github.com/golang/glog"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

// Instances encapsulates an implementation of Instances for OpenStack.
type Instances struct {
	compute *gophercloud.ServiceClient
	opts    MetadataOpts
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := os.NewComputeV2()
	if err != nil {
		return nil, false
	}

	glog.V(4).Info("Claiming to support Instances")

	return &Instances{
		compute: compute,
		opts:    os.metadataOpts,
	}, true
}

// CurrentNodeName implements Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(ctx context.Context, hostname string) (types.NodeName, error) {
	md, err := getMetadata(i.opts.SearchOrder)
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Hostname), nil
}

// AddSSHKeyToAllInstances is not implemented for OpenStack
func (i *Instances) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error {
	return cloudprovider.NotImplemented
}

// NodeAddresses implements Instances.NodeAddresses
func (i *Instances) NodeAddresses(ctx context.Context, name types.NodeName) ([]v1.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	addresses, err := nodeAddresses(server)
	if err != nil {
		return []v1.NodeAddress{}, err
	}

	return addresses, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(ctx context.Context, name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name, true)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
// If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
func (i *Instances) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) {
	instanceID, err := instanceIDFromProviderID(providerID)
	if err != nil {
		return false, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()
	if err != nil {
		if isNotFound(err) {
			return false, nil
		}
		return false, err
	}

	if server.Status != "ACTIVE" {
		glog.Warningf("the instance %s is not active", instanceID)
		return false, nil
	}

	return true, nil
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	if len(os.localInstanceID) == 0 {
		id, err := readInstanceID(os.metadataOpts.SearchOrder)
		if err != nil {
			return "", err
		}
		os.localInstanceID = id
	}
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(ctx context.Context, name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name, true)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return "", err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return "", err
	}

	return srvInstanceType(server)
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(ctx context.Context, name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name, true)

	if err != nil {
		return "", err
	}

	return srvInstanceType(srv)
}

func srvInstanceType(srv *servers.Server) (string, error) {
	keys := []string{"name", "id", "original_name"}
	for _, key := range keys {
		val, found := srv.Flavor[key]
		if found {
			flavor, ok := val.(string)
			if ok {
				return flavor, nil
			}
		}
	}
	return "", fmt.Errorf("flavor name/id not found")
}

// instanceIDFromProviderID splits a provider's id and return instanceID.
// A providerID is build out of '${ProviderName}:///${instance-id}'which contains ':///'.
// See cloudprovider.GetInstanceProviderID and Instances.InstanceID.
func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
	// If Instances.InstanceID or cloudprovider.GetInstanceProviderID is changed, the regexp should be changed too.
	var providerIDRegexp = regexp.MustCompile(`^` + ProviderName + `:///([^/]+)$`)

	matches := providerIDRegexp.FindStringSubmatch(providerID)
	if len(matches) != 2 {
		return "", fmt.Errorf("ProviderID \"%s\" didn't match expected format \"openstack:///InstanceID\"", providerID)
	}
	return matches[1], nil
}
<|endoftext|>


<commit_before>package github

import (
	"errors"
	"fmt"
	"github.com/jingweno/octokat"
)

const (
	GitHubHost  string = "github.com"
	OAuthAppURL string = "http://owenou.com/gh"
)

type GitHub struct {
	Project *Project
	Config  *Config
}

func (gh *GitHub) PullRequest(id string) (*octokat.PullRequest, error) {
	client := gh.client()

	return client.PullRequest(gh.repo(), id, nil)
}

func (gh *GitHub) CreatePullRequest(base, head, title, body string) (string, error) {
	client := gh.client()
	params := octokat.PullRequestParams{Base: base, Head: head, Title: title, Body: body}
	options := octokat.Options{Params: params}
	pullRequest, err := client.CreatePullRequest(gh.repo(), &options)
	if err != nil {
		return "", err
	}

	return pullRequest.HTMLURL, nil
}

func (gh *GitHub) CreatePullRequestForIssue(base, head, issue string) (string, error) {
	client := gh.client()
	params := octokat.PullRequestForIssueParams{Base: base, Head: head, Issue: issue}
	options := octokat.Options{Params: params}
	pullRequest, err := client.CreatePullRequest(gh.repo(), &options)
	if err != nil {
		return "", err
	}

	return pullRequest.HTMLURL, nil
}

func (gh *GitHub) Repository(project Project) (*octokat.Repository, error) {
	client := gh.client()
	repo := octokat.Repo{Name: project.Name, UserName: project.Owner}

	return client.Repository(repo, nil)
}

// TODO: detach GitHub from Project
func (gh *GitHub) IsRepositoryExist(project Project) bool {
	repo, err := gh.Repository(project)

	return err == nil && repo != nil
}

func (gh *GitHub) CreateRepository(project Project, description, homepage string, isPrivate bool) (*octokat.Repository, error) {
	params := octokat.RepositoryParams{Name: project.Name, Description: description, Homepage: homepage, Private: isPrivate}
	var org string
	if project.Owner != gh.Config.FetchUser() {
		org = project.Owner
	}

	client := gh.client()
	options := octokat.Options{Params: params}
	return client.CreateRepository(org, &options)
}

func (gh *GitHub) Releases() ([]octokat.Release, error) {
	client := gh.client()
	releases, err := client.Releases(gh.repo(), nil)
	if err != nil {
		return nil, err
	}

	return releases, nil
}

func (gh *GitHub) CiStatus(sha string) (*octokat.Status, error) {
	client := gh.client()
	statuses, err := client.Statuses(gh.repo(), sha)
	if err != nil {
		return nil, err
	}

	if len(statuses) == 0 {
		return nil, nil
	}

	return &statuses[len(statuses)-1], nil
}

func (gh *GitHub) ForkRepository(name, owner string, noRemote bool) (repo *octokat.Repository, err error) {
	client := gh.client()
	config := gh.Config
	r := octokat.Repo{Name: name, UserName: config.User}
	repo, err = client.Repository(r, nil)
	if repo != nil && err == nil {
		msg := fmt.Sprintf("Error creating fork: %s exists on %s", repo.FullName, GitHubHost)
		err = errors.New(msg)
		return
	}

	repo, err = client.Fork(octokat.Repo{Name: name, UserName: owner}, nil)

	return
}

func (gh *GitHub) ExpandRemoteUrl(owner, name string, isSSH bool) (url string) {
	project := gh.Project
	if owner == "origin" {
		config := gh.Config
		owner = config.FetchUser()
	}

	return project.GitURL(name, owner, isSSH)
}

func (gh *GitHub) repo() octokat.Repo {
	project := gh.Project
	return octokat.Repo{Name: project.Name, UserName: project.Owner}
}

func findOrCreateToken(user, password string) (string, error) {
	client := octokat.NewClient().WithLogin(user, password)
	auths, err := client.Authorizations(nil)
	if err != nil {
		return "", err
	}

	var token string
	for _, auth := range auths {
		if auth.NoteURL == OAuthAppURL {
			token = auth.Token
			break
		}
	}

	if token == "" {
		authParam := octokat.AuthorizationParams{}
		authParam.Scopes = append(authParam.Scopes, "repo")
		authParam.Note = "gh"
		authParam.NoteURL = OAuthAppURL
		options := octokat.Options{Params: authParam}

		auth, err := client.CreateAuthorization(&options)
		if err != nil {
			return "", err
		}

		token = auth.Token
	}

	return token, nil
}

func (gh *GitHub) client() *octokat.Client {
	config := gh.Config
	config.FetchCredentials()

	return octokat.NewClient().WithToken(config.Token)
}

func New() *GitHub {
	project := CurrentProject()
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{project, c}
}

// TODO: detach project from GitHub
func NewWithoutProject() *GitHub {
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{nil, c}
}
<commit_msg>Update to the latest statuses APIs<commit_after>package github

import (
	"errors"
	"fmt"
	"github.com/jingweno/octokat"
)

const (
	GitHubHost  string = "github.com"
	OAuthAppURL string = "http://owenou.com/gh"
)

type GitHub struct {
	Project *Project
	Config  *Config
}

func (gh *GitHub) PullRequest(id string) (*octokat.PullRequest, error) {
	client := gh.client()

	return client.PullRequest(gh.repo(), id, nil)
}

func (gh *GitHub) CreatePullRequest(base, head, title, body string) (string, error) {
	client := gh.client()
	params := octokat.PullRequestParams{Base: base, Head: head, Title: title, Body: body}
	options := octokat.Options{Params: params}
	pullRequest, err := client.CreatePullRequest(gh.repo(), &options)
	if err != nil {
		return "", err
	}

	return pullRequest.HTMLURL, nil
}

func (gh *GitHub) CreatePullRequestForIssue(base, head, issue string) (string, error) {
	client := gh.client()
	params := octokat.PullRequestForIssueParams{Base: base, Head: head, Issue: issue}
	options := octokat.Options{Params: params}
	pullRequest, err := client.CreatePullRequest(gh.repo(), &options)
	if err != nil {
		return "", err
	}

	return pullRequest.HTMLURL, nil
}

func (gh *GitHub) Repository(project Project) (*octokat.Repository, error) {
	client := gh.client()
	repo := octokat.Repo{Name: project.Name, UserName: project.Owner}

	return client.Repository(repo, nil)
}

// TODO: detach GitHub from Project
func (gh *GitHub) IsRepositoryExist(project Project) bool {
	repo, err := gh.Repository(project)

	return err == nil && repo != nil
}

func (gh *GitHub) CreateRepository(project Project, description, homepage string, isPrivate bool) (*octokat.Repository, error) {
	params := octokat.RepositoryParams{Name: project.Name, Description: description, Homepage: homepage, Private: isPrivate}
	var org string
	if project.Owner != gh.Config.FetchUser() {
		org = project.Owner
	}

	client := gh.client()
	options := octokat.Options{Params: params}
	return client.CreateRepository(org, &options)
}

func (gh *GitHub) Releases() ([]octokat.Release, error) {
	client := gh.client()
	releases, err := client.Releases(gh.repo(), nil)
	if err != nil {
		return nil, err
	}

	return releases, nil
}

func (gh *GitHub) CiStatus(sha string) (*octokat.Status, error) {
	client := gh.client()
	statuses, err := client.Statuses(gh.repo(), sha, nil)
	if err != nil {
		return nil, err
	}

	if len(statuses) == 0 {
		return nil, nil
	}

	return &statuses[len(statuses)-1], nil
}

func (gh *GitHub) ForkRepository(name, owner string, noRemote bool) (repo *octokat.Repository, err error) {
	client := gh.client()
	config := gh.Config
	r := octokat.Repo{Name: name, UserName: config.User}
	repo, err = client.Repository(r, nil)
	if repo != nil && err == nil {
		msg := fmt.Sprintf("Error creating fork: %s exists on %s", repo.FullName, GitHubHost)
		err = errors.New(msg)
		return
	}

	repo, err = client.Fork(octokat.Repo{Name: name, UserName: owner}, nil)

	return
}

func (gh *GitHub) ExpandRemoteUrl(owner, name string, isSSH bool) (url string) {
	project := gh.Project
	if owner == "origin" {
		config := gh.Config
		owner = config.FetchUser()
	}

	return project.GitURL(name, owner, isSSH)
}

func (gh *GitHub) repo() octokat.Repo {
	project := gh.Project
	return octokat.Repo{Name: project.Name, UserName: project.Owner}
}

func findOrCreateToken(user, password string) (string, error) {
	client := octokat.NewClient().WithLogin(user, password)
	auths, err := client.Authorizations(nil)
	if err != nil {
		return "", err
	}

	var token string
	for _, auth := range auths {
		if auth.NoteURL == OAuthAppURL {
			token = auth.Token
			break
		}
	}

	if token == "" {
		authParam := octokat.AuthorizationParams{}
		authParam.Scopes = append(authParam.Scopes, "repo")
		authParam.Note = "gh"
		authParam.NoteURL = OAuthAppURL
		options := octokat.Options{Params: authParam}

		auth, err := client.CreateAuthorization(&options)
		if err != nil {
			return "", err
		}

		token = auth.Token
	}

	return token, nil
}

func (gh *GitHub) client() *octokat.Client {
	config := gh.Config
	config.FetchCredentials()

	return octokat.NewClient().WithToken(config.Token)
}

func New() *GitHub {
	project := CurrentProject()
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{project, c}
}

// TODO: detach project from GitHub
func NewWithoutProject() *GitHub {
	c := CurrentConfig()
	c.FetchUser()

	return &GitHub{nil, c}
}
<|endoftext|>


<commit_before>// Copyright 2018 Axel Etcheverry. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package opentracing

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

	"github.com/gorilla/mux"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	otlog "github.com/opentracing/opentracing-go/log"
	"github.com/rs/zerolog/log"
)

// RequestIgnorerFunc is the type of a function for use in
// WithServerRequestIgnorer.
type RequestIgnorerFunc func(*http.Request) bool

// IgnoreNone is a RequestIgnorerFunc which ignores no requests.
func IgnoreNone(*http.Request) bool {
	return false
}

// Handler opentracing
func Handler(tracer opentracing.Tracer, ignore RequestIgnorerFunc) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if ignore(req) {
				next.ServeHTTP(w, req)

				return
			}

			wireContext, err := tracer.Extract(
				opentracing.TextMap,
				opentracing.HTTPHeadersCarrier(req.Header),
			)
			if err != nil {
				log.Debug().Err(err).Msg("trying to extract span")
			}

			path := routeRequestName(req)

			span := tracer.StartSpan(path, ext.RPCServerOption(wireContext))
			defer span.Finish()

			params := mux.Vars(req)

			fields := []otlog.Field{}
			for k, v := range params {
				fields = append(fields, otlog.String(k, v))
			}

			if len(fields) > 0 {
				span.LogFields(fields...)
			}

			ctx := opentracing.ContextWithSpan(req.Context(), span)

			req = req.WithContext(ctx)

			next.ServeHTTP(w, req)
		})
	}
}

func routeRequestName(req *http.Request) string {
	route := mux.CurrentRoute(req)
	if route != nil {
		tpl, err := route.GetPathTemplate()
		if err == nil {
			return req.Method + " " + massageTemplate(tpl)
		}
	}

	return serverRequestName(req)
}

func serverRequestName(req *http.Request) string {
	var b strings.Builder
	b.Grow(len(req.Method) + len(req.URL.Path) + 1)
	b.WriteString(req.Method)
	b.WriteByte(' ')
	b.WriteString(req.URL.Path)
	return b.String()
}

// massageTemplate removes the regexp patterns from template variables.
func massageTemplate(tpl string) string {
	braces := braceIndices(tpl)
	if len(braces) == 0 {
		return tpl
	}

	buf := bytes.NewBuffer(make([]byte, 0, len(tpl)))
	for i := 0; i < len(tpl); {
		var j int
		if i < braces[0] {
			j = braces[0]
			buf.WriteString(tpl[i:j])
		} else {
			j = braces[1]
			field := tpl[i:j]

			if colon := strings.IndexRune(field, ':'); colon >= 0 {
				buf.WriteString(field[:colon])
				buf.WriteRune('}')
			} else {
				buf.WriteString(field)
			}

			braces = braces[2:]
			if len(braces) == 0 {
				buf.WriteString(tpl[j:])
				break
			}
		}

		i = j
	}

	return buf.String()
}

// Copied/adapted from gorilla/mux. The original version checks
// that the braces are matched up correctly; we assume they are,
// as otherwise the path wouldn't have been registered correctly.
func braceIndices(s string) []int {
	var level, idx int
	var idxs []int

	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '{':
			if level++; level == 1 {
				idx = i
			}
		case '}':
			if level--; level == 0 {
				idxs = append(idxs, idx, i+1)
			}
		}
	}

	return idxs
}
<commit_msg>fix(opentracing): added http url and http method to span<commit_after>// Copyright 2018 Axel Etcheverry. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package opentracing

import (
	"bytes"
	"net/http"
	"strconv"
	"strings"

	"github.com/gorilla/mux"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	otlog "github.com/opentracing/opentracing-go/log"
	"github.com/rs/zerolog/log"
)

// RequestIgnorerFunc is the type of a function for use in
// WithServerRequestIgnorer.
type RequestIgnorerFunc func(*http.Request) bool

// IgnoreNone is a RequestIgnorerFunc which ignores no requests.
func IgnoreNone(*http.Request) bool {
	return false
}

// Handler opentracing
func Handler(tracer opentracing.Tracer, ignore RequestIgnorerFunc) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if ignore(req) {
				next.ServeHTTP(w, req)

				return
			}

			wireContext, err := tracer.Extract(
				opentracing.TextMap,
				opentracing.HTTPHeadersCarrier(req.Header),
			)
			if err != nil {
				log.Debug().Err(err).Msg("trying to extract span")
			}

			path := routeRequestName(req)

			span := tracer.StartSpan(path, ext.RPCServerOption(wireContext))
			defer span.Finish()

			ext.HTTPUrl.Set(span, req.URL.Path)
			ext.HTTPMethod.Set(span, req.Method)

			params := mux.Vars(req)

			fields := []otlog.Field{}
			for k, v := range params {
				if i, err := strconv.ParseInt(v, 10, 64); err == nil {
					fields = append(fields, otlog.Int64(k, i))
				} else {
					fields = append(fields, otlog.String(k, v))
				}
			}

			if len(fields) > 0 {
				span.LogFields(fields...)
			}

			ctx := opentracing.ContextWithSpan(req.Context(), span)

			req = req.WithContext(ctx)

			next.ServeHTTP(w, req)
		})
	}
}

func routeRequestName(req *http.Request) string {
	route := mux.CurrentRoute(req)
	if route != nil {
		tpl, err := route.GetPathTemplate()
		if err == nil {
			return req.Method + " " + massageTemplate(tpl)
		}
	}

	return serverRequestName(req)
}

func serverRequestName(req *http.Request) string {
	var b strings.Builder
	b.Grow(len(req.Method) + len(req.URL.Path) + 1)
	b.WriteString(req.Method)
	b.WriteByte(' ')
	b.WriteString(req.URL.Path)
	return b.String()
}

// massageTemplate removes the regexp patterns from template variables.
func massageTemplate(tpl string) string {
	braces := braceIndices(tpl)
	if len(braces) == 0 {
		return tpl
	}

	buf := bytes.NewBuffer(make([]byte, 0, len(tpl)))
	for i := 0; i < len(tpl); {
		var j int
		if i < braces[0] {
			j = braces[0]
			buf.WriteString(tpl[i:j])
		} else {
			j = braces[1]
			field := tpl[i:j]

			if colon := strings.IndexRune(field, ':'); colon >= 0 {
				buf.WriteString(field[:colon])
				buf.WriteRune('}')
			} else {
				buf.WriteString(field)
			}

			braces = braces[2:]
			if len(braces) == 0 {
				buf.WriteString(tpl[j:])
				break
			}
		}

		i = j
	}

	return buf.String()
}

// Copied/adapted from gorilla/mux. The original version checks
// that the braces are matched up correctly; we assume they are,
// as otherwise the path wouldn't have been registered correctly.
func braceIndices(s string) []int {
	var level, idx int
	var idxs []int

	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '{':
			if level++; level == 1 {
				idx = i
			}
		case '}':
			if level--; level == 0 {
				idxs = append(idxs, idx, i+1)
			}
		}
	}

	return idxs
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"io"
	"os"
	"sync"
)

type LogLevel uint8

const (
	LogLevelError LogLevel = iota
	LogLevelWarn
	LogLevelInfo
	LogLevelDebug
)

const (
	Version = "0.1.0" // add LogLevel type and Warn level
)

//--------------------------------------------------------------------------------
// thread safe logger that fires messages from multiple packages one at a time

func init() {
	go readLoop()
}

var (
	// control access to loggers
	mtx     sync.Mutex
	loggers = make(map[string]*Logger)

	// access to writers managed by channels
	writer    io.Writer = os.Stdout
	errWriter io.Writer = os.Stderr

	chanBuffer = 100
	writeCh    = make(chan []byte, chanBuffer)
	errorCh    = make(chan []byte, chanBuffer)

	quitCh = make(chan struct{})
)

type Logger struct {
	Level LogLevel
	Pkg   string

	// these are here for easy access
	// for functions that want the writer
	Writer    *SafeWriter
	ErrWriter *SafeWriter
}

// add a default logger with pkg name
func AddLogger(pkg string) *Logger {
	l := &Logger{
		Level:     LogLevelError,
		Pkg:       pkg,
		Writer:    NewSafeWriter(writeCh),
		ErrWriter: NewSafeWriter(errorCh),
	}
	mtx.Lock()
	loggers[pkg] = l
	mtx.Unlock()
	return l
}

func SetLogLevelGlobal(level LogLevel) {
	mtx.Lock()
	defer mtx.Unlock()

	for _, l := range loggers {
		l.Level = level
	}
}

// set levels for individual packages
func SetLogLevel(pkg string, level LogLevel) {
	mtx.Lock()
	defer mtx.Unlock()

	if l, ok := loggers[pkg]; ok {
		l.Level = level
		if level > LogLevelInfo {
			// TODO: wrap the writers to print [<pkg>]
		}
	}
}

// set level and writer for all loggers
func SetLoggers(level LogLevel, w io.Writer, ew io.Writer) {
	mtx.Lock()
	defer mtx.Unlock()
	for _, l := range loggers {
		l.Level = level
		if l.Level > LogLevelInfo {
			// TODO: wrap the writers to print [<pkg>]
		}
	}
	writer = w
	errWriter = ew
}

//--------------------------------------------------------------------------------
// concurrency

func readLoop() {
LOOP:
	for {
		select {
		case b := <-writeCh:
			writer.Write(b)
		case b := <-errorCh:
			errWriter.Write(b)
		case <-quitCh:
			break LOOP

		}
	}
}

func Flush() {
	quitCh <- struct{}{}
LOOP:
	for {
		select {
		case b := <-writeCh:
			writer.Write(b)
		case b := <-errorCh:
			errWriter.Write(b)
		default:
			break LOOP
		}
	}
}

//--------------------------------------------------------------------------------
// a SafeWriter implements Writer and fires its bytes over the channel
// to be written to the writer or errWriter

type SafeWriter struct {
	ch chan []byte
}

func NewSafeWriter(ch chan []byte) *SafeWriter {
	return &SafeWriter{ch}
}

func (sw *SafeWriter) Write(b []byte) (int, error) {
	sw.ch <- b
	return len(b), nil
}

// thread safe writes
func writef(s string, args ...interface{}) {
	writeCh <- []byte(fmt.Sprintf(s, args...))
}

func writeln(s ...interface{}) {
	writeCh <- []byte(fmt.Sprintln(s...))
}

func errorf(s string, args ...interface{}) {
	errorCh <- []byte(fmt.Sprintf(s, args...))
}

func errorln(s ...interface{}) {
	errorCh <- []byte(fmt.Sprintln(s...))
}

//--------------------------------------------------------------------------------
// public logger functions

// Printf and Println write to the Writer no matter what
func (l *Logger) Printf(s string, args ...interface{}) {
	writef(s, args...)
}

func (l *Logger) Println(s ...interface{}) {
	writeln(s...)
}

// Errorf and Errorln write to the ErrWriter no matter what
func (l *Logger) Errorf(s string, args ...interface{}) {
	errorf(s, args...)
}

func (l *Logger) Errorln(s ...interface{}) {
	errorln(s...)
}

// Warnf and Warnln  write to the Writer if log level >= 1
func (l *Logger) Warnf(s string, args ...interface{}) {
	if l.Level > LogLevelError {
		writef(s, args...)
	}
}

func (l *Logger) Warnln(s ...interface{}) {
	if l.Level > LogLevelError {
		writeln(s...)
	}
}

// Infof and Infoln write to the Writer if log level >= 2
func (l *Logger) Infof(s string, args ...interface{}) {
	if l.Level > LogLevelWarn {
		writef(s, args...)
	}
}

func (l *Logger) Infoln(s ...interface{}) {
	if l.Level > LogLevelWarn {
		writeln(s...)
	}
}

// Debugf and Debugln write to the Writer if log level >= 3
func (l *Logger) Debugf(s string, args ...interface{}) {
	if l.Level > LogLevelInfo {
		writef(s, args...)
	}
}

func (l *Logger) Debugln(s ...interface{}) {
	if l.Level > LogLevelInfo {
		writeln(s...)
	}
}
<commit_msg>logger: atomic running so we dont flush twice<commit_after>package log

import (
	"fmt"
	"io"
	"os"
	"sync"
	"sync/atomic"
)

type LogLevel uint8

const (
	LogLevelError LogLevel = iota
	LogLevelWarn
	LogLevelInfo
	LogLevelDebug
)

const (
	Version = "0.1.1" // atomic running
)

//--------------------------------------------------------------------------------
// thread safe logger that fires messages from multiple packages one at a time

func init() {
	go readLoop()
}

var (
	// control access to loggers
	mtx     sync.Mutex
	loggers = make(map[string]*Logger)

	// access to writers managed by channels
	writer    io.Writer = os.Stdout
	errWriter io.Writer = os.Stderr

	chanBuffer = 100
	writeCh    = make(chan []byte, chanBuffer)
	errorCh    = make(chan []byte, chanBuffer)

	quitCh = make(chan struct{})

	running uint32 // atomic
)

type Logger struct {
	Level LogLevel
	Pkg   string

	// these are here for easy access
	// for functions that want the writer
	Writer    *SafeWriter
	ErrWriter *SafeWriter
}

// add a default logger with pkg name
func AddLogger(pkg string) *Logger {
	l := &Logger{
		Level:     LogLevelError,
		Pkg:       pkg,
		Writer:    NewSafeWriter(writeCh),
		ErrWriter: NewSafeWriter(errorCh),
	}
	mtx.Lock()
	loggers[pkg] = l
	mtx.Unlock()
	return l
}

func SetLogLevelGlobal(level LogLevel) {
	mtx.Lock()
	defer mtx.Unlock()

	for _, l := range loggers {
		l.Level = level
	}
}

// set levels for individual packages
func SetLogLevel(pkg string, level LogLevel) {
	mtx.Lock()
	defer mtx.Unlock()

	if l, ok := loggers[pkg]; ok {
		l.Level = level
		if level > LogLevelInfo {
			// TODO: wrap the writers to print [<pkg>]
		}
	}
}

// set level and writer for all loggers
func SetLoggers(level LogLevel, w io.Writer, ew io.Writer) {
	mtx.Lock()
	defer mtx.Unlock()
	for _, l := range loggers {
		l.Level = level
		if l.Level > LogLevelInfo {
			// TODO: wrap the writers to print [<pkg>]
		}
	}
	writer = w
	errWriter = ew
}

//--------------------------------------------------------------------------------
// concurrency

func readLoop() {
	if atomic.CompareAndSwapUint32(&running, 0, 1) {
	LOOP:
		for {
			select {
			case b := <-writeCh:
				writer.Write(b)
			case b := <-errorCh:
				errWriter.Write(b)
			case <-quitCh:
				break LOOP

			}
		}
	}
}

func flush(ch chan []byte, w io.Writer) {
	for {
		select {
		case b := <-ch:
			w.Write(b)
		default:
			return
		}
	}
}

// Flush the log channels. Concurrent users of the logger should quit before
// Flush() is called to ensure it completes.
func Flush() {
	if atomic.CompareAndSwapUint32(&running, 1, 0) {
		flush(writeCh, writer)
		flush(errorCh, errWriter)
		quitCh <- struct{}{}
	}
}

//--------------------------------------------------------------------------------
// a SafeWriter implements Writer and fires its bytes over the channel
// to be written to the writer or errWriter

type SafeWriter struct {
	ch chan []byte
}

func NewSafeWriter(ch chan []byte) *SafeWriter {
	return &SafeWriter{ch}
}

func (sw *SafeWriter) Write(b []byte) (int, error) {
	sw.ch <- b
	return len(b), nil
}

// thread safe writes
func writef(s string, args ...interface{}) {
	writeCh <- []byte(fmt.Sprintf(s, args...))
}

func writeln(s ...interface{}) {
	writeCh <- []byte(fmt.Sprintln(s...))
}

func errorf(s string, args ...interface{}) {
	errorCh <- []byte(fmt.Sprintf(s, args...))
}

func errorln(s ...interface{}) {
	errorCh <- []byte(fmt.Sprintln(s...))
}

//--------------------------------------------------------------------------------
// public logger functions

// Printf and Println write to the Writer no matter what
func (l *Logger) Printf(s string, args ...interface{}) {
	writef(s, args...)
}

func (l *Logger) Println(s ...interface{}) {
	writeln(s...)
}

// Errorf and Errorln write to the ErrWriter no matter what
func (l *Logger) Errorf(s string, args ...interface{}) {
	errorf(s, args...)
}

func (l *Logger) Errorln(s ...interface{}) {
	errorln(s...)
}

// Warnf and Warnln  write to the Writer if log level >= 1
func (l *Logger) Warnf(s string, args ...interface{}) {
	if l.Level > LogLevelError {
		writef(s, args...)
	}
}

func (l *Logger) Warnln(s ...interface{}) {
	if l.Level > LogLevelError {
		writeln(s...)
	}
}

// Infof and Infoln write to the Writer if log level >= 2
func (l *Logger) Infof(s string, args ...interface{}) {
	if l.Level > LogLevelWarn {
		writef(s, args...)
	}
}

func (l *Logger) Infoln(s ...interface{}) {
	if l.Level > LogLevelWarn {
		writeln(s...)
	}
}

// Debugf and Debugln write to the Writer if log level >= 3
func (l *Logger) Debugf(s string, args ...interface{}) {
	if l.Level > LogLevelInfo {
		writef(s, args...)
	}
}

func (l *Logger) Debugln(s ...interface{}) {
	if l.Level > LogLevelInfo {
		writeln(s...)
	}
}
<|endoftext|>


<commit_before>package clui

import (
	"bufio"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
	"os"
	"strings"
)

/*
ListBox is control to display a list of items and allow to user to select any of them.
Content is scrollable with arrow keys or by clicking up and bottom buttons
on the scroll(now content is scrollable with mouse dragging only on Windows).

ListBox calls onSelectItem item function after a user changes currently
selected item with mouse or using keyboard (extra case: the event is emitted
when a user presses Enter - the case is used in ComboBox to select an item
from drop down list). Event structure has 2 fields filled: Y - selected
item number in list(-1 if nothing is selected), Msg - text of the selected item.
*/
type TextView struct {
	ControlBase
	// own listbox members
	lines   []string
	lengths []int
	// for up/down scroll
	topLine int
	// for side scroll
	leftShift     int
	wordWrap      bool
	colorized     bool
	virtualHeight int
	virtualWidth  int
	multicolor    bool
}

/*
NewListBox creates a new frame.
view - is a View that manages the control
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
width and heigth - are minimal size of the control.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func NewTextView(view View, parent Control, width, height int, scale int) *TextView {
	l := new(TextView)

	if height == AutoSize {
		height = 3
	}
	if width == AutoSize {
		width = 5
	}

	l.SetSize(width, height)
	l.SetConstraints(width, height)
	l.topLine = 0
	l.lines = make([]string, 0)
	l.parent = parent
	l.view = view

	l.SetTabStop(true)

	if parent != nil {
		parent.AddChild(l, scale)
	}

	return l
}

func (l *TextView) outputHeight() int {
	h := l.height
	if !l.wordWrap {
		h--
	}
	return h
}

func (l *TextView) redrawScrolls(canvas Canvas, tm Theme) {
	fg, bg := RealColor(tm, l.fg, ColorScrollText), RealColor(tm, l.bg, ColorScrollBack)
	fgThumb, bgThumb := RealColor(tm, l.fg, ColorThumbText), RealColor(tm, l.bg, ColorThumbBack)

	height := l.outputHeight()
	pos := ThumbPosition(l.topLine, l.virtualHeight-l.outputHeight(), height)
	canvas.DrawScroll(l.x+l.width-1, l.y, 1, height, pos, fg, bg, fgThumb, bgThumb, tm.SysObject(ObjScrollBar))

	if !l.wordWrap {
		pos = ThumbPosition(l.leftShift, l.virtualWidth-l.width+1, l.width-1)
		canvas.DrawScroll(l.x, l.y+l.height-1, l.width-1, 1, pos, fg, bg, fgThumb, bgThumb, tm.SysObject(ObjScrollBar))
	}
}

func (l *TextView) redrawText(canvas Canvas, tm Theme) {
	maxWidth := l.width - 1
	maxHeight := l.outputHeight()

	fg, bg := RealColor(tm, l.fg, ColorEditText), RealColor(tm, l.bg, ColorEditBack)
	if l.Active() {
		fg, bg = RealColor(tm, l.fg, ColorEditActiveText), RealColor(tm, l.bg, ColorEditActiveBack)
	}

	if l.wordWrap {
		lineId := l.posToItemNo(l.topLine)
		linePos := l.itemNoToPos(lineId)

		y := 0
		for {
			if y >= maxHeight || lineId >= len(l.lines) {
				break
			}

			remained := l.lengths[lineId]
			start := 0
			for remained > 0 {
				var s string
				if l.multicolor {
					s = SliceColorized(l.lines[lineId], start, start+maxWidth)
				} else {
					if remained <= maxWidth {
						s = xs.Slice(l.lines[lineId], start, -1)
					} else {
						s = xs.Slice(l.lines[lineId], start, start+maxWidth)
					}
				}

				if linePos >= l.topLine {
					if l.multicolor {
						canvas.PutColorizedText(l.x, l.y+y, maxWidth, s, fg, bg, Horizontal)
					} else {
						canvas.PutText(l.x, l.y+y, s, fg, bg)
					}
				}

				remained -= maxWidth
				y++
				linePos++
				start += maxWidth

				if y >= maxHeight {
					break
				}
			}

			lineId++
		}
	} else {
		y := 0
		total := len(l.lines)
		for {
			if y+l.topLine >= total {
				break
			}
			if y >= maxHeight {
				break
			}

			str := l.lines[l.topLine+y]
			lineLength := l.lengths[l.topLine+y]
			if l.multicolor {
				if l.leftShift == 0 {
					if lineLength > maxWidth {
						str = SliceColorized(str, 0, maxWidth)
					}
				} else {
					if l.leftShift+maxWidth >= lineLength {
						str = SliceColorized(str, l.leftShift, -1)
					} else {
						str = SliceColorized(str, l.leftShift, maxWidth+l.leftShift)
					}
				}
				canvas.PutColorizedText(l.x, l.y+y, maxWidth, str, fg, bg, Horizontal)
			} else {
				if l.leftShift == 0 {
					if lineLength > maxWidth {
						str = CutText(str, maxWidth)
					}
				} else {
					if l.leftShift+maxWidth >= lineLength {
						str = xs.Slice(str, l.leftShift, -1)
					} else {
						str = xs.Slice(str, l.leftShift, maxWidth+l.leftShift)
					}
				}
				canvas.PutText(l.x, l.y+y, str, fg, bg)
			}

			y++
		}
	}
}

// Repaint draws the control on its View surface
func (l *TextView) Repaint() {
	canvas := l.view.Canvas()
	tm := l.view.Screen().Theme()

	x, y := l.Pos()
	w, h := l.Size()

	bg := RealColor(tm, l.bg, ColorEditBack)
	if l.Active() {
		bg = RealColor(tm, l.bg, ColorEditActiveBack)
	}
	canvas.FillRect(x, y, w, h, term.Cell{Bg: bg, Ch: ' '})
	l.redrawText(canvas, tm)
	l.redrawScrolls(canvas, tm)
}

func (l *TextView) home() {
	l.topLine = 0
}

func (l *TextView) end() {
	height := l.outputHeight()

	if l.virtualHeight <= height {
		return
	}

	if l.topLine+height >= l.virtualHeight {
		return
	}

	l.topLine = l.virtualHeight - height
}

func (l *TextView) moveUp() {
	if l.topLine == 0 {
		return
	}

	l.topLine--
}

func (l *TextView) moveDown() {
	end := l.topLine + l.outputHeight()

	if end >= l.virtualHeight {
		return
	}

	l.topLine++
}

func (l *TextView) moveLeft() {
	if l.wordWrap || l.leftShift == 0 {
		return
	}

	l.leftShift--
}

func (l *TextView) moveRight() {
	if l.wordWrap {
		return
	}

	if l.leftShift+l.width-1 >= l.virtualWidth {
		return
	}

	l.leftShift++
}

func (l *TextView) processMouseClick(ev Event) bool {
	if ev.Key != term.MouseLeft {
		return false
	}

	dx := ev.X - l.x
	dy := ev.Y - l.y
	yy := l.outputHeight()

	// cursor is not on any scrollbar
	if dx != l.width-1 && dy != l.height-1 {
		return false
	}
	// wordwrap mode does not have horizontal scroll
	if l.wordWrap && dx != l.width-1 {
		return false
	}
	// corner in not wordwrap mode
	if !l.wordWrap && dx == l.width-1 && dy == l.height-1 {
		return false
	}

	// vertical scroll bar
	if dx == l.width-1 {
		if dy == 0 {
			l.moveUp()
		} else if dy == yy-1 {
			l.moveDown()
		} else {
			newPos := ItemByThumbPosition(dy, l.virtualHeight-yy+1, yy)
			if newPos >= 0 {
				l.topLine = newPos
			}
		}

		return true
	}

	// horizontal scrollbar
	if dx == 0 {
		l.moveLeft()
	} else if dx == l.width-2 {
		l.moveRight()
	} else {
		newPos := ItemByThumbPosition(dx, l.virtualWidth-l.width+2, l.width-1)
		if newPos >= 0 {
			l.leftShift = newPos
		}
	}

	return true
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (l *TextView) ProcessEvent(event Event) bool {
	if !l.Active() || !l.Enabled() {
		return false
	}

	switch event.Type {
	case EventKey:
		switch event.Key {
		case term.KeyHome:
			l.home()
			return true
		case term.KeyEnd:
			l.end()
			return true
		case term.KeyArrowUp:
			l.moveUp()
			return true
		case term.KeyArrowDown:
			l.moveDown()
			return true
		case term.KeyArrowLeft:
			l.moveLeft()
			return true
		case term.KeyArrowRight:
			l.moveRight()
			return true
		default:
			return false
		}
	case EventMouse:
		return l.processMouseClick(event)
	}

	return false
}

// own methods

func (l *TextView) calculateVirtualSize() {
	w := l.width - 1
	l.virtualWidth = l.width - 1
	l.virtualHeight = 0

	l.lengths = make([]int, len(l.lines))
	for idx, str := range l.lines {
		if l.multicolor {
			str = UnColorizeText(str)
		}

		sz := xs.Len(str)
		if l.wordWrap {
			n := sz / w
			r := sz % w
			l.virtualHeight += n
			if r != 0 {
				l.virtualHeight++
			}
		} else {
			l.virtualHeight++
			if sz > l.virtualWidth {
				l.virtualWidth = sz
			}
		}
		l.lengths[idx] = sz
	}
}

func (l *TextView) SetText(text []string) {
	l.lines = make([]string, len(text))
	copy(l.lines, text)

	l.calculateVirtualSize()
}

// MultiColored returns if the TextView checks and applies any
// color related tags inside its text. If MultiColores is
// false then text is displayed as is.
// To read about available color tags, please see ColorParser
func (l *TextView) MultiColored() bool {
	return l.multicolor
}

// SetMultiColored changes how the TextView output its text: as is
// or parse and apply all internal color tags
func (l *TextView) SetMultiColored(multi bool) {
	if l.multicolor != multi {
		l.multicolor = multi
		l.calculateVirtualSize()
	}
}

func (l *TextView) posToItemNo(pos int) int {
	id := 0
	for idx, item := range l.lengths {
		if l.virtualWidth >= item {
			pos--
		} else {
			pos -= item / l.virtualWidth
			if item%l.virtualWidth != 0 {
				pos--
			}
		}

		if pos <= 0 {
			id = idx
			break
		}
	}

	return id
}

func (l *TextView) itemNoToPos(id int) int {
	pos := 0
	for i := 0; i < id; i++ {
		if l.virtualWidth >= l.lengths[i] {
			pos++
		} else {
			pos += l.lengths[i] / l.virtualWidth
			if l.lengths[i]%l.virtualWidth != 0 {
				pos++
			}
		}
	}

	return pos
}

func (l *TextView) WordWrap() bool {
	return l.wordWrap
}

func (l *TextView) recalculateTopLine() {
	currLn := l.topLine

	if l.wordWrap {
		l.topLine = l.itemNoToPos(currLn)
	} else {
		l.topLine = l.posToItemNo(currLn)
	}
}

func (l *TextView) SetWordWrap(wrap bool) {
	if wrap != l.wordWrap {
		l.wordWrap = wrap
		l.calculateVirtualSize()
		l.recalculateTopLine()
		l.Repaint()
	}
}

func (l *TextView) LoadFile(filename string) bool {
	l.lines = make([]string, 0)

	file, err := os.Open(filename)
	if err != nil {
		return false
	}

	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		line = strings.TrimSpace(line)
		l.lines = append(l.lines, line)
	}

	l.calculateVirtualSize()

	return true
}
<commit_msg>closes #41 - TextView supports Pgup and Pgdn<commit_after>package clui

import (
	"bufio"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
	"os"
	"strings"
)

/*
ListBox is control to display a list of items and allow to user to select any of them.
Content is scrollable with arrow keys or by clicking up and bottom buttons
on the scroll(now content is scrollable with mouse dragging only on Windows).

ListBox calls onSelectItem item function after a user changes currently
selected item with mouse or using keyboard (extra case: the event is emitted
when a user presses Enter - the case is used in ComboBox to select an item
from drop down list). Event structure has 2 fields filled: Y - selected
item number in list(-1 if nothing is selected), Msg - text of the selected item.
*/
type TextView struct {
	ControlBase
	// own listbox members
	lines   []string
	lengths []int
	// for up/down scroll
	topLine int
	// for side scroll
	leftShift     int
	wordWrap      bool
	colorized     bool
	virtualHeight int
	virtualWidth  int
	multicolor    bool
}

/*
NewListBox creates a new frame.
view - is a View that manages the control
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
width and heigth - are minimal size of the control.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func NewTextView(view View, parent Control, width, height int, scale int) *TextView {
	l := new(TextView)

	if height == AutoSize {
		height = 3
	}
	if width == AutoSize {
		width = 5
	}

	l.SetSize(width, height)
	l.SetConstraints(width, height)
	l.topLine = 0
	l.lines = make([]string, 0)
	l.parent = parent
	l.view = view

	l.SetTabStop(true)

	if parent != nil {
		parent.AddChild(l, scale)
	}

	return l
}

func (l *TextView) outputHeight() int {
	h := l.height
	if !l.wordWrap {
		h--
	}
	return h
}

func (l *TextView) redrawScrolls(canvas Canvas, tm Theme) {
	fg, bg := RealColor(tm, l.fg, ColorScrollText), RealColor(tm, l.bg, ColorScrollBack)
	fgThumb, bgThumb := RealColor(tm, l.fg, ColorThumbText), RealColor(tm, l.bg, ColorThumbBack)

	height := l.outputHeight()
	pos := ThumbPosition(l.topLine, l.virtualHeight-l.outputHeight(), height)
	canvas.DrawScroll(l.x+l.width-1, l.y, 1, height, pos, fg, bg, fgThumb, bgThumb, tm.SysObject(ObjScrollBar))

	if !l.wordWrap {
		pos = ThumbPosition(l.leftShift, l.virtualWidth-l.width+1, l.width-1)
		canvas.DrawScroll(l.x, l.y+l.height-1, l.width-1, 1, pos, fg, bg, fgThumb, bgThumb, tm.SysObject(ObjScrollBar))
	}
}

func (l *TextView) redrawText(canvas Canvas, tm Theme) {
	maxWidth := l.width - 1
	maxHeight := l.outputHeight()

	fg, bg := RealColor(tm, l.fg, ColorEditText), RealColor(tm, l.bg, ColorEditBack)
	if l.Active() {
		fg, bg = RealColor(tm, l.fg, ColorEditActiveText), RealColor(tm, l.bg, ColorEditActiveBack)
	}

	if l.wordWrap {
		lineId := l.posToItemNo(l.topLine)
		linePos := l.itemNoToPos(lineId)

		y := 0
		for {
			if y >= maxHeight || lineId >= len(l.lines) {
				break
			}

			remained := l.lengths[lineId]
			start := 0
			for remained > 0 {
				var s string
				if l.multicolor {
					s = SliceColorized(l.lines[lineId], start, start+maxWidth)
				} else {
					if remained <= maxWidth {
						s = xs.Slice(l.lines[lineId], start, -1)
					} else {
						s = xs.Slice(l.lines[lineId], start, start+maxWidth)
					}
				}

				if linePos >= l.topLine {
					if l.multicolor {
						canvas.PutColorizedText(l.x, l.y+y, maxWidth, s, fg, bg, Horizontal)
					} else {
						canvas.PutText(l.x, l.y+y, s, fg, bg)
					}
				}

				remained -= maxWidth
				y++
				linePos++
				start += maxWidth

				if y >= maxHeight {
					break
				}
			}

			lineId++
		}
	} else {
		y := 0
		total := len(l.lines)
		for {
			if y+l.topLine >= total {
				break
			}
			if y >= maxHeight {
				break
			}

			str := l.lines[l.topLine+y]
			lineLength := l.lengths[l.topLine+y]
			if l.multicolor {
				if l.leftShift == 0 {
					if lineLength > maxWidth {
						str = SliceColorized(str, 0, maxWidth)
					}
				} else {
					if l.leftShift+maxWidth >= lineLength {
						str = SliceColorized(str, l.leftShift, -1)
					} else {
						str = SliceColorized(str, l.leftShift, maxWidth+l.leftShift)
					}
				}
				canvas.PutColorizedText(l.x, l.y+y, maxWidth, str, fg, bg, Horizontal)
			} else {
				if l.leftShift == 0 {
					if lineLength > maxWidth {
						str = CutText(str, maxWidth)
					}
				} else {
					if l.leftShift+maxWidth >= lineLength {
						str = xs.Slice(str, l.leftShift, -1)
					} else {
						str = xs.Slice(str, l.leftShift, maxWidth+l.leftShift)
					}
				}
				canvas.PutText(l.x, l.y+y, str, fg, bg)
			}

			y++
		}
	}
}

// Repaint draws the control on its View surface
func (l *TextView) Repaint() {
	canvas := l.view.Canvas()
	tm := l.view.Screen().Theme()

	x, y := l.Pos()
	w, h := l.Size()

	bg := RealColor(tm, l.bg, ColorEditBack)
	if l.Active() {
		bg = RealColor(tm, l.bg, ColorEditActiveBack)
	}
	canvas.FillRect(x, y, w, h, term.Cell{Bg: bg, Ch: ' '})
	l.redrawText(canvas, tm)
	l.redrawScrolls(canvas, tm)
}

func (l *TextView) home() {
	l.topLine = 0
}

func (l *TextView) end() {
	height := l.outputHeight()

	if l.virtualHeight <= height {
		return
	}

	if l.topLine+height >= l.virtualHeight {
		return
	}

	l.topLine = l.virtualHeight - height
}

func (l *TextView) moveUp(dy int) {
	if l.topLine == 0 {
		return
	}

	if l.topLine <= dy {
		l.topLine = 0
	} else {
		l.topLine -= dy
	}
}

func (l *TextView) moveDown(dy int) {
	end := l.topLine + l.outputHeight()

	if end >= l.virtualHeight {
		return
	}

	if l.topLine+dy+l.outputHeight() >= l.virtualHeight {
		l.topLine = l.virtualHeight - l.outputHeight()
	} else {
		l.topLine += dy
	}
}

func (l *TextView) moveLeft() {
	if l.wordWrap || l.leftShift == 0 {
		return
	}

	l.leftShift--
}

func (l *TextView) moveRight() {
	if l.wordWrap {
		return
	}

	if l.leftShift+l.width-1 >= l.virtualWidth {
		return
	}

	l.leftShift++
}

func (l *TextView) processMouseClick(ev Event) bool {
	if ev.Key != term.MouseLeft {
		return false
	}

	dx := ev.X - l.x
	dy := ev.Y - l.y
	yy := l.outputHeight()

	// cursor is not on any scrollbar
	if dx != l.width-1 && dy != l.height-1 {
		return false
	}
	// wordwrap mode does not have horizontal scroll
	if l.wordWrap && dx != l.width-1 {
		return false
	}
	// corner in not wordwrap mode
	if !l.wordWrap && dx == l.width-1 && dy == l.height-1 {
		return false
	}

	// vertical scroll bar
	if dx == l.width-1 {
		if dy == 0 {
			l.moveUp(1)
		} else if dy == yy-1 {
			l.moveDown(1)
		} else {
			newPos := ItemByThumbPosition(dy, l.virtualHeight-yy+1, yy)
			if newPos >= 0 {
				l.topLine = newPos
			}
		}

		return true
	}

	// horizontal scrollbar
	if dx == 0 {
		l.moveLeft()
	} else if dx == l.width-2 {
		l.moveRight()
	} else {
		newPos := ItemByThumbPosition(dx, l.virtualWidth-l.width+2, l.width-1)
		if newPos >= 0 {
			l.leftShift = newPos
		}
	}

	return true
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (l *TextView) ProcessEvent(event Event) bool {
	if !l.Active() || !l.Enabled() {
		return false
	}

	switch event.Type {
	case EventKey:
		switch event.Key {
		case term.KeyHome:
			l.home()
			return true
		case term.KeyEnd:
			l.end()
			return true
		case term.KeyArrowUp:
			l.moveUp(1)
			return true
		case term.KeyArrowDown:
			l.moveDown(1)
			return true
		case term.KeyArrowLeft:
			l.moveLeft()
			return true
		case term.KeyArrowRight:
			l.moveRight()
			return true
		case term.KeyPgup:
			l.moveUp(l.outputHeight())
		case term.KeyPgdn:
			l.moveDown(l.outputHeight())
		default:
			return false
		}
	case EventMouse:
		return l.processMouseClick(event)
	}

	return false
}

// own methods

func (l *TextView) calculateVirtualSize() {
	w := l.width - 1
	l.virtualWidth = l.width - 1
	l.virtualHeight = 0

	l.lengths = make([]int, len(l.lines))
	for idx, str := range l.lines {
		if l.multicolor {
			str = UnColorizeText(str)
		}

		sz := xs.Len(str)
		if l.wordWrap {
			n := sz / w
			r := sz % w
			l.virtualHeight += n
			if r != 0 {
				l.virtualHeight++
			}
		} else {
			l.virtualHeight++
			if sz > l.virtualWidth {
				l.virtualWidth = sz
			}
		}
		l.lengths[idx] = sz
	}
}

func (l *TextView) SetText(text []string) {
	l.lines = make([]string, len(text))
	copy(l.lines, text)

	l.calculateVirtualSize()
}

// MultiColored returns if the TextView checks and applies any
// color related tags inside its text. If MultiColores is
// false then text is displayed as is.
// To read about available color tags, please see ColorParser
func (l *TextView) MultiColored() bool {
	return l.multicolor
}

// SetMultiColored changes how the TextView output its text: as is
// or parse and apply all internal color tags
func (l *TextView) SetMultiColored(multi bool) {
	if l.multicolor != multi {
		l.multicolor = multi
		l.calculateVirtualSize()
	}
}

func (l *TextView) posToItemNo(pos int) int {
	id := 0
	for idx, item := range l.lengths {
		if l.virtualWidth >= item {
			pos--
		} else {
			pos -= item / l.virtualWidth
			if item%l.virtualWidth != 0 {
				pos--
			}
		}

		if pos <= 0 {
			id = idx
			break
		}
	}

	return id
}

func (l *TextView) itemNoToPos(id int) int {
	pos := 0
	for i := 0; i < id; i++ {
		if l.virtualWidth >= l.lengths[i] {
			pos++
		} else {
			pos += l.lengths[i] / l.virtualWidth
			if l.lengths[i]%l.virtualWidth != 0 {
				pos++
			}
		}
	}

	return pos
}

func (l *TextView) WordWrap() bool {
	return l.wordWrap
}

func (l *TextView) recalculateTopLine() {
	currLn := l.topLine

	if l.wordWrap {
		l.topLine = l.itemNoToPos(currLn)
	} else {
		l.topLine = l.posToItemNo(currLn)
	}
}

func (l *TextView) SetWordWrap(wrap bool) {
	if wrap != l.wordWrap {
		l.wordWrap = wrap
		l.calculateVirtualSize()
		l.recalculateTopLine()
		l.Repaint()
	}
}

func (l *TextView) LoadFile(filename string) bool {
	l.lines = make([]string, 0)

	file, err := os.Open(filename)
	if err != nil {
		return false
	}

	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		line = strings.TrimSpace(line)
		l.lines = append(l.lines, line)
	}

	l.calculateVirtualSize()

	return true
}
<|endoftext|>


<commit_before>package gofakes3_test

import (
	"bytes"
	"log"
	"net/http/httptest"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/johannesboyne/gofakes3"
)

func TestCreateBucket(t *testing.T) {
	//@TODO(jb): implement them for sanity reasons

	faker := gofakes3.New("tests3.db")
	ts := httptest.NewServer(faker.Server())
	defer ts.Close()

	config := aws.NewConfig()
	config.WithEndpoint(ts.URL)
	config.WithRegion("mine")

	svc := s3.New(session.New(), config)

	_, err := svc.CreateBucket(&s3.CreateBucketInput{
		Bucket: aws.String("BucketName"),
	})
	_, err = svc.HeadBucket(&s3.HeadBucketInput{
		Bucket: aws.String("BucketName"),
	})
	_, err = svc.PutObject(&s3.PutObjectInput{
		Bucket: aws.String("BucketName"),
		Key:    aws.String("ObjectKey"),
		Body:   bytes.NewReader([]byte("{\"test\": \"foo\"}")),
		Metadata: map[string]*string{
			"Key": aws.String("MetadataValue"),
		},
	})
	obj, _ := svc.GetObject(&s3.GetObjectInput{
		Bucket: aws.String("BucketName"),
		Key:    aws.String("ObjectKey"),
	})
	head, _ := svc.HeadObject(&s3.HeadObjectInput{
		Bucket: aws.String("BucketName"),
		Key:    aws.String("ObjectKey"),
	})
	log.Println("-> CONTENT LENGTH", *obj.ContentLength)
	log.Println("-> HEAD ETAG     ", *head.ETag)

	if err != nil {
		t.Errorf("ERROR:\n%+v\n", err)
		return
	}
}
<commit_msg>Testing CI<commit_after>package gofakes3_test

import (
	"bytes"
	"net/http/httptest"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/johannesboyne/gofakes3"
)

func TestCreateBucket(t *testing.T) {
	//@TODO(jb): implement them for sanity reasons

	faker := gofakes3.New("tests3.db")
	ts := httptest.NewServer(faker.Server())
	defer ts.Close()

	config := aws.NewConfig()
	config.WithEndpoint(ts.URL)
	config.WithRegion("mine")

	svc := s3.New(session.New(), config)

	_, err := svc.CreateBucket(&s3.CreateBucketInput{
		Bucket: aws.String("BucketName"),
	})
	_, err = svc.HeadBucket(&s3.HeadBucketInput{
		Bucket: aws.String("BucketName"),
	})
	_, err = svc.PutObject(&s3.PutObjectInput{
		Bucket: aws.String("BucketName"),
		Key:    aws.String("ObjectKey"),
		Body:   bytes.NewReader([]byte("{\"test\": \"foo\"}")),
		Metadata: map[string]*string{
			"Key": aws.String("MetadataValue"),
		},
	})
	//obj, err := svc.GetObject(&s3.GetObjectInput{
	//	Bucket: aws.String("BucketName"),
	//	Key:    aws.String("ObjectKey"),
	//})
	//if err != nil {
	//	t.Errorf("ERROR:\n%+v\n", err)
	//	return
	//}
	//head, err := svc.HeadObject(&s3.HeadObjectInput{
	//	Bucket: aws.String("BucketName"),
	//	Key:    aws.String("ObjectKey"),
	//})
	if err != nil {
		t.Errorf("ERROR:\n%+v\n", err)
		return
	}
	//log.Println("-> HEAD ETAG     ", *head.ETag)

}
<|endoftext|>


<commit_before>package main_test

import (
	"fmt"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/satori/go.uuid"
	"os"
	"os/exec"
	"testing"
)

func TestUtils(t *testing.T) {
	RegisterFailHandler(Fail)
	doSetup()
	RunSpecs(t, "main test suite")
	doTeardown()
}

var (
	cfHome              string
	err                 error
	guid                = uuid.NewV4()
	orgName             = fmt.Sprintf("chaos-galago-smoke-%s", guid)
	spaceName           = orgName
	output              []byte
	serviceInstanceName = fmt.Sprintf("galago_smoke_test_%s", guid)
	appName             = fmt.Sprintf("galago_smoke_test_%s", guid)
)

func doSetup() {
	cfHome = os.Getenv("CF_HOME")
	isEnvSet("CF_HOME", cfHome)
	if _, homeErr := os.Stat(fmt.Sprintf("%s/.cf", cfHome)); homeErr == nil {
		fmt.Println("CF_HOME must be a temp directory")
		os.Exit(1)
	}

	cfPassword := os.Getenv("CF_PASSWORD")
	cfUsername := os.Getenv("CF_USERNAME")
	cfDomain := os.Getenv("CF_DOMAIN")
	isEnvSet("CF_PASSWORD", cfPassword)
	isEnvSet("CF_USERNAME", cfUsername)
	isEnvSet("CF_DOMAIN", cfDomain)

	output, err = exec.Command("cf", "login", "-a", fmt.Sprintf("https://api.%s", cfDomain), "-u", cfUsername, "-p", cfPassword, "--skip-ssl-validation").Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "create-org", orgName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "target", "-o", orgName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "create-space", spaceName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "target", "-s", spaceName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "push", "-f", "fixtures/galago_smoke_test/manifest.yml").Output()
	freakOutDebug(output, err)
}

func doTeardown() {
	output, err = exec.Command("cf", "delete", "-f", appName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "delete-space", "-f", spaceName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "delete-org", "-f", orgName).Output()
	freakOutDebug(output, err)
	os.RemoveAll(fmt.Sprintf("%s/.cf", cfHome))
}

func isEnvSet(envName string, env string) {
	if env == "" {
		fmt.Printf("\n\nEnvironment Variable \"%s\" was not set\n", envName)
		os.Exit(1)
	}
}

func freakOutDebug(output []byte, err error) {
	if err != nil {
		fmt.Printf("\nAn unexpected error occurred: \n%s\n", output)
	}
}
<commit_msg>reduce service name to below 50 characters<commit_after>package main_test

import (
	"fmt"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/satori/go.uuid"
	"os"
	"os/exec"
	"testing"
)

func TestUtils(t *testing.T) {
	RegisterFailHandler(Fail)
	doSetup()
	RunSpecs(t, "main test suite")
	doTeardown()
}

var (
	cfHome              string
	err                 error
	guid                = uuid.NewV4()
	orgName             = fmt.Sprintf("chaos-galago-smoke-%s", guid)
	spaceName           = orgName
	output              []byte
	serviceInstanceName = fmt.Sprintf("test_%s", guid)
	appName             = fmt.Sprintf("test_%s", guid)
)

func doSetup() {
	cfHome = os.Getenv("CF_HOME")
	isEnvSet("CF_HOME", cfHome)
	if _, homeErr := os.Stat(fmt.Sprintf("%s/.cf", cfHome)); homeErr == nil {
		fmt.Println("CF_HOME must be a temp directory")
		os.Exit(1)
	}

	cfPassword := os.Getenv("CF_PASSWORD")
	cfUsername := os.Getenv("CF_USERNAME")
	cfDomain := os.Getenv("CF_DOMAIN")
	isEnvSet("CF_PASSWORD", cfPassword)
	isEnvSet("CF_USERNAME", cfUsername)
	isEnvSet("CF_DOMAIN", cfDomain)

	output, err = exec.Command("cf", "login", "-a", fmt.Sprintf("https://api.%s", cfDomain), "-u", cfUsername, "-p", cfPassword, "--skip-ssl-validation").Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "create-org", orgName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "target", "-o", orgName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "create-space", spaceName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "target", "-s", spaceName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "push", "-f", "fixtures/galago_smoke_test/manifest.yml").Output()
	freakOutDebug(output, err)
}

func doTeardown() {
	output, err = exec.Command("cf", "delete", "-f", appName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "delete-space", "-f", spaceName).Output()
	freakOutDebug(output, err)
	output, err = exec.Command("cf", "delete-org", "-f", orgName).Output()
	freakOutDebug(output, err)
	os.RemoveAll(fmt.Sprintf("%s/.cf", cfHome))
}

func isEnvSet(envName string, env string) {
	if env == "" {
		fmt.Printf("\n\nEnvironment Variable \"%s\" was not set\n", envName)
		os.Exit(1)
	}
}

func freakOutDebug(output []byte, err error) {
	if err != nil {
		fmt.Printf("\nAn unexpected error occurred: \n%s\n", output)
	}
}
<|endoftext|>


<commit_before>package system

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

func GetExitCode(err error) (int, error) {

	exitcode := 0
	if exiterr, ok := err.(*exec.ExitError); ok {
		if procexit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return procexit.ExitStatus(), nil
		}
	}
	return exitcode, fmt.Errorf("failed to get exit code")
}

func PorcessExitCode(err error) int {

	if err != nil {
		var (
			exitcode int
			exiterr  error
		)
		if exitcode, exiterr = GetExitCode(err); exiterr != nil {
			return 127
		}
		return exitcode
	}
	return 0
}
<commit_msg>exitcode funcation rename.<commit_after>package system

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

//GetExitCode is exported
func GetExitCode(err error) (int, error) {

	exitcode := 0
	if exiterr, ok := err.(*exec.ExitError); ok {
		if procexit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			return procexit.ExitStatus(), nil
		}
	}
	return exitcode, fmt.Errorf("failed to get exit code.")
}

//ErrorExitCode is exported
func ErrorExitCode(err error) int {

	if err != nil {
		var (
			exitcode int
			exiterr  error
		)
		if exitcode, exiterr = GetExitCode(err); exiterr != nil {
			return 127
		}
		return exitcode
	}
	return 0
}
<|endoftext|>


<commit_before>package dbr

import (
	"database/sql"
)

type Tx struct {
	*Session
	*sql.Tx
}

func (sess *Session) Begin() (*Tx, error) {
	tx, err := sess.cxn.Db.Begin()
	if err != nil {
		return nil, sess.EventErr("dbr.begin", err)
	}

	return &Tx{
		Session: sess,
		Tx:      tx,
	}, nil
}

func (tx *Tx) Commit() error {
	err := tx.Tx.Commit()
	if err != nil {
		return tx.EventErr("dbr.commit", err)
	}
	return nil
}

func (tx *Tx) Rollback() error {
	err := tx.Tx.Rollback()
	if err != nil {
		return tx.EventErr("dbr.rollback", err)
	}
	return nil
}

// Rollsback the transaction unless it has already been committed or rolled back.
// Useful to defer tx.RollbackUnlessCommitted() -- so you don't have to handle N failure cases
// Keep in mind the only way to detect an error on the rollback is via the event log.
func (tx *Tx) RollbackUnlessCommitted() {
	err := tx.Tx.Rollback()
	if err == sql.ErrTxDone {
		// ok
	} else if err != nil {
		tx.EventErr("dbr.rollback_unless_committed", err)
	}
}<commit_msg>Wire up user stories and verify it mostly kinda works<commit_after>package dbr

import (
	"database/sql"
)

type Tx struct {
	*Session
	*sql.Tx
}

func (sess *Session) Begin() (*Tx, error) {
	tx, err := sess.cxn.Db.Begin()
	if err != nil {
		return nil, sess.EventErr("dbr.begin.error", err)
	} else {
		sess.Event("dbr.begin")
	}

	return &Tx{
		Session: sess,
		Tx:      tx,
	}, nil
}

func (tx *Tx) Commit() error {
	err := tx.Tx.Commit()
	if err != nil {
		return tx.EventErr("dbr.commit.error", err)
	} else {
		tx.Event("dbr.commit")
	}
	return nil
}

func (tx *Tx) Rollback() error {
	err := tx.Tx.Rollback()
	if err != nil {
		return tx.EventErr("dbr.rollback", err)
	} else {
		tx.Event("dbr.rollback")
	}
	return nil
}

// Rollsback the transaction unless it has already been committed or rolled back.
// Useful to defer tx.RollbackUnlessCommitted() -- so you don't have to handle N failure cases
// Keep in mind the only way to detect an error on the rollback is via the event log.
func (tx *Tx) RollbackUnlessCommitted() {
	err := tx.Tx.Rollback()
	if err == sql.ErrTxDone {
		// ok
	} else if err != nil {
		tx.EventErr("dbr.rollback_unless_committed", err)
	} else {
		tx.Event("dbr.rollback")
	}
}<|endoftext|>


<commit_before>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/

package firebirdsql

type firebirdsqlTx struct {
    wp *wireProtocol
    transHandle int32
}

func (tx *firebirdsqlTx) Commit() (err error) {
    tx.wp.opCommit(tx.transHandle)
    tx.transHandle, _, _, err = tx.wp.opResponse()
    return
}

func (tx *firebirdsqlTx) Rollback() (err error) {
    tx.wp.opRollback(tx.transHandle)
    tx.transHandle, _, _, err = tx.wp.opResponse()
    return
}

func newFirebirdsqlTx(wp *wireProtocol) (tx *firebirdsqlTx, err error) {
    tx = new(firebirdsqlTx)
    tx.wp = wp
    wp.opTransaction([]byte {
        byte(isc_tpb_version3),
        byte(isc_tpb_write),
        byte(isc_tpb_wait),
        byte(isc_tpb_read_committed),
        byte(isc_tpb_no_rec_version),
    })
    tx.transHandle, _, _, err = wp.opResponse()
    return
}
<commit_msg>fix wrong transaction_handle overwrite.<commit_after>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013 Hajime Nakagami

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/

package firebirdsql

type firebirdsqlTx struct {
    wp *wireProtocol
    transHandle int32
}

func (tx *firebirdsqlTx) Commit() (err error) {
    tx.wp.opCommit(tx.transHandle)
    _, _, _, err = tx.wp.opResponse()
    return
}

func (tx *firebirdsqlTx) Rollback() (err error) {
    tx.wp.opRollback(tx.transHandle)
    _, _, _, err = tx.wp.opResponse()
    return
}

func newFirebirdsqlTx(wp *wireProtocol) (tx *firebirdsqlTx, err error) {
    tx = new(firebirdsqlTx)
    tx.wp = wp
    wp.opTransaction([]byte {
        byte(isc_tpb_version3),
        byte(isc_tpb_write),
        byte(isc_tpb_wait),
        byte(isc_tpb_read_committed),
        byte(isc_tpb_no_rec_version),
    })
    tx.transHandle, _, _, err = wp.opResponse()
    return
}
<|endoftext|>


<commit_before>package pigae

import (
	"encoding/xml"
	"fmt"
	"net/http"
	"sort"
	"text/template"

	. "github.com/Deleplace/programming-idioms/pig"

	"appengine"
)

// RssItem is a news.
type RssItem struct {
	XMLName     xml.Name `xml:"item"`
	Link        string   `xml:"link"`
	Title       string   `xml:"title"`
	Description string   `xml:"description"`
	PubDate     string   `xml:"pubDate"`
	GUID        GUID     `xml:"guid"`
}

// GUID wraps a URI that is probably not a Permalink.
type GUID struct {
	Value       string `xml:",innerxml"`
	IsPermaLink bool   `xml:"isPermaLink,attr"`
}

const rssTemplateString = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{.FeedTitle}}</title>
<link>{{.SiteLink}}</link>
<description>{{.FeedDescription}}</description>
<atom:link href="{{.FeedURL}}" rel="self" type="application/rss+xml" />
{{range .Items}}
{{.}}
{{end}}
</channel>
</rss>`

const rssPubDatelayout = "Mon, 2 Jan 2006 15:04:05 GMT"

var rssTemplate, _ = template.New("rss").Parse(rssTemplateString)

// RssFacade is the Facade for the RSS news feeds.
type RssFacade struct {
	FeedTitle       string
	SiteLink        string
	FeedDescription string
	Items           []string
	FeedURL         string
}

func rssRecentlyUpdated(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	_, idioms, _ := dao.getAllIdioms(c, 10, "-VersionDate")
	dateUpdate := func(idiom *Idiom) string { return idiom.VersionDate.Format(rssPubDatelayout) }
	idiomVersionGuidation := func(idiom *Idiom) string {
		return fmt.Sprintf("%v/guid/idiom/%v/version/%v", env.Host, idiom.Id, idiom.Version)
	}
	return rss(w, c, r, idioms, dateUpdate, idiomVersionGuidation, "/rss-recently-updated", "Programming Idioms recently updated idioms", "Idioms recently modified or having new implementations", "

Last updated in ")
}

func rssRecentlyCreated(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	_, idioms, _ := dao.getAllIdioms(c, 10, "-Id")
	dateCreation := func(idiom *Idiom) string { return idiom.CreationDate.Format(rssPubDatelayout) }
	idiomGuidation := func(idiom *Idiom) string { return fmt.Sprintf("%v/guid/idiom/%v", env.Host, idiom.Id) }
	return rss(w, c, r, idioms, dateCreation, idiomGuidation, "/rss-recently-created", "Programming Idioms recently created idioms", "Idioms recently created", "

Implemented in ")
}

func rss(w http.ResponseWriter, c appengine.Context, r *http.Request, idioms []*Idiom, datation func(*Idiom) string, guidation func(*Idiom) string, path string, feedTitle string, feedDescription string, listIntro string) error {

	itemsAsStrings := make([]string, len(idioms))
	for i, idiom := range idioms {
		desc := idiom.LeadParagraph
		sort.Sort(&implByVersionDateSorter{idiom.Implementations})
		if len(idiom.Implementations) > 0 {
			desc += listIntro + printNiceLang(idiom.Implementations[0].LanguageName)
			for _, impl := range idiom.Implementations[1:] {
				desc += ", " + impl.LanguageName
			}
			desc += "."
		}
		item := &RssItem{
			Link:        env.Host + NiceIdiomRelativeURL(idiom),
			Title:       idiom.Title,
			Description: desc,
			PubDate:     datation(idiom),
			GUID: GUID{
				Value:       guidation(idiom),
				IsPermaLink: false,
			},
		}
		buff, err := xml.MarshalIndent(item, "  ", "    ")
		if err != nil {
			return err
		}
		itemsAsStrings[i] = string(buff)
	}

	w.Header().Set("Content-Type", "application/rss+xml")
	data := &RssFacade{
		FeedTitle:       feedTitle,
		SiteLink:        env.Host,
		FeedDescription: feedDescription,
		Items:           itemsAsStrings,
		FeedURL:         env.Host + path,
	}
	return rssTemplate.ExecuteTemplate(w, "rss", data)
}
<commit_msg>Much success, too many updates per day: augment RSS items count.<commit_after>package pigae

import (
	"encoding/xml"
	"fmt"
	"net/http"
	"sort"
	"text/template"

	. "github.com/Deleplace/programming-idioms/pig"

	"appengine"
)

// RssItem is a news.
type RssItem struct {
	XMLName     xml.Name `xml:"item"`
	Link        string   `xml:"link"`
	Title       string   `xml:"title"`
	Description string   `xml:"description"`
	PubDate     string   `xml:"pubDate"`
	GUID        GUID     `xml:"guid"`
}

// GUID wraps a URI that is probably not a Permalink.
type GUID struct {
	Value       string `xml:",innerxml"`
	IsPermaLink bool   `xml:"isPermaLink,attr"`
}

const rssTemplateString = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{.FeedTitle}}</title>
<link>{{.SiteLink}}</link>
<description>{{.FeedDescription}}</description>
<atom:link href="{{.FeedURL}}" rel="self" type="application/rss+xml" />
{{range .Items}}
{{.}}
{{end}}
</channel>
</rss>`

const rssPubDatelayout = "Mon, 2 Jan 2006 15:04:05 GMT"

const nbItemsCreated = 10
const nbItemsUpdated = 25

var rssTemplate, _ = template.New("rss").Parse(rssTemplateString)

// RssFacade is the Facade for the RSS news feeds.
type RssFacade struct {
	FeedTitle       string
	SiteLink        string
	FeedDescription string
	Items           []string
	FeedURL         string
}

func rssRecentlyUpdated(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	_, idioms, _ := dao.getAllIdioms(c, nbItemsUpdated, "-VersionDate")
	dateUpdate := func(idiom *Idiom) string { return idiom.VersionDate.Format(rssPubDatelayout) }
	idiomVersionGuidation := func(idiom *Idiom) string {
		return fmt.Sprintf("%v/guid/idiom/%v/version/%v", env.Host, idiom.Id, idiom.Version)
	}
	return rss(w, c, r, idioms, dateUpdate, idiomVersionGuidation, "/rss-recently-updated", "Programming Idioms recently updated idioms", "Idioms recently modified or having new implementations", "

Last updated in ")
}

func rssRecentlyCreated(w http.ResponseWriter, r *http.Request) error {
	c := appengine.NewContext(r)
	_, idioms, _ := dao.getAllIdioms(c, nbItemsCreated, "-Id")
	dateCreation := func(idiom *Idiom) string { return idiom.CreationDate.Format(rssPubDatelayout) }
	idiomGuidation := func(idiom *Idiom) string { return fmt.Sprintf("%v/guid/idiom/%v", env.Host, idiom.Id) }
	return rss(w, c, r, idioms, dateCreation, idiomGuidation, "/rss-recently-created", "Programming Idioms recently created idioms", "Idioms recently created", "

Implemented in ")
}

func rss(w http.ResponseWriter, c appengine.Context, r *http.Request, idioms []*Idiom, datation func(*Idiom) string, guidation func(*Idiom) string, path string, feedTitle string, feedDescription string, listIntro string) error {

	itemsAsStrings := make([]string, len(idioms))
	for i, idiom := range idioms {
		desc := idiom.LeadParagraph
		sort.Sort(&implByVersionDateSorter{idiom.Implementations})
		if len(idiom.Implementations) > 0 {
			desc += listIntro + printNiceLang(idiom.Implementations[0].LanguageName)
			for _, impl := range idiom.Implementations[1:] {
				desc += ", " + impl.LanguageName
			}
			desc += "."
		}
		item := &RssItem{
			Link:        env.Host + NiceIdiomRelativeURL(idiom),
			Title:       idiom.Title,
			Description: desc,
			PubDate:     datation(idiom),
			GUID: GUID{
				Value:       guidation(idiom),
				IsPermaLink: false,
			},
		}
		buff, err := xml.MarshalIndent(item, "  ", "    ")
		if err != nil {
			return err
		}
		itemsAsStrings[i] = string(buff)
	}

	w.Header().Set("Content-Type", "application/rss+xml")
	data := &RssFacade{
		FeedTitle:       feedTitle,
		SiteLink:        env.Host,
		FeedDescription: feedDescription,
		Items:           itemsAsStrings,
		FeedURL:         env.Host + path,
	}
	return rssTemplate.ExecuteTemplate(w, "rss", data)
}
<|endoftext|>