<commit_before>// Copyright 2018 Prometheus Team
// 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 cluster

import (
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/gogo/protobuf/proto"
	"github.com/hashicorp/memberlist"
	"github.com/prometheus/alertmanager/cluster/clusterpb"
	"github.com/prometheus/client_golang/prometheus"
)

// Maximum number of messages to be held in the queue.
const maxQueueSize = 4096

// delegate implements memberlist.Delegate and memberlist.EventDelegate
// and broadcasts its peer's state in the cluster.
type delegate struct {
	*Peer

	logger log.Logger
	bcast  *memberlist.TransmitLimitedQueue

	messagesReceived     *prometheus.CounterVec
	messagesReceivedSize *prometheus.CounterVec
	messagesSent         *prometheus.CounterVec
	messagesSentSize     *prometheus.CounterVec
	messagesPruned       prometheus.Counter
}

func newDelegate(l log.Logger, reg prometheus.Registerer, p *Peer, retransmit int) *delegate {
	bcast := &memberlist.TransmitLimitedQueue{
		NumNodes:       p.ClusterSize,
		RetransmitMult: retransmit,
	}
	messagesReceived := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_received_total",
		Help: "Total number of cluster messsages received.",
	}, []string{"msg_type"})
	messagesReceivedSize := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_received_size_total",
		Help: "Total size of cluster messages received.",
	}, []string{"msg_type"})
	messagesSent := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_sent_total",
		Help: "Total number of cluster messsages sent.",
	}, []string{"msg_type"})
	messagesSentSize := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_sent_size_total",
		Help: "Total size of cluster messages sent.",
	}, []string{"msg_type"})
	messagesPruned := prometheus.NewCounter(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_pruned_total",
		Help: "Total number of cluster messsages pruned.",
	})
	gossipClusterMembers := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_cluster_members",
		Help: "Number indicating current number of members in cluster.",
	}, func() float64 {
		return float64(p.ClusterSize())
	})
	peerPosition := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_peer_position",
		Help: "Position the Alertmanager instance believes it's in. The position determines a peer's behavior in the cluster.",
	}, func() float64 {
		return float64(p.Position())
	})
	healthScore := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_cluster_health_score",
		Help: "Health score of the cluster. Lower values are better and zero means 'totally healthy'.",
	}, func() float64 {
		return float64(p.mlist.GetHealthScore())
	})
	messagesQueued := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_cluster_messages_queued",
		Help: "Number of cluster messsages which are queued.",
	}, func() float64 {
		return float64(bcast.NumQueued())
	})

	messagesReceived.WithLabelValues("full_state")
	messagesReceivedSize.WithLabelValues("full_state")
	messagesReceived.WithLabelValues("update")
	messagesReceivedSize.WithLabelValues("update")
	messagesSent.WithLabelValues("full_state")
	messagesSentSize.WithLabelValues("full_state")
	messagesSent.WithLabelValues("update")
	messagesSentSize.WithLabelValues("update")

	reg.MustRegister(messagesReceived, messagesReceivedSize, messagesSent, messagesSentSize,
		gossipClusterMembers, peerPosition, healthScore, messagesQueued, messagesPruned)

	d := &delegate{
		logger:               l,
		Peer:                 p,
		bcast:                bcast,
		messagesReceived:     messagesReceived,
		messagesReceivedSize: messagesReceivedSize,
		messagesSent:         messagesSent,
		messagesSentSize:     messagesSentSize,
		messagesPruned:       messagesPruned,
	}

	go d.handleQueueDepth()

	return d
}

// NodeMeta retrieves meta-data about the current node when broadcasting an alive message.
func (d *delegate) NodeMeta(limit int) []byte {
	return []byte{}
}

// NotifyMsg is the callback invoked when a user-level gossip message is received.
func (d *delegate) NotifyMsg(b []byte) {
	d.messagesReceived.WithLabelValues("update").Inc()
	d.messagesReceivedSize.WithLabelValues("update").Add(float64(len(b)))

	var p clusterpb.Part
	if err := proto.Unmarshal(b, &p); err != nil {
		level.Warn(d.logger).Log("msg", "decode broadcast", "err", err)
		return
	}

	s, ok := d.states[p.Key]
	if !ok {
		return
	}
	if err := s.Merge(p.Data); err != nil {
		level.Warn(d.logger).Log("msg", "merge broadcast", "err", err, "key", p.Key)
		return
	}
}

// GetBroadcasts is called when user data messages can be broadcasted.
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
	msgs := d.bcast.GetBroadcasts(overhead, limit)
	d.messagesSent.WithLabelValues("update").Add(float64(len(msgs)))
	for _, m := range msgs {
		d.messagesSentSize.WithLabelValues("update").Add(float64(len(m)))
	}
	return msgs
}

// LocalState is called when gossip fetches local state.
func (d *delegate) LocalState(_ bool) []byte {
	all := &clusterpb.FullState{
		Parts: make([]clusterpb.Part, 0, len(d.states)),
	}

	for key, s := range d.states {
		b, err := s.MarshalBinary()
		if err != nil {
			level.Warn(d.logger).Log("msg", "encode local state", "err", err, "key", key)
			return nil
		}
		all.Parts = append(all.Parts, clusterpb.Part{Key: key, Data: b})
	}
	b, err := proto.Marshal(all)
	if err != nil {
		level.Warn(d.logger).Log("msg", "encode local state", "err", err)
		return nil
	}
	d.messagesSent.WithLabelValues("full_state").Inc()
	d.messagesSentSize.WithLabelValues("full_state").Add(float64(len(b)))
	return b
}

func (d *delegate) MergeRemoteState(buf []byte, _ bool) {
	d.messagesReceived.WithLabelValues("full_state").Inc()
	d.messagesReceivedSize.WithLabelValues("full_state").Add(float64(len(buf)))

	var fs clusterpb.FullState
	if err := proto.Unmarshal(buf, &fs); err != nil {
		level.Warn(d.logger).Log("msg", "merge remote state", "err", err)
		return
	}
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	for _, p := range fs.Parts {
		s, ok := d.states[p.Key]
		if !ok {
			level.Warn(d.logger).Log("received", "unknown state key", "len", len(buf), "key", p.Key)
			continue
		}
		if err := s.Merge(p.Data); err != nil {
			level.Warn(d.logger).Log("msg", "merge remote state", "err", err, "key", p.Key)
			return
		}
	}
}

// NotifyJoin is called if a peer joins the cluster.
func (d *delegate) NotifyJoin(n *memberlist.Node) {
	level.Debug(d.logger).Log("received", "NotifyJoin", "node", n.Name, "addr", n.Address())
	d.Peer.peerJoin(n)
}

// NotifyLeave is called if a peer leaves the cluster.
func (d *delegate) NotifyLeave(n *memberlist.Node) {
	level.Debug(d.logger).Log("received", "NotifyLeave", "node", n.Name, "addr", n.Address())
	d.Peer.peerLeave(n)
}

// NotifyUpdate is called if a cluster peer gets updated.
func (d *delegate) NotifyUpdate(n *memberlist.Node) {
	level.Debug(d.logger).Log("received", "NotifyUpdate", "node", n.Name, "addr", n.Address())
	d.Peer.peerUpdate(n)
}

// handleQueueDepth ensures that the queue doesn't grow unbounded by pruning
// older messages at regular interval.
func (d *delegate) handleQueueDepth() {
	for {
		select {
		case <-d.stopc:
			return
		case <-time.After(15 * time.Minute):
			n := d.bcast.NumQueued()
			if n > maxQueueSize {
				level.Warn(d.logger).Log("msg", "dropping messages because too many are queued", "current", n, "limit", maxQueueSize)
				d.bcast.Prune(maxQueueSize)
				d.messagesPruned.Add(float64(n - maxQueueSize))
			}
		}
	}
}
<commit_msg>cluster/delegate: Replace labels to const to reduce hardcode (#1724)<commit_after>// Copyright 2018 Prometheus Team
// 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 cluster

import (
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/gogo/protobuf/proto"
	"github.com/hashicorp/memberlist"
	"github.com/prometheus/alertmanager/cluster/clusterpb"
	"github.com/prometheus/client_golang/prometheus"
)

const (
	// Maximum number of messages to be held in the queue.
	maxQueueSize = 4096
	fullState    = "full_state"
	update       = "update"
)

// delegate implements memberlist.Delegate and memberlist.EventDelegate
// and broadcasts its peer's state in the cluster.
type delegate struct {
	*Peer

	logger log.Logger
	bcast  *memberlist.TransmitLimitedQueue

	messagesReceived     *prometheus.CounterVec
	messagesReceivedSize *prometheus.CounterVec
	messagesSent         *prometheus.CounterVec
	messagesSentSize     *prometheus.CounterVec
	messagesPruned       prometheus.Counter
}

func newDelegate(l log.Logger, reg prometheus.Registerer, p *Peer, retransmit int) *delegate {
	bcast := &memberlist.TransmitLimitedQueue{
		NumNodes:       p.ClusterSize,
		RetransmitMult: retransmit,
	}
	messagesReceived := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_received_total",
		Help: "Total number of cluster messsages received.",
	}, []string{"msg_type"})
	messagesReceivedSize := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_received_size_total",
		Help: "Total size of cluster messages received.",
	}, []string{"msg_type"})
	messagesSent := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_sent_total",
		Help: "Total number of cluster messsages sent.",
	}, []string{"msg_type"})
	messagesSentSize := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_sent_size_total",
		Help: "Total size of cluster messages sent.",
	}, []string{"msg_type"})
	messagesPruned := prometheus.NewCounter(prometheus.CounterOpts{
		Name: "alertmanager_cluster_messages_pruned_total",
		Help: "Total number of cluster messsages pruned.",
	})
	gossipClusterMembers := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_cluster_members",
		Help: "Number indicating current number of members in cluster.",
	}, func() float64 {
		return float64(p.ClusterSize())
	})
	peerPosition := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_peer_position",
		Help: "Position the Alertmanager instance believes it's in. The position determines a peer's behavior in the cluster.",
	}, func() float64 {
		return float64(p.Position())
	})
	healthScore := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_cluster_health_score",
		Help: "Health score of the cluster. Lower values are better and zero means 'totally healthy'.",
	}, func() float64 {
		return float64(p.mlist.GetHealthScore())
	})
	messagesQueued := prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Name: "alertmanager_cluster_messages_queued",
		Help: "Number of cluster messsages which are queued.",
	}, func() float64 {
		return float64(bcast.NumQueued())
	})

	messagesReceived.WithLabelValues(fullState)
	messagesReceivedSize.WithLabelValues(fullState)
	messagesReceived.WithLabelValues(update)
	messagesReceivedSize.WithLabelValues(update)
	messagesSent.WithLabelValues(fullState)
	messagesSentSize.WithLabelValues(fullState)
	messagesSent.WithLabelValues(update)
	messagesSentSize.WithLabelValues(update)

	reg.MustRegister(messagesReceived, messagesReceivedSize, messagesSent, messagesSentSize,
		gossipClusterMembers, peerPosition, healthScore, messagesQueued, messagesPruned)

	d := &delegate{
		logger:               l,
		Peer:                 p,
		bcast:                bcast,
		messagesReceived:     messagesReceived,
		messagesReceivedSize: messagesReceivedSize,
		messagesSent:         messagesSent,
		messagesSentSize:     messagesSentSize,
		messagesPruned:       messagesPruned,
	}

	go d.handleQueueDepth()

	return d
}

// NodeMeta retrieves meta-data about the current node when broadcasting an alive message.
func (d *delegate) NodeMeta(limit int) []byte {
	return []byte{}
}

// NotifyMsg is the callback invoked when a user-level gossip message is received.
func (d *delegate) NotifyMsg(b []byte) {
	d.messagesReceived.WithLabelValues(update).Inc()
	d.messagesReceivedSize.WithLabelValues(update).Add(float64(len(b)))

	var p clusterpb.Part
	if err := proto.Unmarshal(b, &p); err != nil {
		level.Warn(d.logger).Log("msg", "decode broadcast", "err", err)
		return
	}

	s, ok := d.states[p.Key]
	if !ok {
		return
	}
	if err := s.Merge(p.Data); err != nil {
		level.Warn(d.logger).Log("msg", "merge broadcast", "err", err, "key", p.Key)
		return
	}
}

// GetBroadcasts is called when user data messages can be broadcasted.
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
	msgs := d.bcast.GetBroadcasts(overhead, limit)
	d.messagesSent.WithLabelValues(update).Add(float64(len(msgs)))
	for _, m := range msgs {
		d.messagesSentSize.WithLabelValues(update).Add(float64(len(m)))
	}
	return msgs
}

// LocalState is called when gossip fetches local state.
func (d *delegate) LocalState(_ bool) []byte {
	all := &clusterpb.FullState{
		Parts: make([]clusterpb.Part, 0, len(d.states)),
	}

	for key, s := range d.states {
		b, err := s.MarshalBinary()
		if err != nil {
			level.Warn(d.logger).Log("msg", "encode local state", "err", err, "key", key)
			return nil
		}
		all.Parts = append(all.Parts, clusterpb.Part{Key: key, Data: b})
	}
	b, err := proto.Marshal(all)
	if err != nil {
		level.Warn(d.logger).Log("msg", "encode local state", "err", err)
		return nil
	}
	d.messagesSent.WithLabelValues(fullState).Inc()
	d.messagesSentSize.WithLabelValues(fullState).Add(float64(len(b)))
	return b
}

func (d *delegate) MergeRemoteState(buf []byte, _ bool) {
	d.messagesReceived.WithLabelValues(fullState).Inc()
	d.messagesReceivedSize.WithLabelValues(fullState).Add(float64(len(buf)))

	var fs clusterpb.FullState
	if err := proto.Unmarshal(buf, &fs); err != nil {
		level.Warn(d.logger).Log("msg", "merge remote state", "err", err)
		return
	}
	d.mtx.RLock()
	defer d.mtx.RUnlock()
	for _, p := range fs.Parts {
		s, ok := d.states[p.Key]
		if !ok {
			level.Warn(d.logger).Log("received", "unknown state key", "len", len(buf), "key", p.Key)
			continue
		}
		if err := s.Merge(p.Data); err != nil {
			level.Warn(d.logger).Log("msg", "merge remote state", "err", err, "key", p.Key)
			return
		}
	}
}

// NotifyJoin is called if a peer joins the cluster.
func (d *delegate) NotifyJoin(n *memberlist.Node) {
	level.Debug(d.logger).Log("received", "NotifyJoin", "node", n.Name, "addr", n.Address())
	d.Peer.peerJoin(n)
}

// NotifyLeave is called if a peer leaves the cluster.
func (d *delegate) NotifyLeave(n *memberlist.Node) {
	level.Debug(d.logger).Log("received", "NotifyLeave", "node", n.Name, "addr", n.Address())
	d.Peer.peerLeave(n)
}

// NotifyUpdate is called if a cluster peer gets updated.
func (d *delegate) NotifyUpdate(n *memberlist.Node) {
	level.Debug(d.logger).Log("received", "NotifyUpdate", "node", n.Name, "addr", n.Address())
	d.Peer.peerUpdate(n)
}

// handleQueueDepth ensures that the queue doesn't grow unbounded by pruning
// older messages at regular interval.
func (d *delegate) handleQueueDepth() {
	for {
		select {
		case <-d.stopc:
			return
		case <-time.After(15 * time.Minute):
			n := d.bcast.NumQueued()
			if n > maxQueueSize {
				level.Warn(d.logger).Log("msg", "dropping messages because too many are queued", "current", n, "limit", maxQueueSize)
				d.bcast.Prune(maxQueueSize)
				d.messagesPruned.Add(float64(n - maxQueueSize))
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd 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 walking

import (
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"time"

	"github.com/containerd/containerd/archive"
	"github.com/containerd/containerd/archive/compression"
	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/diff"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	digest "github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

type walkingDiff struct {
	store content.Store
}

var emptyDesc = ocispec.Descriptor{}

// NewWalkingDiff is a generic implementation of diff.Comparer.  The diff is
// calculated by mounting both the upper and lower mount sets and walking the
// mounted directories concurrently. Changes are calculated by comparing files
// against each other or by comparing file existence between directories.
// NewWalkingDiff uses no special characteristics of the mount sets and is
// expected to work with any filesystem.
func NewWalkingDiff(store content.Store) diff.Comparer {
	return &walkingDiff{
		store: store,
	}
}

// Compare creates a diff between the given mounts and uploads the result
// to the content store.
func (s *walkingDiff) Compare(ctx context.Context, lower, upper []mount.Mount, opts ...diff.Opt) (d ocispec.Descriptor, err error) {
	var config diff.Config
	for _, opt := range opts {
		if err := opt(&config); err != nil {
			return emptyDesc, err
		}
	}

	if config.MediaType == "" {
		config.MediaType = ocispec.MediaTypeImageLayerGzip
	}

	var isCompressed bool
	switch config.MediaType {
	case ocispec.MediaTypeImageLayer:
	case ocispec.MediaTypeImageLayerGzip:
		isCompressed = true
	default:
		return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", config.MediaType)
	}

	var ocidesc ocispec.Descriptor
	if err := mount.WithTempMount(ctx, lower, func(lowerRoot string) error {
		return mount.WithTempMount(ctx, upper, func(upperRoot string) error {
			var newReference bool
			if config.Reference == "" {
				newReference = true
				config.Reference = uniqueRef()
			}

			cw, err := s.store.Writer(ctx,
				content.WithRef(config.Reference),
				content.WithDescriptor(ocispec.Descriptor{
					MediaType: config.MediaType, // most contentstore implementations just ignore this
				}))
			if err != nil {
				return errors.Wrap(err, "failed to open writer")
			}
			defer func() {
				if err != nil {
					cw.Close()
					if newReference {
						if err := s.store.Abort(ctx, config.Reference); err != nil {
							log.G(ctx).WithField("ref", config.Reference).Warnf("failed to delete diff upload")
						}
					}
				}
			}()
			if !newReference {
				if err := cw.Truncate(0); err != nil {
					return err
				}
			}

			if isCompressed {
				dgstr := digest.SHA256.Digester()
				compressed, err := compression.CompressStream(cw, compression.Gzip)
				if err != nil {
					return errors.Wrap(err, "failed to get compressed stream")
				}
				err = archive.WriteDiff(ctx, io.MultiWriter(compressed, dgstr.Hash()), lowerRoot, upperRoot)
				compressed.Close()
				if err != nil {
					return errors.Wrap(err, "failed to write compressed diff")
				}

				if config.Labels == nil {
					config.Labels = map[string]string{}
				}
				config.Labels["containerd.io/uncompressed"] = dgstr.Digest().String()
			} else {
				if err = archive.WriteDiff(ctx, cw, lowerRoot, upperRoot); err != nil {
					return errors.Wrap(err, "failed to write diff")
				}
			}

			var commitopts []content.Opt
			if config.Labels != nil {
				commitopts = append(commitopts, content.WithLabels(config.Labels))
			}

			dgst := cw.Digest()
			if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil {
				if !errdefs.IsAlreadyExists(err) {
					return errors.Wrap(err, "failed to commit")
				}
			}

			info, err := s.store.Info(ctx, dgst)
			if err != nil {
				return errors.Wrap(err, "failed to get info from content store")
			}

			ocidesc = ocispec.Descriptor{
				MediaType: config.MediaType,
				Size:      info.Size,
				Digest:    info.Digest,
			}
			return nil
		})
	}); err != nil {
		return emptyDesc, err
	}

	return ocidesc, nil
}

func uniqueRef() string {
	t := time.Now()
	var b [3]byte
	// Ignore read failures, just decreases uniqueness
	rand.Read(b[:])
	return fmt.Sprintf("%d-%s", t.UnixNano(), base64.URLEncoding.EncodeToString(b[:]))
}
<commit_msg>Set uncompressed label on diff when already exists<commit_after>/*
   Copyright The containerd 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 walking

import (
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"time"

	"github.com/containerd/containerd/archive"
	"github.com/containerd/containerd/archive/compression"
	"github.com/containerd/containerd/content"
	"github.com/containerd/containerd/diff"
	"github.com/containerd/containerd/errdefs"
	"github.com/containerd/containerd/log"
	"github.com/containerd/containerd/mount"
	digest "github.com/opencontainers/go-digest"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

type walkingDiff struct {
	store content.Store
}

var emptyDesc = ocispec.Descriptor{}
var uncompressed = "containerd.io/uncompressed"

// NewWalkingDiff is a generic implementation of diff.Comparer.  The diff is
// calculated by mounting both the upper and lower mount sets and walking the
// mounted directories concurrently. Changes are calculated by comparing files
// against each other or by comparing file existence between directories.
// NewWalkingDiff uses no special characteristics of the mount sets and is
// expected to work with any filesystem.
func NewWalkingDiff(store content.Store) diff.Comparer {
	return &walkingDiff{
		store: store,
	}
}

// Compare creates a diff between the given mounts and uploads the result
// to the content store.
func (s *walkingDiff) Compare(ctx context.Context, lower, upper []mount.Mount, opts ...diff.Opt) (d ocispec.Descriptor, err error) {
	var config diff.Config
	for _, opt := range opts {
		if err := opt(&config); err != nil {
			return emptyDesc, err
		}
	}

	if config.MediaType == "" {
		config.MediaType = ocispec.MediaTypeImageLayerGzip
	}

	var isCompressed bool
	switch config.MediaType {
	case ocispec.MediaTypeImageLayer:
	case ocispec.MediaTypeImageLayerGzip:
		isCompressed = true
	default:
		return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", config.MediaType)
	}

	var ocidesc ocispec.Descriptor
	if err := mount.WithTempMount(ctx, lower, func(lowerRoot string) error {
		return mount.WithTempMount(ctx, upper, func(upperRoot string) error {
			var newReference bool
			if config.Reference == "" {
				newReference = true
				config.Reference = uniqueRef()
			}

			cw, err := s.store.Writer(ctx,
				content.WithRef(config.Reference),
				content.WithDescriptor(ocispec.Descriptor{
					MediaType: config.MediaType, // most contentstore implementations just ignore this
				}))
			if err != nil {
				return errors.Wrap(err, "failed to open writer")
			}
			defer func() {
				if err != nil {
					cw.Close()
					if newReference {
						if err := s.store.Abort(ctx, config.Reference); err != nil {
							log.G(ctx).WithField("ref", config.Reference).Warnf("failed to delete diff upload")
						}
					}
				}
			}()
			if !newReference {
				if err := cw.Truncate(0); err != nil {
					return err
				}
			}

			if isCompressed {
				dgstr := digest.SHA256.Digester()
				compressed, err := compression.CompressStream(cw, compression.Gzip)
				if err != nil {
					return errors.Wrap(err, "failed to get compressed stream")
				}
				err = archive.WriteDiff(ctx, io.MultiWriter(compressed, dgstr.Hash()), lowerRoot, upperRoot)
				compressed.Close()
				if err != nil {
					return errors.Wrap(err, "failed to write compressed diff")
				}

				if config.Labels == nil {
					config.Labels = map[string]string{}
				}
				config.Labels[uncompressed] = dgstr.Digest().String()
			} else {
				if err = archive.WriteDiff(ctx, cw, lowerRoot, upperRoot); err != nil {
					return errors.Wrap(err, "failed to write diff")
				}
			}

			var commitopts []content.Opt
			if config.Labels != nil {
				commitopts = append(commitopts, content.WithLabels(config.Labels))
			}

			dgst := cw.Digest()
			if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil {
				if !errdefs.IsAlreadyExists(err) {
					return errors.Wrap(err, "failed to commit")
				}
			}

			info, err := s.store.Info(ctx, dgst)
			if err != nil {
				return errors.Wrap(err, "failed to get info from content store")
			}

			// Set uncompressed label if digest already existed without label
			if _, ok := info.Labels[uncompressed]; !ok {
				info.Labels[uncompressed] = config.Labels[uncompressed]
				if _, err := s.store.Update(ctx, info, "labels."+uncompressed); err != nil {
					return errors.Wrap(err, "error setting uncompressed label")
				}
			}

			ocidesc = ocispec.Descriptor{
				MediaType: config.MediaType,
				Size:      info.Size,
				Digest:    info.Digest,
			}
			return nil
		})
	}); err != nil {
		return emptyDesc, err
	}

	return ocidesc, nil
}

func uniqueRef() string {
	t := time.Now()
	var b [3]byte
	// Ignore read failures, just decreases uniqueness
	rand.Read(b[:])
	return fmt.Sprintf("%d-%s", t.UnixNano(), base64.URLEncoding.EncodeToString(b[:]))
}
<|endoftext|>


<commit_before>package cm

import "time"

// ApisResponse is the main struct for the RSS feed
type ApisResponse struct {
	Channel      Channel `json:"channel"`
	FaultCode    string  `json:"faultcode"`
	FaultMessage string  `json:"faultstring"`
}

// Channel is the container for items
type Channel struct {
	Title string `json:"title"`
	Items []Item `json:"item"`
}

// Item is the generic container
type Item struct {
	Title       string        `json:"title"`
	Description string        `json:"description"`
	Category    []ValueDomain `json:"category"`
	Guid        Guid          `json:"guid"`
	PubDate     string        `json:"pubDate"`
	// Note, the XML field tag notation does not work for JSON
	//EntityReferences []EntityReference `json:"EntityReferences.EntityReference"`
	EntityReferences struct {
		EntityReference []EntityReference
	}
	EntityReference EntityReference `json:"EntityReference"`
	ImageUrl        string          `json:"Image.Url"`
	// App
	Connections int
	Followers   int
	Rating      float32
	// User
	LastLogin     int
	Email         string
	UserName      string
	ApisCount     int
	AppsCount     int
	PostsCount    int
	CommentsCount int
	GroupsCount   int
	Domain        string
	//Endpoints     []Endpoint `json:"Endpoints.Endpoint"`
	Endpoints struct {
		Endpoint []Endpoint
	}
}

// ValueDomain is a key - value pair
type ValueDomain struct {
	Value  string `json:"value"`
	Domain string `json:"domain"`
}

// Guid is a guid string
type Guid struct {
	Value string `json:"value"`
}

// EntityReference is a reference to another entity
type EntityReference struct {
	Title    string
	Guid     string `json:"Guid"`
	Category ValueDomain
}

// Endpoint is a structure of an endpoint
type Endpoint struct {
	BindingQName         string
	BindingType          string
	CName                string
	Category             string
	ConnectionPorperties []ValueDomain
	DeploymentZoneRule   string
	//EndpointImplementationDetails DeploymentZoneEndpoint `json:"EndpointImplementationDetails.DeploymentZoneEndpoint"`
	EndpointImplementationDetails struct {
		DeploymentZoneEndpoint
	}
	EndpointKey        string
	ImplementationCode string
	URI                string `json:"Uri"`
}

// DeploymentZoneEndpoint contains information about a Deployment Zone Endpoint
type DeploymentZoneEndpoint struct {
	BindingQName     string
	BindingType      string
	ContainerKey     string
	DeploymentZoneID string
	EndpointHostname string
	EndpointKey      string
	EndpointPath     string
	GatewayHostName  string
	GatewayHostPath  string
	ListenerName     string
	Path             string
	Protocol         string
	Public           bool
	URL              string `json:"Url"`
}

// APICreatedResponse is the information that comes back from a successfully created API
type APICreatedResponse struct {
	APIID           string
	Name            string
	Description     string
	Visibility      string
	LatestVersionID string
	IsFollowed      bool
	RatingSummary   RatingSummary
	APIVersion      APIVersion
	AdminGroupID    string
	Created         string
	Updated         string
	AvatarURL       string
}

// RatingSummary holds a summary of ratings for an API
type RatingSummary struct {
	One   int
	Two   int
	Three int
	Four  int
	Five  int
}

// APIVersion contains information about a version of an API
type APIVersion struct {
	APIVersionID       string        `json:"APIVersionID"`
	APIID              string        `json:"APIID"`
	Name               string        `json:"Name"`
	Description        string        `json:"Description"`
	Tag                []interface{} `json:"Tag"`
	ProductionEndpoint string        `json:"ProductionEndpoint"`
	Endpoints          struct {
		Endpoint []struct {
			CName                string `json:"CName"`
			Category             string `json:"Category"`
			URI                  string `json:"Uri"`
			DeploymentZoneRule   string `json:"DeploymentZoneRule"`
			ConnectionProperties []struct {
				Name  string `json:"Name"`
				Value string `json:"Value"`
			} `json:"ConnectionProperties"`
			BindingQName                  string `json:"BindingQName"`
			BindingType                   string `json:"BindingType"`
			EndpointKey                   string `json:"EndpointKey"`
			EndpointImplementationDetails struct {
				DeploymentZoneEndpoint struct {
					DeploymentZoneID string `json:"DeploymentZoneID"`
					EndpointKey      string `json:"EndpointKey"`
					ListenerName     string `json:"ListenerName"`
					ContainerKey     string `json:"ContainerKey"`
					GatewayHostName  string `json:"GatewayHostName"`
					GatewayHostPath  string `json:"GatewayHostPath"`
					EndpointHostName string `json:"EndpointHostName"`
					EndpointPath     string `json:"EndpointPath"`
					Protocol         string `json:"Protocol"`
					Path             string `json:"Path"`
					URL              string `json:"Url"`
					BindingQName     string `json:"BindingQName"`
					BindingType      string `json:"BindingType"`
					Public           bool   `json:"Public"`
				} `json:"DeploymentZoneEndpoint"`
			} `json:"EndpointImplementationDetails"`
			ImplementationCode string `json:"ImplementationCode"`
		} `json:"Endpoint"`
	} `json:"Endpoints"`
	Visibility                           string    `json:"Visibility"`
	Created                              time.Time `json:"Created"`
	Updated                              time.Time `json:"Updated"`
	State                                string    `json:"State"`
	ProductionEndpointAccessAutoApproved bool      `json:"ProductionEndpointAccessAutoApproved"`
	SandboxEndpointAccessAutoApproved    bool      `json:"SandboxEndpointAccessAutoApproved"`
	RatingSummary                        RatingSummary
	SandboxAnonymousAccessAllowed        bool   `json:"SandboxAnonymousAccessAllowed"`
	ProductionAnonymousAccessAllowed     bool   `json:"ProductionAnonymousAccessAllowed"`
	ResourceLevelPermissionsSupported    bool   `json:"ResourceLevelPermissionsSupported"`
	APIOwnedImplementations              bool   `json:"APIOwnedImplementations"`
	ProductionServiceKey                 string `json:"ProductionServiceKey"`
	APIDesign                            struct {
		CommonDesign bool
	}
}

// APIDetails is the response to an API details request
type APIDetails struct {
	APIID           string `json:"APIID"`
	Name            string `json:"Name"`
	Description     string `json:"Description"`
	Visibility      string `json:"Visibility"`
	LatestVersionID string `json:"LatestVersionID"`
	IsFollowed      bool   `json:"IsFollowed"`
	RatingSummary   RatingSummary
	APIVersion      APIVersion
	AdminGroupID    string    `json:"AdminGroupID"`
	Created         time.Time `json:"Created"`
	Updated         time.Time `json:"Updated"`
	AvatarURL       string    `json:"AvatarURL"`
}
<commit_msg>Fault struct<commit_after>package cm

import "time"

// ApisResponse is the main struct for the RSS feed
type ApisResponse struct {
	Channel      Channel `json:"channel"`
	FaultCode    string  `json:"faultcode"`
	FaultMessage string  `json:"faultstring"`
}

// Channel is the container for items
type Channel struct {
	Title string `json:"title"`
	Items []Item `json:"item"`
}

// Item is the generic container
type Item struct {
	Title       string        `json:"title"`
	Description string        `json:"description"`
	Category    []ValueDomain `json:"category"`
	Guid        Guid          `json:"guid"`
	PubDate     string        `json:"pubDate"`
	// Note, the XML field tag notation does not work for JSON
	//EntityReferences []EntityReference `json:"EntityReferences.EntityReference"`
	EntityReferences struct {
		EntityReference []EntityReference
	}
	EntityReference EntityReference `json:"EntityReference"`
	ImageUrl        string          `json:"Image.Url"`
	// App
	Connections int
	Followers   int
	Rating      float32
	// User
	LastLogin     int
	Email         string
	UserName      string
	ApisCount     int
	AppsCount     int
	PostsCount    int
	CommentsCount int
	GroupsCount   int
	Domain        string
	//Endpoints     []Endpoint `json:"Endpoints.Endpoint"`
	Endpoints struct {
		Endpoint []Endpoint
	}
}

// ValueDomain is a key - value pair
type ValueDomain struct {
	Value  string `json:"value"`
	Domain string `json:"domain"`
}

// Guid is a guid string
type Guid struct {
	Value string `json:"value"`
}

// EntityReference is a reference to another entity
type EntityReference struct {
	Title    string
	Guid     string `json:"Guid"`
	Category ValueDomain
}

// Endpoint is a structure of an endpoint
type Endpoint struct {
	BindingQName         string
	BindingType          string
	CName                string
	Category             string
	ConnectionPorperties []ValueDomain
	DeploymentZoneRule   string
	//EndpointImplementationDetails DeploymentZoneEndpoint `json:"EndpointImplementationDetails.DeploymentZoneEndpoint"`
	EndpointImplementationDetails struct {
		DeploymentZoneEndpoint
	}
	EndpointKey        string
	ImplementationCode string
	URI                string `json:"Uri"`
}

// DeploymentZoneEndpoint contains information about a Deployment Zone Endpoint
type DeploymentZoneEndpoint struct {
	BindingQName     string
	BindingType      string
	ContainerKey     string
	DeploymentZoneID string
	EndpointHostname string
	EndpointKey      string
	EndpointPath     string
	GatewayHostName  string
	GatewayHostPath  string
	ListenerName     string
	Path             string
	Protocol         string
	Public           bool
	URL              string `json:"Url"`
}

// APICreatedResponse is the information that comes back from a successfully created API
type APICreatedResponse struct {
	APIID           string
	Name            string
	Description     string
	Visibility      string
	LatestVersionID string
	IsFollowed      bool
	RatingSummary   RatingSummary
	APIVersion      APIVersion
	AdminGroupID    string
	Created         string
	Updated         string
	AvatarURL       string
}

// RatingSummary holds a summary of ratings for an API
type RatingSummary struct {
	One   int
	Two   int
	Three int
	Four  int
	Five  int
}

// APIVersion contains information about a version of an API
type APIVersion struct {
	APIVersionID       string        `json:"APIVersionID"`
	APIID              string        `json:"APIID"`
	Name               string        `json:"Name"`
	Description        string        `json:"Description"`
	Tag                []interface{} `json:"Tag"`
	ProductionEndpoint string        `json:"ProductionEndpoint"`
	Endpoints          struct {
		Endpoint []struct {
			CName                string `json:"CName"`
			Category             string `json:"Category"`
			URI                  string `json:"Uri"`
			DeploymentZoneRule   string `json:"DeploymentZoneRule"`
			ConnectionProperties []struct {
				Name  string `json:"Name"`
				Value string `json:"Value"`
			} `json:"ConnectionProperties"`
			BindingQName                  string `json:"BindingQName"`
			BindingType                   string `json:"BindingType"`
			EndpointKey                   string `json:"EndpointKey"`
			EndpointImplementationDetails struct {
				DeploymentZoneEndpoint struct {
					DeploymentZoneID string `json:"DeploymentZoneID"`
					EndpointKey      string `json:"EndpointKey"`
					ListenerName     string `json:"ListenerName"`
					ContainerKey     string `json:"ContainerKey"`
					GatewayHostName  string `json:"GatewayHostName"`
					GatewayHostPath  string `json:"GatewayHostPath"`
					EndpointHostName string `json:"EndpointHostName"`
					EndpointPath     string `json:"EndpointPath"`
					Protocol         string `json:"Protocol"`
					Path             string `json:"Path"`
					URL              string `json:"Url"`
					BindingQName     string `json:"BindingQName"`
					BindingType      string `json:"BindingType"`
					Public           bool   `json:"Public"`
				} `json:"DeploymentZoneEndpoint"`
			} `json:"EndpointImplementationDetails"`
			ImplementationCode string `json:"ImplementationCode"`
		} `json:"Endpoint"`
	} `json:"Endpoints"`
	Visibility                           string    `json:"Visibility"`
	Created                              time.Time `json:"Created"`
	Updated                              time.Time `json:"Updated"`
	State                                string    `json:"State"`
	ProductionEndpointAccessAutoApproved bool      `json:"ProductionEndpointAccessAutoApproved"`
	SandboxEndpointAccessAutoApproved    bool      `json:"SandboxEndpointAccessAutoApproved"`
	RatingSummary                        RatingSummary
	SandboxAnonymousAccessAllowed        bool   `json:"SandboxAnonymousAccessAllowed"`
	ProductionAnonymousAccessAllowed     bool   `json:"ProductionAnonymousAccessAllowed"`
	ResourceLevelPermissionsSupported    bool   `json:"ResourceLevelPermissionsSupported"`
	APIOwnedImplementations              bool   `json:"APIOwnedImplementations"`
	ProductionServiceKey                 string `json:"ProductionServiceKey"`
	APIDesign                            struct {
		CommonDesign bool
	}
}

// APIDetails is the response to an API details request
type APIDetails struct {
	APIID           string `json:"APIID"`
	Name            string `json:"Name"`
	Description     string `json:"Description"`
	Visibility      string `json:"Visibility"`
	LatestVersionID string `json:"LatestVersionID"`
	IsFollowed      bool   `json:"IsFollowed"`
	RatingSummary   RatingSummary
	APIVersion      APIVersion
	AdminGroupID    string    `json:"AdminGroupID"`
	Created         time.Time `json:"Created"`
	Updated         time.Time `json:"Updated"`
	AvatarURL       string    `json:"AvatarURL"`
}

//{"faultcode":"server", "faultstring":"The API definition could not be read: [Service Definition parser not found.]"}

// Fault represents a fault error message from the API
type Fault struct {
	Code string `json:"faultcode"`
	Message string `json:"faultstring"`
}<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 cmac

import (
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestSubkey(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type SubkeyTest struct{}

func init() { RegisterTestSuite(&SubkeyTest{}) }

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

func (t *SubkeyTest) NilKey() {
	ExpectEq("TODO", "")
}

func (t *SubkeyTest) KeyTooShort() {
	ExpectEq("TODO", "")
}

func (t *SubkeyTest) KeyTooLong() {
	ExpectEq("TODO", "")
}

func (t *SubkeyTest) Rfc4493GoldenTestCase() {
	ExpectEq("TODO", "")
}
<commit_msg>SubkeyTest.KeyTooLong<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 cmac

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestSubkey(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type SubkeyTest struct{}

func init() { RegisterTestSuite(&SubkeyTest{}) }

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

func (t *SubkeyTest) NilKey() {
	f := func() { generateSubkey(nil) }
	ExpectThat(f, Panics(HasSubstr("16 bytes")))
}

func (t *SubkeyTest) KeyTooShort() {
	key := make([]byte, 15)
	f := func() { generateSubkey(key) }
	ExpectThat(f, Panics(HasSubstr("16 bytes")))
}

func (t *SubkeyTest) KeyTooLong() {
	key := make([]byte, 17)
	f := func() { generateSubkey(key) }
	ExpectThat(f, Panics(HasSubstr("16 bytes")))
}

func (t *SubkeyTest) Rfc4493GoldenTestCase() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"math/rand"
	"os"
	"os/signal"
	"strings"
	"time"

	"github.com/iopred/bruxism"
	"github.com/iopred/bruxism/directmessageinviteplugin"
	"github.com/iopred/bruxism/emojiplugin"
	"github.com/iopred/bruxism/inviteplugin"
	"github.com/iopred/bruxism/numbertriviaplugin"
	"github.com/iopred/bruxism/playingplugin"
	"github.com/iopred/bruxism/reminderplugin"
	"github.com/iopred/bruxism/slowmodeplugin"
	"github.com/iopred/bruxism/statsplugin"
	"github.com/iopred/bruxism/streamerplugin"
	"github.com/iopred/bruxism/topstreamersplugin"
)

var youtubeURL bool
var youtubeAuth string
var youtubeConfigFilename string
var youtubeTokenFilename string
var youtubeLiveVideoIDs string
var youtubeLiveChatIDs string
var discordToken string
var discordEmail string
var discordPassword string
var discordApplicationClientID string
var discordOwnerUserID string
var ircServer string
var ircUsername string
var ircPassword string
var ircChannels string
var imgurID string
var imgurAlbum string
var mashableKey string

func init() {
	flag.BoolVar(&youtubeURL, "youtubeurl", false, "Generates a URL that provides an auth code.")
	flag.StringVar(&youtubeAuth, "youtubeauth", "", "Exchanges the provided auth code for an oauth2 token.")
	flag.StringVar(&youtubeConfigFilename, "youtubeconfig", "youtubeoauth2config.json", "The filename that contains the oauth2 config.")
	flag.StringVar(&youtubeTokenFilename, "youtubetoken", "youtubeoauth2token.json", "The filename to store the oauth2 token.")
	flag.StringVar(&youtubeLiveVideoIDs, "youtubelivevideoids", "", "Comma separated list of video id's to poll.")
	flag.StringVar(&youtubeLiveChatIDs, "youtubelivechatids", "", "Comma separated list of chat id's to poll.")
	flag.StringVar(&discordToken, "discordtoken", "", "Discord token.")
	flag.StringVar(&discordEmail, "discordemail", "", "Discord account email.")
	flag.StringVar(&discordPassword, "discordpassword", "", "Discord account password.")
	flag.StringVar(&discordOwnerUserID, "discordowneruserid", "", "Discord owner user id.")
	flag.StringVar(&discordApplicationClientID, "discordapplicationclientid", "", "Discord application client id.")
	flag.StringVar(&ircServer, "ircserver", "", "IRC server.")
	flag.StringVar(&ircUsername, "ircusername", "", "IRC user name.")
	flag.StringVar(&ircPassword, "ircpassword", "", "IRC password.")
	flag.StringVar(&ircChannels, "ircchannels", "", "Comma separated list of IRC channels.")
	flag.StringVar(&imgurID, "imgurid", "", "Imgur client id.")
	flag.StringVar(&imgurAlbum, "imguralbum", "", "Imgur album id.")
	flag.StringVar(&mashableKey, "mashablekey", "", "Mashable key.")
	flag.Parse()

	rand.Seed(time.Now().UnixNano())
}

func main() {
	// Set our variables.
	bot := bruxism.NewBot()
	bot.ImgurID = imgurID
	bot.ImgurAlbum = imgurAlbum
	bot.MashableKey = mashableKey

	// Generally CommandPlugins don't hold state, so we share one instance of the command plugin for all services.
	cp := bruxism.NewCommandPlugin()
	cp.AddCommand("help", bruxism.HelpCommand, bruxism.HelpHelp)
	cp.AddCommand("command", bruxism.HelpCommand, nil)
	cp.AddCommand("commands", bruxism.HelpCommand, nil)
	cp.AddCommand("invite", inviteplugin.InviteCommand, inviteplugin.InviteHelp)
	cp.AddCommand("join", inviteplugin.InviteCommand, nil)
	cp.AddCommand("stats", statsplugin.StatsCommand, statsplugin.StatsHelp)
	cp.AddCommand("info", statsplugin.StatsCommand, nil)
	cp.AddCommand("stat", statsplugin.StatsCommand, nil)
	if bot.MashableKey != "" {
		cp.AddCommand("numbertrivia", numbertriviaplugin.NumberTriviaCommand, numbertriviaplugin.NumberTriviaHelp)
	}

	youtube := bruxism.NewYouTube(youtubeURL, youtubeAuth, youtubeConfigFilename, youtubeTokenFilename, youtubeLiveVideoIDs, youtubeLiveChatIDs)
	bot.RegisterService(youtube)

	bot.RegisterPlugin(youtube, cp)
	bot.RegisterPlugin(youtube, slowmodeplugin.NewSlowModePlugin())
	bot.RegisterPlugin(youtube, topstreamersplugin.NewTopStreamersPlugin(youtube))
	bot.RegisterPlugin(youtube, streamerplugin.NewStreamerPlugin(youtube))
	bot.RegisterPlugin(youtube, reminderplugin.NewReminderPlugin())

	// Register the Discord service if we have an email or token.
	if (discordEmail != "" && discordPassword != "") || discordToken != "" {
		var discord *bruxism.Discord
		if discordToken != "" {
			discord = bruxism.NewDiscord(discordToken)
		} else {
			discord = bruxism.NewDiscord(discordEmail, discordPassword)
		}
		discord.ApplicationClientID = discordApplicationClientID
		discord.OwnerUserID = discordOwnerUserID
		bot.RegisterService(discord)

		bot.RegisterPlugin(discord, cp)
		bot.RegisterPlugin(discord, topstreamersplugin.NewTopStreamersPlugin(youtube))
		bot.RegisterPlugin(discord, streamerplugin.NewStreamerPlugin(youtube))
		bot.RegisterPlugin(discord, playingplugin.NewPlayingPlugin())
		bot.RegisterPlugin(discord, directmessageinviteplugin.NewDirectMessageInvitePlugin())
		bot.RegisterPlugin(discord, reminderplugin.NewReminderPlugin())
		bot.RegisterPlugin(discord, emojiplugin.NewEmojiPlugin())
	}

	// Register the IRC service if we have an IRC server and Username.
	if ircServer != "" && ircUsername != "" {
		irc := bruxism.NewIRC(ircServer, ircUsername, ircPassword, strings.Split(ircChannels, ","))
		bot.RegisterService(irc)

		bot.RegisterPlugin(irc, cp)
		bot.RegisterPlugin(irc, topstreamersplugin.NewTopStreamersPlugin(youtube))
		bot.RegisterPlugin(irc, streamerplugin.NewStreamerPlugin(youtube))
		bot.RegisterPlugin(irc, reminderplugin.NewReminderPlugin())
	}

	// Start all our services.
	bot.Open()

	// Wait for a termination signal, while saving the bot state every minute. Save on close.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	t := time.Tick(1 * time.Minute)

	for {
		select {
		case <-c:
			bot.Save()
			return
		case <-t:
			bot.Save()
		}
	}
}
<commit_msg>Update main command.<commit_after>package main

import (
	"flag"
	"math/rand"
	"os"
	"os/signal"
	"strings"
	"time"

	"github.com/iopred/bruxism"
	"github.com/iopred/bruxism/directmessageinviteplugin"
	"github.com/iopred/bruxism/discordavatarplugin"
	"github.com/iopred/bruxism/emojiplugin"
	"github.com/iopred/bruxism/inviteplugin"
	"github.com/iopred/bruxism/numbertriviaplugin"
	"github.com/iopred/bruxism/playingplugin"
	"github.com/iopred/bruxism/reminderplugin"
	"github.com/iopred/bruxism/slowmodeplugin"
	"github.com/iopred/bruxism/statsplugin"
	"github.com/iopred/bruxism/streamerplugin"
	"github.com/iopred/bruxism/topstreamersplugin"
)

var youtubeURL bool
var youtubeAuth string
var youtubeConfigFilename string
var youtubeTokenFilename string
var youtubeLiveVideoIDs string
var youtubeLiveChatIDs string
var discordToken string
var discordEmail string
var discordPassword string
var discordApplicationClientID string
var discordOwnerUserID string
var ircServer string
var ircUsername string
var ircPassword string
var ircChannels string
var imgurID string
var imgurAlbum string
var mashableKey string

func init() {
	flag.BoolVar(&youtubeURL, "youtubeurl", false, "Generates a URL that provides an auth code.")
	flag.StringVar(&youtubeAuth, "youtubeauth", "", "Exchanges the provided auth code for an oauth2 token.")
	flag.StringVar(&youtubeConfigFilename, "youtubeconfig", "youtubeoauth2config.json", "The filename that contains the oauth2 config.")
	flag.StringVar(&youtubeTokenFilename, "youtubetoken", "youtubeoauth2token.json", "The filename to store the oauth2 token.")
	flag.StringVar(&youtubeLiveVideoIDs, "youtubelivevideoids", "", "Comma separated list of video id's to poll.")
	flag.StringVar(&youtubeLiveChatIDs, "youtubelivechatids", "", "Comma separated list of chat id's to poll.")
	flag.StringVar(&discordToken, "discordtoken", "", "Discord token.")
	flag.StringVar(&discordEmail, "discordemail", "", "Discord account email.")
	flag.StringVar(&discordPassword, "discordpassword", "", "Discord account password.")
	flag.StringVar(&discordOwnerUserID, "discordowneruserid", "", "Discord owner user id.")
	flag.StringVar(&discordApplicationClientID, "discordapplicationclientid", "", "Discord application client id.")
	flag.StringVar(&ircServer, "ircserver", "", "IRC server.")
	flag.StringVar(&ircUsername, "ircusername", "", "IRC user name.")
	flag.StringVar(&ircPassword, "ircpassword", "", "IRC password.")
	flag.StringVar(&ircChannels, "ircchannels", "", "Comma separated list of IRC channels.")
	flag.StringVar(&imgurID, "imgurid", "", "Imgur client id.")
	flag.StringVar(&imgurAlbum, "imguralbum", "", "Imgur album id.")
	flag.StringVar(&mashableKey, "mashablekey", "", "Mashable key.")
	flag.Parse()

	rand.Seed(time.Now().UnixNano())
}

func main() {
	// Set our variables.
	bot := bruxism.NewBot()
	bot.ImgurID = imgurID
	bot.ImgurAlbum = imgurAlbum
	bot.MashableKey = mashableKey

	// Generally CommandPlugins don't hold state, so we share one instance of the command plugin for all services.
	cp := bruxism.NewCommandPlugin()
	cp.AddCommand("help", bruxism.HelpCommand, bruxism.HelpHelp)
	cp.AddCommand("command", bruxism.HelpCommand, nil)
	cp.AddCommand("commands", bruxism.HelpCommand, nil)
	cp.AddCommand("invite", inviteplugin.InviteCommand, inviteplugin.InviteHelp)
	cp.AddCommand("join", inviteplugin.InviteCommand, nil)
	cp.AddCommand("stats", statsplugin.StatsCommand, statsplugin.StatsHelp)
	cp.AddCommand("info", statsplugin.StatsCommand, nil)
	cp.AddCommand("stat", statsplugin.StatsCommand, nil)
	if bot.MashableKey != "" {
		cp.AddCommand("numbertrivia", numbertriviaplugin.NumberTriviaCommand, numbertriviaplugin.NumberTriviaHelp)
	}

	youtube := bruxism.NewYouTube(youtubeURL, youtubeAuth, youtubeConfigFilename, youtubeTokenFilename, youtubeLiveVideoIDs, youtubeLiveChatIDs)
	bot.RegisterService(youtube)

	bot.RegisterPlugin(youtube, cp)
	bot.RegisterPlugin(youtube, slowmodeplugin.New())
	bot.RegisterPlugin(youtube, topstreamersplugin.New(youtube))
	bot.RegisterPlugin(youtube, streamerplugin.New(youtube))
	bot.RegisterPlugin(youtube, reminderplugin.New())

	// Register the Discord service if we have an email or token.
	if (discordEmail != "" && discordPassword != "") || discordToken != "" {
		var discord *bruxism.Discord
		if discordToken != "" {
			discord = bruxism.NewDiscord(discordToken)
		} else {
			discord = bruxism.NewDiscord(discordEmail, discordPassword)
		}
		discord.ApplicationClientID = discordApplicationClientID
		discord.OwnerUserID = discordOwnerUserID
		bot.RegisterService(discord)

		bot.RegisterPlugin(discord, cp)
		bot.RegisterPlugin(discord, topstreamersplugin.New(youtube))
		bot.RegisterPlugin(discord, streamerplugin.New(youtube))
		bot.RegisterPlugin(discord, playingplugin.New())
		bot.RegisterPlugin(discord, directmessageinviteplugin.New())
		bot.RegisterPlugin(discord, reminderplugin.New())
		bot.RegisterPlugin(discord, emojiplugin.New())
		bot.RegisterPlugin(discord, discordavatarplugin.New())
	}

	// Register the IRC service if we have an IRC server and Username.
	if ircServer != "" && ircUsername != "" {
		irc := bruxism.NewIRC(ircServer, ircUsername, ircPassword, strings.Split(ircChannels, ","))
		bot.RegisterService(irc)

		bot.RegisterPlugin(irc, cp)
		bot.RegisterPlugin(irc, topstreamersplugin.New(youtube))
		bot.RegisterPlugin(irc, streamerplugin.New(youtube))
		bot.RegisterPlugin(irc, reminderplugin.New())
	}

	// Start all our services.
	bot.Open()

	// Wait for a termination signal, while saving the bot state every minute. Save on close.
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	t := time.Tick(1 * time.Minute)

	for {
		select {
		case <-c:
			bot.Save()
			return
		case <-t:
			bot.Save()
		}
	}
}
<|endoftext|>


<commit_before>// Package cmount implents a FUSE mounting system for rclone remotes.
//
// This uses the cgo based cgofuse library

// +build cgo
// +build linux darwin freebsd windows

package cmount

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"github.com/billziss-gh/cgofuse/fuse"
	"github.com/ncw/rclone/cmd"
	"github.com/ncw/rclone/cmd/mountlib"
	"github.com/ncw/rclone/fs"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

// Globals
var (
	noModTime    = false
	noChecksum   = false
	debugFUSE    = false
	noSeek       = false
	dirCacheTime = 5 * 60 * time.Second
	// mount options
	readOnly                         = false
	allowNonEmpty                    = false
	allowRoot                        = false
	allowOther                       = false
	defaultPermissions               = false
	writebackCache                   = false
	maxReadAhead       fs.SizeSuffix = 128 * 1024
	umask                            = 0
	uid                              = ^uint32(0) // these values instruct WinFSP-FUSE to use the current user
	gid                              = ^uint32(0) // overriden for non windows in mount_unix.go
	// foreground                 = false
	// default permissions for directories - modified by umask in Mount
	dirPerms  = os.FileMode(0777)
	filePerms = os.FileMode(0666)
)

func init() {
	cmd.Root.AddCommand(commandDefintion)
	commandDefintion.Flags().BoolVarP(&noModTime, "no-modtime", "", noModTime, "Don't read/write the modification time (can speed things up).")
	commandDefintion.Flags().BoolVarP(&noChecksum, "no-checksum", "", noChecksum, "Don't compare checksums on up/download.")
	commandDefintion.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
	commandDefintion.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
	commandDefintion.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
	// mount options
	commandDefintion.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
	commandDefintion.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")
	commandDefintion.Flags().BoolVarP(&allowRoot, "allow-root", "", allowRoot, "Allow access to root user.")
	commandDefintion.Flags().BoolVarP(&allowOther, "allow-other", "", allowOther, "Allow access to other users.")
	commandDefintion.Flags().BoolVarP(&defaultPermissions, "default-permissions", "", defaultPermissions, "Makes kernel enforce access control based on the file mode.")
	commandDefintion.Flags().BoolVarP(&writebackCache, "write-back-cache", "", writebackCache, "Makes kernel buffer writes before sending them to rclone. Without this, writethrough caching is used.")
	commandDefintion.Flags().VarP(&maxReadAhead, "max-read-ahead", "", "The number of bytes that can be prefetched for sequential reads.")
	commandDefintion.Flags().IntVarP(&umask, "umask", "", umask, "Override the permission bits set by the filesystem.")
	//commandDefintion.Flags().BoolVarP(&foreground, "foreground", "", foreground, "Do not detach.")
}

var commandDefintion = &cobra.Command{
	Use:   commandName + " remote:path /path/to/mountpoint",
	Short: `Mount the remote as a mountpoint. **EXPERIMENTAL**`,
	Long: `
rclone ` + commandName + ` allows Linux, FreeBSD, macOS and Windows to
mount any of Rclone's cloud storage systems as a file system with
FUSE.

This is **EXPERIMENTAL** - use with care.

First set up your remote using ` + "`rclone config`" + `.  Check it works with ` + "`rclone ls`" + ` etc.

Start the mount like this

    rclone ` + commandName + ` remote:path/to/files /path/to/local/mount

Or on Windows like this where X: is an unused drive letter

    rclone ` + commandName + ` remote:path/to/files X:

When the program ends, either via Ctrl+C or receiving a SIGINT or SIGTERM signal,
the mount is automatically stopped.

The umount operation can fail, for example when the mountpoint is busy.
When that happens, it is the user's responsibility to stop the mount manually with

    # Linux
    fusermount -u /path/to/local/mount
    # OS X
    umount /path/to/local/mount

### Limitations ###

This can only write files seqentially, it can only seek when reading.
This means that many applications won't work with their files on an
rclone mount.

The bucket based remotes (eg Swift, S3, Google Compute Storage, B2,
Hubic) won't work from the root - you will need to specify a bucket,
or a path within the bucket.  So ` + "`swift:`" + ` won't work whereas
` + "`swift:bucket`" + ` will as will ` + "`swift:bucket/path`" + `.
None of these support the concept of directories, so empty
directories will have a tendency to disappear once they fall out of
the directory cache.

Only supported on Linux, FreeBSD, OS X and Windows at the moment.

### rclone ` + commandName + ` vs rclone sync/copy ##

File systems expect things to be 100% reliable, whereas cloud storage
systems are a long way from 100% reliable. The rclone sync/copy
commands cope with this with lots of retries.  However rclone ` + commandName + `
can't use retries in the same way without making local copies of the
uploads.  This might happen in the future, but for the moment rclone
` + commandName + ` won't do that, so will be less reliable than the rclone command.

### Filters ###

Note that all the rclone filters can be used to select a subset of the
files to be visible in the mount.

### Directory Cache ###

Using the ` + "`--dir-cache-time`" + ` flag, you can set how long a
directory should be considered up to date and not refreshed from the
backend. Changes made locally in the mount may appear immediately or
invalidate the cache. However, changes done on the remote will only
be picked up once the cache expires.

Alternatively, you can send a ` + "`SIGHUP`" + ` signal to rclone for
it to flush all directory caches, regardless of how old they are.
Assuming only one rclone instance is running, you can reset the cache
like this:

    kill -SIGHUP $(pidof rclone)

### Bugs ###

  * All the remotes should work for read, but some may not for write
    * those which need to know the size in advance won't - eg B2
    * maybe should pass in size as -1 to mean work it out
    * Or put in an an upload cache to cache the files on disk first
`,
	Run: func(command *cobra.Command, args []string) {
		cmd.CheckArgs(2, 2, command, args)
		fdst := cmd.NewFsDst(args)
		err := Mount(fdst, args[1])
		if err != nil {
			log.Fatalf("Fatal error: %v", err)
		}
	},
}

// mountOptions configures the options from the command line flags
func mountOptions(device string, mountpoint string) (options []string) {
	// Options
	options = []string{
		"-o", "fsname=" + device,
		"-o", "subtype=rclone",
		"-o", fmt.Sprintf("max_readahead=%d", maxReadAhead),
	}
	if debugFUSE {
		options = append(options, "-o", "debug")
	}

	// OSX options
	if runtime.GOOS == "darwin" {
		options = append(options, "-o", "volname="+device)
		options = append(options, "-o", "noappledouble")
		options = append(options, "-o", "noapplexattr")
	}

	if allowNonEmpty {
		options = append(options, "-o", "nonempty")
	}
	if allowOther {
		options = append(options, "-o", "allow_other")
	}
	if allowRoot {
		options = append(options, "-o", "allow_root")
	}
	if defaultPermissions {
		options = append(options, "-o", "default_permissions")
	}
	if readOnly {
		options = append(options, "-o", "ro")
	}
	if writebackCache {
		// FIXME? options = append(options, "-o", WritebackCache())
	}
	return options
}

// mount the file system
//
// The mount point will be ready when this returns.
//
// returns an error, and an error channel for the serve process to
// report an error when fusermount is called.
func mount(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error, error) {
	fs.Debugf(f, "Mounting on %q", mountpoint)

	// Check the mountpoint
	if runtime.GOOS != "windows" {
		fi, err := os.Stat(mountpoint)
		if err != nil {
			return nil, nil, nil, errors.Wrap(err, "mountpoint")
		}
		if !fi.IsDir() {
			return nil, nil, nil, errors.New("mountpoint is not a directory")
		}
	}

	// Create underlying FS
	fsys := NewFS(f)
	host := fuse.NewFileSystemHost(fsys)

	// Create options
	options := mountOptions(f.Name()+":"+f.Root(), mountpoint)
	fs.Debugf(f, "Mounting with options: %q", options)

	// Serve the mount point in the background returning error to errChan
	errChan := make(chan error, 1)
	go func() {
		var err error
		ok := host.Mount(mountpoint, options)
		if !ok {
			err = errors.New("mount failed")
			fs.Errorf(f, "Mount failed")
		}
		errChan <- err
	}()

	// unmount
	unmount := func() error {
		fs.Debugf(nil, "Calling host.Unmount")
		if host.Unmount() {
			fs.Debugf(nil, "host.Unmount succeeded")
			return nil
		}
		fs.Debugf(nil, "host.Unmount failed")
		return errors.New("host unmount failed")
	}

	// Wait for the filesystem to become ready
	<-fsys.ready
	return fsys.FS, errChan, unmount, nil
}

// Mount mounts the remote at mountpoint.
//
// If noModTime is set then it
func Mount(f fs.Fs, mountpoint string) error {
	// Set permissions
	dirPerms = 0777 &^ os.FileMode(umask)
	filePerms = 0666 &^ os.FileMode(umask)

	// Show stats if the user has specifically requested them
	if cmd.ShowStats() {
		stopStats := cmd.StartStats()
		defer close(stopStats)
	}

	// Mount it
	FS, errChan, _, err := mount(f, mountpoint)
	if err != nil {
		return errors.Wrap(err, "failed to mount FUSE fs")
	}

	// Note cgofuse unmounts the fs on SIGINT etc

	sigHup := make(chan os.Signal, 1)
	signal.Notify(sigHup, syscall.SIGHUP)

waitloop:
	for {
		select {
		// umount triggered outside the app
		case err = <-errChan:
			break waitloop
		// user sent SIGHUP to clear the cache
		case <-sigHup:
			root, err := FS.Root()
			if err != nil {
				fs.Errorf(f, "Error reading root: %v", err)
			} else {
				root.ForgetAll()
			}
		}
	}

	if err != nil {
		return errors.Wrap(err, "failed to umount FUSE fs")
	}

	return nil
}
<commit_msg>Add -o uid=-1 -o gid=-1 for Windows/WinFsp<commit_after>// Package cmount implents a FUSE mounting system for rclone remotes.
//
// This uses the cgo based cgofuse library

// +build cgo
// +build linux darwin freebsd windows

package cmount

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	"github.com/billziss-gh/cgofuse/fuse"
	"github.com/ncw/rclone/cmd"
	"github.com/ncw/rclone/cmd/mountlib"
	"github.com/ncw/rclone/fs"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

// Globals
var (
	noModTime    = false
	noChecksum   = false
	debugFUSE    = false
	noSeek       = false
	dirCacheTime = 5 * 60 * time.Second
	// mount options
	readOnly                         = false
	allowNonEmpty                    = false
	allowRoot                        = false
	allowOther                       = false
	defaultPermissions               = false
	writebackCache                   = false
	maxReadAhead       fs.SizeSuffix = 128 * 1024
	umask                            = 0
	uid                              = ^uint32(0) // these values instruct WinFSP-FUSE to use the current user
	gid                              = ^uint32(0) // overriden for non windows in mount_unix.go
	// foreground                 = false
	// default permissions for directories - modified by umask in Mount
	dirPerms  = os.FileMode(0777)
	filePerms = os.FileMode(0666)
)

func init() {
	cmd.Root.AddCommand(commandDefintion)
	commandDefintion.Flags().BoolVarP(&noModTime, "no-modtime", "", noModTime, "Don't read/write the modification time (can speed things up).")
	commandDefintion.Flags().BoolVarP(&noChecksum, "no-checksum", "", noChecksum, "Don't compare checksums on up/download.")
	commandDefintion.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
	commandDefintion.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
	commandDefintion.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
	// mount options
	commandDefintion.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
	commandDefintion.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")
	commandDefintion.Flags().BoolVarP(&allowRoot, "allow-root", "", allowRoot, "Allow access to root user.")
	commandDefintion.Flags().BoolVarP(&allowOther, "allow-other", "", allowOther, "Allow access to other users.")
	commandDefintion.Flags().BoolVarP(&defaultPermissions, "default-permissions", "", defaultPermissions, "Makes kernel enforce access control based on the file mode.")
	commandDefintion.Flags().BoolVarP(&writebackCache, "write-back-cache", "", writebackCache, "Makes kernel buffer writes before sending them to rclone. Without this, writethrough caching is used.")
	commandDefintion.Flags().VarP(&maxReadAhead, "max-read-ahead", "", "The number of bytes that can be prefetched for sequential reads.")
	commandDefintion.Flags().IntVarP(&umask, "umask", "", umask, "Override the permission bits set by the filesystem.")
	//commandDefintion.Flags().BoolVarP(&foreground, "foreground", "", foreground, "Do not detach.")
}

var commandDefintion = &cobra.Command{
	Use:   commandName + " remote:path /path/to/mountpoint",
	Short: `Mount the remote as a mountpoint. **EXPERIMENTAL**`,
	Long: `
rclone ` + commandName + ` allows Linux, FreeBSD, macOS and Windows to
mount any of Rclone's cloud storage systems as a file system with
FUSE.

This is **EXPERIMENTAL** - use with care.

First set up your remote using ` + "`rclone config`" + `.  Check it works with ` + "`rclone ls`" + ` etc.

Start the mount like this

    rclone ` + commandName + ` remote:path/to/files /path/to/local/mount

Or on Windows like this where X: is an unused drive letter

    rclone ` + commandName + ` remote:path/to/files X:

When the program ends, either via Ctrl+C or receiving a SIGINT or SIGTERM signal,
the mount is automatically stopped.

The umount operation can fail, for example when the mountpoint is busy.
When that happens, it is the user's responsibility to stop the mount manually with

    # Linux
    fusermount -u /path/to/local/mount
    # OS X
    umount /path/to/local/mount

### Limitations ###

This can only write files seqentially, it can only seek when reading.
This means that many applications won't work with their files on an
rclone mount.

The bucket based remotes (eg Swift, S3, Google Compute Storage, B2,
Hubic) won't work from the root - you will need to specify a bucket,
or a path within the bucket.  So ` + "`swift:`" + ` won't work whereas
` + "`swift:bucket`" + ` will as will ` + "`swift:bucket/path`" + `.
None of these support the concept of directories, so empty
directories will have a tendency to disappear once they fall out of
the directory cache.

Only supported on Linux, FreeBSD, OS X and Windows at the moment.

### rclone ` + commandName + ` vs rclone sync/copy ##

File systems expect things to be 100% reliable, whereas cloud storage
systems are a long way from 100% reliable. The rclone sync/copy
commands cope with this with lots of retries.  However rclone ` + commandName + `
can't use retries in the same way without making local copies of the
uploads.  This might happen in the future, but for the moment rclone
` + commandName + ` won't do that, so will be less reliable than the rclone command.

### Filters ###

Note that all the rclone filters can be used to select a subset of the
files to be visible in the mount.

### Directory Cache ###

Using the ` + "`--dir-cache-time`" + ` flag, you can set how long a
directory should be considered up to date and not refreshed from the
backend. Changes made locally in the mount may appear immediately or
invalidate the cache. However, changes done on the remote will only
be picked up once the cache expires.

Alternatively, you can send a ` + "`SIGHUP`" + ` signal to rclone for
it to flush all directory caches, regardless of how old they are.
Assuming only one rclone instance is running, you can reset the cache
like this:

    kill -SIGHUP $(pidof rclone)

### Bugs ###

  * All the remotes should work for read, but some may not for write
    * those which need to know the size in advance won't - eg B2
    * maybe should pass in size as -1 to mean work it out
    * Or put in an an upload cache to cache the files on disk first
`,
	Run: func(command *cobra.Command, args []string) {
		cmd.CheckArgs(2, 2, command, args)
		fdst := cmd.NewFsDst(args)
		err := Mount(fdst, args[1])
		if err != nil {
			log.Fatalf("Fatal error: %v", err)
		}
	},
}

// mountOptions configures the options from the command line flags
func mountOptions(device string, mountpoint string) (options []string) {
	// Options
	options = []string{
		"-o", "fsname=" + device,
		"-o", "subtype=rclone",
		"-o", fmt.Sprintf("max_readahead=%d", maxReadAhead),
	}
	if debugFUSE {
		options = append(options, "-o", "debug")
	}

	// OSX options
	if runtime.GOOS == "darwin" {
		options = append(options, "-o", "volname="+device)
		options = append(options, "-o", "noappledouble")
		options = append(options, "-o", "noapplexattr")
	}

	// Windows options
	if runtime.GOOS == "windows" {
		// These cause WinFsp to mean the current user
		options = append(options, "-o", "uid=-1")
		options = append(options, "-o", "gid=-1")
	}

	if allowNonEmpty {
		options = append(options, "-o", "nonempty")
	}
	if allowOther {
		options = append(options, "-o", "allow_other")
	}
	if allowRoot {
		options = append(options, "-o", "allow_root")
	}
	if defaultPermissions {
		options = append(options, "-o", "default_permissions")
	}
	if readOnly {
		options = append(options, "-o", "ro")
	}
	if writebackCache {
		// FIXME? options = append(options, "-o", WritebackCache())
	}
	return options
}

// mount the file system
//
// The mount point will be ready when this returns.
//
// returns an error, and an error channel for the serve process to
// report an error when fusermount is called.
func mount(f fs.Fs, mountpoint string) (*mountlib.FS, <-chan error, func() error, error) {
	fs.Debugf(f, "Mounting on %q", mountpoint)

	// Check the mountpoint
	if runtime.GOOS != "windows" {
		fi, err := os.Stat(mountpoint)
		if err != nil {
			return nil, nil, nil, errors.Wrap(err, "mountpoint")
		}
		if !fi.IsDir() {
			return nil, nil, nil, errors.New("mountpoint is not a directory")
		}
	}

	// Create underlying FS
	fsys := NewFS(f)
	host := fuse.NewFileSystemHost(fsys)

	// Create options
	options := mountOptions(f.Name()+":"+f.Root(), mountpoint)
	fs.Debugf(f, "Mounting with options: %q", options)

	// Serve the mount point in the background returning error to errChan
	errChan := make(chan error, 1)
	go func() {
		var err error
		ok := host.Mount(mountpoint, options)
		if !ok {
			err = errors.New("mount failed")
			fs.Errorf(f, "Mount failed")
		}
		errChan <- err
	}()

	// unmount
	unmount := func() error {
		fs.Debugf(nil, "Calling host.Unmount")
		if host.Unmount() {
			fs.Debugf(nil, "host.Unmount succeeded")
			return nil
		}
		fs.Debugf(nil, "host.Unmount failed")
		return errors.New("host unmount failed")
	}

	// Wait for the filesystem to become ready
	<-fsys.ready
	return fsys.FS, errChan, unmount, nil
}

// Mount mounts the remote at mountpoint.
//
// If noModTime is set then it
func Mount(f fs.Fs, mountpoint string) error {
	// Set permissions
	dirPerms = 0777 &^ os.FileMode(umask)
	filePerms = 0666 &^ os.FileMode(umask)

	// Show stats if the user has specifically requested them
	if cmd.ShowStats() {
		stopStats := cmd.StartStats()
		defer close(stopStats)
	}

	// Mount it
	FS, errChan, _, err := mount(f, mountpoint)
	if err != nil {
		return errors.Wrap(err, "failed to mount FUSE fs")
	}

	// Note cgofuse unmounts the fs on SIGINT etc

	sigHup := make(chan os.Signal, 1)
	signal.Notify(sigHup, syscall.SIGHUP)

waitloop:
	for {
		select {
		// umount triggered outside the app
		case err = <-errChan:
			break waitloop
		// user sent SIGHUP to clear the cache
		case <-sigHup:
			root, err := FS.Root()
			if err != nil {
				fs.Errorf(f, "Error reading root: %v", err)
			} else {
				root.ForgetAll()
			}
		}
	}

	if err != nil {
		return errors.Wrap(err, "failed to umount FUSE fs")
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.

// ethtest executes Ethereum JSON tests.
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/tests"
)

var (
	continueOnError = false
	testExtension   = ".json"
	defaultTest     = "all"
	defaultDir      = "."
	allTests        = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests", "RLPTests"}
	skipTests       = []string{}

	TestFlag = cli.StringFlag{
		Name:  "test",
		Usage: "Test type (string): VMTests, TransactionTests, StateTests, BlockTests",
		Value: defaultTest,
	}
	FileFlag = cli.StringFlag{
		Name:   "file",
		Usage:  "Test file or directory. Directories are searched for .json files 1 level deep",
		Value:  defaultDir,
		EnvVar: "ETHEREUM_TEST_PATH",
	}
	ContinueOnErrorFlag = cli.BoolFlag{
		Name:  "continue",
		Usage: "Continue running tests on error (true) or [default] exit immediately (false)",
	}
	ReadStdInFlag = cli.BoolFlag{
		Name:  "stdin",
		Usage: "Accept input from stdin instead of reading from file",
	}
	SkipTestsFlag = cli.StringFlag{
		Name:  "skip",
		Usage: "Tests names to skip",
	}
)

func runTestWithReader(test string, r io.Reader) error {
	glog.Infoln("runTest", test)
	var err error
	switch strings.ToLower(test) {
	case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
		err = tests.RunBlockTestWithReader(r, skipTests)
	case "st", "state", "statetest", "statetests":
		err = tests.RunStateTestWithReader(r, skipTests)
	case "tx", "transactiontest", "transactiontests":
		err = tests.RunTransactionTestsWithReader(r, skipTests)
	case "vm", "vmtest", "vmtests":
		err = tests.RunVmTestWithReader(r, skipTests)
	case "rlp", "rlptest", "rlptests":
		err = tests.RunRLPTestWithReader(r, skipTests)
	default:
		err = fmt.Errorf("Invalid test type specified: %v", test)
	}

	if err != nil {
		return err
	}

	return nil
}

func getFiles(path string) ([]string, error) {
	glog.Infoln("getFiles", path)
	var files []string
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	switch mode := fi.Mode(); {
	case mode.IsDir():
		fi, _ := ioutil.ReadDir(path)
		files = make([]string, len(fi))
		for i, v := range fi {
			// only go 1 depth and leave directory entires blank
			if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension {
				files[i] = filepath.Join(path, v.Name())
				glog.Infoln("Found file", files[i])
			}
		}
	case mode.IsRegular():
		files = make([]string, 1)
		files[0] = path
	}

	return files, nil
}

func runSuite(test, file string) {
	var tests []string

	if test == defaultTest {
		tests = allTests
	} else {
		tests = []string{test}
	}

	for _, curTest := range tests {
		glog.Infoln("runSuite", curTest, file)
		var err error
		var files []string
		if test == defaultTest {
			files, err = getFiles(filepath.Join(file, curTest))

		} else {
			files, err = getFiles(file)
		}
		if err != nil {
			glog.Fatalln(err)
		}

		if len(files) == 0 {
			glog.Warningln("No files matched path")
		}
		for _, curFile := range files {
			// Skip blank entries
			if len(curFile) == 0 {
				continue
			}

			r, err := os.Open(curFile)
			if err != nil {
				glog.Fatalln(err)
			}
			defer r.Close()

			err = runTestWithReader(curTest, r)
			if err != nil {
				if continueOnError {
					glog.Errorln(err)
				} else {
					glog.Fatalln(err)
				}
			}

		}
	}
}

func setupApp(c *cli.Context) {
	flagTest := c.GlobalString(TestFlag.Name)
	flagFile := c.GlobalString(FileFlag.Name)
	continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name)
	useStdIn := c.GlobalBool(ReadStdInFlag.Name)
	skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ")

	if !useStdIn {
		runSuite(flagTest, flagFile)
	} else {
		if err := runTestWithReader(flagTest, os.Stdin); err != nil {
			glog.Fatalln(err)
		}

	}
}

func main() {
	glog.SetToStderr(true)

	app := cli.NewApp()
	app.Name = "ethtest"
	app.Usage = "go-ethereum test interface"
	app.Action = setupApp
	app.Version = "0.2.0"
	app.Author = "go-ethereum team"

	app.Flags = []cli.Flag{
		TestFlag,
		FileFlag,
		ContinueOnErrorFlag,
		ReadStdInFlag,
		SkipTestsFlag,
	}

	if err := app.Run(os.Args); err != nil {
		glog.Fatalln(err)
	}

}
<commit_msg>Disassociate test directory from test name<commit_after>// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.

// ethtest executes Ethereum JSON tests.
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/tests"
)

var (
	continueOnError = false
	testExtension   = ".json"
	defaultTest     = "all"
	defaultDir      = "."
	allTests        = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests", "RLPTests"}
	testDirMapping  = map[string]string{"BlockTests": "BlockchainTests"}
	skipTests       = []string{}

	TestFlag = cli.StringFlag{
		Name:  "test",
		Usage: "Test type (string): VMTests, TransactionTests, StateTests, BlockTests",
		Value: defaultTest,
	}
	FileFlag = cli.StringFlag{
		Name:   "file",
		Usage:  "Test file or directory. Directories are searched for .json files 1 level deep",
		Value:  defaultDir,
		EnvVar: "ETHEREUM_TEST_PATH",
	}
	ContinueOnErrorFlag = cli.BoolFlag{
		Name:  "continue",
		Usage: "Continue running tests on error (true) or [default] exit immediately (false)",
	}
	ReadStdInFlag = cli.BoolFlag{
		Name:  "stdin",
		Usage: "Accept input from stdin instead of reading from file",
	}
	SkipTestsFlag = cli.StringFlag{
		Name:  "skip",
		Usage: "Tests names to skip",
	}
)

func runTestWithReader(test string, r io.Reader) error {
	glog.Infoln("runTest", test)
	var err error
	switch strings.ToLower(test) {
	case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
		err = tests.RunBlockTestWithReader(r, skipTests)
	case "st", "state", "statetest", "statetests":
		err = tests.RunStateTestWithReader(r, skipTests)
	case "tx", "transactiontest", "transactiontests":
		err = tests.RunTransactionTestsWithReader(r, skipTests)
	case "vm", "vmtest", "vmtests":
		err = tests.RunVmTestWithReader(r, skipTests)
	case "rlp", "rlptest", "rlptests":
		err = tests.RunRLPTestWithReader(r, skipTests)
	default:
		err = fmt.Errorf("Invalid test type specified: %v", test)
	}

	if err != nil {
		return err
	}

	return nil
}

func getFiles(path string) ([]string, error) {
	glog.Infoln("getFiles", path)
	var files []string
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

	switch mode := fi.Mode(); {
	case mode.IsDir():
		fi, _ := ioutil.ReadDir(path)
		files = make([]string, len(fi))
		for i, v := range fi {
			// only go 1 depth and leave directory entires blank
			if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension {
				files[i] = filepath.Join(path, v.Name())
				glog.Infoln("Found file", files[i])
			}
		}
	case mode.IsRegular():
		files = make([]string, 1)
		files[0] = path
	}

	return files, nil
}

func runSuite(test, file string) {
	var tests []string

	if test == defaultTest {
		tests = allTests
	} else {
		tests = []string{test}
	}

	for _, curTest := range tests {
		glog.Infoln("runSuite", curTest, file)
		var err error
		var files []string
		if test == defaultTest {
			// check if we have an explicit directory mapping for the test
			if _, ok := testDirMapping[curTest]; ok {
				files, err = getFiles(filepath.Join(file, testDirMapping[curTest]))
			} else {
				// otherwise assume test name
				files, err = getFiles(filepath.Join(file, curTest))
			}
		} else {
			files, err = getFiles(file)
		}
		if err != nil {
			glog.Fatalln(err)
		}

		if len(files) == 0 {
			glog.Warningln("No files matched path")
		}
		for _, curFile := range files {
			// Skip blank entries
			if len(curFile) == 0 {
				continue
			}

			r, err := os.Open(curFile)
			if err != nil {
				glog.Fatalln(err)
			}
			defer r.Close()

			err = runTestWithReader(curTest, r)
			if err != nil {
				if continueOnError {
					glog.Errorln(err)
				} else {
					glog.Fatalln(err)
				}
			}

		}
	}
}

func setupApp(c *cli.Context) {
	flagTest := c.GlobalString(TestFlag.Name)
	flagFile := c.GlobalString(FileFlag.Name)
	continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name)
	useStdIn := c.GlobalBool(ReadStdInFlag.Name)
	skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ")

	if !useStdIn {
		runSuite(flagTest, flagFile)
	} else {
		if err := runTestWithReader(flagTest, os.Stdin); err != nil {
			glog.Fatalln(err)
		}

	}
}

func main() {
	glog.SetToStderr(true)

	app := cli.NewApp()
	app.Name = "ethtest"
	app.Usage = "go-ethereum test interface"
	app.Action = setupApp
	app.Version = "0.2.0"
	app.Author = "go-ethereum team"

	app.Flags = []cli.Flag{
		TestFlag,
		FileFlag,
		ContinueOnErrorFlag,
		ReadStdInFlag,
		SkipTestsFlag,
	}

	if err := app.Run(os.Args); err != nil {
		glog.Fatalln(err)
	}

}
<|endoftext|>


<commit_before>package main

import "os"

func main() {
	root := rootCommand()
	service := serviceCommand(root)
	images := imagesCommand(root)
	serviceList := serviceListCommand(service)
	serviceRelease := serviceReleaseCommand(service)

	rootCmd := root.Command()
	imagesCmd := images.Command()
	serviceCmd := service.Command()
	serviceListCmd := serviceList.Command()
	serviceReleaseCmd := serviceRelease.Command()

	rootCmd.AddCommand(imagesCmd)
	rootCmd.AddCommand(serviceCmd)
	rootCmd.AddCommand(imagesCommand(rootOpts))
	serviceCmd.AddCommand(serviceListCmd)
	serviceCmd.AddCommand(serviceReleaseCmd)

	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}
<commit_msg>Cleanup from a slightly off rebase<commit_after>package main

import "os"

func main() {
	root := rootCommand()
	service := serviceCommand(root)
	images := imagesCommand(root)
	serviceList := serviceListCommand(service)
	serviceRelease := serviceReleaseCommand(service)

	rootCmd := root.Command()
	imagesCmd := images.Command()
	serviceCmd := service.Command()
	serviceListCmd := serviceList.Command()
	serviceReleaseCmd := serviceRelease.Command()

	rootCmd.AddCommand(imagesCmd)
	rootCmd.AddCommand(serviceCmd)
	serviceCmd.AddCommand(serviceListCmd)
	serviceCmd.AddCommand(serviceReleaseCmd)

	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/* Copyright 2016 The Bazel Authors. All rights reserved.

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

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

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

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/bazelbuild/bazel-gazelle/config"
	"github.com/bazelbuild/bazel-gazelle/rule"
	"github.com/pmezard/go-difflib/difflib"
)

var exitError = fmt.Errorf("encountered changes while running diff")

func diffFile(c *config.Config, f *rule.File) error {
	rel, err := filepath.Rel(c.RepoRoot, f.Path)
	if err != nil {
		return fmt.Errorf("error getting old path for file %q: %v", f.Path, err)
	}
	rel = filepath.ToSlash(rel)

	date := "1970-01-01 00:00:00.000000000 +0000"
	diff := difflib.UnifiedDiff{
		Context:  3,
		FromDate: date,
		ToDate:   date,
	}

	newContent := f.Format()
	if bytes.Equal(newContent, f.Content) {
		// No change.
		return nil
	}

	if _, err := os.Stat(f.Path); os.IsNotExist(err) {
		diff.FromFile = "/dev/null"
	} else if err != nil {
		return fmt.Errorf("error reading original file: %v", err)
	} else if c.ReadBuildFilesDir == "" {
		path, err := filepath.Rel(c.RepoRoot, f.Path)
		if err != nil {
			return fmt.Errorf("error getting old path for file %q: %v", f.Path, err)
		}
		diff.FromFile = filepath.ToSlash(path)
	} else {
		diff.FromFile = f.Path
	}

	if len(f.Content) != 0 {
    		diff.A = difflib.SplitLines(string(f.Content))
	}

	diff.B = difflib.SplitLines(string(newContent))
	outPath := findOutputPath(c, f)
	if c.WriteBuildFilesDir == "" {
		path, err := filepath.Rel(c.RepoRoot, f.Path)
		if err != nil {
			return fmt.Errorf("error getting new path for file %q: %v", f.Path, err)
		}
		diff.ToFile = filepath.ToSlash(path)
	} else {
		diff.ToFile = outPath
	}

	uc := getUpdateConfig(c)
	var out io.Writer = os.Stdout
	if uc.patchPath != "" {
		out = &uc.patchBuffer
	}
	if err := difflib.WriteUnifiedDiff(out, diff); err != nil {
		return fmt.Errorf("error diffing %s: %v", f.Path, err)
	}
	if ds, _ := difflib.GetUnifiedDiffString(diff); ds != "" {
		return exitError
	}

	return nil
}
<commit_msg>cleanup unused variable warning in diff.go; reuse rel (#957)<commit_after>/* Copyright 2016 The Bazel Authors. All rights reserved.

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

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

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

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/bazelbuild/bazel-gazelle/config"
	"github.com/bazelbuild/bazel-gazelle/rule"
	"github.com/pmezard/go-difflib/difflib"
)

var exitError = fmt.Errorf("encountered changes while running diff")

func diffFile(c *config.Config, f *rule.File) error {
	rel, err := filepath.Rel(c.RepoRoot, f.Path)
	if err != nil {
		return fmt.Errorf("error getting old path for file %q: %v", f.Path, err)
	}
	rel = filepath.ToSlash(rel)

	date := "1970-01-01 00:00:00.000000000 +0000"
	diff := difflib.UnifiedDiff{
		Context:  3,
		FromDate: date,
		ToDate:   date,
	}

	newContent := f.Format()
	if bytes.Equal(newContent, f.Content) {
		// No change.
		return nil
	}

	if _, err := os.Stat(f.Path); os.IsNotExist(err) {
		diff.FromFile = "/dev/null"
	} else if err != nil {
		return fmt.Errorf("error reading original file: %v", err)
	} else if c.ReadBuildFilesDir == "" {
		diff.FromFile = rel
	} else {
		diff.FromFile = f.Path
	}

	if len(f.Content) != 0 {
    		diff.A = difflib.SplitLines(string(f.Content))
	}

	diff.B = difflib.SplitLines(string(newContent))
	outPath := findOutputPath(c, f)
	if c.WriteBuildFilesDir == "" {
		diff.ToFile = rel
	} else {
		diff.ToFile = outPath
	}

	uc := getUpdateConfig(c)
	var out io.Writer = os.Stdout
	if uc.patchPath != "" {
		out = &uc.patchBuffer
	}
	if err := difflib.WriteUnifiedDiff(out, diff); err != nil {
		return fmt.Errorf("error diffing %s: %v", f.Path, err)
	}
	if ds, _ := difflib.GetUnifiedDiffString(diff); ds != "" {
		return exitError
	}

	return nil
}
<|endoftext|>