<commit_before>package brokerage_server_ib

import (
	"context"
	"sort"
	"sync"
	"time"

	"github.com/dimfeld/brokerage_server/types"
	"github.com/dimfeld/ib"
)

var (
	optionHistTypes = []ib.HistDataToShow{ib.HistBid, ib.HistAsk, ib.HistTrades}
)

func (p *IB) GetOptionsChain(ctx context.Context, underlying string) (types.OptionChain, error) {

	p.optionMetaCacheMutex.Lock()
	output := p.optionMetaCache[underlying]
	p.optionMetaCacheMutex.Unlock()

	if output != nil {
		return *output, nil
	}

	key := ContractKey{
		Symbol: underlying,
	}
	contractDetails, err := p.contractManager.GetContractDetails(ctx, key)
	if err != nil {
		return types.OptionChain{}, err
	}

	p.LogDebugNormal("Got contract details", "data", contractDetails)

	contract := contractDetails[0].Summary
	request := &ib.RequestSecDefOptParams{
		Symbol:     contract.Symbol,
		SecType:    contract.SecurityType,
		ContractId: contract.ContractID,
		// No exchange, since the options trade on a different exchange than the underlying.
		Exchange: "",
	}

	expirations := map[string]bool{}
	strikes := map[float64]bool{}
	exchanges := []string{}
	var multiplier string

	_, err = p.syncMatchedRequest(ctx, request, func(r ib.Reply) (replyBehavior, error) {
		switch data := r.(type) {
		case *ib.SecurityDefinitionOptionParameter:

			for _, value := range data.Expirations {
				expirations[value] = true
			}

			for _, value := range data.Strikes {
				strikes[value] = true
			}

			exchanges = append(exchanges, data.Exchange)
			multiplier = data.Multiplier // I think multiplier is always the same?
			return REPLY_CONTINUE, nil
		case *ib.SecurityDefinitionOptionParameterEnd:
			return REPLY_DONE, nil
		case *ib.ErrorMessage:
			if data.Code == IBErrSymbolNotFound {
				return REPLY_DONE, types.ErrSymbolNotFound
			} else {
				return REPLY_DONE, data.Error()
			}
		}

		return REPLY_CONTINUE, nil
	})

	if err != nil {
		return types.OptionChain{}, nil
	}

	strikesList := make([]float64, 0, len(strikes))
	for s := range strikes {
		strikesList = append(strikesList, s)
	}

	expiresList := make([]string, 0, len(expirations))
	for e := range expirations {
		expiresList = append(expiresList, e)
	}

	sort.Strings(expiresList)
	sort.Float64s(strikesList)

	output = &types.OptionChain{
		Underlying:  underlying,
		Multiplier:  multiplier,
		Strikes:     strikesList,
		Expirations: expiresList,
	}

	p.optionMetaCacheMutex.Lock()
	p.optionMetaCache[underlying] = output
	p.optionMetaCacheMutex.Unlock()

	return *output, err
}

func (p *IB) getOneOptionQuote(ctx context.Context, contract *ib.Contract) (types.OptionQuote, error) {

	output := types.OptionQuote{
		Rho: -1, // IB doesn't return Rho
	}

	wg := &sync.WaitGroup{}

	var mktDataErr error
	getMktData := func() {
		defer wg.Done()

		// TODO timer should be configurable.
		ctx, cancelFunc := context.WithTimeout(ctx, time.Duration(6)*time.Second)
		defer cancelFunc()

		req := &ib.RequestMarketData{
			Contract: *contract,
			Snapshot: true,
		}

		optionTicks := make([]*ib.TickOptionComputation, 4)

		seen := map[int64]bool{}
		const needed = 4

		_, mktDataErr = p.syncMatchedRequest(ctx, req, func(r ib.Reply) (replyBehavior, error) {
			switch data := r.(type) {
			case *ib.TickOptionComputation:
				// Save the ticks in an array. These are ordered with the best first.
				// The only one that we really want/need is the model computation.
				switch data.Type {

				case ib.TickModelOption:
					optionTicks[0] = data
					output.ModelPrice = data.OptionPrice
					seen[data.Type] = true
				case ib.TickLastOptionComputation:
					optionTicks[1] = data
				case ib.TickBidOptionComputation:
					optionTicks[2] = data
				case ib.TickAskOptionComputation:
					optionTicks[3] = data
				}
			case *ib.TickPrice:
				countSeen := true
				switch data.Type {
				case ib.TickBid:
					output.Bid = data.Price
					output.BidSize = data.Size
				case ib.TickAsk:
					output.Ask = data.Price
					output.BidSize = data.Size
				case ib.TickClose:
					output.Close = data.Price
				case ib.TickLast:
					output.Last = data.Price
					output.LastSize = data.Size
					countSeen = false // TODO Not sure yet if this comes through during trading hours
				default:
					countSeen = false
				}

				if countSeen {
					seen[data.Type] = true
				}

			case *ib.TickSnapshotEnd:
				return REPLY_DONE, nil
			case *ib.ErrorMessage:
				return REPLY_DONE, data.Error()
			}

			if len(seen) >= needed {
				return REPLY_DONE, nil
			}
			return REPLY_CONTINUE, nil
		})

		// Assign common data from the ticks we received. They are already in priority order.
		for _, data := range optionTicks {
			if data == nil {
				continue
			}

			output.Delta = data.Delta
			output.Gamma = data.Gamma
			output.Theta = data.Theta
			output.Vega = data.Vega
			output.OptionImpliedVolatility = data.ImpliedVol
			break // Once we find one, no need to keep going.
		}
	}

	// var histData []*ib.HistoricalData
	// var histErr error
	// getHistData := func() {
	// 	defer wg.Done()
	// 	histData, histErr = p.getLatestHistData(ctx, contract, optionHistTypes)
	// }

	var contractDetails *ib.ContractDetails
	var contractErr error
	getContractDetails := func() {
		defer wg.Done()
		details, err := p.contractManager.GetContractDetails(ctx, NewContractKey(contract))
		if err == nil {
			contractDetails = &details[0]
		}
	}

	wg.Add(2)
	go getMktData()
	go getContractDetails()
	// TODO probably dont need to fetch historical data during trading hours. I think?
	// go getHistData()
	wg.Wait()

	if contractErr != nil {
		return output, contractErr
	} else if mktDataErr != nil {
		return output, mktDataErr
		// } else if histErr != nil {
		// 	return output, histErr
	}

	output.Underlying = contract.Symbol
	output.MinPriceDelta = contractDetails.MinTick
	output.FullSymbol = contractDetails.Summary.LocalSymbol
	output.Strike = contract.Strike
	output.Expiration = contract.Expiry
	if contract.Right[0] == 'P' {
		output.Type = types.Put
	} else {
		output.Type = types.Call
	}

	// for i, data := range histData {
	// 	if data == nil {
	// 		continue
	// 	}

	// 	items := data.Data
	// 	if len(items) == 0 {
	// 		continue
	// 	}

	// 	item := &items[0]

	// 	switch optionHistTypes[i] {
	// 	case ib.HistAsk:
	// 		if output.Ask <= 0 || output.Ask >= 99999 {
	// 			output.Ask = item.Close
	// 		}
	// 	case ib.HistBid:
	// 		if output.Bid <= 0 || output.Bid >= 99999 {
	// 			output.Bid = item.Close
	// 		}
	// 	case ib.HistTrades:
	// 		if output.Last <= 0 || output.Last >= 99999 {
	// 			output.Last = item.Close
	// 		}
	// 	}
	// }

	return output, nil
}

func (p *IB) GetOptionsQuotes(ctx context.Context, params types.OptionsQuoteParams) ([]types.OptionQuote, error) {

	p.LogDebugNormal("GetOptionsQuotes", "params", params)

	details, err := p.contractManager.GetContractDetails(ctx, ContractKey{Symbol: params.Underlying})
	if err != nil {
		return nil, err
	}

	numNeeded := len(params.Expirations) * len(params.Strikes)
	if params.Puts && params.Calls {
		numNeeded *= 2
	}

	if !params.Puts && !params.Calls {
		params.Puts = true
		params.Calls = true
	}

	contracts := make([]ib.Contract, 0, numNeeded)

	detail := &details[0].Summary
	contract := ib.Contract{
		Symbol:       detail.Symbol,
		SecurityType: "OPT",
		Exchange:     "SMART",
	}

	for _, expiration := range params.Expirations {
		contract.Expiry = expiration

		for _, strike := range params.Strikes {
			contract.Strike = strike

			if params.Puts {
				contract.Right = "P"
				contracts = append(contracts, contract)
			}

			if params.Calls {
				contract.Right = "C"
				contracts = append(contracts, contract)
			}
		}
	}

	output := make([]types.OptionQuote, len(contracts))
	errs := make([]error, len(contracts))
	wg := &sync.WaitGroup{}
	wg.Add(len(contracts))

	for i := range contracts {
		go func(i int) {
			output[i], errs[i] = p.getOneOptionQuote(ctx, &contracts[i])
			wg.Done()
		}(i)
	}

	wg.Wait()

	return output, nil

}
<commit_msg>Try to get volume and open interest on options<commit_after>package brokerage_server_ib

import (
	"context"
	"sort"
	"sync"
	"time"

	"github.com/dimfeld/brokerage_server/types"
	"github.com/dimfeld/ib"
)

var (
	optionHistTypes = []ib.HistDataToShow{ib.HistBid, ib.HistAsk, ib.HistTrades}
)

func (p *IB) GetOptionsChain(ctx context.Context, underlying string) (types.OptionChain, error) {

	p.optionMetaCacheMutex.Lock()
	output := p.optionMetaCache[underlying]
	p.optionMetaCacheMutex.Unlock()

	if output != nil {
		return *output, nil
	}

	key := ContractKey{
		Symbol: underlying,
	}
	contractDetails, err := p.contractManager.GetContractDetails(ctx, key)
	if err != nil {
		return types.OptionChain{}, err
	}

	p.LogDebugNormal("Got contract details", "data", contractDetails)

	contract := contractDetails[0].Summary
	request := &ib.RequestSecDefOptParams{
		Symbol:     contract.Symbol,
		SecType:    contract.SecurityType,
		ContractId: contract.ContractID,
		// No exchange, since the options trade on a different exchange than the underlying.
		Exchange: "",
	}

	expirations := map[string]bool{}
	strikes := map[float64]bool{}
	exchanges := []string{}
	var multiplier string

	_, err = p.syncMatchedRequest(ctx, request, func(r ib.Reply) (replyBehavior, error) {
		switch data := r.(type) {
		case *ib.SecurityDefinitionOptionParameter:

			for _, value := range data.Expirations {
				expirations[value] = true
			}

			for _, value := range data.Strikes {
				strikes[value] = true
			}

			exchanges = append(exchanges, data.Exchange)
			multiplier = data.Multiplier // I think multiplier is always the same?
			return REPLY_CONTINUE, nil
		case *ib.SecurityDefinitionOptionParameterEnd:
			return REPLY_DONE, nil
		case *ib.ErrorMessage:
			if data.Code == IBErrSymbolNotFound {
				return REPLY_DONE, types.ErrSymbolNotFound
			} else {
				return REPLY_DONE, data.Error()
			}
		}

		return REPLY_CONTINUE, nil
	})

	if err != nil {
		return types.OptionChain{}, nil
	}

	strikesList := make([]float64, 0, len(strikes))
	for s := range strikes {
		strikesList = append(strikesList, s)
	}

	expiresList := make([]string, 0, len(expirations))
	for e := range expirations {
		expiresList = append(expiresList, e)
	}

	sort.Strings(expiresList)
	sort.Float64s(strikesList)

	output = &types.OptionChain{
		Underlying:  underlying,
		Multiplier:  multiplier,
		Strikes:     strikesList,
		Expirations: expiresList,
	}

	p.optionMetaCacheMutex.Lock()
	p.optionMetaCache[underlying] = output
	p.optionMetaCacheMutex.Unlock()

	return *output, err
}

func (p *IB) getOneOptionQuote(ctx context.Context, contract *ib.Contract) (types.OptionQuote, error) {

	output := types.OptionQuote{
		Rho: -1, // IB doesn't return Rho
	}

	wg := &sync.WaitGroup{}

	var mktDataErr error
	getMktData := func() {
		defer wg.Done()

		// TODO timer should be configurable.
		ctx, cancelFunc := context.WithTimeout(ctx, time.Duration(6)*time.Second)
		defer cancelFunc()

		req := &ib.RequestMarketData{
			Contract:        *contract,
			GenericTickList: "100,101",
			Snapshot:        false,
		}

		optionTicks := make([]*ib.TickOptionComputation, 4)

		seen := map[int64]bool{}
		const needed = 6

		var reqId int64
		reqId, mktDataErr = p.syncMatchedRequest(ctx, req, func(r ib.Reply) (replyBehavior, error) {
			switch data := r.(type) {
			case *ib.TickGeneric:
				countSeen := true
				switch data.Type {
				case ib.TickOptionCallOpenInt:
					output.OpenInterest = int64(data.Value)
				case ib.TickOptionPutOpenInt:
					output.OpenInterest = int64(data.Value)
				case ib.TickOptionCallVolume:
					output.Volume = int64(data.Value)
				case ib.TickOptionPutVolume:
					output.Volume = int64(data.Value)
				default:
					countSeen = false
				}

				if countSeen {
					seen[data.Type] = true
				}

			case *ib.TickOptionComputation:
				// Save the ticks in an array. These are ordered with the best first.
				// The only one that we really want/need is the model computation
				// but the others will do if the model doesn't arrive in a timely manner.
				switch data.Type {

				case ib.TickModelOption:
					optionTicks[0] = data
					output.ModelPrice = data.OptionPrice
					seen[data.Type] = true
				case ib.TickLastOptionComputation:
					optionTicks[1] = data
				case ib.TickBidOptionComputation:
					optionTicks[2] = data
				case ib.TickAskOptionComputation:
					optionTicks[3] = data
				}
			case *ib.TickSize:
				switch data.Type {
				case ib.TickVolume:

				}
			case *ib.TickPrice:
				countSeen := true
				switch data.Type {
				case ib.TickBid:
					output.Bid = data.Price
					output.BidSize = data.Size
				case ib.TickAsk:
					output.Ask = data.Price
					output.AskSize = data.Size
				case ib.TickClose:
					output.Close = data.Price
				case ib.TickLast:
					output.Last = data.Price
					output.LastSize = data.Size
					countSeen = false // TODO Not sure yet if this comes through during trading hours
				default:
					countSeen = false
				}

				if countSeen {
					seen[data.Type] = true
				}

			case *ib.TickSnapshotEnd:
				output.Incomplete = true
				return REPLY_DONE, nil
			case *ib.ErrorMessage:
				return REPLY_DONE, data.Error()
			}

			if len(seen) >= needed {
				return REPLY_DONE, nil
			}
			return REPLY_CONTINUE, nil
		})

		// Make sure to cancel the stream once we're all done.
		cancelReq := ib.CancelMarketData{}
		cancelReq.SetID(reqId)
		p.sendUnmatchedRequest(&cancelReq)

		// Assign common data from the ticks we received. They are already in priority order.
		for _, data := range optionTicks {
			if data == nil {
				continue
			}

			output.Delta = data.Delta
			output.Gamma = data.Gamma
			output.Theta = data.Theta
			output.Vega = data.Vega
			output.OptionImpliedVolatility = data.ImpliedVol
			break // Once we find one, no need to keep going.
		}
	}

	// var histData []*ib.HistoricalData
	// var histErr error
	// getHistData := func() {
	// 	defer wg.Done()
	// 	histData, histErr = p.getLatestHistData(ctx, contract, optionHistTypes)
	// }

	var contractDetails *ib.ContractDetails
	var contractErr error
	getContractDetails := func() {
		defer wg.Done()
		details, err := p.contractManager.GetContractDetails(ctx, NewContractKey(contract))
		if err == nil {
			contractDetails = &details[0]
		}
	}

	wg.Add(2)
	go getMktData()
	go getContractDetails()
	// TODO probably dont need to fetch historical data during trading hours. I think?
	// go getHistData()
	wg.Wait()

	if contractErr != nil {
		return output, contractErr
	} else if mktDataErr != nil {
		return output, mktDataErr
		// } else if histErr != nil {
		// 	return output, histErr
	}

	output.Underlying = contract.Symbol
	output.MinPriceDelta = contractDetails.MinTick
	output.FullSymbol = contractDetails.Summary.LocalSymbol
	output.Strike = contract.Strike
	output.Expiration = contract.Expiry
	output.Time = time.Now()
	if contract.Right[0] == 'P' {
		output.Type = types.Put
	} else {
		output.Type = types.Call
	}

	// for i, data := range histData {
	// 	if data == nil {
	// 		continue
	// 	}

	// 	items := data.Data
	// 	if len(items) == 0 {
	// 		continue
	// 	}

	// 	item := &items[0]

	// 	switch optionHistTypes[i] {
	// 	case ib.HistAsk:
	// 		if output.Ask <= 0 || output.Ask >= 99999 {
	// 			output.Ask = item.Close
	// 		}
	// 	case ib.HistBid:
	// 		if output.Bid <= 0 || output.Bid >= 99999 {
	// 			output.Bid = item.Close
	// 		}
	// 	case ib.HistTrades:
	// 		if output.Last <= 0 || output.Last >= 99999 {
	// 			output.Last = item.Close
	// 		}
	// 	}
	// }

	return output, nil
}

func (p *IB) GetOptionsQuotes(ctx context.Context, params types.OptionsQuoteParams) ([]types.OptionQuote, error) {

	p.LogDebugNormal("GetOptionsQuotes", "params", params)

	details, err := p.contractManager.GetContractDetails(ctx, ContractKey{Symbol: params.Underlying})
	if err != nil {
		return nil, err
	}

	numNeeded := len(params.Expirations) * len(params.Strikes)
	if params.Puts && params.Calls {
		numNeeded *= 2
	}

	if !params.Puts && !params.Calls {
		params.Puts = true
		params.Calls = true
	}

	contracts := make([]ib.Contract, 0, numNeeded)

	detail := &details[0].Summary
	contract := ib.Contract{
		Symbol:       detail.Symbol,
		SecurityType: "OPT",
		Exchange:     "SMART",
	}

	for _, expiration := range params.Expirations {
		contract.Expiry = expiration

		for _, strike := range params.Strikes {
			contract.Strike = strike

			if params.Puts {
				contract.Right = "P"
				contracts = append(contracts, contract)
			}

			if params.Calls {
				contract.Right = "C"
				contracts = append(contracts, contract)
			}
		}
	}

	output := make([]types.OptionQuote, len(contracts))
	errs := make([]error, len(contracts))
	wg := &sync.WaitGroup{}
	wg.Add(len(contracts))

	for i := range contracts {
		go func(i int) {
			output[i], errs[i] = p.getOneOptionQuote(ctx, &contracts[i])
			wg.Done()
		}(i)
	}

	wg.Wait()

	return output, nil

}
<|endoftext|>


<commit_before>package consumer

import "github.com/aws/aws-sdk-go/service/kinesis/kinesisiface"

// Option is used to override defaults when creating a new Consumer
type Option func(*Consumer)

// WithStore overrides the default storage
func WithStore(store Store) Option {
	return func(c *Consumer) {
		c.store = store
	}
}

// WithLogger overrides the default logger
func WithLogger(logger Logger) Option {
	return func(c *Consumer) {
		c.logger = logger
	}
}

// WithCounter overrides the default counter
func WithCounter(counter Counter) Option {
	return func(c *Consumer) {
		c.counter = counter
	}
}

// WithClient overrides the default client
func WithClient(client kinesisiface.KinesisAPI) Option {
	return func(c *Consumer) {
		c.client = client
	}
}

// WithShardIteratorType overrides the starting point for the consumer
func WithShardIteratorType(t string) Option {
	return func(c *Consumer) {
		c.initialShardIteratorType = t
	}
}
<commit_msg>Add WithGroup option<commit_after>package consumer

import "github.com/aws/aws-sdk-go/service/kinesis/kinesisiface"

// Option is used to override defaults when creating a new Consumer
type Option func(*Consumer)

// WithGroup overrides the default storage
func WithGroup(group Group) Option {
	return func(c *Consumer) {
		c.group = group
	}
}

// WithStore overrides the default storage
func WithStore(store Store) Option {
	return func(c *Consumer) {
		c.store = store
	}
}

// WithLogger overrides the default logger
func WithLogger(logger Logger) Option {
	return func(c *Consumer) {
		c.logger = logger
	}
}

// WithCounter overrides the default counter
func WithCounter(counter Counter) Option {
	return func(c *Consumer) {
		c.counter = counter
	}
}

// WithClient overrides the default client
func WithClient(client kinesisiface.KinesisAPI) Option {
	return func(c *Consumer) {
		c.client = client
	}
}

// WithShardIteratorType overrides the starting point for the consumer
func WithShardIteratorType(t string) Option {
	return func(c *Consumer) {
		c.initialShardIteratorType = t
	}
}
<|endoftext|>


<commit_before>package qingcloud

import (
	"fmt"

	"github.com/hashicorp/terraform/helper/schema"
	qc "github.com/yunify/qingcloud-sdk-go/service"
)

func modifyTagAttributes(d *schema.ResourceData, meta interface{}, create bool) error {
	clt := meta.(*QingCloudClient).tag
	input := new(qc.ModifyTagAttributesInput)
	input.Tag = qc.String(d.Id())
	if create {
		if !d.HasChange("description") && !d.HasChange("color") {
			return nil
		}
		if d.HasChange("description") {
			input.Description = qc.String(d.Get("description").(string))
		}
		if d.HasChange("color") {
			input.Color = qc.String(d.Get("color").(string))
		}
	} else {
		if !d.HasChange("description") && !d.HasChange("name") && !d.HasChange("color") {
			return nil
		}
		if d.HasChange("description") {
			if d.Get("description").(string) == "" {
				input.Description = qc.String(" ")
			} else {
				input.Description = qc.String(d.Get("description").(string))
			}
		}
		if d.HasChange("name") {
			input.TagName = qc.String(d.Get("name").(string))
		}
		if d.HasChange("color") {
			input.Color = qc.String(d.Get("color").(string))
		}
	}
	output, err := clt.ModifyTagAttributes(input)
	if err != nil {
		return fmt.Errorf("Error modify tag attributes: %s", err)
	}
	if output.RetCode != nil && qc.IntValue(output.RetCode) != 0 {
		return fmt.Errorf("Error modify tag attributes: %s", *output.Message)
	}
	return nil
}

func resourceSetTag(d *schema.ResourceData, tags []*qc.Tag) {
	tagIDs := make([]string, 0, len(tags))
	tagNames := make([]string, 0, len(tags))
	for _, tag := range tags {
		tagIDs = append(tagIDs, qc.StringValue(tag.TagID))
		tagNames = append(tagNames, qc.StringValue(tag.TagName))
	}
	d.Set("tag_ids", tagIDs)
	d.Set("tag_names", tagNames)
}

func resourceUpdateTag(d *schema.ResourceData, meta interface{}, resourceType string) error {
	if !d.HasChange("tag_ids") {
		return nil
	}
	clt := meta.(*QingCloudClient).tag
	oldV, newV := d.GetChange("tag_ids")
	var oldTags []string
	var newTags []string
	for _, v := range oldV.(*schema.Set).List() {
		oldTags = append(oldTags, v.(string))
	}
	for _, v := range newV.(*schema.Set).List() {
		newTags = append(newTags, v.(string))
	}
	attachTags, detachTags := stringSliceDiff(newTags, oldTags)

	if len(detachTags) > 0 {
		input := new(qc.DetachTagsInput)
		for _, tag := range detachTags {
			rtp := qc.ResourceTagPair{
				TagID:        qc.String(tag),
				ResourceID:   qc.String(d.Id()),
				ResourceType: qc.String(resourceType),
			}
			input.ResourceTagPairs = append(input.ResourceTagPairs, &rtp)
		}
		_, err := clt.DetachTags(input)
		if err != nil {
			return fmt.Errorf("Error detach tag: %s", err)
		}
	}
	if len(attachTags) > 0 {
		input := new(qc.AttachTagsInput)
		for _, tag := range attachTags {
			rtp := qc.ResourceTagPair{
				TagID:        qc.String(tag),
				ResourceID:   qc.String(d.Id()),
				ResourceType: qc.String(resourceType),
			}
			input.ResourceTagPairs = append(input.ResourceTagPairs, &rtp)
		}
		_, err := clt.AttachTags(input)
		if err != nil {
			return fmt.Errorf("Error detach tag: %s", err)
		}
	}
	return nil
}
<commit_msg>remove redundant logic in modifytaghelper<commit_after>package qingcloud

import (
	"fmt"

	"github.com/hashicorp/terraform/helper/schema"
	qc "github.com/yunify/qingcloud-sdk-go/service"
)

func modifyTagAttributes(d *schema.ResourceData, meta interface{}, create bool) error {
	clt := meta.(*QingCloudClient).tag
	input := new(qc.ModifyTagAttributesInput)
	input.Tag = qc.String(d.Id())
	if d.HasChange("color") {
		input.Color = qc.String(d.Get("color").(string))
	}

	if d.HasChange("description") {
		if d.Get("description").(string) == "" {
			input.Description = qc.String(" ")
		} else {
			input.Description = qc.String(d.Get("description").(string))
		}
	}
	if d.HasChange("name") {
		input.TagName = qc.String(d.Get("name").(string))
	}
	output, err := clt.ModifyTagAttributes(input)
	if err != nil {
		return fmt.Errorf("Error modify tag attributes: %s", err)
	}
	if output.RetCode != nil && qc.IntValue(output.RetCode) != 0 {
		return fmt.Errorf("Error modify tag attributes: %s", *output.Message)
	}
	return nil
}

func resourceSetTag(d *schema.ResourceData, tags []*qc.Tag) {
	tagIDs := make([]string, 0, len(tags))
	tagNames := make([]string, 0, len(tags))
	for _, tag := range tags {
		tagIDs = append(tagIDs, qc.StringValue(tag.TagID))
		tagNames = append(tagNames, qc.StringValue(tag.TagName))
	}
	d.Set("tag_ids", tagIDs)
	d.Set("tag_names", tagNames)
}

func resourceUpdateTag(d *schema.ResourceData, meta interface{}, resourceType string) error {
	if !d.HasChange("tag_ids") {
		return nil
	}
	clt := meta.(*QingCloudClient).tag
	oldV, newV := d.GetChange("tag_ids")
	var oldTags []string
	var newTags []string
	for _, v := range oldV.(*schema.Set).List() {
		oldTags = append(oldTags, v.(string))
	}
	for _, v := range newV.(*schema.Set).List() {
		newTags = append(newTags, v.(string))
	}
	attachTags, detachTags := stringSliceDiff(newTags, oldTags)

	if len(detachTags) > 0 {
		input := new(qc.DetachTagsInput)
		for _, tag := range detachTags {
			rtp := qc.ResourceTagPair{
				TagID:        qc.String(tag),
				ResourceID:   qc.String(d.Id()),
				ResourceType: qc.String(resourceType),
			}
			input.ResourceTagPairs = append(input.ResourceTagPairs, &rtp)
		}
		_, err := clt.DetachTags(input)
		if err != nil {
			return fmt.Errorf("Error detach tag: %s", err)
		}
	}
	if len(attachTags) > 0 {
		input := new(qc.AttachTagsInput)
		for _, tag := range attachTags {
			rtp := qc.ResourceTagPair{
				TagID:        qc.String(tag),
				ResourceID:   qc.String(d.Id()),
				ResourceType: qc.String(resourceType),
			}
			input.ResourceTagPairs = append(input.ResourceTagPairs, &rtp)
		}
		_, err := clt.AttachTags(input)
		if err != nil {
			return fmt.Errorf("Error detach tag: %s", err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/unixvoid/glogger"
)

func testDnsAdd(hostUrl *string) {

	// add DNS entry
	glogger.Info.Println("Testing :: POST to /dns A type")
	// correct
	returnVal := twoKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "127.0.0.1")
	checkResponse("Correct", 201, returnVal)
	// domain not set
	returnVal = twoKeyPostEndpoint(*hostUrl, "/dns", "", "test.domain", "value", "127.0.0.1")
	checkResponse("Domain not set", 400, returnVal)
	// value not set
	returnVal = twoKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "", "127.0.0.1")
	checkResponse("Value not set", 400, returnVal)
	glogger.Info.Println("")

	glogger.Info.Println("Testing :: POST to /dns cname type")
	// correct
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "test.domain.lb", "dnstype", "cname")
	checkResponse("Correct", 200, returnVal)
	// domain not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "", "test.domain", "value", "test.domain.lb", "dnstype", "cname")
	checkResponse("Domain not set", 400, returnVal)
	// value not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "", "test.domain.lb", "dnstype", "cname")
	checkResponse("Value not set", 400, returnVal)
	// unsupported type
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "test.domain.lb", "dnstype", "cnames")
	checkResponse("Unsupported dnstype", 400, returnVal)
	glogger.Info.Println("")

	glogger.Info.Println("Testing :: POST to /dns aaaa type")
	// correct
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "::1", "dnstype", "aaaa")
	checkResponse("Correct", 200, returnVal)
	// domain not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "", "test.domain", "value", "::1", "dnstype", "aaaa")
	checkResponse("Domain not set", 400, returnVal)
	// value not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "", "::1", "dnstype", "aaaa")
	checkResponse("Value not set", 400, returnVal)
	// unsupported type
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "::1", "dnstype", "aaab")
	checkResponse("Unsupported dnstype", 400, returnVal)
}
<commit_msg>Travis now operating as expected, fixing<commit_after>package main

import (
	"github.com/unixvoid/glogger"
)

func testDnsAdd(hostUrl *string) {

	// add DNS entry
	glogger.Info.Println("Testing :: POST to /dns A type")
	// correct
	returnVal := twoKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "127.0.0.1")
	checkResponse("Correct", 200, returnVal)
	// domain not set
	returnVal = twoKeyPostEndpoint(*hostUrl, "/dns", "", "test.domain", "value", "127.0.0.1")
	checkResponse("Domain not set", 400, returnVal)
	// value not set
	returnVal = twoKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "", "127.0.0.1")
	checkResponse("Value not set", 400, returnVal)
	glogger.Info.Println("")

	glogger.Info.Println("Testing :: POST to /dns cname type")
	// correct
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "test.domain.lb", "dnstype", "cname")
	checkResponse("Correct", 200, returnVal)
	// domain not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "", "test.domain", "value", "test.domain.lb", "dnstype", "cname")
	checkResponse("Domain not set", 400, returnVal)
	// value not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "", "test.domain.lb", "dnstype", "cname")
	checkResponse("Value not set", 400, returnVal)
	// unsupported type
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "test.domain.lb", "dnstype", "cnames")
	checkResponse("Unsupported dnstype", 400, returnVal)
	glogger.Info.Println("")

	glogger.Info.Println("Testing :: POST to /dns aaaa type")
	// correct
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "::1", "dnstype", "aaaa")
	checkResponse("Correct", 200, returnVal)
	// domain not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "", "test.domain", "value", "::1", "dnstype", "aaaa")
	checkResponse("Domain not set", 400, returnVal)
	// value not set
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "", "::1", "dnstype", "aaaa")
	checkResponse("Value not set", 400, returnVal)
	// unsupported type
	returnVal = threeKeyPostEndpoint(*hostUrl, "/dns", "domain", "test.domain", "value", "::1", "dnstype", "aaab")
	checkResponse("Unsupported dnstype", 400, returnVal)
}
<|endoftext|>


<commit_before>package nsqlookupd

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type RegistrationDB struct {
	sync.RWMutex
	registrationMap map[Registration]Producers
}

type Registration struct {
	Category string
	Key      string
	SubKey   string
}
type Registrations []Registration

type PeerInfo struct {
	lastUpdate       int64
	id               string
	RemoteAddress    string `json:"remote_address"`
	Hostname         string `json:"hostname"`
	BroadcastAddress string `json:"broadcast_address"`
	TCPPort          int    `json:"tcp_port"`
	HTTPPort         int    `json:"http_port"`
	Version          string `json:"version"`
}

type Producer struct {
	peerInfo     *PeerInfo
	tombstoned   bool
	tombstonedAt time.Time
}

type Producers []*Producer

func (p *Producer) String() string {
	return fmt.Sprintf("%s [%d, %d]", p.peerInfo.BroadcastAddress, p.peerInfo.TCPPort, p.peerInfo.HTTPPort)
}

func (p *Producer) Tombstone() {
	p.tombstoned = true
	p.tombstonedAt = time.Now()
}

func (p *Producer) IsTombstoned(lifetime time.Duration) bool {
	return p.tombstoned && time.Now().Sub(p.tombstonedAt) < lifetime
}

func NewRegistrationDB() *RegistrationDB {
	return &RegistrationDB{
		registrationMap: make(map[Registration]Producers),
	}
}

// add a registration key
func (r *RegistrationDB) AddRegistration(k Registration) {
	r.Lock()
	defer r.Unlock()
	_, ok := r.registrationMap[k]
	if !ok {
		r.registrationMap[k] = Producers{}
	}
}

// add a producer to a registration
func (r *RegistrationDB) AddProducer(k Registration, p *Producer) bool {
	r.Lock()
	defer r.Unlock()
	producers := r.registrationMap[k]
	found := false
	for _, producer := range producers {
		if producer.peerInfo.id == p.peerInfo.id {
			found = true
			break
		}
	}
	if found == false {
		r.registrationMap[k] = append(producers, p)
	}
	return !found
}

// remove a producer from a registration
func (r *RegistrationDB) RemoveProducer(k Registration, id string) (bool, int) {
	r.Lock()
	defer r.Unlock()
	producers, ok := r.registrationMap[k]
	if !ok {
		return false, 0
	}
	removed := false
	cleaned := Producers{}
	for _, producer := range producers {
		if producer.peerInfo.id != id {
			cleaned = append(cleaned, producer)
		} else {
			removed = true
		}
	}
	// Note: this leaves keys in the DB even if they have empty lists
	r.registrationMap[k] = cleaned
	return removed, len(cleaned)
}

// remove a Registration and all it's producers
func (r *RegistrationDB) RemoveRegistration(k Registration) {
	r.Lock()
	defer r.Unlock()
	delete(r.registrationMap, k)
}

func (r *RegistrationDB) needFilter(key string, subkey string) bool {
	return key == "*" || subkey == "*"
}

func (r *RegistrationDB) FindRegistrations(category string, key string, subkey string) Registrations {
	r.RLock()
	defer r.RUnlock()
	if !r.needFilter(key, subkey) {
		k := Registration{category, key, subkey}
		if _, ok := r.registrationMap[k]; ok {
			return Registrations{k}
		}
		return Registrations{}
	}
	results := Registrations{}
	for k := range r.registrationMap {
		if !k.IsMatch(category, key, subkey) {
			continue
		}
		results = append(results, k)
	}
	return results
}

func (r *RegistrationDB) FindProducers(category string, key string, subkey string) Producers {
	r.RLock()
	defer r.RUnlock()
	if !r.needFilter(key, subkey) {
		k := Registration{category, key, subkey}
		return r.registrationMap[k]
	}

	results := Producers{}
	for k, producers := range r.registrationMap {
		if !k.IsMatch(category, key, subkey) {
			continue
		}
		for _, producer := range producers {
			found := false
			for _, p := range results {
				if producer.peerInfo.id == p.peerInfo.id {
					found = true
				}
			}
			if found == false {
				results = append(results, producer)
			}
		}
	}
	return results
}

func (r *RegistrationDB) LookupRegistrations(id string) Registrations {
	r.RLock()
	defer r.RUnlock()
	results := Registrations{}
	for k, producers := range r.registrationMap {
		for _, p := range producers {
			if p.peerInfo.id == id {
				results = append(results, k)
				break
			}
		}
	}
	return results
}

func (k Registration) IsMatch(category string, key string, subkey string) bool {
	if category != k.Category {
		return false
	}
	if key != "*" && k.Key != key {
		return false
	}
	if subkey != "*" && k.SubKey != subkey {
		return false
	}
	return true
}

func (rr Registrations) Filter(category string, key string, subkey string) Registrations {
	output := Registrations{}
	for _, k := range rr {
		if k.IsMatch(category, key, subkey) {
			output = append(output, k)
		}
	}
	return output
}

func (rr Registrations) Keys() []string {
	keys := make([]string, len(rr))
	for i, k := range rr {
		keys[i] = k.Key
	}
	return keys
}

func (rr Registrations) SubKeys() []string {
	subkeys := make([]string, len(rr))
	for i, k := range rr {
		subkeys[i] = k.SubKey
	}
	return subkeys
}

func (pp Producers) FilterByActive(inactivityTimeout time.Duration, tombstoneLifetime time.Duration) Producers {
	now := time.Now()
	results := Producers{}
	for _, p := range pp {
		cur := time.Unix(0, atomic.LoadInt64(&p.peerInfo.lastUpdate))
		if now.Sub(cur) > inactivityTimeout || p.IsTombstoned(tombstoneLifetime) {
			continue
		}
		results = append(results, p)
	}
	return results
}

func (pp Producers) PeerInfo() []*PeerInfo {
	results := []*PeerInfo{}
	for _, p := range pp {
		results = append(results, p.peerInfo)
	}
	return results
}
<commit_msg>nsqlookupd: change producer slice to map<commit_after>package nsqlookupd

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type RegistrationDB struct {
	sync.RWMutex
	registrationMap map[Registration]ProducerMap
}

type Registration struct {
	Category string
	Key      string
	SubKey   string
}
type Registrations []Registration

type PeerInfo struct {
	lastUpdate       int64
	id               string
	RemoteAddress    string `json:"remote_address"`
	Hostname         string `json:"hostname"`
	BroadcastAddress string `json:"broadcast_address"`
	TCPPort          int    `json:"tcp_port"`
	HTTPPort         int    `json:"http_port"`
	Version          string `json:"version"`
}

type Producer struct {
	peerInfo     *PeerInfo
	tombstoned   bool
	tombstonedAt time.Time
}

type Producers []*Producer
type ProducerMap map[string]*Producer

func (p *Producer) String() string {
	return fmt.Sprintf("%s [%d, %d]", p.peerInfo.BroadcastAddress, p.peerInfo.TCPPort, p.peerInfo.HTTPPort)
}

func (p *Producer) Tombstone() {
	p.tombstoned = true
	p.tombstonedAt = time.Now()
}

func (p *Producer) IsTombstoned(lifetime time.Duration) bool {
	return p.tombstoned && time.Now().Sub(p.tombstonedAt) < lifetime
}

func NewRegistrationDB() *RegistrationDB {
	return &RegistrationDB{
		registrationMap: make(map[Registration]ProducerMap),
	}
}

// add a registration key
func (r *RegistrationDB) AddRegistration(k Registration) {
	r.Lock()
	defer r.Unlock()
	_, ok := r.registrationMap[k]
	if !ok {
		r.registrationMap[k] = make(map[string]*Producer)
	}
}

// add a producer to a registration
func (r *RegistrationDB) AddProducer(k Registration, p *Producer) bool {
	r.Lock()
	defer r.Unlock()
	_, ok := r.registrationMap[k]
	if !ok {
		r.registrationMap[k] = make(map[string]*Producer)
	}
	producers := r.registrationMap[k]
	_, found := producers[p.peerInfo.id]
	if found == false {
		producers[p.peerInfo.id] = p
	}
	return !found
}

// remove a producer from a registration
func (r *RegistrationDB) RemoveProducer(k Registration, id string) (bool, int) {
	r.Lock()
	defer r.Unlock()
	producers, ok := r.registrationMap[k]
	if !ok {
		return false, 0
	}
	removed := false
	if _, exists := producers[id]; exists {
		removed = true
	}

	// Note: this leaves keys in the DB even if they have empty lists
	delete(producers, id)
	return removed, len(producers)
}

// remove a Registration and all it's producers
func (r *RegistrationDB) RemoveRegistration(k Registration) {
	r.Lock()
	defer r.Unlock()
	delete(r.registrationMap, k)
}

func (r *RegistrationDB) needFilter(key string, subkey string) bool {
	return key == "*" || subkey == "*"
}

func (r *RegistrationDB) FindRegistrations(category string, key string, subkey string) Registrations {
	r.RLock()
	defer r.RUnlock()
	if !r.needFilter(key, subkey) {
		k := Registration{category, key, subkey}
		if _, ok := r.registrationMap[k]; ok {
			return Registrations{k}
		}
		return Registrations{}
	}
	results := Registrations{}
	for k := range r.registrationMap {
		if !k.IsMatch(category, key, subkey) {
			continue
		}
		results = append(results, k)
	}
	return results
}

func (r *RegistrationDB) FindProducers(category string, key string, subkey string) Producers {
	r.RLock()
	defer r.RUnlock()
	if !r.needFilter(key, subkey) {
		k := Registration{category, key, subkey}
		return ProducerMap2Slice(r.registrationMap[k])
	}

	results := make(map[string]*Producer)
	for k, producers := range r.registrationMap {
		if !k.IsMatch(category, key, subkey) {
			continue
		}
		for _, producer := range producers {
			_, found := results[producer.peerInfo.id]
			if found == false {
				results[producer.peerInfo.id] = producer
			}
		}
	}
	return ProducerMap2Slice(results)
}

func (r *RegistrationDB) LookupRegistrations(id string) Registrations {
	r.RLock()
	defer r.RUnlock()
	results := Registrations{}
	for k, producers := range r.registrationMap {
		if _, exists := producers[id]; exists {
			results = append(results, k)
		}
	}
	return results
}

func (k Registration) IsMatch(category string, key string, subkey string) bool {
	if category != k.Category {
		return false
	}
	if key != "*" && k.Key != key {
		return false
	}
	if subkey != "*" && k.SubKey != subkey {
		return false
	}
	return true
}

func (rr Registrations) Filter(category string, key string, subkey string) Registrations {
	output := Registrations{}
	for _, k := range rr {
		if k.IsMatch(category, key, subkey) {
			output = append(output, k)
		}
	}
	return output
}

func (rr Registrations) Keys() []string {
	keys := make([]string, len(rr))
	for i, k := range rr {
		keys[i] = k.Key
	}
	return keys
}

func (rr Registrations) SubKeys() []string {
	subkeys := make([]string, len(rr))
	for i, k := range rr {
		subkeys[i] = k.SubKey
	}
	return subkeys
}

func (pp Producers) FilterByActive(inactivityTimeout time.Duration, tombstoneLifetime time.Duration) Producers {
	now := time.Now()
	results := Producers{}
	for _, p := range pp {
		cur := time.Unix(0, atomic.LoadInt64(&p.peerInfo.lastUpdate))
		if now.Sub(cur) > inactivityTimeout || p.IsTombstoned(tombstoneLifetime) {
			continue
		}
		results = append(results, p)
	}
	return results
}

func (pp Producers) PeerInfo() []*PeerInfo {
	results := []*PeerInfo{}
	for _, p := range pp {
		results = append(results, p.peerInfo)
	}
	return results
}

func ProducerMap2Slice(pm ProducerMap) Producers {
	var producers Producers
	for _, producer := range pm {
		producers = append(producers, producer)
	}

	return producers
}
<|endoftext|>


<commit_before>package main

import (
	"math"

	"github.com/peterstace/grayt"
)

func main() {
	r := grayt.NewRunner()
	r.PxWide = 200
	r.PxHigh = 200
	r.Quality = 100
	r.BaseName = "CornellBox"
	r.Run(scene())
}

// Cornell Box
//
// (0,y,-1) +---------+ (1,y,-1)
//          | BB      |
//          | BB  BB  |
//          |     BB  |
// (0,y,0)  +         + (1,y,0)
//            \     /
//             \   /
//              \ /
//               C (0.5,0.5,D)
//
//
// SINE(0.5*T) = 0.5 / SQRT(0.5^2 + D^2)
// T = 2*ARCSINE(0.5/SQRT(0.25 + D^2))

var (
	up    = grayt.Vect(0.0, 1.0, 0.0)
	down  = grayt.Vect(0.0, -1.0, 0.0)
	left  = grayt.Vect(-1.0, 0.0, 0.0)
	right = grayt.Vect(1.0, 0.0, 0.0)
	back  = grayt.Vect(0.0, 0.0, 1.0)
	zero  = grayt.Vect(0.0, 0.0, 0.0)
	one   = grayt.Vect(1.0, 1.0, -1.0)
)

var (
	white = grayt.Material{Colour: grayt.White, Emittance: 0.0}
	green = grayt.Material{Colour: grayt.Green, Emittance: 0.0}
	red   = grayt.Material{Colour: grayt.Red, Emittance: 0.0}
	blue  = grayt.Material{Colour: grayt.Blue, Emittance: 0.0}
)

func scene() grayt.Scene {
	ee := []grayt.Entity{
		{
			Material: grayt.Material{Colour: grayt.White, Emittance: 5},
			Surface: grayt.AlignedBox(
				grayt.Vect(0.4, 1.0, -0.4),
				grayt.Vect(0.6, 0.999, -0.6),
			),
		},
	}
	ee = append(ee, box()...)
	for _, s := range tallBlock() {
		ee = append(ee, grayt.Entity{Material: white, Surface: s})
	}
	for _, s := range shortBlock() {
		ee = append(ee, grayt.Entity{Material: white, Surface: s})
	}
	return grayt.Scene{Camera: cam(), Entities: ee}
}

func cam() grayt.Camera {
	const D = 1.3 // Estimated.
	return grayt.NewRectilinearCamera(grayt.CameraConfig{
		Location:      grayt.Vect(0.5, 0.5, D),
		ViewDirection: grayt.Vect(0.0, 0.0, -1.0),
		UpDirection:   up,
		FieldOfView:   2 * math.Asin(0.5/math.Sqrt(0.25+D*D)),
		FocalLength:   0.5 + D,
		FocalRatio:    math.Inf(+1),
	})
}

func box() []grayt.Entity {
	return []grayt.Entity{
		{
			Material: white,
			Surface:  grayt.YPlane().Translate(0, 0, 0),
		},
		{
			Material: white,
			Surface:  grayt.YPlane().Translate(0, 1, 0),
		},
		{
			Material: red,
			Surface:  grayt.XPlane().Translate(0, 0, 0),
		},
		{
			Material: green,
			Surface:  grayt.XPlane().Translate(1, 0, 0),
		},
		{
			Material: white,
			Surface:  grayt.ZPlane().Translate(0, 0, -1),
		},
	}
}

func shortBlock() []grayt.Surface {
	var (
		// Left/Right, Top/Bottom, Front/Back.
		LBF = grayt.Vect(0.76, 0.00, -0.12)
		LBB = grayt.Vect(0.85, 0.00, -0.41)
		RBF = grayt.Vect(0.47, 0.00, -0.21)
		RBB = grayt.Vect(0.56, 0.00, -0.49)
		LTF = grayt.Vect(0.76, 0.30, -0.12)
		LTB = grayt.Vect(0.85, 0.30, -0.41)
		RTF = grayt.Vect(0.47, 0.30, -0.21)
		RTB = grayt.Vect(0.56, 0.30, -0.49)
	)
	var ss []grayt.Surface
	ss = append(ss, grayt.Square(LTF, LTB, RTB, RTF)...)
	ss = append(ss, grayt.Square(LBF, RBF, RTF, LTF)...)
	ss = append(ss, grayt.Square(LBB, RBB, RTB, LTB)...)
	ss = append(ss, grayt.Square(LBF, LBB, LTB, LTF)...)
	ss = append(ss, grayt.Square(RBF, RBB, RTB, RTF)...)
	return ss
}

func tallBlock() []grayt.Surface {
	var (
		// Left/Right, Top/Bottom, Front/Back.
		LBF = grayt.Vect(0.52, 0.00, -0.54)
		LBB = grayt.Vect(0.43, 0.00, -0.83)
		RBF = grayt.Vect(0.23, 0.00, -0.45)
		RBB = grayt.Vect(0.14, 0.00, -0.74)
		LTF = grayt.Vect(0.52, 0.60, -0.54)
		LTB = grayt.Vect(0.43, 0.60, -0.83)
		RTF = grayt.Vect(0.23, 0.60, -0.45)
		RTB = grayt.Vect(0.14, 0.60, -0.74)
	)
	var ss []grayt.Surface
	ss = append(ss, grayt.Square(LTF, LTB, RTB, RTF)...)
	ss = append(ss, grayt.Square(LBF, RBF, RTF, LTF)...)
	ss = append(ss, grayt.Square(LBB, RBB, RTB, LTB)...)
	ss = append(ss, grayt.Square(LBF, LBB, LTB, LTF)...)
	ss = append(ss, grayt.Square(RBF, RBB, RTB, RTF)...)
	return ss
}
<commit_msg>Remove old Cornell box scene<commit_after><|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

// Package testapi provides a helper for retrieving the KUBE_TEST_API environment variable.
package testapi

import (
	"fmt"
	"os"
	"strings"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/apis/autoscaling"
	"k8s.io/kubernetes/pkg/apis/batch"
	"k8s.io/kubernetes/pkg/apis/extensions"
	"k8s.io/kubernetes/pkg/runtime"

	_ "k8s.io/kubernetes/pkg/api/install"
	_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
	_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
	_ "k8s.io/kubernetes/pkg/apis/extensions/install"
	_ "k8s.io/kubernetes/pkg/apis/metrics/install"
)

var (
	Groups      = make(map[string]TestGroup)
	Default     TestGroup
	Autoscaling TestGroup
	Batch       TestGroup
	Extensions  TestGroup
)

type TestGroup struct {
	externalGroupVersion unversioned.GroupVersion
	internalGroupVersion unversioned.GroupVersion
}

func init() {
	kubeTestAPI := os.Getenv("KUBE_TEST_API")
	if len(kubeTestAPI) != 0 {
		testGroupVersions := strings.Split(kubeTestAPI, ",")
		for _, gvString := range testGroupVersions {
			groupVersion, err := unversioned.ParseGroupVersion(gvString)
			if err != nil {
				panic(fmt.Sprintf("Error parsing groupversion %v: %v", gvString, err))
			}

			Groups[groupVersion.Group] = TestGroup{
				externalGroupVersion: groupVersion,
				internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal},
			}
		}
	}

	if _, ok := Groups[api.GroupName]; !ok {
		Groups[api.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: api.GroupName, Version: registered.GroupOrDie(api.GroupName).GroupVersion.Version},
			internalGroupVersion: api.SchemeGroupVersion,
		}
	}
	if _, ok := Groups[autoscaling.GroupName]; !ok {
		Groups[autoscaling.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version},
			internalGroupVersion: extensions.SchemeGroupVersion,
		}
	}
	if _, ok := Groups[batch.GroupName]; !ok {
		Groups[batch.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: batch.GroupName, Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version},
			internalGroupVersion: extensions.SchemeGroupVersion,
		}
	}
	if _, ok := Groups[extensions.GroupName]; !ok {
		Groups[extensions.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version},
			internalGroupVersion: extensions.SchemeGroupVersion,
		}
	}

	Default = Groups[api.GroupName]
	Autoscaling = Groups[autoscaling.GroupName]
	Batch = Groups[batch.GroupName]
	Extensions = Groups[extensions.GroupName]
}

func (g TestGroup) ContentConfig() (string, *unversioned.GroupVersion, runtime.Codec) {
	return "application/json", g.GroupVersion(), g.Codec()
}

func (g TestGroup) GroupVersion() *unversioned.GroupVersion {
	copyOfGroupVersion := g.externalGroupVersion
	return &copyOfGroupVersion
}

// InternalGroupVersion returns the group,version used to identify the internal
// types for this API
func (g TestGroup) InternalGroupVersion() unversioned.GroupVersion {
	return g.internalGroupVersion
}

// Codec returns the codec for the API version to test against, as set by the
// KUBE_TEST_API env var.
func (g TestGroup) Codec() runtime.Codec {
	return api.Codecs.LegacyCodec(g.externalGroupVersion)
}

// Converter returns the api.Scheme for the API version to test against, as set by the
// KUBE_TEST_API env var.
func (g TestGroup) Converter() runtime.ObjectConvertor {
	interfaces, err := registered.GroupOrDie(g.externalGroupVersion.Group).InterfacesFor(g.externalGroupVersion)
	if err != nil {
		panic(err)
	}
	return interfaces.ObjectConvertor
}

// MetadataAccessor returns the MetadataAccessor for the API version to test against,
// as set by the KUBE_TEST_API env var.
func (g TestGroup) MetadataAccessor() meta.MetadataAccessor {
	interfaces, err := registered.GroupOrDie(g.externalGroupVersion.Group).InterfacesFor(g.externalGroupVersion)
	if err != nil {
		panic(err)
	}
	return interfaces.MetadataAccessor
}

// SelfLink returns a self link that will appear to be for the version Version().
// 'resource' should be the resource path, e.g. "pods" for the Pod type. 'name' should be
// empty for lists.
func (g TestGroup) SelfLink(resource, name string) string {
	if g.externalGroupVersion.Group == api.GroupName {
		if name == "" {
			return fmt.Sprintf("/api/%s/%s", g.externalGroupVersion.Version, resource)
		}
		return fmt.Sprintf("/api/%s/%s/%s", g.externalGroupVersion.Version, resource, name)
	} else {
		// TODO: will need a /apis prefix once we have proper multi-group
		// support
		if name == "" {
			return fmt.Sprintf("/apis/%s/%s/%s", g.externalGroupVersion.Group, g.externalGroupVersion.Version, resource)
		}
		return fmt.Sprintf("/apis/%s/%s/%s/%s", g.externalGroupVersion.Group, g.externalGroupVersion.Version, resource, name)
	}
}

// Returns the appropriate path for the given prefix (watch, proxy, redirect, etc), resource, namespace and name.
// For ex, this is of the form:
// /api/v1/watch/namespaces/foo/pods/pod0 for v1.
func (g TestGroup) ResourcePathWithPrefix(prefix, resource, namespace, name string) string {
	var path string
	if g.externalGroupVersion.Group == api.GroupName {
		path = "/api/" + g.externalGroupVersion.Version
	} else {
		// TODO: switch back once we have proper multiple group support
		// path = "/apis/" + g.Group + "/" + Version(group...)
		path = "/apis/" + g.externalGroupVersion.Group + "/" + g.externalGroupVersion.Version
	}

	if prefix != "" {
		path = path + "/" + prefix
	}
	if namespace != "" {
		path = path + "/namespaces/" + namespace
	}
	// Resource names are lower case.
	resource = strings.ToLower(resource)
	if resource != "" {
		path = path + "/" + resource
	}
	if name != "" {
		path = path + "/" + name
	}
	return path
}

// Returns the appropriate path for the given resource, namespace and name.
// For example, this is of the form:
// /api/v1/namespaces/foo/pods/pod0 for v1.
func (g TestGroup) ResourcePath(resource, namespace, name string) string {
	return g.ResourcePathWithPrefix("", resource, namespace, name)
}

func (g TestGroup) RESTMapper() meta.RESTMapper {
	return registered.GroupOrDie(g.externalGroupVersion.Group).RESTMapper
}

// Get codec based on runtime.Object
func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) {
	kind, err := api.Scheme.ObjectKind(obj)
	if err != nil {
		return nil, fmt.Errorf("unexpected encoding error: %v", err)
	}

	for _, group := range Groups {
		if group.GroupVersion().Group != kind.Group {
			continue
		}

		if api.Scheme.Recognizes(kind) {
			return group.Codec(), nil
		}
	}
	// Codec used for unversioned types
	if api.Scheme.Recognizes(kind) {
		serializer, ok := api.Codecs.SerializerForFileExtension("json")
		if !ok {
			return nil, fmt.Errorf("no serializer registered for json")
		}
		return serializer, nil
	}
	return nil, fmt.Errorf("unexpected kind: %v", kind)
}

func NewTestGroup(external, internal unversioned.GroupVersion) TestGroup {
	return TestGroup{external, internal}
}
<commit_msg>Implemented Batch client<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

// Package testapi provides a helper for retrieving the KUBE_TEST_API environment variable.
package testapi

import (
	"fmt"
	"os"
	"strings"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/apis/autoscaling"
	"k8s.io/kubernetes/pkg/apis/batch"
	"k8s.io/kubernetes/pkg/apis/extensions"
	"k8s.io/kubernetes/pkg/runtime"

	_ "k8s.io/kubernetes/pkg/api/install"
	_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
	_ "k8s.io/kubernetes/pkg/apis/batch/install"
	_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
	_ "k8s.io/kubernetes/pkg/apis/extensions/install"
	_ "k8s.io/kubernetes/pkg/apis/metrics/install"
)

var (
	Groups      = make(map[string]TestGroup)
	Default     TestGroup
	Autoscaling TestGroup
	Batch       TestGroup
	Extensions  TestGroup
)

type TestGroup struct {
	externalGroupVersion unversioned.GroupVersion
	internalGroupVersion unversioned.GroupVersion
}

func init() {
	kubeTestAPI := os.Getenv("KUBE_TEST_API")
	if len(kubeTestAPI) != 0 {
		testGroupVersions := strings.Split(kubeTestAPI, ",")
		for _, gvString := range testGroupVersions {
			groupVersion, err := unversioned.ParseGroupVersion(gvString)
			if err != nil {
				panic(fmt.Sprintf("Error parsing groupversion %v: %v", gvString, err))
			}

			Groups[groupVersion.Group] = TestGroup{
				externalGroupVersion: groupVersion,
				internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal},
			}
		}
	}

	if _, ok := Groups[api.GroupName]; !ok {
		Groups[api.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: api.GroupName, Version: registered.GroupOrDie(api.GroupName).GroupVersion.Version},
			internalGroupVersion: api.SchemeGroupVersion,
		}
	}
	if _, ok := Groups[autoscaling.GroupName]; !ok {
		Groups[autoscaling.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version},
			internalGroupVersion: extensions.SchemeGroupVersion,
		}
	}
	if _, ok := Groups[batch.GroupName]; !ok {
		Groups[batch.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: batch.GroupName, Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version},
			internalGroupVersion: extensions.SchemeGroupVersion,
		}
	}
	if _, ok := Groups[extensions.GroupName]; !ok {
		Groups[extensions.GroupName] = TestGroup{
			externalGroupVersion: unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version},
			internalGroupVersion: extensions.SchemeGroupVersion,
		}
	}

	Default = Groups[api.GroupName]
	Autoscaling = Groups[autoscaling.GroupName]
	Batch = Groups[batch.GroupName]
	Extensions = Groups[extensions.GroupName]
}

func (g TestGroup) ContentConfig() (string, *unversioned.GroupVersion, runtime.Codec) {
	return "application/json", g.GroupVersion(), g.Codec()
}

func (g TestGroup) GroupVersion() *unversioned.GroupVersion {
	copyOfGroupVersion := g.externalGroupVersion
	return &copyOfGroupVersion
}

// InternalGroupVersion returns the group,version used to identify the internal
// types for this API
func (g TestGroup) InternalGroupVersion() unversioned.GroupVersion {
	return g.internalGroupVersion
}

// Codec returns the codec for the API version to test against, as set by the
// KUBE_TEST_API env var.
func (g TestGroup) Codec() runtime.Codec {
	return api.Codecs.LegacyCodec(g.externalGroupVersion)
}

// Converter returns the api.Scheme for the API version to test against, as set by the
// KUBE_TEST_API env var.
func (g TestGroup) Converter() runtime.ObjectConvertor {
	interfaces, err := registered.GroupOrDie(g.externalGroupVersion.Group).InterfacesFor(g.externalGroupVersion)
	if err != nil {
		panic(err)
	}
	return interfaces.ObjectConvertor
}

// MetadataAccessor returns the MetadataAccessor for the API version to test against,
// as set by the KUBE_TEST_API env var.
func (g TestGroup) MetadataAccessor() meta.MetadataAccessor {
	interfaces, err := registered.GroupOrDie(g.externalGroupVersion.Group).InterfacesFor(g.externalGroupVersion)
	if err != nil {
		panic(err)
	}
	return interfaces.MetadataAccessor
}

// SelfLink returns a self link that will appear to be for the version Version().
// 'resource' should be the resource path, e.g. "pods" for the Pod type. 'name' should be
// empty for lists.
func (g TestGroup) SelfLink(resource, name string) string {
	if g.externalGroupVersion.Group == api.GroupName {
		if name == "" {
			return fmt.Sprintf("/api/%s/%s", g.externalGroupVersion.Version, resource)
		}
		return fmt.Sprintf("/api/%s/%s/%s", g.externalGroupVersion.Version, resource, name)
	} else {
		// TODO: will need a /apis prefix once we have proper multi-group
		// support
		if name == "" {
			return fmt.Sprintf("/apis/%s/%s/%s", g.externalGroupVersion.Group, g.externalGroupVersion.Version, resource)
		}
		return fmt.Sprintf("/apis/%s/%s/%s/%s", g.externalGroupVersion.Group, g.externalGroupVersion.Version, resource, name)
	}
}

// Returns the appropriate path for the given prefix (watch, proxy, redirect, etc), resource, namespace and name.
// For ex, this is of the form:
// /api/v1/watch/namespaces/foo/pods/pod0 for v1.
func (g TestGroup) ResourcePathWithPrefix(prefix, resource, namespace, name string) string {
	var path string
	if g.externalGroupVersion.Group == api.GroupName {
		path = "/api/" + g.externalGroupVersion.Version
	} else {
		// TODO: switch back once we have proper multiple group support
		// path = "/apis/" + g.Group + "/" + Version(group...)
		path = "/apis/" + g.externalGroupVersion.Group + "/" + g.externalGroupVersion.Version
	}

	if prefix != "" {
		path = path + "/" + prefix
	}
	if namespace != "" {
		path = path + "/namespaces/" + namespace
	}
	// Resource names are lower case.
	resource = strings.ToLower(resource)
	if resource != "" {
		path = path + "/" + resource
	}
	if name != "" {
		path = path + "/" + name
	}
	return path
}

// Returns the appropriate path for the given resource, namespace and name.
// For example, this is of the form:
// /api/v1/namespaces/foo/pods/pod0 for v1.
func (g TestGroup) ResourcePath(resource, namespace, name string) string {
	return g.ResourcePathWithPrefix("", resource, namespace, name)
}

func (g TestGroup) RESTMapper() meta.RESTMapper {
	return registered.GroupOrDie(g.externalGroupVersion.Group).RESTMapper
}

// Get codec based on runtime.Object
func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) {
	kind, err := api.Scheme.ObjectKind(obj)
	if err != nil {
		return nil, fmt.Errorf("unexpected encoding error: %v", err)
	}

	for _, group := range Groups {
		if group.GroupVersion().Group != kind.Group {
			continue
		}

		if api.Scheme.Recognizes(kind) {
			return group.Codec(), nil
		}
	}
	// Codec used for unversioned types
	if api.Scheme.Recognizes(kind) {
		serializer, ok := api.Codecs.SerializerForFileExtension("json")
		if !ok {
			return nil, fmt.Errorf("no serializer registered for json")
		}
		return serializer, nil
	}
	return nil, fmt.Errorf("unexpected kind: %v", kind)
}

func NewTestGroup(external, internal unversioned.GroupVersion) TestGroup {
	return TestGroup{external, internal}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package apiserver

import (
	"k8s.io/apimachinery/pkg/apimachinery/registered"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/version"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"

	"k8s.io/sample-apiserver/pkg/apis/wardle"
	"k8s.io/sample-apiserver/pkg/apis/wardle/install"
	"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
	wardleregistry "k8s.io/sample-apiserver/pkg/registry"
	fischerstorage "k8s.io/sample-apiserver/pkg/registry/wardle/fischer"
	flunderstorage "k8s.io/sample-apiserver/pkg/registry/wardle/flunder"
)

var (
	registry = registered.NewAPIRegistrationManager()
	Scheme   = runtime.NewScheme()
	Codecs   = serializer.NewCodecFactory(Scheme)
)

func init() {
	install.Install(registry, Scheme)

	// we need to add the options to empty v1
	// TODO fix the server code to avoid this
	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})

	// TODO: keep the generic API server from wanting this
	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	Scheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

type ExtraConfig struct {
	// Place you custom config here.
}

type Config struct {
	GenericConfig *genericapiserver.RecommendedConfig
	ExtraConfig   ExtraConfig
}

// WardleServer contains state for a Kubernetes cluster master/api server.
type WardleServer struct {
	GenericAPIServer *genericapiserver.GenericAPIServer
}

type completedConfig struct {
	GenericConfig genericapiserver.CompletedConfig
	ExtraConfig   *ExtraConfig
}

type CompletedConfig struct {
	// Embed a private pointer that cannot be instantiated outside of this package.
	*completedConfig
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (cfg *Config) Complete() CompletedConfig {
	c := completedConfig{
		cfg.GenericConfig.Complete(),
		&cfg.ExtraConfig,
	}

	c.GenericConfig.Version = &version.Info{
		Major: "1",
		Minor: "0",
	}

	return CompletedConfig{&c}
}

// New returns a new instance of WardleServer from the given config.
func (c completedConfig) New() (*WardleServer, error) {
	genericServer, err := c.GenericConfig.New("sample-apiserver", genericapiserver.NewEmptyDelegate())
	if err != nil {
		return nil, err
	}

	s := &WardleServer{
		GenericAPIServer: genericServer,
	}

	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(wardle.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)
	apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion

	v1alpha1storage := map[string]rest.Storage{}
	v1alpha1storage["flunders"] = wardleregistry.RESTInPeace(flunderstorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter))
	v1alpha1storage["fischers"] = wardleregistry.RESTInPeace(fischerstorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter))
	apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage

	v1beta1storage := map[string]rest.Storage{}
	v1beta1storage["flunders"] = wardleregistry.RESTInPeace(flunderstorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter))
	apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = v1beta1storage

	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	return s, nil
}
<commit_msg>stop duplicating preferred version order<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package apiserver

import (
	"k8s.io/apimachinery/pkg/apimachinery/registered"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/version"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"

	"k8s.io/sample-apiserver/pkg/apis/wardle"
	"k8s.io/sample-apiserver/pkg/apis/wardle/install"
	wardleregistry "k8s.io/sample-apiserver/pkg/registry"
	fischerstorage "k8s.io/sample-apiserver/pkg/registry/wardle/fischer"
	flunderstorage "k8s.io/sample-apiserver/pkg/registry/wardle/flunder"
)

var (
	registry = registered.NewAPIRegistrationManager()
	Scheme   = runtime.NewScheme()
	Codecs   = serializer.NewCodecFactory(Scheme)
)

func init() {
	install.Install(registry, Scheme)

	// we need to add the options to empty v1
	// TODO fix the server code to avoid this
	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})

	// TODO: keep the generic API server from wanting this
	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
	Scheme.AddUnversionedTypes(unversioned,
		&metav1.Status{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	)
}

type ExtraConfig struct {
	// Place you custom config here.
}

type Config struct {
	GenericConfig *genericapiserver.RecommendedConfig
	ExtraConfig   ExtraConfig
}

// WardleServer contains state for a Kubernetes cluster master/api server.
type WardleServer struct {
	GenericAPIServer *genericapiserver.GenericAPIServer
}

type completedConfig struct {
	GenericConfig genericapiserver.CompletedConfig
	ExtraConfig   *ExtraConfig
}

type CompletedConfig struct {
	// Embed a private pointer that cannot be instantiated outside of this package.
	*completedConfig
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (cfg *Config) Complete() CompletedConfig {
	c := completedConfig{
		cfg.GenericConfig.Complete(),
		&cfg.ExtraConfig,
	}

	c.GenericConfig.Version = &version.Info{
		Major: "1",
		Minor: "0",
	}

	return CompletedConfig{&c}
}

// New returns a new instance of WardleServer from the given config.
func (c completedConfig) New() (*WardleServer, error) {
	genericServer, err := c.GenericConfig.New("sample-apiserver", genericapiserver.NewEmptyDelegate())
	if err != nil {
		return nil, err
	}

	s := &WardleServer{
		GenericAPIServer: genericServer,
	}

	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(wardle.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)

	v1alpha1storage := map[string]rest.Storage{}
	v1alpha1storage["flunders"] = wardleregistry.RESTInPeace(flunderstorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter))
	v1alpha1storage["fischers"] = wardleregistry.RESTInPeace(fischerstorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter))
	apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage

	v1beta1storage := map[string]rest.Storage{}
	v1beta1storage["flunders"] = wardleregistry.RESTInPeace(flunderstorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter))
	apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = v1beta1storage

	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	return s, nil
}
<|endoftext|>


<commit_before>package controller

import (
	"fmt"
	"path"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	api_v1 "k8s.io/api/core/v1"
	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	kubernetes_informers "k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes/scheme"
	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/tools/record"

	v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	helmutil "github.com/sapcc/kubernikus/pkg/client/helm"
	kube "github.com/sapcc/kubernikus/pkg/client/kubernetes"
	"github.com/sapcc/kubernikus/pkg/client/kubernikus"
	"github.com/sapcc/kubernikus/pkg/client/openstack"
	"github.com/sapcc/kubernikus/pkg/controller/certs"
	"github.com/sapcc/kubernikus/pkg/controller/config"
	"github.com/sapcc/kubernikus/pkg/controller/deorbit"
	"github.com/sapcc/kubernikus/pkg/controller/flight"
	"github.com/sapcc/kubernikus/pkg/controller/hammertime"
	"github.com/sapcc/kubernikus/pkg/controller/launch"
	"github.com/sapcc/kubernikus/pkg/controller/migration"
	"github.com/sapcc/kubernikus/pkg/controller/nodeobservatory"
	"github.com/sapcc/kubernikus/pkg/controller/routegc"
	"github.com/sapcc/kubernikus/pkg/controller/servicing"
	kubernikus_informers "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions"
	_ "github.com/sapcc/kubernikus/pkg/util/workqueue/prometheus"
	"github.com/sapcc/kubernikus/pkg/version"
)

type KubernikusOperatorOptions struct {
	KubeConfig string
	Context    string

	ChartDirectory string

	AuthURL           string
	AuthUsername      string
	AuthPassword      string
	AuthDomain        string
	AuthProject       string
	AuthProjectDomain string
	Region            string

	KubernikusDomain    string
	KubernikusProjectID string
	KubernikusNetworkID string
	Namespace           string
	Controllers         []string
	MetricPort          int
	LogLevel            int

	NodeUpdateHoldoff time.Duration
}

type KubernikusOperator struct {
	config.Config
	config.Factories
	config.Clients
	Logger log.Logger
}

const (
	DEFAULT_RECONCILIATION = 5 * time.Minute
)

func NewKubernikusOperator(options *KubernikusOperatorOptions, logger log.Logger) (*KubernikusOperator, error) {
	var err error

	imageRegistry, err := version.NewImageRegistry(path.Join(options.ChartDirectory, "images.yaml"), options.Region)
	if err != nil {
		return nil, fmt.Errorf("Unable to initialize image registry: %s", err)
	}

	o := &KubernikusOperator{
		Config: config.Config{
			Openstack: config.OpenstackConfig{
				AuthURL:           options.AuthURL,
				AuthUsername:      options.AuthUsername,
				AuthPassword:      options.AuthPassword,
				AuthProject:       options.AuthProjectDomain,
				AuthProjectDomain: options.AuthProjectDomain,
			},
			Helm: config.HelmConfig{
				ChartDirectory: options.ChartDirectory,
			},
			Kubernikus: config.KubernikusConfig{
				Domain:      options.KubernikusDomain,
				Namespace:   options.Namespace,
				ProjectID:   options.KubernikusProjectID,
				NetworkID:   options.KubernikusNetworkID,
				Controllers: make(map[string]config.Controller),
			},
			Images: *imageRegistry,
		},
		Logger: logger,
	}

	o.Clients.Kubernetes, err = kube.NewClient(options.KubeConfig, options.Context, logger)

	if err != nil {
		return nil, fmt.Errorf("Failed to create kubernetes clients: %s", err)
	}

	o.Clients.Kubernikus, err = kubernikus.NewClient(options.KubeConfig, options.Context)
	if err != nil {
		return nil, fmt.Errorf("Failed to create kubernikus clients: %s", err)
	}

	config, err := kube.NewConfig(options.KubeConfig, options.Context)
	if err != nil {
		return nil, fmt.Errorf("Failed to create kubernetes config: %s", err)
	}
	o.Clients.Helm3, err = helmutil.NewClient3(options.Namespace, options.KubeConfig, options.Context, logger)
	if err != nil {
		return nil, fmt.Errorf("Failed to create helm 3 client: %s", err)
	}

	apiextensionsclientset, err := apiextensionsclient.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("Failed to create apiextenstionsclient: %s", err)
	}

	if err := kube.EnsureCRD(apiextensionsclientset, logger); err != nil {
		return nil, fmt.Errorf("Couldn't create CRD: %s", err)
	}

	adminAuthOptions := &tokens.AuthOptions{
		IdentityEndpoint: options.AuthURL,
		Username:         options.AuthUsername,
		Password:         options.AuthPassword,
		DomainName:       options.AuthDomain,
		AllowReauth:      true,
		Scope: tokens.Scope{
			ProjectName: options.AuthProject,
			DomainName:  options.AuthProjectDomain,
		},
	}

	o.Factories.Kubernikus = kubernikus_informers.NewFilteredSharedInformerFactory(o.Clients.Kubernikus, DEFAULT_RECONCILIATION, options.Namespace, nil)
	o.Factories.Kubernetes = kubernetes_informers.NewFilteredSharedInformerFactory(o.Clients.Kubernetes, DEFAULT_RECONCILIATION, options.Namespace, nil)

	klusters := o.Factories.Kubernikus.Kubernikus().V1().Klusters().Informer()

	if options.AuthURL != "" {
		o.Factories.Openstack = openstack.NewSharedOpenstackClientFactory(o.Clients.Kubernetes, klusters, adminAuthOptions, logger)
	} else {
		o.Factories.Openstack = openstack.NotAvailableFactory{}
	}

	o.Clients.Satellites = kube.NewSharedClientFactory(o.Clients.Kubernetes, klusters, logger)

	o.Factories.NodesObservatory = nodeobservatory.NewInformerFactory(o.Factories.Kubernikus.Kubernikus().V1().Klusters(), o.Clients.Satellites, logger)

	// Add kubernikus types to the default Kubernetes Scheme so events can be
	// logged for those types.
	v1.AddToScheme(scheme.Scheme)
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartEventWatcher(func(e *api_v1.Event) {
		logger.Log(
			"controller", "operator",
			"resource", "event",
			"msg", e.Message,
			"reason", e.Reason,
			"type", e.Type,
			"kind", e.InvolvedObject.Kind,
			"namespace", e.InvolvedObject.Namespace,
			"name", e.InvolvedObject.Name,
			"v", 2)
	})

	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: o.Clients.Kubernetes.CoreV1().Events(o.Config.Kubernikus.Namespace)})
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, api_v1.EventSource{Component: "operator"})

	for _, k := range options.Controllers {
		switch k {
		case "groundctl":
			o.Config.Kubernikus.Controllers["groundctl"] = NewGroundController(10, o.Factories, o.Clients, recorder, o.Config, logger)
		case "launchctl":
			o.Config.Kubernikus.Controllers["launchctl"] = launch.NewController(10, o.Factories, o.Clients, recorder, o.Config.Images, logger)
		case "routegc":
			o.Config.Kubernikus.Controllers["routegc"] = routegc.New(300*time.Second, o.Factories, logger)
		case "deorbiter":
			o.Config.Kubernikus.Controllers["deorbiter"] = deorbit.NewController(10, o.Factories, o.Clients, recorder, logger)
		case "flight":
			o.Config.Kubernikus.Controllers["flight"] = flight.NewController(10, o.Factories, o.Clients, recorder, logger)
		case "migration":
			o.Config.Kubernikus.Controllers["migration"] = migration.NewController(3, o.Factories, o.Clients, recorder, logger)
		case "hammertime":
			o.Config.Kubernikus.Controllers["hammertime"] = hammertime.New(10*time.Second, 20*time.Second, o.Factories, o.Clients, recorder, logger)
		case "servicing":
			o.Config.Kubernikus.Controllers["servicing"] = servicing.NewController(10, o.Factories, o.Clients, recorder, options.NodeUpdateHoldoff, logger)
		case "certs":
			o.Config.Kubernikus.Controllers["certs"] = certs.New(12*time.Hour, o.Factories, o.Config, o.Clients, logger)
		}
	}

	return o, err
}

func (o *KubernikusOperator) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	o.Logger.Log(
		"msg", "starting kubernikus operator",
		"namespace", o.Config.Kubernikus.Namespace,
		"version", version.GitCommit)

	kube.WaitForServer(o.Clients.Kubernetes, stopCh, o.Logger)

	o.Factories.Kubernikus.Start(stopCh)
	o.Factories.Kubernetes.Start(stopCh)
	o.Factories.NodesObservatory.Start(stopCh)

	o.Factories.Kubernikus.WaitForCacheSync(stopCh)
	o.Factories.Kubernetes.WaitForCacheSync(stopCh)

	o.Logger.Log("msg", "Cache primed")
	if _, found := o.Config.Kubernikus.Controllers["migration"]; found {
		migration.UpdateMigrationStatus(o.Clients.Kubernikus, o.Factories.Kubernikus.Kubernikus().V1().Klusters().Lister())
	}
	o.Logger.Log("msg", "Starting controllers")
	for _, controller := range o.Config.Kubernikus.Controllers {
		go controller.Run(stopCh, wg)
	}
}
<commit_msg>Disable servicing controller<commit_after>package controller

import (
	"fmt"
	"path"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	api_v1 "k8s.io/api/core/v1"
	apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	kubernetes_informers "k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes/scheme"
	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/tools/record"

	v1 "github.com/sapcc/kubernikus/pkg/apis/kubernikus/v1"
	helmutil "github.com/sapcc/kubernikus/pkg/client/helm"
	kube "github.com/sapcc/kubernikus/pkg/client/kubernetes"
	"github.com/sapcc/kubernikus/pkg/client/kubernikus"
	"github.com/sapcc/kubernikus/pkg/client/openstack"
	"github.com/sapcc/kubernikus/pkg/controller/certs"
	"github.com/sapcc/kubernikus/pkg/controller/config"
	"github.com/sapcc/kubernikus/pkg/controller/deorbit"
	"github.com/sapcc/kubernikus/pkg/controller/flight"
	"github.com/sapcc/kubernikus/pkg/controller/hammertime"
	"github.com/sapcc/kubernikus/pkg/controller/launch"
	"github.com/sapcc/kubernikus/pkg/controller/migration"
	"github.com/sapcc/kubernikus/pkg/controller/nodeobservatory"
	"github.com/sapcc/kubernikus/pkg/controller/routegc"
	kubernikus_informers "github.com/sapcc/kubernikus/pkg/generated/informers/externalversions"
	_ "github.com/sapcc/kubernikus/pkg/util/workqueue/prometheus"
	"github.com/sapcc/kubernikus/pkg/version"
)

type KubernikusOperatorOptions struct {
	KubeConfig string
	Context    string

	ChartDirectory string

	AuthURL           string
	AuthUsername      string
	AuthPassword      string
	AuthDomain        string
	AuthProject       string
	AuthProjectDomain string
	Region            string

	KubernikusDomain    string
	KubernikusProjectID string
	KubernikusNetworkID string
	Namespace           string
	Controllers         []string
	MetricPort          int
	LogLevel            int

	NodeUpdateHoldoff time.Duration
}

type KubernikusOperator struct {
	config.Config
	config.Factories
	config.Clients
	Logger log.Logger
}

const (
	DEFAULT_RECONCILIATION = 5 * time.Minute
)

func NewKubernikusOperator(options *KubernikusOperatorOptions, logger log.Logger) (*KubernikusOperator, error) {
	var err error

	imageRegistry, err := version.NewImageRegistry(path.Join(options.ChartDirectory, "images.yaml"), options.Region)
	if err != nil {
		return nil, fmt.Errorf("Unable to initialize image registry: %s", err)
	}

	o := &KubernikusOperator{
		Config: config.Config{
			Openstack: config.OpenstackConfig{
				AuthURL:           options.AuthURL,
				AuthUsername:      options.AuthUsername,
				AuthPassword:      options.AuthPassword,
				AuthProject:       options.AuthProjectDomain,
				AuthProjectDomain: options.AuthProjectDomain,
			},
			Helm: config.HelmConfig{
				ChartDirectory: options.ChartDirectory,
			},
			Kubernikus: config.KubernikusConfig{
				Domain:      options.KubernikusDomain,
				Namespace:   options.Namespace,
				ProjectID:   options.KubernikusProjectID,
				NetworkID:   options.KubernikusNetworkID,
				Controllers: make(map[string]config.Controller),
			},
			Images: *imageRegistry,
		},
		Logger: logger,
	}

	o.Clients.Kubernetes, err = kube.NewClient(options.KubeConfig, options.Context, logger)

	if err != nil {
		return nil, fmt.Errorf("Failed to create kubernetes clients: %s", err)
	}

	o.Clients.Kubernikus, err = kubernikus.NewClient(options.KubeConfig, options.Context)
	if err != nil {
		return nil, fmt.Errorf("Failed to create kubernikus clients: %s", err)
	}

	config, err := kube.NewConfig(options.KubeConfig, options.Context)
	if err != nil {
		return nil, fmt.Errorf("Failed to create kubernetes config: %s", err)
	}
	o.Clients.Helm3, err = helmutil.NewClient3(options.Namespace, options.KubeConfig, options.Context, logger)
	if err != nil {
		return nil, fmt.Errorf("Failed to create helm 3 client: %s", err)
	}

	apiextensionsclientset, err := apiextensionsclient.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("Failed to create apiextenstionsclient: %s", err)
	}

	if err := kube.EnsureCRD(apiextensionsclientset, logger); err != nil {
		return nil, fmt.Errorf("Couldn't create CRD: %s", err)
	}

	adminAuthOptions := &tokens.AuthOptions{
		IdentityEndpoint: options.AuthURL,
		Username:         options.AuthUsername,
		Password:         options.AuthPassword,
		DomainName:       options.AuthDomain,
		AllowReauth:      true,
		Scope: tokens.Scope{
			ProjectName: options.AuthProject,
			DomainName:  options.AuthProjectDomain,
		},
	}

	o.Factories.Kubernikus = kubernikus_informers.NewFilteredSharedInformerFactory(o.Clients.Kubernikus, DEFAULT_RECONCILIATION, options.Namespace, nil)
	o.Factories.Kubernetes = kubernetes_informers.NewFilteredSharedInformerFactory(o.Clients.Kubernetes, DEFAULT_RECONCILIATION, options.Namespace, nil)

	klusters := o.Factories.Kubernikus.Kubernikus().V1().Klusters().Informer()

	if options.AuthURL != "" {
		o.Factories.Openstack = openstack.NewSharedOpenstackClientFactory(o.Clients.Kubernetes, klusters, adminAuthOptions, logger)
	} else {
		o.Factories.Openstack = openstack.NotAvailableFactory{}
	}

	o.Clients.Satellites = kube.NewSharedClientFactory(o.Clients.Kubernetes, klusters, logger)

	o.Factories.NodesObservatory = nodeobservatory.NewInformerFactory(o.Factories.Kubernikus.Kubernikus().V1().Klusters(), o.Clients.Satellites, logger)

	// Add kubernikus types to the default Kubernetes Scheme so events can be
	// logged for those types.
	v1.AddToScheme(scheme.Scheme)
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartEventWatcher(func(e *api_v1.Event) {
		logger.Log(
			"controller", "operator",
			"resource", "event",
			"msg", e.Message,
			"reason", e.Reason,
			"type", e.Type,
			"kind", e.InvolvedObject.Kind,
			"namespace", e.InvolvedObject.Namespace,
			"name", e.InvolvedObject.Name,
			"v", 2)
	})

	eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: o.Clients.Kubernetes.CoreV1().Events(o.Config.Kubernikus.Namespace)})
	recorder := eventBroadcaster.NewRecorder(scheme.Scheme, api_v1.EventSource{Component: "operator"})

	for _, k := range options.Controllers {
		switch k {
		case "groundctl":
			o.Config.Kubernikus.Controllers["groundctl"] = NewGroundController(10, o.Factories, o.Clients, recorder, o.Config, logger)
		case "launchctl":
			o.Config.Kubernikus.Controllers["launchctl"] = launch.NewController(10, o.Factories, o.Clients, recorder, o.Config.Images, logger)
		case "routegc":
			o.Config.Kubernikus.Controllers["routegc"] = routegc.New(300*time.Second, o.Factories, logger)
		case "deorbiter":
			o.Config.Kubernikus.Controllers["deorbiter"] = deorbit.NewController(10, o.Factories, o.Clients, recorder, logger)
		case "flight":
			o.Config.Kubernikus.Controllers["flight"] = flight.NewController(10, o.Factories, o.Clients, recorder, logger)
		case "migration":
			o.Config.Kubernikus.Controllers["migration"] = migration.NewController(3, o.Factories, o.Clients, recorder, logger)
		case "hammertime":
			o.Config.Kubernikus.Controllers["hammertime"] = hammertime.New(10*time.Second, 20*time.Second, o.Factories, o.Clients, recorder, logger)
		//case "servicing":
		//o.Config.Kubernikus.Controllers["servicing"] = servicing.NewController(10, o.Factories, o.Clients, recorder, options.NodeUpdateHoldoff, logger)
		case "certs":
			o.Config.Kubernikus.Controllers["certs"] = certs.New(12*time.Hour, o.Factories, o.Config, o.Clients, logger)
		}
	}

	return o, err
}

func (o *KubernikusOperator) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
	o.Logger.Log(
		"msg", "starting kubernikus operator",
		"namespace", o.Config.Kubernikus.Namespace,
		"version", version.GitCommit)

	kube.WaitForServer(o.Clients.Kubernetes, stopCh, o.Logger)

	o.Factories.Kubernikus.Start(stopCh)
	o.Factories.Kubernetes.Start(stopCh)
	o.Factories.NodesObservatory.Start(stopCh)

	o.Factories.Kubernikus.WaitForCacheSync(stopCh)
	o.Factories.Kubernetes.WaitForCacheSync(stopCh)

	o.Logger.Log("msg", "Cache primed")
	if _, found := o.Config.Kubernikus.Controllers["migration"]; found {
		migration.UpdateMigrationStatus(o.Clients.Kubernikus, o.Factories.Kubernikus.Kubernikus().V1().Klusters().Lister())
	}
	o.Logger.Log("msg", "Starting controllers")
	for _, controller := range o.Config.Kubernikus.Controllers {
		go controller.Run(stopCh, wg)
	}
}
<|endoftext|>


<commit_before>package file

import (
	"os"

	"src.elv.sh/pkg/eval"
	"src.elv.sh/pkg/eval/vals"
)

var Ns = eval.NsBuilder{}.AddGoFns("file:", fns).Ns()

var fns = map[string]interface{}{
	"close":   close,
	"open":    open,
	"pipe":    pipe,
	"prclose": prclose,
	"pwclose": pwclose,
}

//elvdoc:fn open
//
// ```elvish
// file:open $filename
// ```
//
// Opens a file. Currently, `open` only supports opening a file for reading.
// File must be closed with `close` explicitly. Example:
//
// ```elvish-transcript
// ~> cat a.txt
// This is
// a file.
// ~> use file
// ~> f = (file:open a.txt)
// ~> cat < $f
// This is
// a file.
// ~> close $f
// ```
//
// @cf close

func open(name string) (vals.File, error) {
	return os.Open(name)
}

//elvdoc:fn close
//
// ```elvish
// ~> file:close $file
// ```
//
// Closes a file opened with `open`.
//
// @cf open

func close(f vals.File) error {
	return f.Close()
}

//elvdoc:fn pipe
//
// ```elvish
// file:pipe
// ```
//
// Create a new Unix pipe that can be used in redirections.
//
// A pipe contains both the read FD and the write FD. When redirecting command
// input to a pipe with `<`, the read FD is used. When redirecting command output
// to a pipe with `>`, the write FD is used. It is not supported to redirect both
// input and output with `<>` to a pipe.
//
// Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
// does not necessarily block. Pipes **must** be explicitly closed with `prclose`
// and `pwclose`.
//
// Putting values into pipes will cause those values to be discarded.
//
// Examples (assuming the pipe has a large enough buffer):
//
// ```elvish-transcript
// ~> p = (file:pipe)
// ~> echo 'lorem ipsum' > $p
// ~> head -n1 < $p
// lorem ipsum
// ~> put 'lorem ipsum' > $p
// ~> head -n1 < $p
// # blocks
// # $p should be closed with prclose and pwclose afterwards
// ```
//
// @cf prclose pwclose

func pipe() (vals.Pipe, error) {
	r, w, err := os.Pipe()
	return vals.NewPipe(r, w), err
}

//elvdoc:fn prclose
//
// ```elvish
// file:prclose $pipe
// ```
//
// Close the read end of a pipe.
//
// @cf pwclose pipe

func prclose(p vals.Pipe) error {
	return p.ReadEnd.Close()
}

//elvdoc:fn pwclose
//
// ```elvish
// file:pwclose $pipe
// ```
//
// Close the write end of a pipe.
//
// @cf prclose pipe

func pwclose(p vals.Pipe) error {
	return p.WriteEnd.Close()
}
<commit_msg>file:truncate added to file module, with requested changes<commit_after>package file

import (
	"math/big"
	"os"

	"src.elv.sh/pkg/eval"
	"src.elv.sh/pkg/eval/errs"
	"src.elv.sh/pkg/eval/vals"
)

var Ns = eval.NsBuilder{}.AddGoFns("file:", fns).Ns()

var fns = map[string]interface{}{
	"close":    close,
	"open":     open,
	"pipe":     pipe,
	"prclose":  prclose,
	"pwclose":  pwclose,
	"truncate": truncate,
}

//elvdoc:fn open
//
// ```elvish
// file:open $filename
// ```
//
// Opens a file. Currently, `open` only supports opening a file for reading.
// File must be closed with `close` explicitly. Example:
//
// ```elvish-transcript
// ~> cat a.txt
// This is
// a file.
// ~> use file
// ~> f = (file:open a.txt)
// ~> cat < $f
// This is
// a file.
// ~> close $f
// ```
//
// @cf close

func open(name string) (vals.File, error) {
	return os.Open(name)
}

//elvdoc:fn close
//
// ```elvish
// ~> file:close $file
// ```
//
// Closes a file opened with `open`.
//
// @cf open

func close(f vals.File) error {
	return f.Close()
}

//elvdoc:fn pipe
//
// ```elvish
// file:pipe
// ```
//
// Create a new Unix pipe that can be used in redirections.
//
// A pipe contains both the read FD and the write FD. When redirecting command
// input to a pipe with `<`, the read FD is used. When redirecting command output
// to a pipe with `>`, the write FD is used. It is not supported to redirect both
// input and output with `<>` to a pipe.
//
// Pipes have an OS-dependent buffer, so writing to a pipe without an active reader
// does not necessarily block. Pipes **must** be explicitly closed with `prclose`
// and `pwclose`.
//
// Putting values into pipes will cause those values to be discarded.
//
// Examples (assuming the pipe has a large enough buffer):
//
// ```elvish-transcript
// ~> p = (file:pipe)
// ~> echo 'lorem ipsum' > $p
// ~> head -n1 < $p
// lorem ipsum
// ~> put 'lorem ipsum' > $p
// ~> head -n1 < $p
// # blocks
// # $p should be closed with prclose and pwclose afterwards
// ```
//
// @cf prclose pwclose

func pipe() (vals.Pipe, error) {
	r, w, err := os.Pipe()
	return vals.NewPipe(r, w), err
}

//elvdoc:fn prclose
//
// ```elvish
// file:prclose $pipe
// ```
//
// Close the read end of a pipe.
//
// @cf pwclose pipe

func prclose(p vals.Pipe) error {
	return p.ReadEnd.Close()
}

//elvdoc:fn pwclose
//
// ```elvish
// file:pwclose $pipe
// ```
//
// Close the write end of a pipe.
//
// @cf prclose pipe

func pwclose(p vals.Pipe) error {
	return p.WriteEnd.Close()
}

//elvdoc:fn truncate
//
// ```elvish
// file:truncate $filename $size
// ```
// Truncate changes the size of
// the named file. If the file is
// a symbolic link, it changes the
// size of the link's target.
//
// If there is an error it will
// be of type *PathError.

func truncate(name string, size vals.Num) error {
	var offset int64
	switch size.(type) {
	case int:
		offset = int64(size.(int))
		if offset < 0 {
			return errs.BadValue{
				What:   "Numeric input for the function is wrong",
				Valid:  "positive 64 bit integer",
				Actual: "Number less than 0",
			}
		}
	case *big.Int:
		if (size.(*big.Int)).IsInt64() {
			offset = (size.(*big.Int)).Int64()
			if offset < 0 {
				return errs.BadValue{
					What:   "Numeric input for the function is wrong",
					Valid:  "positive 64 bit integer",
					Actual: "Number less than 0",
				}
			}
		} else {
			return errs.BadValue{
				What:   "Numeric input for function is wrong",
				Valid:  "positive 64 bit integer",
				Actual: "exceeding the MaxInt64",
			}
		}
	default:
		return errs.BadValue{
			What:   "size argument for os.Truncate",
			Valid:  "int or *big.Int",
			Actual: "string",
		}
	}
	return os.Truncate(name, offset)
}
<|endoftext|>