<commit_before>/*
Copyright 2020 The Knative 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 fanout provides an http.Handler that takes in one request and fans it out to N other
// requests, based on a list of Subscriptions. Logically, it represents all the Subscriptions to a
// single Knative Channel.
// It will normally be used in conjunction with multichannelfanout.MessageHandler, which contains multiple
// fanout.MessageHandler, each corresponding to a single Knative Channel.
package fanout

import (
	"context"
	"errors"
	nethttp "net/http"
	"net/url"
	"sync"
	"time"

	"github.com/cloudevents/sdk-go/v2/binding"
	"github.com/cloudevents/sdk-go/v2/binding/buffering"
	"go.opencensus.io/trace"
	"go.uber.org/zap"

	eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1"
	"knative.dev/eventing/pkg/channel"
	"knative.dev/eventing/pkg/kncloudevents"
)

const (
	defaultTimeout = 15 * time.Minute
)

type Subscription struct {
	Subscriber  *url.URL
	Reply       *url.URL
	DeadLetter  *url.URL
	RetryConfig *kncloudevents.RetryConfig
}

// Config for a fanout.MessageHandler.
type Config struct {
	Subscriptions []Subscription `json:"subscriptions"`
	// AsyncHandler controls whether the Subscriptions are called synchronous or asynchronously.
	// It is expected to be false when used as a sidecar.
	AsyncHandler bool `json:"asyncHandler,omitempty"`
}

// MessageHandler is an http.Handler but has methods for managing
// the fanout Subscriptions. Get/Set methods are synchronized, and
// GetSubscriptions returns a copy of the Subscriptions, so you can
// use it to fetch a snapshot and use it after that safely.
type MessageHandler interface {
	nethttp.Handler
	SetSubscriptions(ctx context.Context, subs []Subscription)
	GetSubscriptions(ctx context.Context) []Subscription
}

// MessageHandler is a http.Handler that takes a single request in and fans it out to N other servers.
type FanoutMessageHandler struct {
	// AsyncHandler controls whether the Subscriptions are called synchronous or asynchronously.
	// It is expected to be false when used as a sidecar.
	asyncHandler bool

	subscriptionsMutex sync.RWMutex
	subscriptions      []Subscription

	receiver   *channel.MessageReceiver
	dispatcher channel.MessageDispatcher

	// TODO: Plumb context through the receiver and dispatcher and use that to store the timeout,
	// rather than a member variable.
	timeout time.Duration

	reporter channel.StatsReporter
	logger   *zap.Logger
}

// NewMessageHandler creates a new fanout.MessageHandler.

func NewFanoutMessageHandler(logger *zap.Logger, messageDispatcher channel.MessageDispatcher, config Config, reporter channel.StatsReporter) (*FanoutMessageHandler, error) {
	handler := &FanoutMessageHandler{
		logger:       logger,
		dispatcher:   messageDispatcher,
		timeout:      defaultTimeout,
		reporter:     reporter,
		asyncHandler: config.AsyncHandler,
	}
	handler.subscriptions = make([]Subscription, len(config.Subscriptions))
	for i := range config.Subscriptions {
		handler.subscriptions[i] = config.Subscriptions[i]
	}
	// The receiver function needs to point back at the handler itself, so set it up after
	// initialization.
	receiver, err := channel.NewMessageReceiver(createMessageReceiverFunction(handler), logger, reporter)
	if err != nil {
		return nil, err
	}
	handler.receiver = receiver

	return handler, nil
}

func SubscriberSpecToFanoutConfig(sub eventingduckv1.SubscriberSpec) (*Subscription, error) {
	var destination *url.URL
	if sub.SubscriberURI != nil {
		destination = sub.SubscriberURI.URL()
	}

	var reply *url.URL
	if sub.ReplyURI != nil {
		reply = sub.ReplyURI.URL()
	}

	var deadLetter *url.URL
	if sub.Delivery != nil && sub.Delivery.DeadLetterSink != nil && sub.Delivery.DeadLetterSink.URI != nil {
		// TODO: Bug(?) this does not seem to support refing the Ref field.
		// https://github.com/knative/eventing/issues/4376
		deadLetter = sub.Delivery.DeadLetterSink.URI.URL()
	}

	var retryConfig *kncloudevents.RetryConfig
	if sub.Delivery != nil {
		if rc, err := kncloudevents.RetryConfigFromDeliverySpec(*sub.Delivery); err != nil {
			return nil, err
		} else {
			retryConfig = &rc
		}
	}

	return &Subscription{Subscriber: destination, Reply: reply, DeadLetter: deadLetter, RetryConfig: retryConfig}, nil
}

func (f *FanoutMessageHandler) SetSubscriptions(ctx context.Context, subs []Subscription) {
	f.subscriptionsMutex.Lock()
	defer f.subscriptionsMutex.Unlock()
	s := make([]Subscription, len(subs))
	copy(s, subs)
	f.subscriptions = s
}

func (f *FanoutMessageHandler) GetSubscriptions(ctx context.Context) []Subscription {
	f.subscriptionsMutex.RLock()
	defer f.subscriptionsMutex.RUnlock()
	ret := make([]Subscription, len(f.subscriptions))
	copy(ret, f.subscriptions)
	return ret
}

func createMessageReceiverFunction(f *FanoutMessageHandler) func(context.Context, channel.ChannelReference, binding.Message, []binding.Transformer, nethttp.Header) error {
	if f.asyncHandler {
		return func(ctx context.Context, ref channel.ChannelReference, message binding.Message, transformers []binding.Transformer, additionalHeaders nethttp.Header) error {
			subs := f.GetSubscriptions(ctx)

			if len(subs) == 0 {
				// Nothing to do here, finish the message and return
				_ = message.Finish(nil)
				return nil
			}

			parentSpan := trace.FromContext(ctx)
			te := kncloudevents.TypeExtractorTransformer("")
			transformers = append(transformers, &te)
			// Message buffering here is done before starting the dispatch goroutine
			// Because the message could be closed before the buffering happens
			bufferedMessage, err := buffering.CopyMessage(ctx, message, transformers...)
			if err != nil {
				return err
			}

			reportArgs := channel.ReportArgs{}
			reportArgs.EventType = string(te)
			reportArgs.Ns = ref.Namespace

			// We don't need the original message anymore
			_ = message.Finish(nil)
			go func(m binding.Message, h nethttp.Header, s *trace.Span, r *channel.StatsReporter, args *channel.ReportArgs) {
				// Run async dispatch with background context.
				ctx = trace.NewContext(context.Background(), s)
				// Any returned error is already logged in f.dispatch().
				dispatchResultForFanout := f.dispatch(ctx, subs, m, h)
				_ = parseFanoutResultAndReportMetrics(dispatchResultForFanout, *r, *args)
			}(bufferedMessage, additionalHeaders, parentSpan, &f.reporter, &reportArgs)
			return nil
		}
	}
	return func(ctx context.Context, ref channel.ChannelReference, message binding.Message, transformers []binding.Transformer, additionalHeaders nethttp.Header) error {
		subs := f.GetSubscriptions(ctx)
		if len(subs) == 0 {
			// Nothing to do here, finish the message and return
			_ = message.Finish(nil)
			return nil
		}

		te := kncloudevents.TypeExtractorTransformer("")
		transformers = append(transformers, &te)
		// We buffer the message to send it several times
		bufferedMessage, err := buffering.CopyMessage(ctx, message, transformers...)
		if err != nil {
			return err
		}
		// We don't need the original message anymore
		_ = message.Finish(nil)

		reportArgs := channel.ReportArgs{}
		reportArgs.EventType = string(te)
		reportArgs.Ns = ref.Namespace
		dispatchResultForFanout := f.dispatch(ctx, subs, bufferedMessage, additionalHeaders)
		return parseFanoutResultAndReportMetrics(dispatchResultForFanout, f.reporter, reportArgs)
	}
}

func (f *FanoutMessageHandler) ServeHTTP(response nethttp.ResponseWriter, request *nethttp.Request) {
	f.receiver.ServeHTTP(response, request)
}

func parseFanoutResultAndReportMetrics(result dispatchResult, reporter channel.StatsReporter, reportArgs channel.ReportArgs) error {
	if result.info != nil && result.info.Time > channel.NoDuration {
		if result.info.ResponseCode > channel.NoResponse {
			_ = reporter.ReportEventDispatchTime(&reportArgs, result.info.ResponseCode, result.info.Time)
		} else {
			_ = reporter.ReportEventDispatchTime(&reportArgs, nethttp.StatusInternalServerError, result.info.Time)
		}
	}
	err := result.err
	if err != nil {
		channel.ReportEventCountMetricsForDispatchError(err, reporter, &reportArgs)
	} else {
		if result.info != nil {
			_ = reporter.ReportEventCount(&reportArgs, result.info.ResponseCode)
		}
	}
	return err
}

// dispatch takes the event, fans it out to each subscription in subs. If all the fanned out
// events return successfully, then return nil. Else, return an error.
func (f *FanoutMessageHandler) dispatch(ctx context.Context, subs []Subscription, bufferedMessage binding.Message, additionalHeaders nethttp.Header) dispatchResult {
	// Bind the lifecycle of the buffered message to the number of subs
	bufferedMessage = buffering.WithAcksBeforeFinish(bufferedMessage, len(subs))

	errorCh := make(chan dispatchResult, len(subs))
	for _, sub := range subs {
		go func(s Subscription) {
			dispatchedResultPerSub, err := f.makeFanoutRequest(ctx, bufferedMessage, additionalHeaders, s)
			errorCh <- dispatchResult{err: err, info: dispatchedResultPerSub}
		}(sub)
	}

	var totalDispatchTimeForFanout time.Duration = channel.NoDuration
	dispatchResultForFanout := dispatchResult{
		info: &channel.DispatchExecutionInfo{
			Time:         channel.NoDuration,
			ResponseCode: channel.NoResponse,
		},
	}
	for range subs {
		select {
		case dispatchResult := <-errorCh:
			if dispatchResult.info != nil {
				if dispatchResult.info.Time > channel.NoDuration {
					if totalDispatchTimeForFanout > channel.NoDuration {
						totalDispatchTimeForFanout += dispatchResult.info.Time
					} else {
						totalDispatchTimeForFanout = dispatchResult.info.Time
					}
				}
				dispatchResultForFanout.info.Time = totalDispatchTimeForFanout
				dispatchResultForFanout.info.ResponseCode = dispatchResult.info.ResponseCode
			}
			if dispatchResult.err != nil {
				f.logger.Error("Fanout had an error", zap.Error(dispatchResult.err))
				dispatchResultForFanout.err = dispatchResult.err
				return dispatchResultForFanout
			}
		case <-time.After(f.timeout):
			f.logger.Error("Fanout timed out")
			dispatchResultForFanout.err = errors.New("fanout timed out")
			return dispatchResultForFanout
		}
	}
	// All Subscriptions returned err = nil.
	return dispatchResultForFanout
}

// makeFanoutRequest sends the request to exactly one subscription. It handles both the `call` and
// the `sink` portions of the subscription.
func (f *FanoutMessageHandler) makeFanoutRequest(ctx context.Context, message binding.Message, additionalHeaders nethttp.Header, sub Subscription) (*channel.DispatchExecutionInfo, error) {
	return f.dispatcher.DispatchMessageWithRetries(
		ctx,
		message,
		additionalHeaders,
		sub.Subscriber,
		sub.Reply,
		sub.DeadLetter,
		sub.RetryConfig,
	)
}

type dispatchResult struct {
	err  error
	info *channel.DispatchExecutionInfo
}
<commit_msg>checks out (#4448)<commit_after>/*
Copyright 2020 The Knative 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 fanout provides an http.Handler that takes in one request and fans it out to N other
// requests, based on a list of Subscriptions. Logically, it represents all the Subscriptions to a
// single Knative Channel.
// It will normally be used in conjunction with multichannelfanout.MessageHandler, which contains multiple
// fanout.MessageHandler, each corresponding to a single Knative Channel.
package fanout

import (
	"context"
	"errors"
	nethttp "net/http"
	"net/url"
	"sync"
	"time"

	"github.com/cloudevents/sdk-go/v2/binding"
	"github.com/cloudevents/sdk-go/v2/binding/buffering"
	"go.opencensus.io/trace"
	"go.uber.org/zap"

	eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1"
	"knative.dev/eventing/pkg/channel"
	"knative.dev/eventing/pkg/kncloudevents"
)

const (
	defaultTimeout = 15 * time.Minute
)

type Subscription struct {
	Subscriber  *url.URL
	Reply       *url.URL
	DeadLetter  *url.URL
	RetryConfig *kncloudevents.RetryConfig
}

// Config for a fanout.MessageHandler.
type Config struct {
	Subscriptions []Subscription `json:"subscriptions"`
	// AsyncHandler controls whether the Subscriptions are called synchronous or asynchronously.
	// It is expected to be false when used as a sidecar.
	AsyncHandler bool `json:"asyncHandler,omitempty"`
}

// MessageHandler is an http.Handler but has methods for managing
// the fanout Subscriptions. Get/Set methods are synchronized, and
// GetSubscriptions returns a copy of the Subscriptions, so you can
// use it to fetch a snapshot and use it after that safely.
type MessageHandler interface {
	nethttp.Handler
	SetSubscriptions(ctx context.Context, subs []Subscription)
	GetSubscriptions(ctx context.Context) []Subscription
}

// MessageHandler is a http.Handler that takes a single request in and fans it out to N other servers.
type FanoutMessageHandler struct {
	// AsyncHandler controls whether the Subscriptions are called synchronous or asynchronously.
	// It is expected to be false when used as a sidecar.
	asyncHandler bool

	subscriptionsMutex sync.RWMutex
	subscriptions      []Subscription

	receiver   *channel.MessageReceiver
	dispatcher channel.MessageDispatcher

	// TODO: Plumb context through the receiver and dispatcher and use that to store the timeout,
	// rather than a member variable.
	timeout time.Duration

	reporter channel.StatsReporter
	logger   *zap.Logger
}

// NewMessageHandler creates a new fanout.MessageHandler.

func NewFanoutMessageHandler(logger *zap.Logger, messageDispatcher channel.MessageDispatcher, config Config, reporter channel.StatsReporter) (*FanoutMessageHandler, error) {
	handler := &FanoutMessageHandler{
		logger:       logger,
		dispatcher:   messageDispatcher,
		timeout:      defaultTimeout,
		reporter:     reporter,
		asyncHandler: config.AsyncHandler,
	}
	handler.subscriptions = make([]Subscription, len(config.Subscriptions))
	for i := range config.Subscriptions {
		handler.subscriptions[i] = config.Subscriptions[i]
	}
	// The receiver function needs to point back at the handler itself, so set it up after
	// initialization.
	receiver, err := channel.NewMessageReceiver(createMessageReceiverFunction(handler), logger, reporter)
	if err != nil {
		return nil, err
	}
	handler.receiver = receiver

	return handler, nil
}

func SubscriberSpecToFanoutConfig(sub eventingduckv1.SubscriberSpec) (*Subscription, error) {
	var destination *url.URL
	if sub.SubscriberURI != nil {
		destination = sub.SubscriberURI.URL()
	}

	var reply *url.URL
	if sub.ReplyURI != nil {
		reply = sub.ReplyURI.URL()
	}

	var deadLetter *url.URL
	if sub.Delivery != nil && sub.Delivery.DeadLetterSink != nil && sub.Delivery.DeadLetterSink.URI != nil {
		// Subscription reconcilers resolves the URI.
		deadLetter = sub.Delivery.DeadLetterSink.URI.URL()
	}

	var retryConfig *kncloudevents.RetryConfig
	if sub.Delivery != nil {
		if rc, err := kncloudevents.RetryConfigFromDeliverySpec(*sub.Delivery); err != nil {
			return nil, err
		} else {
			retryConfig = &rc
		}
	}

	return &Subscription{Subscriber: destination, Reply: reply, DeadLetter: deadLetter, RetryConfig: retryConfig}, nil
}

func (f *FanoutMessageHandler) SetSubscriptions(ctx context.Context, subs []Subscription) {
	f.subscriptionsMutex.Lock()
	defer f.subscriptionsMutex.Unlock()
	s := make([]Subscription, len(subs))
	copy(s, subs)
	f.subscriptions = s
}

func (f *FanoutMessageHandler) GetSubscriptions(ctx context.Context) []Subscription {
	f.subscriptionsMutex.RLock()
	defer f.subscriptionsMutex.RUnlock()
	ret := make([]Subscription, len(f.subscriptions))
	copy(ret, f.subscriptions)
	return ret
}

func createMessageReceiverFunction(f *FanoutMessageHandler) func(context.Context, channel.ChannelReference, binding.Message, []binding.Transformer, nethttp.Header) error {
	if f.asyncHandler {
		return func(ctx context.Context, ref channel.ChannelReference, message binding.Message, transformers []binding.Transformer, additionalHeaders nethttp.Header) error {
			subs := f.GetSubscriptions(ctx)

			if len(subs) == 0 {
				// Nothing to do here, finish the message and return
				_ = message.Finish(nil)
				return nil
			}

			parentSpan := trace.FromContext(ctx)
			te := kncloudevents.TypeExtractorTransformer("")
			transformers = append(transformers, &te)
			// Message buffering here is done before starting the dispatch goroutine
			// Because the message could be closed before the buffering happens
			bufferedMessage, err := buffering.CopyMessage(ctx, message, transformers...)
			if err != nil {
				return err
			}

			reportArgs := channel.ReportArgs{}
			reportArgs.EventType = string(te)
			reportArgs.Ns = ref.Namespace

			// We don't need the original message anymore
			_ = message.Finish(nil)
			go func(m binding.Message, h nethttp.Header, s *trace.Span, r *channel.StatsReporter, args *channel.ReportArgs) {
				// Run async dispatch with background context.
				ctx = trace.NewContext(context.Background(), s)
				// Any returned error is already logged in f.dispatch().
				dispatchResultForFanout := f.dispatch(ctx, subs, m, h)
				_ = parseFanoutResultAndReportMetrics(dispatchResultForFanout, *r, *args)
			}(bufferedMessage, additionalHeaders, parentSpan, &f.reporter, &reportArgs)
			return nil
		}
	}
	return func(ctx context.Context, ref channel.ChannelReference, message binding.Message, transformers []binding.Transformer, additionalHeaders nethttp.Header) error {
		subs := f.GetSubscriptions(ctx)
		if len(subs) == 0 {
			// Nothing to do here, finish the message and return
			_ = message.Finish(nil)
			return nil
		}

		te := kncloudevents.TypeExtractorTransformer("")
		transformers = append(transformers, &te)
		// We buffer the message to send it several times
		bufferedMessage, err := buffering.CopyMessage(ctx, message, transformers...)
		if err != nil {
			return err
		}
		// We don't need the original message anymore
		_ = message.Finish(nil)

		reportArgs := channel.ReportArgs{}
		reportArgs.EventType = string(te)
		reportArgs.Ns = ref.Namespace
		dispatchResultForFanout := f.dispatch(ctx, subs, bufferedMessage, additionalHeaders)
		return parseFanoutResultAndReportMetrics(dispatchResultForFanout, f.reporter, reportArgs)
	}
}

func (f *FanoutMessageHandler) ServeHTTP(response nethttp.ResponseWriter, request *nethttp.Request) {
	f.receiver.ServeHTTP(response, request)
}

func parseFanoutResultAndReportMetrics(result dispatchResult, reporter channel.StatsReporter, reportArgs channel.ReportArgs) error {
	if result.info != nil && result.info.Time > channel.NoDuration {
		if result.info.ResponseCode > channel.NoResponse {
			_ = reporter.ReportEventDispatchTime(&reportArgs, result.info.ResponseCode, result.info.Time)
		} else {
			_ = reporter.ReportEventDispatchTime(&reportArgs, nethttp.StatusInternalServerError, result.info.Time)
		}
	}
	err := result.err
	if err != nil {
		channel.ReportEventCountMetricsForDispatchError(err, reporter, &reportArgs)
	} else {
		if result.info != nil {
			_ = reporter.ReportEventCount(&reportArgs, result.info.ResponseCode)
		}
	}
	return err
}

// dispatch takes the event, fans it out to each subscription in subs. If all the fanned out
// events return successfully, then return nil. Else, return an error.
func (f *FanoutMessageHandler) dispatch(ctx context.Context, subs []Subscription, bufferedMessage binding.Message, additionalHeaders nethttp.Header) dispatchResult {
	// Bind the lifecycle of the buffered message to the number of subs
	bufferedMessage = buffering.WithAcksBeforeFinish(bufferedMessage, len(subs))

	errorCh := make(chan dispatchResult, len(subs))
	for _, sub := range subs {
		go func(s Subscription) {
			dispatchedResultPerSub, err := f.makeFanoutRequest(ctx, bufferedMessage, additionalHeaders, s)
			errorCh <- dispatchResult{err: err, info: dispatchedResultPerSub}
		}(sub)
	}

	var totalDispatchTimeForFanout time.Duration = channel.NoDuration
	dispatchResultForFanout := dispatchResult{
		info: &channel.DispatchExecutionInfo{
			Time:         channel.NoDuration,
			ResponseCode: channel.NoResponse,
		},
	}
	for range subs {
		select {
		case dispatchResult := <-errorCh:
			if dispatchResult.info != nil {
				if dispatchResult.info.Time > channel.NoDuration {
					if totalDispatchTimeForFanout > channel.NoDuration {
						totalDispatchTimeForFanout += dispatchResult.info.Time
					} else {
						totalDispatchTimeForFanout = dispatchResult.info.Time
					}
				}
				dispatchResultForFanout.info.Time = totalDispatchTimeForFanout
				dispatchResultForFanout.info.ResponseCode = dispatchResult.info.ResponseCode
			}
			if dispatchResult.err != nil {
				f.logger.Error("Fanout had an error", zap.Error(dispatchResult.err))
				dispatchResultForFanout.err = dispatchResult.err
				return dispatchResultForFanout
			}
		case <-time.After(f.timeout):
			f.logger.Error("Fanout timed out")
			dispatchResultForFanout.err = errors.New("fanout timed out")
			return dispatchResultForFanout
		}
	}
	// All Subscriptions returned err = nil.
	return dispatchResultForFanout
}

// makeFanoutRequest sends the request to exactly one subscription. It handles both the `call` and
// the `sink` portions of the subscription.
func (f *FanoutMessageHandler) makeFanoutRequest(ctx context.Context, message binding.Message, additionalHeaders nethttp.Header, sub Subscription) (*channel.DispatchExecutionInfo, error) {
	return f.dispatcher.DispatchMessageWithRetries(
		ctx,
		message,
		additionalHeaders,
		sub.Subscriber,
		sub.Reply,
		sub.DeadLetter,
		sub.RetryConfig,
	)
}

type dispatchResult struct {
	err  error
	info *channel.DispatchExecutionInfo
}
<|endoftext|>


<commit_before>// Package code39 can create Code39 barcodes
package code93

import (
	"errors"
	"strings"

	"github.com/boombuler/barcode"
	"github.com/boombuler/barcode/utils"
)

type encodeInfo struct {
	value int
	data  int
}

const (
	// Special Function 1 ($)
	FNC1 = '\u00f1'
	// Special Function 2 (%)
	FNC2 = '\u00f2'
	// Special Function 3 (/)
	FNC3 = '\u00f3'
	// Special Function 4 (+)
	FNC4 = '\u00f4'
)

var encodeTable = map[rune]encodeInfo{
	'0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144},
	'3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124},
	'6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112},
	'9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4},
	'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192},
	'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164},
	'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A},
	'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146},
	'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4},
	'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6},
	'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C},
	'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A},
	'-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2},
	'$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176},
	'%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA},
	FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E},
}

// Encode returns a code93 barcode for the given content
func Encode(content string) (barcode.Barcode, error) {
	if strings.ContainsRune(content, '*') {
		return nil, errors.New("invalid data! content may not contain '*'")
	}

	data := content + string(getChecksum(content, 20))
	data += string(getChecksum(data, 15))

	data = "*" + data + "*"
	result := new(utils.BitList)

	for _, r := range data {
		info, ok := encodeTable[r]
		if !ok {
			return nil, errors.New("invalid data!")
		}
		result.AddBits(info.data, 9)
	}
	result.AddBit(true)

	return utils.New1DCode("Code 93", content, result), nil
}

func reverse(value string) string {
	data := []rune(value)
	result := []rune{}
	for i := len(data) - 1; i >= 0; i-- {
		result = append(result, data[i])
	}
	return string(result)
}

func getChecksum(content string, maxWeight int) rune {
	weight := 1
	total := 0

	for _, r := range reverse(content) {
		info, ok := encodeTable[r]
		if !ok {
			return ' '
		}
		total += info.value * weight
		if weight++; weight > maxWeight {
			weight = 1
		}
	}
	total = total % 47
	for r, info := range encodeTable {
		if info.value == total {
			return r
		}
	}
	return ' '
}
<commit_msg>code cleanup.<commit_after>// Package code39 can create Code39 barcodes
package code93

import (
	"errors"
	"strings"

	"github.com/boombuler/barcode"
	"github.com/boombuler/barcode/utils"
)

type encodeInfo struct {
	value int
	data  int
}

const (
	// Special Function 1 ($)
	FNC1 = '\u00f1'
	// Special Function 2 (%)
	FNC2 = '\u00f2'
	// Special Function 3 (/)
	FNC3 = '\u00f3'
	// Special Function 4 (+)
	FNC4 = '\u00f4'
)

var encodeTable = map[rune]encodeInfo{
	'0': encodeInfo{0, 0x114}, '1': encodeInfo{1, 0x148}, '2': encodeInfo{2, 0x144},
	'3': encodeInfo{3, 0x142}, '4': encodeInfo{4, 0x128}, '5': encodeInfo{5, 0x124},
	'6': encodeInfo{6, 0x122}, '7': encodeInfo{7, 0x150}, '8': encodeInfo{8, 0x112},
	'9': encodeInfo{9, 0x10A}, 'A': encodeInfo{10, 0x1A8}, 'B': encodeInfo{11, 0x1A4},
	'C': encodeInfo{12, 0x1A2}, 'D': encodeInfo{13, 0x194}, 'E': encodeInfo{14, 0x192},
	'F': encodeInfo{15, 0x18A}, 'G': encodeInfo{16, 0x168}, 'H': encodeInfo{17, 0x164},
	'I': encodeInfo{18, 0x162}, 'J': encodeInfo{19, 0x134}, 'K': encodeInfo{20, 0x11A},
	'L': encodeInfo{21, 0x158}, 'M': encodeInfo{22, 0x14C}, 'N': encodeInfo{23, 0x146},
	'O': encodeInfo{24, 0x12C}, 'P': encodeInfo{25, 0x116}, 'Q': encodeInfo{26, 0x1B4},
	'R': encodeInfo{27, 0x1B2}, 'S': encodeInfo{28, 0x1AC}, 'T': encodeInfo{29, 0x1A6},
	'U': encodeInfo{30, 0x196}, 'V': encodeInfo{31, 0x19A}, 'W': encodeInfo{32, 0x16C},
	'X': encodeInfo{33, 0x166}, 'Y': encodeInfo{34, 0x136}, 'Z': encodeInfo{35, 0x13A},
	'-': encodeInfo{36, 0x12E}, '.': encodeInfo{37, 0x1D4}, ' ': encodeInfo{38, 0x1D2},
	'$': encodeInfo{39, 0x1CA}, '/': encodeInfo{40, 0x16E}, '+': encodeInfo{41, 0x176},
	'%': encodeInfo{42, 0x1AE}, FNC1: encodeInfo{43, 0x126}, FNC2: encodeInfo{44, 0x1DA},
	FNC3: encodeInfo{45, 0x1D6}, FNC4: encodeInfo{46, 0x132}, '*': encodeInfo{47, 0x15E},
}

// Encode returns a code93 barcode for the given content
func Encode(content string) (barcode.Barcode, error) {
	if strings.ContainsRune(content, '*') {
		return nil, errors.New("invalid data! content may not contain '*'")
	}

	data := content + string(getChecksum(content, 20))
	data += string(getChecksum(data, 15))

	data = "*" + data + "*"
	result := new(utils.BitList)

	for _, r := range data {
		info, ok := encodeTable[r]
		if !ok {
			return nil, errors.New("invalid data!")
		}
		result.AddBits(info.data, 9)
	}
	result.AddBit(true)

	return utils.New1DCode("Code 93", content, result), nil
}

func getChecksum(content string, maxWeight int) rune {
	weight := 1
	total := 0

	data := []rune(content)
	for i := len(data) - 1; i >= 0; i-- {
		r := data[i]
		info, ok := encodeTable[r]
		if !ok {
			return ' '
		}
		total += info.value * weight
		if weight++; weight > maxWeight {
			weight = 1
		}
	}
	total = total % 47
	for r, info := range encodeTable {
		if info.value == total {
			return r
		}
	}
	return ' '
}
<|endoftext|>


<commit_before>package txbuilder

import (
	"context"
	"encoding/json"

	"golang.org/x/crypto/sha3"

	chainjson "chain/encoding/json"
	"chain/errors"
	"chain/protocol/bc"
	"chain/protocol/vm"
)

// WitnessComponent encodes instructions for finalizing a transaction
// by populating its InputWitness fields. Each WitnessComponent object
// produces zero or more items for the InputWitness of the txinput it
// corresponds to.
type WitnessComponent interface {
	// Stage is called on the component after all the inputs of a tx
	// template are present (e.g., to add the tx sighash). It produces a
	// p2dp predicate.
	Stage(*Template, int) []byte

	// Sign is called to add signatures. Actual signing is delegated to
	// a callback function.
	Sign(context.Context, *Template, int, func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error

	// Materialize is called to turn the component into a vector of
	// arguments for the input witness.
	Materialize(*Template, int) ([][]byte, error)
}

// MaterializeWitnesses takes a filled in Template and "materializes"
// each witness component, turning it into a vector of arguments for
// the tx's input witness, creating a fully-signed transaction.
func MaterializeWitnesses(txTemplate *Template) (*bc.Tx, error) {
	msg := txTemplate.Unsigned
	for i, input := range txTemplate.Inputs {
		if msg.Inputs[input.Position] == nil {
			return nil, errors.WithDetailf(ErrBadTxInputIdx, "input %d references missing tx input %d", i, input.Position)
		}

		var witness [][]byte
		for j, c := range input.WitnessComponents {
			items, err := c.Materialize(txTemplate, i)
			if err != nil {
				return nil, errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
			}
			witness = append(witness, items...)
		}

		msg.Inputs[input.Position].InputWitness = witness
	}

	return bc.NewTx(*msg), nil
}

type DataWitness []byte

func (_ DataWitness) Stage(_ *Template, _ int) []byte { return nil }
func (_ DataWitness) Sign(_ context.Context, _ *Template, _ int, _ func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error {
	return nil
}

func (d DataWitness) Materialize(_ *Template, _ int) ([][]byte, error) {
	return [][]byte{d}, nil
}

func (d DataWitness) MarshalJSON() ([]byte, error) {
	obj := struct {
		Type string             `json:"type"`
		Data chainjson.HexBytes `json:"data"`
	}{
		Type: "data",
		Data: chainjson.HexBytes(d),
	}
	return json.Marshal(obj)
}

type (
	SignatureWitness struct {
		// Quorum is the number of signatures required.
		Quorum int `json:"quorum"`

		// Keys are the identities of the keys to sign with.
		Keys []KeyID `json:"keys"`

		// Constraints is a list of constraints to express in the deferred
		// predicate in the txinput.
		Constraints []Constraint `json:"constraints"`

		// Sigs is the output of Sign, where program (the output of Stage)
		// is signed by each of the keys in Keys.
		Sigs []chainjson.HexBytes `json:"signatures"`
	}

	KeyID struct {
		XPub           string   `json:"xpub"`
		DerivationPath []uint32 `json:"derivation_path"`
	}
)

func (sw *SignatureWitness) Stage(tpl *Template, index int) []byte {
	if len(sw.Constraints) == 0 {
		// When in doubt, commit to the hash of the current tx
		// TODO(bobg): When we add other Constraint types, require callers
		// to specify this explicitly rather than as a default.
		h := tpl.Hash(index, bc.SigHashAll)
		sw.Constraints = []Constraint{TxHashConstraint(h)}
	}
	var program []byte
	for i, c := range sw.Constraints {
		program = append(program, c.Code()...)
		if i < len(sw.Constraints)-1 { // leave the final bool on top of the stack
			program = append(program, byte(vm.OP_VERIFY))
		}
	}
	return program
}

func (sw *SignatureWitness) Sign(ctx context.Context, tpl *Template, index int, signFn func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error {
	if len(sw.Sigs) < len(sw.Keys) {
		// Each key in sw.Keys will produce a signature in sw.Sigs. Make
		// sure there are enough slots in sw.Sigs and that we preserve any
		// sigs already present.
		newSigs := make([]chainjson.HexBytes, len(sw.Keys))
		copy(newSigs, sw.Sigs)
		sw.Sigs = newSigs
	}
	program := sw.Stage(tpl, index)
	h := sha3.Sum256(program)
	for i, keyID := range sw.Keys {
		if len(sw.Sigs[i]) > 0 {
			// Already have a signature for this key
			continue
		}
		sigBytes, err := signFn(ctx, keyID.XPub, keyID.DerivationPath, h)
		if err != nil {
			return errors.WithDetailf(err, "computing signature %d", i)
		}
		sw.Sigs[i] = sigBytes
	}
	return nil
}

func (sw SignatureWitness) Materialize(tpl *Template, index int) ([][]byte, error) {
	added := 0
	result := make([][]byte, 0, 1+len(sw.Keys))
	for _, s := range sw.Sigs {
		if len(s) == 0 {
			continue
		}
		result = append(result, s)
		added++
		if added >= sw.Quorum {
			break
		}
	}
	if added < sw.Quorum {
		return nil, errors.WithDetailf(ErrMissingSig, "requires %d signature(s), got %d", sw.Quorum, added)
	}
	program := sw.Stage(tpl, index)
	result = append(result, program)
	return result, nil
}

func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
	obj := struct {
		Type        string               `json:"type"`
		Quorum      int                  `json:"quorum"`
		Keys        []KeyID              `json:"keys"`
		Constraints []Constraint         `json:"constraints"`
		Sigs        []chainjson.HexBytes `json:"signatures"`
	}{
		Type:        "signature",
		Quorum:      sw.Quorum,
		Keys:        sw.Keys,
		Constraints: sw.Constraints,
		Sigs:        sw.Sigs,
	}
	return json.Marshal(obj)
}

func (sw *SignatureWitness) UnmarshalJSON(b []byte) error {
	var pre struct {
		Quorum      int     `json:"quorum"`
		Keys        []KeyID `json:"keys"`
		Constraints []json.RawMessage
		Sigs        []chainjson.HexBytes `json:"signatures"`
	}
	err := json.Unmarshal(b, &pre)
	if err != nil {
		return err
	}
	sw.Quorum = pre.Quorum
	sw.Keys = pre.Keys
	sw.Sigs = pre.Sigs
	for i, c := range pre.Constraints {
		var t struct {
			Type string `json:"type"`
		}
		err = json.Unmarshal(c, &t)
		if err != nil {
			return err
		}
		var constraint Constraint
		switch t.Type {
		case "transaction_id":
			var txhash struct {
				Hash bc.Hash `json:"transaction_id"`
			}
			err = json.Unmarshal(c, &txhash)
			if err != nil {
				return err
			}
			constraint = TxHashConstraint(txhash.Hash)
		default:
			return errors.WithDetailf(ErrBadConstraint, "constraint %d has unknown type '%s'", i, t.Type)
		}
		sw.Constraints = append(sw.Constraints, constraint)
	}
	return nil
}

func (inp *Input) AddWitnessData(data []byte) {
	inp.WitnessComponents = append(inp.WitnessComponents, DataWitness(data))
}

func (inp *Input) AddWitnessKeys(keys []KeyID, quorum int, constraints []Constraint) {
	sw := &SignatureWitness{
		Quorum:      quorum,
		Keys:        keys,
		Constraints: constraints,
	}
	inp.WitnessComponents = append(inp.WitnessComponents, sw)
}
<commit_msg>core/txbuilder: Remove Stage from the WitnessComponent interface<commit_after>package txbuilder

import (
	"context"
	"encoding/json"

	"golang.org/x/crypto/sha3"

	chainjson "chain/encoding/json"
	"chain/errors"
	"chain/protocol/bc"
	"chain/protocol/vm"
)

// WitnessComponent encodes instructions for finalizing a transaction
// by populating its InputWitness fields. Each WitnessComponent object
// produces zero or more items for the InputWitness of the txinput it
// corresponds to.
type WitnessComponent interface {
	// Sign is called to add signatures. Actual signing is delegated to
	// a callback function.
	Sign(context.Context, *Template, int, func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error

	// Materialize is called to turn the component into a vector of
	// arguments for the input witness.
	Materialize(*Template, int) ([][]byte, error)
}

// MaterializeWitnesses takes a filled in Template and "materializes"
// each witness component, turning it into a vector of arguments for
// the tx's input witness, creating a fully-signed transaction.
func MaterializeWitnesses(txTemplate *Template) (*bc.Tx, error) {
	msg := txTemplate.Unsigned
	for i, input := range txTemplate.Inputs {
		if msg.Inputs[input.Position] == nil {
			return nil, errors.WithDetailf(ErrBadTxInputIdx, "input %d references missing tx input %d", i, input.Position)
		}

		var witness [][]byte
		for j, c := range input.WitnessComponents {
			items, err := c.Materialize(txTemplate, i)
			if err != nil {
				return nil, errors.WithDetailf(err, "error in witness component %d of input %d", j, i)
			}
			witness = append(witness, items...)
		}

		msg.Inputs[input.Position].InputWitness = witness
	}

	return bc.NewTx(*msg), nil
}

type DataWitness []byte

func (_ DataWitness) Sign(_ context.Context, _ *Template, _ int, _ func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error {
	return nil
}

func (d DataWitness) Materialize(_ *Template, _ int) ([][]byte, error) {
	return [][]byte{d}, nil
}

func (d DataWitness) MarshalJSON() ([]byte, error) {
	obj := struct {
		Type string             `json:"type"`
		Data chainjson.HexBytes `json:"data"`
	}{
		Type: "data",
		Data: chainjson.HexBytes(d),
	}
	return json.Marshal(obj)
}

type (
	SignatureWitness struct {
		// Quorum is the number of signatures required.
		Quorum int `json:"quorum"`

		// Keys are the identities of the keys to sign with.
		Keys []KeyID `json:"keys"`

		// Constraints is a list of constraints to express in the deferred
		// predicate in the txinput.
		Constraints []Constraint `json:"constraints"`

		// Sigs is the output of Sign, where program (the output of Stage)
		// is signed by each of the keys in Keys.
		Sigs []chainjson.HexBytes `json:"signatures"`
	}

	KeyID struct {
		XPub           string   `json:"xpub"`
		DerivationPath []uint32 `json:"derivation_path"`
	}
)

func (sw *SignatureWitness) stage(tpl *Template, index int) []byte {
	if len(sw.Constraints) == 0 {
		// When in doubt, commit to the hash of the current tx
		// TODO(bobg): When we add other Constraint types, require callers
		// to specify this explicitly rather than as a default.
		h := tpl.Hash(index, bc.SigHashAll)
		sw.Constraints = []Constraint{TxHashConstraint(h)}
	}
	var program []byte
	for i, c := range sw.Constraints {
		program = append(program, c.Code()...)
		if i < len(sw.Constraints)-1 { // leave the final bool on top of the stack
			program = append(program, byte(vm.OP_VERIFY))
		}
	}
	return program
}

func (sw *SignatureWitness) Sign(ctx context.Context, tpl *Template, index int, signFn func(context.Context, string, []uint32, [32]byte) ([]byte, error)) error {
	if len(sw.Sigs) < len(sw.Keys) {
		// Each key in sw.Keys will produce a signature in sw.Sigs. Make
		// sure there are enough slots in sw.Sigs and that we preserve any
		// sigs already present.
		newSigs := make([]chainjson.HexBytes, len(sw.Keys))
		copy(newSigs, sw.Sigs)
		sw.Sigs = newSigs
	}
	program := sw.stage(tpl, index)
	h := sha3.Sum256(program)
	for i, keyID := range sw.Keys {
		if len(sw.Sigs[i]) > 0 {
			// Already have a signature for this key
			continue
		}
		sigBytes, err := signFn(ctx, keyID.XPub, keyID.DerivationPath, h)
		if err != nil {
			return errors.WithDetailf(err, "computing signature %d", i)
		}
		sw.Sigs[i] = sigBytes
	}
	return nil
}

func (sw SignatureWitness) Materialize(tpl *Template, index int) ([][]byte, error) {
	added := 0
	result := make([][]byte, 0, 1+len(sw.Keys))
	for _, s := range sw.Sigs {
		if len(s) == 0 {
			continue
		}
		result = append(result, s)
		added++
		if added >= sw.Quorum {
			break
		}
	}
	if added < sw.Quorum {
		return nil, errors.WithDetailf(ErrMissingSig, "requires %d signature(s), got %d", sw.Quorum, added)
	}
	program := sw.stage(tpl, index)
	result = append(result, program)
	return result, nil
}

func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
	obj := struct {
		Type        string               `json:"type"`
		Quorum      int                  `json:"quorum"`
		Keys        []KeyID              `json:"keys"`
		Constraints []Constraint         `json:"constraints"`
		Sigs        []chainjson.HexBytes `json:"signatures"`
	}{
		Type:        "signature",
		Quorum:      sw.Quorum,
		Keys:        sw.Keys,
		Constraints: sw.Constraints,
		Sigs:        sw.Sigs,
	}
	return json.Marshal(obj)
}

func (sw *SignatureWitness) UnmarshalJSON(b []byte) error {
	var pre struct {
		Quorum      int     `json:"quorum"`
		Keys        []KeyID `json:"keys"`
		Constraints []json.RawMessage
		Sigs        []chainjson.HexBytes `json:"signatures"`
	}
	err := json.Unmarshal(b, &pre)
	if err != nil {
		return err
	}
	sw.Quorum = pre.Quorum
	sw.Keys = pre.Keys
	sw.Sigs = pre.Sigs
	for i, c := range pre.Constraints {
		var t struct {
			Type string `json:"type"`
		}
		err = json.Unmarshal(c, &t)
		if err != nil {
			return err
		}
		var constraint Constraint
		switch t.Type {
		case "transaction_id":
			var txhash struct {
				Hash bc.Hash `json:"transaction_id"`
			}
			err = json.Unmarshal(c, &txhash)
			if err != nil {
				return err
			}
			constraint = TxHashConstraint(txhash.Hash)
		default:
			return errors.WithDetailf(ErrBadConstraint, "constraint %d has unknown type '%s'", i, t.Type)
		}
		sw.Constraints = append(sw.Constraints, constraint)
	}
	return nil
}

func (inp *Input) AddWitnessData(data []byte) {
	inp.WitnessComponents = append(inp.WitnessComponents, DataWitness(data))
}

func (inp *Input) AddWitnessKeys(keys []KeyID, quorum int, constraints []Constraint) {
	sw := &SignatureWitness{
		Quorum:      quorum,
		Keys:        keys,
		Constraints: constraints,
	}
	inp.WitnessComponents = append(inp.WitnessComponents, sw)
}
<|endoftext|>


<commit_before>/*
Copyright The Helm 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 portforwarder

import (
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
)

func mockTillerPod() v1.Pod {
	return v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "orca",
			Namespace: v1.NamespaceDefault,
			Labels:    tillerPodLabels,
		},
		Status: v1.PodStatus{
			Phase: v1.PodRunning,
			Conditions: []v1.PodCondition{
				{
					Status: v1.ConditionTrue,
					Type:   v1.PodReady,
				},
			},
		},
	}
}

func mockTillerPodPending() v1.Pod {
	p := mockTillerPod()
	p.Name = "blue"
	p.Status.Conditions[0].Status = v1.ConditionFalse
	return p
}

func TestGetFirstPod(t *testing.T) {
	tests := []struct {
		name     string
		pods     []v1.Pod
		expected string
		err      bool
	}{
		{
			name:     "with a ready pod",
			pods:     []v1.Pod{mockTillerPod()},
			expected: "orca",
		},
		{
			name: "without a ready pod",
			pods: []v1.Pod{mockTillerPodPending()},
			err:  true,
		},
		{
			name: "without a pod",
			pods: []v1.Pod{},
			err:  true,
		},
	}

	for _, tt := range tests {
		client := fake.NewSimpleClientset(&v1.PodList{Items: tt.pods})
		name, err := GetTillerPodName(client.CoreV1(), v1.NamespaceDefault)
		if (err != nil) != tt.err {
			t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err)
		}
		if name != tt.expected {
			t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, name)
		}
	}
}
<commit_msg>add unit tests for portforwarder (#4979)<commit_after>/*
Copyright The Helm 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 portforwarder

import (
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
)

func mockTillerPod() v1.Pod {
	return v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "orca",
			Namespace: v1.NamespaceDefault,
			Labels:    tillerPodLabels,
		},
		Status: v1.PodStatus{
			Phase: v1.PodRunning,
			Conditions: []v1.PodCondition{
				{
					Status: v1.ConditionTrue,
					Type:   v1.PodReady,
				},
			},
		},
	}
}

func mockTillerPodPending() v1.Pod {
	p := mockTillerPod()
	p.Name = "blue"
	p.Status.Conditions[0].Status = v1.ConditionFalse
	return p
}

func TestGetFirstPod(t *testing.T) {
	tests := []struct {
		name     string
		pods     []v1.Pod
		expected string
		err      bool
	}{
		{
			name:     "with a ready pod",
			pods:     []v1.Pod{mockTillerPod()},
			expected: "orca",
		},
		{
			name: "without a ready pod",
			pods: []v1.Pod{mockTillerPodPending()},
			err:  true,
		},
		{
			name: "without a pod",
			pods: []v1.Pod{},
			err:  true,
		},
	}

	for _, tt := range tests {
		client := fake.NewSimpleClientset(&v1.PodList{Items: tt.pods})
		name, err := GetTillerPodName(client.CoreV1(), v1.NamespaceDefault)
		if (err != nil) != tt.err {
			t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err)
		}
		if name != tt.expected {
			t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, name)
		}
	}
}

func TestGetTillerPodImage(t *testing.T) {
	tests := []struct {
		name     string
		podSpec  v1.PodSpec
		expected string
		err      bool
	}{
		{
			name: "pod with tiller container image",
			podSpec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "tiller",
						Image: "gcr.io/kubernetes-helm/tiller:v2.0.0",
					},
				},
			},
			expected: "gcr.io/kubernetes-helm/tiller:v2.0.0",
			err:      false,
		},
		{
			name: "pod without tiller container image",
			podSpec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "not_tiller",
						Image: "gcr.io/kubernetes-helm/not_tiller:v1.0.0",
					},
				},
			},
			expected: "",
			err:      true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mockPod := mockTillerPod()
			mockPod.Spec = tt.podSpec
			client := fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{mockPod}})
			imageName, err := GetTillerPodImage(client.CoreV1(), v1.NamespaceDefault)
			if (err != nil) != tt.err {
				t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err)
			}
			if imageName != tt.expected {
				t.Errorf("%q. expected %q, got %q", tt.name, tt.expected, imageName)
			}
		})
	}
}
<|endoftext|>


<commit_before>package request

import (
	"errors"
	"memmi/card"
	"memmi/pbuf"
	"testing"
)

func getCardSetMocked() (RequestHandler, *MockProtoIO, *card.MockCardManagement) {
	handler := &CardSetRequestHandler{}
	pio := &MockProtoIO{}
	cardMan := &card.MockCardManagement{}
	handler.Pio = pio
	handler.CardMan = cardMan
	return handler, pio, cardMan
}

func Test_CardSetHandler_ExactUrl_ShouldHandle(t *testing.T) {
	var req = RequestFromURL(CARD_SET_API_URL)
	handler := CardSetRequestHandler{}
	if !handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should handle with URL:", CARD_API_URL)
	}
}

func Test_CardSetHandler_URLPlusQuery_ShouldHandle(t *testing.T) {
	test_url := CARD_SET_API_URL + "?asdf"
	var req = RequestFromURL(test_url)
	handler := CardSetRequestHandler{}
	if !handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should handle with URL:", test_url)
	}
}

func Test_CardSetHandler_URLPlusSubUrl_ShouldHandle(t *testing.T) {
	test_url := CARD_SET_API_URL + "/asdf/fffa"
	var req = RequestFromURL(test_url)
	handler := CardSetRequestHandler{}
	if !handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should handle with URL:", test_url)
	}
}

func Test_CardSetHandler_AnyDifferentPrefix_ShouldNotHandle(t *testing.T) {
	test_url := "/test" + CARD_SET_API_URL
	var req = RequestFromURL(test_url)
	handler := CardSetRequestHandler{}
	if handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should not handle with URL:", test_url)
	}
}

func Test_CardSetHandler_GetCardSet_ProtoIOReadError_WriteError(t *testing.T) {
	var handler, pio, cm = getCardSetMocked()
	var req = RequestFromURL(GET_CARDSET_URL)
	testUser := pbuf.User{}
	pio.CardSetError = errors.New("")

	handler.Handle(nil, req, testUser)

	if len(pio.MessageWrites) != 1 {
		t.Fatal("There should be one write to proto io, got:", len(pio.MessageWrites))
	}

	if pio.MessageWrites[0] != BODY_READ_ERROR {
		t.Error("Wrong error type written to proto io.",
			"Expected:", BODY_READ_ERROR,
			"Got:", pio.MessageWrites[0])
	}

	if cm.TotalCalls() != 0 {
		t.Error("Expected total calls to card management to be zero. Got: ", cm.TotalCalls())
	}

}
<commit_msg>Added more tests for card set handler.<commit_after>package request

import (
	"errors"
	"memmi/card"
	"memmi/pbuf"
	"testing"
)

func getCardSetMocked() (RequestHandler, *MockProtoIO, *card.MockCardManagement) {
	handler := &CardSetRequestHandler{}
	pio := &MockProtoIO{}
	cardMan := &card.MockCardManagement{}
	handler.Pio = pio
	handler.CardMan = cardMan
	return handler, pio, cardMan
}

func Test_CardSetHandler_ExactUrl_ShouldHandle(t *testing.T) {
	var req = RequestFromURL(CARD_SET_API_URL)
	handler := CardSetRequestHandler{}
	if !handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should handle with URL:", CARD_API_URL)
	}
}

func Test_CardSetHandler_URLPlusQuery_ShouldHandle(t *testing.T) {
	test_url := CARD_SET_API_URL + "?asdf"
	var req = RequestFromURL(test_url)
	handler := CardSetRequestHandler{}
	if !handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should handle with URL:", test_url)
	}
}

func Test_CardSetHandler_URLPlusSubUrl_ShouldHandle(t *testing.T) {
	test_url := CARD_SET_API_URL + "/asdf/fffa"
	var req = RequestFromURL(test_url)
	handler := CardSetRequestHandler{}
	if !handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should handle with URL:", test_url)
	}
}

func Test_CardSetHandler_AnyDifferentPrefix_ShouldNotHandle(t *testing.T) {
	test_url := "/test" + CARD_SET_API_URL
	var req = RequestFromURL(test_url)
	handler := CardSetRequestHandler{}
	if handler.ShouldHandle(req, pbuf.User{}, false) {
		t.Error("Handler should not handle with URL:", test_url)
	}
}

func Test_CardSetHandler_GetCardSet_ProtoIOReadError_WriteError(t *testing.T) {
	var handler, pio, cm = getCardSetMocked()
	var req = RequestFromURL(GET_CARDSET_URL)
	testUser := pbuf.User{}
	pio.CardSetError = errors.New("")

	handler.Handle(nil, req, testUser)

	if len(pio.MessageWrites) != 1 {
		t.Fatal("There should be one write to proto io, got:", len(pio.MessageWrites))
	}

	if pio.MessageWrites[0] != BODY_READ_ERROR {
		t.Error("Wrong error type written to proto io.",
			"Expected:", BODY_READ_ERROR,
			"Got:", pio.MessageWrites[0])
	}

	if cm.TotalCalls() != 0 {
		t.Error("Expected total calls to card management to be zero. Got: ", cm.TotalCalls())
	}

}

func Test_CardSetHandler_GetCardSet_RequestPassed(t *testing.T) {
	var handler, pio, _ = getCardSetMocked()
	var req = RequestFromURL(GET_CARDSET_URL)
	testUser := pbuf.User{}
	handler.Handle(nil, req, testUser)

	if len(pio.CardSetRequests) != 1 {
		t.Fatal("There should have been one request passed to proto io. Received: ", len(pio.CardSetRequests))
	}

	if pio.CardSetRequests[0] != req {
		t.Error("Wrong request passed to proto io.",
			"Expected:", req,
			"Got:", pio.CardSetRequests[0])
	}
}

func Test_CardSetHandler_GetCardSet_NoError_HandledCorrectly(t *testing.T) {
	var handler, pio, cm = getCardSetMocked()
	var req = RequestFromURL(GET_CARDSET_URL)
	testUser := pbuf.User{}
	testCardSetRequest := pbuf.CardSetRequest{Id: []byte{3, 7, 9}}
	testCardSet := pbuf.CardSet{SetName: "TestCard"}

	cm.ReturnCardSet = testCardSet
	pio.CardSetReturn = testCardSetRequest
	handler.Handle(nil, req, testUser)

	if len(pio.MessageWrites) != 1 {
		t.Fatal("There should be one write to proto io, got:", len(pio.MessageWrites))
	}

	if pio.MessageWrites[0].String() != testCardSet.String() {
		t.Error("Wrong message written to proto io.",
			"Expected:", testCardSet.String(),
			"Got:", pio.MessageWrites[0].String())
	}

	if cm.TotalCalls() != 1 {
		t.Fatal("Expected total calls to card management to be one. Got: ", cm.TotalCalls())
	}

	if !CompareByteSlices(testCardSetRequest.Id, cm.GetCardSetIds[0]) {
		t.Error("Wrong cardSetId passed to card management.",
			"Expected:", testCardSetRequest.Id,
			"Got:", cm.GetCardSetIds[0])
	}
}

func Test_CardSetHandler_GetCard_ProtoIOReadError_WriteError(t *testing.T) {
	var handler, pio, cm = getCardSetMocked()
	var req = RequestFromURL(GET_CARD_URL)
	testUser := pbuf.User{}
	pio.CardError = errors.New("")

	handler.Handle(nil, req, testUser)

	if len(pio.MessageWrites) != 1 {
		t.Fatal("There should be one write to proto io, got:", len(pio.MessageWrites))
	}

	if pio.MessageWrites[0] != BODY_READ_ERROR {
		t.Error("Wrong error type written to proto io.",
			"Expected:", BODY_READ_ERROR,
			"Got:", pio.MessageWrites[0])
	}

	if cm.TotalCalls() != 0 {
		t.Error("Expected total calls to card management to be zero. Got: ", cm.TotalCalls())
	}
}

func Test_CardSetHandler_GetCard_RequestPassed(t *testing.T) {
	var handler, pio, _ = getCardSetMocked()
	var req = RequestFromURL(GET_CARD_URL)
	testUser := pbuf.User{}
	handler.Handle(nil, req, testUser)

	if len(pio.CardRequests) != 1 {
		t.Fatal("There should have been one request passed to proto io. Received: ", len(pio.CardRequests))
	}

	if pio.CardRequests[0] != req {
		t.Error("Wrong request passed to proto io.",
			"Expected:", req,
			"Got:", pio.CardRequests[0])
	}
}

func Test_CardSetHandler_GetCard_NoError_HandledCorrectly(t *testing.T) {
	var handler, pio, cm = getCardSetMocked()
	var req = RequestFromURL(GET_CARD_URL)
	testUser := pbuf.User{}
	testCardRequest := pbuf.CardRequest{Id: []byte{3, 7, 9}}
	testCard := pbuf.Card{Title: "TestCard"}

	cm.ReturnCard = testCard
	pio.CardReturn = testCardRequest
	handler.Handle(nil, req, testUser)

	if len(pio.MessageWrites) != 1 {
		t.Fatal("There should be one write to proto io, got:", len(pio.MessageWrites))
	}

	if pio.MessageWrites[0].String() != testCard.String() {
		t.Error("Wrong message written to proto io.",
			"Expected:", testCard.String(),
			"Got:", pio.MessageWrites[0].String())
	}

	if cm.TotalCalls() != 1 {
		t.Fatal("Expected total calls to card management to be one. Got: ", cm.TotalCalls())
	}

	if !CompareByteSlices(testCardRequest.Id, cm.GetCardIds[0]) {
		t.Error("Wrong cardId passed to card management.",
			"Expected:", testCardRequest.Id,
			"Got:", cm.GetCardIds[0])
	}
}
<|endoftext|>


<commit_before>// freenovel project freenovel.go
package freenovel

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/axgle/mahonia"
)

type Novel struct {
	wetsite         string
	charset         string
	menuRefer       string
	noveName        string
	menuList        string
	chtRefer        string
	chtTitle        string
	chtContent      string
	chtContentStrip string
}

type bookInfo struct {
	name        string
	chtNameList []string
	chtUrlList  []string
}

var mapNovel map[string]*Novel = make(map[string]*Novel)
var chtReplacer = strings.NewReplacer("
", "\r\n", "
", "\r\n", "
", "\r\n")

func init() {
	mapNovel["www.xxbiquge.com"] = &Novel{
		wetsite:         "www.xxbiquge.com",
		charset:         "utf-8",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.zwdu.com"] = &Novel{
		wetsite:         "www.zwdu.com",
		charset:         "gbk",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.23us.com"] = &Novel{
		wetsite:         "www.23us.com",
		charset:         "gbk",
		menuRefer:       "",
		noveName:        "div.bdsub dl dd h1",
		menuList:        "#at tbody tr td a",
		chtRefer:        "",
		chtTitle:        "div.bdsub dl dd",
		chtContent:      "#contents",
		chtContentStrip: "顶点小说 ２３ＵＳ．ＣＯＭ更新最快",
	}

	mapNovel["www.88dushu.com"] = &Novel{
		wetsite:         "www.88dushu.com",
		charset:         "gbk",
		menuRefer:       "",
		noveName:        "div.rt h1",
		menuList:        "div.mulu ul li a",
		chtRefer:        "",
		chtTitle:        "div.novel h1",
		chtContent:      "div.yd_text2",
		chtContentStrip: "",
	}

	mapNovel["www.qu.la"] = &Novel{
		wetsite:         "www.qu.la",
		charset:         "utf-8",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.biqudao.com"] = &Novel{
		wetsite:         "www.biqudao.com",
		charset:         "utf-8",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.shoujikanshu.org"] = &Novel{
		wetsite:         "www.shoujikanshu.org",
		charset:         "gb2312",
		menuRefer:       "",
		noveName:        "div.box-artic h1",
		menuList:        "div.list li a",
		chtRefer:        "",
		chtTitle:        "div.subNav h1",
		chtContent:      "div.content",
		chtContentStrip: "",
	}

}

func viewSource(strUrl, charset string, outBuf *bytes.Buffer, hc *http.Client, tryCount int) {
	outBuf.Reset()
	nTry := 0
	if tryCount < 1 {
		tryCount = 1
	}
RETRYGET:
	func() {
		rsp, err := hc.Get(strUrl)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rsp.Body.Close()

		p, err := ioutil.ReadAll(rsp.Body)
		if err != nil {
			fmt.Println(err)
			return
		}

		switch charset {
		case "gb2312":
			outBuf.WriteString(mahonia.NewDecoder("gbk").ConvertByte(p))
		case "gbk":
			outBuf.WriteString(mahonia.NewDecoder("gbk").ConvertByte(p))
		case "gb18030":
			outBuf.WriteString(mahonia.NewDecoder("gb18030").ConvertByte(p))
		case "utf-16":
			outBuf.WriteString(mahonia.NewDecoder("utf-16").ConvertByte(p))
		default:
			outBuf.Write(p)
		}
	}()

	if outBuf.Len() == 0 && nTry < tryCount {
		nTry += 1
		goto RETRYGET
	}
}

func getBookInfo(bi *bookInfo, nl *Novel, noveUrl string) bool {
	hc := &http.Client{}
	buf := &bytes.Buffer{}
	viewSource(noveUrl, nl.charset, buf, hc, 3)

	doc, err := goquery.NewDocumentFromReader(buf)
	if err != nil {
		fmt.Println(err)
		return false
	}

	bi.name = doc.Find(nl.noveName).Text()
	nodes := doc.Find(nl.menuList)

	itemCount := nodes.Length()
	if itemCount <= 0 {
		return false
	}

	strPreUrl := ""
	strItemLink := "href"
	if strUrl, ok := nodes.Eq(0).Attr(strItemLink); ok {
		if strUrl[0] == '/' {
			strPreUrl = "http://" + nl.wetsite
		} else {
			urlIdx := strings.LastIndex(noveUrl, "/")
			strPreUrl = noveUrl[0 : urlIdx+1]
		}
	}

	for i := 0; i < itemCount; i++ {
		v := nodes.Eq(i)
		strTitle := v.Text()
		strUrl, _ := v.Attr(strItemLink)
		if strTitle != "" {
			bi.chtUrlList = append(bi.chtUrlList, strPreUrl+strUrl)
			bi.chtNameList = append(bi.chtNameList, strTitle)
		}
	}

	return true
}

func NovelDownload(noveUrl string) bool {
	u, err := url.Parse(noveUrl)
	if err != nil {
		fmt.Println(err)
		return false
	}

	nitem, ok := mapNovel[u.Host]
	if !ok {
		fmt.Println("not supported website:", noveUrl)
		return false
	}

	bi := bookInfo{}

	if !getBookInfo(&bi, nitem, noveUrl) {
		fmt.Println("parse website tag err")
		return false
	}

	hc := &http.Client{}
	buf := &bytes.Buffer{}

	f, err := os.Create(bi.name + ".txt")
	if err != nil {
		fmt.Println(err)
		return false
	}
	defer f.Close()

	nChapter := len(bi.chtUrlList)
	for i := 0; i < nChapter; i++ {
		func(strTitle, strUrl string) {
			viewSource(strUrl, nitem.charset, buf, hc, 3)
			doc, err := goquery.NewDocumentFromReader(buf)
			if err != nil {
				fmt.Println(err)
				return
			}

			strContentHtml, _ := doc.Find(nitem.chtContent).Html()
			strContent := chtReplacer.Replace(strContentHtml)
			if nitem.chtContentStrip != "" {
				strContent = strings.Replace(strContent, nitem.chtContentStrip, "", -1)
			}

			if strContent == "" {
				fmt.Println("get charpter error:", strTitle, strUrl)
			}

			f.WriteString(strTitle + "\r\n\r\n")
			f.WriteString(strContent)
			f.WriteString("\r\n")
			fmt.Println(i+1, "/", nChapter, strTitle, strUrl)
			f.Sync()
		}(bi.chtNameList[i], bi.chtUrlList[i])
	}

	return true
}
<commit_msg>add decoder gb2312<commit_after>// freenovel project freenovel.go
package freenovel

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/axgle/mahonia"
)

type novel struct {
	wetsite         string
	charset         string
	menuRefer       string
	noveName        string
	menuList        string
	chtRefer        string
	chtTitle        string
	chtContent      string
	chtContentStrip string
}

type bookInfo struct {
	name        string
	chtNameList []string
	chtUrlList  []string
}

var mapNovel map[string]*novel = make(map[string]*novel)
var chtReplacer = strings.NewReplacer("
", "\r\n", "
", "\r\n", "
", "\r\n")

func init() {
	mapNovel["www.xxbiquge.com"] = &novel{
		wetsite:         "www.xxbiquge.com",
		charset:         "utf-8",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.zwdu.com"] = &novel{
		wetsite:         "www.zwdu.com",
		charset:         "gbk",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.23us.com"] = &novel{
		wetsite:         "www.23us.com",
		charset:         "gbk",
		menuRefer:       "",
		noveName:        "div.bdsub dl dd h1",
		menuList:        "#at tbody tr td a",
		chtRefer:        "",
		chtTitle:        "div.bdsub dl dd",
		chtContent:      "#contents",
		chtContentStrip: "顶点小说 ２３ＵＳ．ＣＯＭ更新最快",
	}

	mapNovel["www.88dushu.com"] = &novel{
		wetsite:         "www.88dushu.com",
		charset:         "gbk",
		menuRefer:       "",
		noveName:        "div.rt h1",
		menuList:        "div.mulu ul li a",
		chtRefer:        "",
		chtTitle:        "div.novel h1",
		chtContent:      "div.yd_text2",
		chtContentStrip: "",
	}

	mapNovel["www.qu.la"] = &novel{
		wetsite:         "www.qu.la",
		charset:         "utf-8",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.biqudao.com"] = &novel{
		wetsite:         "www.biqudao.com",
		charset:         "utf-8",
		menuRefer:       "",
		noveName:        "#info h1",
		menuList:        "#list dl dd a",
		chtRefer:        "",
		chtTitle:        "div.bookname h1",
		chtContent:      "#content",
		chtContentStrip: "",
	}

	mapNovel["www.shoujikanshu.org"] = &novel{
		wetsite:         "www.shoujikanshu.org",
		charset:         "gb2312",
		menuRefer:       "",
		noveName:        "div.box-artic h1",
		menuList:        "div.list li a",
		chtRefer:        "",
		chtTitle:        "div.subNav h1",
		chtContent:      "div.content",
		chtContentStrip: "",
	}

}

func viewSource(strUrl, charset string, outBuf *bytes.Buffer, hc *http.Client, tryCount int) {
	outBuf.Reset()
	nTry := 0
	if tryCount < 1 {
		tryCount = 1
	}
RETRYGET:
	func() {
		rsp, err := hc.Get(strUrl)
		if err != nil {
			fmt.Println(err)
			return
		}
		defer rsp.Body.Close()

		p, err := ioutil.ReadAll(rsp.Body)
		if err != nil {
			fmt.Println(err)
			return
		}

		switch charset {
		case "gb2312":
			outBuf.WriteString(mahonia.NewDecoder("gbk").ConvertByte(p))
		case "gbk":
			outBuf.WriteString(mahonia.NewDecoder("gbk").ConvertByte(p))
		case "gb18030":
			outBuf.WriteString(mahonia.NewDecoder("gb18030").ConvertByte(p))
		case "utf-16":
			outBuf.WriteString(mahonia.NewDecoder("utf-16").ConvertByte(p))
		default:
			outBuf.Write(p)
		}
	}()

	if outBuf.Len() == 0 && nTry < tryCount {
		nTry += 1
		goto RETRYGET
	}
}

func getBookInfo(bi *bookInfo, nl *novel, noveUrl string) bool {
	hc := &http.Client{}
	buf := &bytes.Buffer{}
	viewSource(noveUrl, nl.charset, buf, hc, 3)

	doc, err := goquery.NewDocumentFromReader(buf)
	if err != nil {
		fmt.Println(err)
		return false
	}

	bi.name = doc.Find(nl.noveName).Text()
	nodes := doc.Find(nl.menuList)

	itemCount := nodes.Length()
	if itemCount <= 0 {
		return false
	}

	strPreUrl := ""
	strItemLink := "href"
	if strUrl, ok := nodes.Eq(0).Attr(strItemLink); ok {
		if strUrl[0] == '/' {
			strPreUrl = "http://" + nl.wetsite
		} else {
			urlIdx := strings.LastIndex(noveUrl, "/")
			strPreUrl = noveUrl[0 : urlIdx+1]
		}
	}

	for i := 0; i < itemCount; i++ {
		v := nodes.Eq(i)
		strTitle := v.Text()
		strUrl, _ := v.Attr(strItemLink)
		if strTitle != "" {
			bi.chtUrlList = append(bi.chtUrlList, strPreUrl+strUrl)
			bi.chtNameList = append(bi.chtNameList, strTitle)
		}
	}

	return true
}

func NovelDownload(noveUrl string) bool {
	u, err := url.Parse(noveUrl)
	if err != nil {
		fmt.Println(err)
		return false
	}

	nitem, ok := mapNovel[u.Host]
	if !ok {
		fmt.Println("not supported website:", noveUrl)
		return false
	}

	bi := bookInfo{}

	if !getBookInfo(&bi, nitem, noveUrl) {
		fmt.Println("parse website tag err")
		return false
	}

	hc := &http.Client{}
	buf := &bytes.Buffer{}

	f, err := os.Create(bi.name + ".txt")
	if err != nil {
		fmt.Println(err)
		return false
	}
	defer f.Close()

	nChapter := len(bi.chtUrlList)
	for i := 0; i < nChapter; i++ {
		func(strTitle, strUrl string) {
			viewSource(strUrl, nitem.charset, buf, hc, 3)
			doc, err := goquery.NewDocumentFromReader(buf)
			if err != nil {
				fmt.Println(err)
				return
			}

			strContentHtml, _ := doc.Find(nitem.chtContent).Html()
			strContent := chtReplacer.Replace(strContentHtml)
			if nitem.chtContentStrip != "" {
				strContent = strings.Replace(strContent, nitem.chtContentStrip, "", -1)
			}

			if strContent == "" {
				fmt.Println("get charpter error:", strTitle, strUrl)
			}

			f.WriteString(strTitle + "\r\n\r\n")
			f.WriteString(strContent)
			f.WriteString("\r\n")
			fmt.Println(i+1, "/", nChapter, strTitle, strUrl)
			f.Sync()
		}(bi.chtNameList[i], bi.chtUrlList[i])
	}

	return true
}
<|endoftext|>


<commit_before>package azurerm

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"bytes"

	"github.com/Azure/azure-sdk-for-go/arm/web"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmAppServicePlan() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmAppServicePlanCreateUpdate,
		Read:   resourceArmAppServicePlanRead,
		Update: resourceArmAppServicePlanCreateUpdate,
		Delete: resourceArmAppServicePlanDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"location": locationSchema(),
			"sku": {
				Type:     schema.TypeSet,
				Required: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"tier": {
							Type:     schema.TypeString,
							Required: true,
						},
						"size": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
				Set: resourceAzureRMAppServicePlanSkuHash,
			},
			"maximum_number_of_workers": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceArmAppServicePlanCreateUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	AppServicePlanClient := client.appServicePlansClient
	//AppServicePlanClient := meta.(*ArmClient).appServicePlansClient

	log.Printf("[INFO] preparing arguments for AzureRM App Service Plan creation.")

	resGroup := d.Get("resource_group_name").(string)
	name := d.Get("name").(string)
	location := d.Get("location").(string)

	sku := expandAzureRmAppServicePlanSku(d)

	properties := web.AppServicePlanProperties{}
	if v, ok := d.GetOk("maximum_number_of_workers"); ok {
		maximumNumberOfWorkers := v.(int32)
		properties.MaximumNumberOfWorkers = &maximumNumberOfWorkers
	}

	appServicePlan := web.AppServicePlan{
		Location:                 &location,
		AppServicePlanProperties: &properties,
		Sku: &sku,
	}

	_, error := AppServicePlanClient.CreateOrUpdate(resGroup, name, appServicePlan, make(chan struct{}))
	err := <-error
	if err != nil {
		return err
	}

	read, err := AppServicePlanClient.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read AzureRM App Service Plan %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	log.Printf("[DEBUG] Waiting for App Service Plan (%s) to become available", name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"Accepted", "Updating"},
		Target:  []string{"Succeeded"},
		Refresh: appServicePlanStateRefreshFunc(client, resGroup, name),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for App Service Plan (%s) to become available: %s", name, err)
	}

	return resourceArmAppServicePlanRead(d, meta)
}

func resourceArmAppServicePlanRead(d *schema.ResourceData, meta interface{}) error {
	AppServicePlanClient := meta.(*ArmClient).appServicePlansClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Reading Azure App Service Plan %s", id)

	resGroup := id.ResourceGroup
	name := id.Path["serverfarms"]

	resp, err := AppServicePlanClient.Get(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error making Read request on Azure App Service Plan %s: %s", name, err)
	}

	d.Set("name", name)
	d.Set("resource_group_name", resGroup)
	d.Set("location", azureRMNormalizeLocation(*resp.Location))

	if props := resp.AppServicePlanProperties; props != nil {
		d.Set("maximum_number_of_workers", props.MaximumNumberOfWorkers)
	}

	sku := flattenAzureRmAppServicePlanSku(*resp.Sku)
	d.Set("sku", &sku)

	return nil
}

func resourceArmAppServicePlanDelete(d *schema.ResourceData, meta interface{}) error {
	AppServicePlanClient := meta.(*ArmClient).appServicePlansClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["serverfarms"]

	log.Printf("[DEBUG] Deleting app service plan %s: %s", resGroup, name)

	_, err = AppServicePlanClient.Delete(resGroup, name)

	return err
}

func resourceAzureRMAppServicePlanSkuHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	tier := m["tier"].(string)
	size := m["size"].(string)

	buf.WriteString(fmt.Sprintf("%s-", tier))
	buf.WriteString(fmt.Sprintf("%s-", size))

	return hashcode.String(buf.String())
}

func expandAzureRmAppServicePlanSku(d *schema.ResourceData) web.SkuDescription {
	configs := d.Get("sku").(*schema.Set).List()
	config := configs[0].(map[string]interface{})

	tier := config["tier"].(string)
	size := config["size"].(string)

	sku := web.SkuDescription{
		Name: &size,
		Tier: &tier,
		Size: &size,
	}

	return sku
}

func flattenAzureRmAppServicePlanSku(profile web.SkuDescription) *schema.Set {
	skus := &schema.Set{
		F: resourceAzureRMAppServicePlanSkuHash,
	}

	sku := make(map[string]interface{}, 3)

	sku["tier"] = *profile.Tier
	sku["size"] = *profile.Size

	skus.Add(sku)

	return skus
}
<commit_msg>formatting and removing old comments<commit_after>package azurerm

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"bytes"

	"github.com/Azure/azure-sdk-for-go/arm/web"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmAppServicePlan() *schema.Resource {
	return &schema.Resource{
		Create: resourceArmAppServicePlanCreateUpdate,
		Read:   resourceArmAppServicePlanRead,
		Update: resourceArmAppServicePlanCreateUpdate,
		Delete: resourceArmAppServicePlanDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"resource_group_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"location": locationSchema(),
			"sku": {
				Type:     schema.TypeSet,
				Required: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"tier": {
							Type:     schema.TypeString,
							Required: true,
						},
						"size": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
				Set: resourceAzureRMAppServicePlanSkuHash,
			},
			"maximum_number_of_workers": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func resourceArmAppServicePlanCreateUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*ArmClient)
	AppServicePlanClient := client.appServicePlansClient

	log.Printf("[INFO] preparing arguments for AzureRM App Service Plan creation.")

	resGroup := d.Get("resource_group_name").(string)
	name := d.Get("name").(string)
	location := d.Get("location").(string)

	sku := expandAzureRmAppServicePlanSku(d)

	properties := web.AppServicePlanProperties{}
	if v, ok := d.GetOk("maximum_number_of_workers"); ok {
		maximumNumberOfWorkers := v.(int32)
		properties.MaximumNumberOfWorkers = &maximumNumberOfWorkers
	}

	appServicePlan := web.AppServicePlan{
		Location:                 &location,
		AppServicePlanProperties: &properties,
		Sku: &sku,
	}

	_, error := AppServicePlanClient.CreateOrUpdate(resGroup, name, appServicePlan, make(chan struct{}))
	err := <-error
	if err != nil {
		return err
	}

	read, err := AppServicePlanClient.Get(resGroup, name)
	if err != nil {
		return err
	}
	if read.ID == nil {
		return fmt.Errorf("Cannot read AzureRM App Service Plan %s (resource group %s) ID", name, resGroup)
	}

	d.SetId(*read.ID)

	log.Printf("[DEBUG] Waiting for App Service Plan (%s) to become available", name)
	stateConf := &resource.StateChangeConf{
		Pending: []string{"Accepted", "Updating"},
		Target:  []string{"Succeeded"},
		Refresh: appServicePlanStateRefreshFunc(client, resGroup, name),
		Timeout: 10 * time.Minute,
	}
	if _, err := stateConf.WaitForState(); err != nil {
		return fmt.Errorf("Error waiting for App Service Plan (%s) to become available: %s", name, err)
	}

	return resourceArmAppServicePlanRead(d, meta)
}

func resourceArmAppServicePlanRead(d *schema.ResourceData, meta interface{}) error {
	AppServicePlanClient := meta.(*ArmClient).appServicePlansClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Reading Azure App Service Plan %s", id)

	resGroup := id.ResourceGroup
	name := id.Path["serverfarms"]

	resp, err := AppServicePlanClient.Get(resGroup, name)
	if err != nil {
		if resp.StatusCode == http.StatusNotFound {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error making Read request on Azure App Service Plan %s: %s", name, err)
	}

	d.Set("name", name)
	d.Set("resource_group_name", resGroup)
	d.Set("location", azureRMNormalizeLocation(*resp.Location))

	if props := resp.AppServicePlanProperties; props != nil {
		d.Set("maximum_number_of_workers", props.MaximumNumberOfWorkers)
	}

	sku := flattenAzureRmAppServicePlanSku(*resp.Sku)
	d.Set("sku", &sku)

	return nil
}

func resourceArmAppServicePlanDelete(d *schema.ResourceData, meta interface{}) error {
	AppServicePlanClient := meta.(*ArmClient).appServicePlansClient

	id, err := parseAzureResourceID(d.Id())
	if err != nil {
		return err
	}
	resGroup := id.ResourceGroup
	name := id.Path["serverfarms"]

	log.Printf("[DEBUG] Deleting app service plan %s: %s", resGroup, name)

	_, err = AppServicePlanClient.Delete(resGroup, name)

	return err
}

func resourceAzureRMAppServicePlanSkuHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	tier := m["tier"].(string)
	size := m["size"].(string)

	buf.WriteString(fmt.Sprintf("%s-", tier))
	buf.WriteString(fmt.Sprintf("%s-", size))

	return hashcode.String(buf.String())
}

func expandAzureRmAppServicePlanSku(d *schema.ResourceData) web.SkuDescription {
	configs := d.Get("sku").(*schema.Set).List()
	config := configs[0].(map[string]interface{})

	tier := config["tier"].(string)
	size := config["size"].(string)

	sku := web.SkuDescription{
		Name: &size,
		Tier: &tier,
		Size: &size,
	}

	return sku
}

func flattenAzureRmAppServicePlanSku(profile web.SkuDescription) *schema.Set {
	skus := &schema.Set{
		F: resourceAzureRMAppServicePlanSkuHash,
	}

	sku := make(map[string]interface{}, 3)

	sku["tier"] = *profile.Tier
	sku["size"] = *profile.Size

	skus.Add(sku)

	return skus
}
<|endoftext|>


<commit_before>package cassandra_store

import (
	"fmt"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"

	"github.com/gocql/gocql"
)

/*

Basically you need a table just like this:

CREATE TABLE seaweed_files (
   path varchar,
   fids list<varchar>,
   PRIMARY KEY (path)
);

Need to match flat_namespace.FlatNamespaceStore interface
	Put(fullFileName string, fid string) (err error)
	Get(fullFileName string) (fid string, err error)
	Delete(fullFileName string) (fid string, err error)

*/
type CassandraStore struct {
	cluster *gocql.ClusterConfig
	session *gocql.Session
}

func NewCassandraStore(keyspace string, hosts ...string) (c *CassandraStore, err error) {
	c = &CassandraStore{}
	c.cluster = gocql.NewCluster(hosts...)
	c.cluster.Keyspace = keyspace
	c.cluster.Consistency = gocql.Quorum
	c.session, err = c.cluster.CreateSession()
	if err != nil {
		glog.V(0).Infof("Failed to open cassandra store, hosts %v, keyspace %s", hosts, keyspace)
	}
	return
}

func (c *CassandraStore) Put(fullFileName string, fid string) (err error) {
	var input []string
	input = append(input, fid)
	if err := c.session.Query(
		`INSERT INTO seaweed_files (path, fids) VALUES (?, ?)`,
		fullFileName, input).Exec(); err != nil {
		glog.V(0).Infof("Failed to save file %s with id %s: %v", fullFileName, fid, err)
		return err
	}
	return nil
}
func (c *CassandraStore) Get(fullFileName string) (fid string, err error) {
	var output []string
	if err := c.session.Query(
		`select fids FROM seaweed_files WHERE path = ? LIMIT 1`,
		fullFileName).Consistency(gocql.One).Scan(&output); err != nil {
		if err != gocql.ErrNotFound {
			glog.V(0).Infof("Failed to find file %s: %v", fullFileName, fid, err)
			return "", filer.ErrNotFound
		}
	}
	if len(output) == 0 {
		return "", fmt.Errorf("No file id found for %s", fullFileName)
	}
	return output[0], nil
}

// Currently the fid is not returned
func (c *CassandraStore) Delete(fullFileName string) (err error) {
	if err := c.session.Query(
		`DELETE FROM seaweed_files WHERE path = ?`,
		fullFileName).Exec(); err != nil {
		if err != gocql.ErrNotFound {
			glog.V(0).Infof("Failed to delete file %s: %v", fullFileName, err)
		}
		return err
	}
	return nil
}

func (c *CassandraStore) Close() {
	if c.session != nil {
		c.session.Close()
	}
}
<commit_msg>update cassandra connections<commit_after>package cassandra_store

import (
	"fmt"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"

	"github.com/gocql/gocql"
)

/*

Basically you need a table just like this:

CREATE TABLE seaweed_files (
   path varchar,
   fids list<varchar>,
   PRIMARY KEY (path)
);

Need to match flat_namespace.FlatNamespaceStore interface
	Put(fullFileName string, fid string) (err error)
	Get(fullFileName string) (fid string, err error)
	Delete(fullFileName string) (fid string, err error)

*/
type CassandraStore struct {
	cluster *gocql.ClusterConfig
	session *gocql.Session
}

func NewCassandraStore(keyspace string, hosts ...string) (c *CassandraStore, err error) {
	c = &CassandraStore{}
	        s := strings.Split(hosts, ",")
        if len(s) == 1 {
                c.cluster = gocql.NewCluster(hosts...)
        } else if len(s) > 1 {
                c.cluster = gocql.NewCluster(s[0], s[1])
        }
	c.cluster.Keyspace = keyspace
	c.cluster.Consistency = gocql.Quorum
	c.session, err = c.cluster.CreateSession()
	if err != nil {
		glog.V(0).Infof("Failed to open cassandra store, hosts %v, keyspace %s", hosts, keyspace)
	}
	return
}

func (c *CassandraStore) Put(fullFileName string, fid string) (err error) {
	var input []string
	input = append(input, fid)
	if err := c.session.Query(
		`INSERT INTO seaweed_files (path, fids) VALUES (?, ?)`,
		fullFileName, input).Exec(); err != nil {
		glog.V(0).Infof("Failed to save file %s with id %s: %v", fullFileName, fid, err)
		return err
	}
	return nil
}
func (c *CassandraStore) Get(fullFileName string) (fid string, err error) {
	var output []string
	if err := c.session.Query(
		`select fids FROM seaweed_files WHERE path = ? LIMIT 1`,
		fullFileName).Consistency(gocql.One).Scan(&output); err != nil {
		if err != gocql.ErrNotFound {
			glog.V(0).Infof("Failed to find file %s: %v", fullFileName, fid, err)
			return "", filer.ErrNotFound
		}
	}
	if len(output) == 0 {
		return "", fmt.Errorf("No file id found for %s", fullFileName)
	}
	return output[0], nil
}

// Currently the fid is not returned
func (c *CassandraStore) Delete(fullFileName string) (err error) {
	if err := c.session.Query(
		`DELETE FROM seaweed_files WHERE path = ?`,
		fullFileName).Exec(); err != nil {
		if err != gocql.ErrNotFound {
			glog.V(0).Infof("Failed to delete file %s: %v", fullFileName, err)
		}
		return err
	}
	return nil
}

func (c *CassandraStore) Close() {
	if c.session != nil {
		c.session.Close()
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/deis/deis/client/parser"
	"github.com/deis/deis/version"
	docopt "github.com/docopt/docopt-go"
)

// main exits with the return value of Command(os.Args[1:]), deferring all logic to
// a func we can test.
func main() {
	os.Exit(Command(os.Args[1:]))
}

// Command routes deis commands to their proper parser.
func Command(argv []string) int {
	usage := `
The Deis command-line client issues API calls to a Deis controller.

Usage: deis <command> [<args>...]

Auth commands::

  register      register a new user with a controller
  login         login to a controller
  logout        logout from the current controller

Subcommands, use 'deis help [subcommand]' to learn more::

  apps          manage applications used to provide services
  ps            manage processes inside an app container
  config        manage environment variables that define app config
  domains       manage and assign domain names to your applications
  builds        manage builds created using 'git push'
  limits        manage resource limits for your application
  tags          manage tags for application containers
  releases      manage releases of an application
  certs         manage SSL endpoints for an app

  keys          manage ssh keys used for 'git push' deployments
  perms         manage permissions for applications
  git           manage git for applications
  users         manage users

Shortcut commands, use 'deis shortcuts' to see all::

  create        create a new application
  scale         scale processes by type (web=2, worker=1)
  info          view information about the current app
  open          open a URL to the app in a browser
  logs          view aggregated log info for the app
  run           run a command in an ephemeral app container
  destroy       destroy an application
  pull          imports an image and deploys as a new release

Use 'git push deis master' to deploy to an application.
`
	// Reorganize some command line flags and commands.
	command, argv := parseArgs(argv)
	// Give docopt an optional final false arg so it doesn't call os.Exit().
	_, err := docopt.Parse(usage, []string{command}, false, version.Version, true, false)

	if err != nil {
		fmt.Println(err)
		return 1
	}

	if len(argv) == 0 {
		return 0
	}

	// Dispatch the command, passing the argv through so subcommands can
	// re-parse it according to their usage strings.
	switch command {
	case "auth":
		err = parser.Auth(argv)
	case "ps":
		err = parser.Ps(argv)
	case "apps":
		err = parser.Apps(argv)
	case "config":
		err = parser.Config(argv)
	case "domains":
		err = parser.Domains(argv)
	case "builds":
		err = parser.Builds(argv)
	case "limits":
		err = parser.Limits(argv)
	case "tags":
		err = parser.Tags(argv)
	case "releases":
		err = parser.Releases(argv)
	case "certs":
		err = parser.Certs(argv)
	case "keys":
		err = parser.Keys(argv)
	case "perms":
		err = parser.Perms(argv)
	case "git":
		err = parser.Git(argv)
	case "users":
		err = parser.Users(argv)
	case "help":
		fmt.Print(usage)
		return 0
	case "--version":
		return 0
	default:
		env := os.Environ()
		extCmd := "deis-" + command

		binary, err := exec.LookPath(extCmd)
		if err != nil {
			parser.PrintUsage()
			return 1
		}

		cmdArgv := []string{extCmd}

		cmdSplit := strings.Split(argv[0], command+":")

		if len(cmdSplit) > 1 {
			argv[0] = cmdSplit[1]
		}

		cmdArgv = append(cmdArgv, argv...)

		err = syscall.Exec(binary, cmdArgv, env)
		if err != nil {
			parser.PrintUsage()
			return 1
		}
	}
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return 1
	}
	return 0
}

// parseArgs returns the provided args with "--help" as the last arg if need be,
// expands shortcuts and formats commands to be properly routed.
func parseArgs(argv []string) (string, []string) {
	if len(argv) == 1 {
		// rearrange "deis --help" as "deis help"
		if argv[0] == "--help" || argv[0] == "-h" {
			argv[0] = "help"
		}
	}

	if len(argv) >= 2 {
		// Rearrange "deis help <command>" to "deis <command> --help".
		if argv[0] == "help" || argv[0] == "--help" || argv[0] == "-h" {
			argv = append(argv[1:], "--help")
		}
	}

	if len(argv) > 0 {
		argv[0] = replaceShortcut(argv[0])

		index := strings.Index(argv[0], ":")

		if index != -1 {
			command := argv[0]
			return command[:index], argv
		}

		return argv[0], argv
	}

	return "", argv
}

func replaceShortcut(command string) string {
	shortcuts := map[string]string{
		"create":         "apps:create",
		"destroy":        "apps:destroy",
		"info":           "apps:info",
		"login":          "auth:login",
		"logout":         "auth:logout",
		"logs":           "apps:logs",
		"open":           "apps:open",
		"passwd":         "auth:passwd",
		"pull":           "builds:create",
		"register":       "auth:register",
		"rollback":       "releases:rollback",
		"run":            "apps:run",
		"scale":          "ps:scale",
		"sharing":        "perms:list",
		"sharing:list":   "perms:list",
		"sharing:add":    "perms:create",
		"sharing:remove": "perms:delete",
		"whoami":         "auth:whoami",
	}

	expandedCommand := shortcuts[command]
	if expandedCommand == "" {
		return command
	}

	return expandedCommand
}
<commit_msg>fix(client): print usage if no args were given<commit_after>package main

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

	"github.com/deis/deis/client/parser"
	"github.com/deis/deis/version"
	docopt "github.com/docopt/docopt-go"
)

// main exits with the return value of Command(os.Args[1:]), deferring all logic to
// a func we can test.
func main() {
	os.Exit(Command(os.Args[1:]))
}

// Command routes deis commands to their proper parser.
func Command(argv []string) int {
	usage := `
The Deis command-line client issues API calls to a Deis controller.

Usage: deis <command> [<args>...]

Auth commands::

  register      register a new user with a controller
  login         login to a controller
  logout        logout from the current controller

Subcommands, use 'deis help [subcommand]' to learn more::

  apps          manage applications used to provide services
  ps            manage processes inside an app container
  config        manage environment variables that define app config
  domains       manage and assign domain names to your applications
  builds        manage builds created using 'git push'
  limits        manage resource limits for your application
  tags          manage tags for application containers
  releases      manage releases of an application
  certs         manage SSL endpoints for an app

  keys          manage ssh keys used for 'git push' deployments
  perms         manage permissions for applications
  git           manage git for applications
  users         manage users

Shortcut commands, use 'deis shortcuts' to see all::

  create        create a new application
  scale         scale processes by type (web=2, worker=1)
  info          view information about the current app
  open          open a URL to the app in a browser
  logs          view aggregated log info for the app
  run           run a command in an ephemeral app container
  destroy       destroy an application
  pull          imports an image and deploys as a new release

Use 'git push deis master' to deploy to an application.
`
	// Reorganize some command line flags and commands.
	command, argv := parseArgs(argv)
	// Give docopt an optional final false arg so it doesn't call os.Exit().
	_, err := docopt.Parse(usage, []string{command}, false, version.Version, true, false)

	if err != nil {
		fmt.Println(err)
		return 1
	}

	if len(argv) == 0 {
		fmt.Println("Usage: deis <command> [<args>...]")
		return 1
	}

	// Dispatch the command, passing the argv through so subcommands can
	// re-parse it according to their usage strings.
	switch command {
	case "auth":
		err = parser.Auth(argv)
	case "ps":
		err = parser.Ps(argv)
	case "apps":
		err = parser.Apps(argv)
	case "config":
		err = parser.Config(argv)
	case "domains":
		err = parser.Domains(argv)
	case "builds":
		err = parser.Builds(argv)
	case "limits":
		err = parser.Limits(argv)
	case "tags":
		err = parser.Tags(argv)
	case "releases":
		err = parser.Releases(argv)
	case "certs":
		err = parser.Certs(argv)
	case "keys":
		err = parser.Keys(argv)
	case "perms":
		err = parser.Perms(argv)
	case "git":
		err = parser.Git(argv)
	case "users":
		err = parser.Users(argv)
	case "help":
		fmt.Print(usage)
		return 0
	case "--version":
		return 0
	default:
		env := os.Environ()
		extCmd := "deis-" + command

		binary, err := exec.LookPath(extCmd)
		if err != nil {
			parser.PrintUsage()
			return 1
		}

		cmdArgv := []string{extCmd}

		cmdSplit := strings.Split(argv[0], command+":")

		if len(cmdSplit) > 1 {
			argv[0] = cmdSplit[1]
		}

		cmdArgv = append(cmdArgv, argv...)

		err = syscall.Exec(binary, cmdArgv, env)
		if err != nil {
			parser.PrintUsage()
			return 1
		}
	}
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return 1
	}
	return 0
}

// parseArgs returns the provided args with "--help" as the last arg if need be,
// expands shortcuts and formats commands to be properly routed.
func parseArgs(argv []string) (string, []string) {
	if len(argv) == 1 {
		// rearrange "deis --help" as "deis help"
		if argv[0] == "--help" || argv[0] == "-h" {
			argv[0] = "help"
		}
	}

	if len(argv) >= 2 {
		// Rearrange "deis help <command>" to "deis <command> --help".
		if argv[0] == "help" || argv[0] == "--help" || argv[0] == "-h" {
			argv = append(argv[1:], "--help")
		}
	}

	if len(argv) > 0 {
		argv[0] = replaceShortcut(argv[0])

		index := strings.Index(argv[0], ":")

		if index != -1 {
			command := argv[0]
			return command[:index], argv
		}

		return argv[0], argv
	}

	return "", argv
}

func replaceShortcut(command string) string {
	shortcuts := map[string]string{
		"create":         "apps:create",
		"destroy":        "apps:destroy",
		"info":           "apps:info",
		"login":          "auth:login",
		"logout":         "auth:logout",
		"logs":           "apps:logs",
		"open":           "apps:open",
		"passwd":         "auth:passwd",
		"pull":           "builds:create",
		"register":       "auth:register",
		"rollback":       "releases:rollback",
		"run":            "apps:run",
		"scale":          "ps:scale",
		"sharing":        "perms:list",
		"sharing:list":   "perms:list",
		"sharing:add":    "perms:create",
		"sharing:remove": "perms:delete",
		"whoami":         "auth:whoami",
	}

	expandedCommand := shortcuts[command]
	if expandedCommand == "" {
		return command
	}

	return expandedCommand
}
<|endoftext|>