<commit_before>package emotechief

import (
	"errors"
	"fmt"
	"math/rand"
	"regexp"

	"github.com/gempir/gempbot/internal/channelpoint"
	"github.com/gempir/gempbot/internal/dto"
	"github.com/gempir/gempbot/internal/log"
	"github.com/gempir/gempbot/internal/store"
	"github.com/nicklaw5/helix/v2"
)

var sevenTvRegex = regexp.MustCompile(`https?:\/\/(next\.)?7tv.app\/emotes\/(\w*)`)

func (ec *EmoteChief) VerifySetSevenTvEmote(channelUserID, emoteId, channel, redeemedByUsername string, slots int) (emoteAddType dto.EmoteChangeType, removalTargetEmoteId string, err error) {
	if ec.db.IsEmoteBlocked(channelUserID, emoteId, dto.REWARD_SEVENTV) {
		return dto.EMOTE_ADD_ADD, "", errors.New("Emote is blocked")
	}

	nextEmote, err := ec.sevenTvClient.GetEmote(emoteId)
	if err != nil {
		return
	}

	user, err := ec.sevenTvClient.GetUser(channelUserID)
	if err != nil {
		return
	}

	for _, emote := range user.Emotes {
		if emote.Code == nextEmote.Code {
			return dto.EMOTE_ADD_ADD, "", fmt.Errorf("Emote code \"%s\" already added", nextEmote.Code)
		}
	}
	log.Infof("Current 7tv emotes: %d/%d", len(user.Emotes), user.EmoteSlots)

	emotesAdded := ec.db.GetEmoteAdded(channelUserID, dto.REWARD_SEVENTV, slots)
	log.Infof("Total Previous emotes %d in %s", len(emotesAdded), channelUserID)

	if len(emotesAdded) > 0 {
		oldestEmote := emotesAdded[len(emotesAdded)-1]
		if !oldestEmote.Blocked {
			for _, sharedEmote := range user.Emotes {
				if oldestEmote.EmoteID == sharedEmote.ID {
					removalTargetEmoteId = oldestEmote.EmoteID
					log.Infof("Found removal target %s in %s", removalTargetEmoteId, channelUserID)
				}
			}
		} else {
			log.Infof("Removal target %s is already blocked, so already removed, skipping removal", oldestEmote.EmoteID)
		}
	}

	emoteAddType = dto.EMOTE_ADD_REMOVED_PREVIOUS
	if removalTargetEmoteId == "" && len(user.Emotes) >= user.EmoteSlots {
		if len(user.Emotes) == 0 {
			return dto.EMOTE_ADD_ADD, "", errors.New("emotes limit reached and can't find amount of emotes added to choose random")
		}

		emoteAddType = dto.EMOTE_ADD_REMOVED_RANDOM
		log.Infof("Didn't find previous emote history of %d emotes and limit reached, choosing random in %s", slots, channelUserID)
		removalTargetEmoteId = user.Emotes[rand.Intn(len(user.Emotes))].ID
	}

	return
}

func (ec *EmoteChief) setSevenTvEmote(channelUserID, emoteId, channel, redeemedByUsername string, slots int) (addedEmoteId string, removedEmoteID string, err error) {
	emoteAddType, removalTargetEmoteId, err := ec.VerifySetSevenTvEmote(channelUserID, emoteId, channel, redeemedByUsername, slots)
	if err != nil {
		return "", "", err
	}

	// do we need to remove the emote?
	if removalTargetEmoteId != "" {
		err := ec.sevenTvClient.RemoveEmote(channelUserID, removalTargetEmoteId)
		if err != nil {
			return "", "", err
		}

		ec.db.CreateEmoteAdd(channelUserID, dto.REWARD_SEVENTV, removalTargetEmoteId, emoteAddType)
	}

	err = ec.sevenTvClient.AddEmote(channelUserID, emoteId)
	if err != nil {
		return "", removalTargetEmoteId, err
	}

	ec.db.CreateEmoteAdd(channelUserID, dto.REWARD_SEVENTV, emoteId, dto.EMOTE_ADD_ADD)

	return emoteId, removalTargetEmoteId, nil
}

func GetSevenTvEmoteId(message string) (string, error) {
	matches := sevenTvRegex.FindAllStringSubmatch(message, -1)

	if len(matches) == 1 && len(matches[0]) == 2 {
		return matches[0][1], nil
	}

	return "", errors.New("no 7tv emote link found")
}

func (ec *EmoteChief) VerifySeventvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent) bool {
	opts := channelpoint.UnmarshallSevenTvAdditionalOptions(reward.AdditionalOptions)

	emoteID, err := GetSevenTvEmoteId(redemption.UserInput)
	if err == nil {
		_, _, err := ec.VerifySetSevenTvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, redemption.UserLogin, opts.Slots)
		if err != nil {
			log.Warnf("7tv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s error: %s", redemption.UserName, err.Error()))
			return false
		}

		return true
	}

	ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s error: %s", redemption.UserName, err.Error()))
	return false
}

func (ec *EmoteChief) HandleSeventvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent, updateStatus bool) {
	opts := channelpoint.UnmarshallSevenTvAdditionalOptions(reward.AdditionalOptions)
	success := false

	emoteID, err := GetSevenTvEmoteId(redemption.UserInput)
	if err == nil {
		added, removed, settingErr := ec.setSevenTvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, redemption.UserName, opts.Slots)
		addedEmote, err := ec.sevenTvClient.GetEmote(added)
		if err != nil {
			log.Error("Error fetching added emote: " + err.Error())
		}
		removedEmote, err := ec.sevenTvClient.GetEmote(removed)
		if err != nil {
			log.Error("Error fetching removed emote: " + err.Error())
		}

		if settingErr != nil {
			log.Warnf("7tv error %s %s", redemption.BroadcasterUserLogin, settingErr)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s %s", redemption.UserName, settingErr.Error()))
		} else if addedEmote.Code != "" && removedEmote.Code != "" {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote %s redeemed by @%s removed %s", addedEmote.Code, redemption.UserName, removedEmote.Code))
		} else if addedEmote.Code != "" {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote %s redeemed by @%s", addedEmote.Code, redemption.UserName))
		} else {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote [unknown] redeemed by @%s", redemption.UserName))
		}
	} else {
		ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s %s", redemption.UserName, err.Error()))
	}

	if redemption.UserID == dto.GEMPIR_USER_ID {
		return
	}

	if updateStatus {
		err := ec.helixClient.UpdateRedemptionStatus(redemption.BroadcasterUserID, redemption.Reward.ID, redemption.ID, success)
		if err != nil {
			log.Errorf("Failed to update redemption status %s", err.Error())
			return
		}
	}
}
<commit_msg>non capturing group<commit_after>package emotechief

import (
	"errors"
	"fmt"
	"math/rand"
	"regexp"

	"github.com/gempir/gempbot/internal/channelpoint"
	"github.com/gempir/gempbot/internal/dto"
	"github.com/gempir/gempbot/internal/log"
	"github.com/gempir/gempbot/internal/store"
	"github.com/nicklaw5/helix/v2"
)

var sevenTvRegex = regexp.MustCompile(`https?:\/\/(?:next\.)?7tv.app\/emotes\/(\w*)`)

func (ec *EmoteChief) VerifySetSevenTvEmote(channelUserID, emoteId, channel, redeemedByUsername string, slots int) (emoteAddType dto.EmoteChangeType, removalTargetEmoteId string, err error) {
	if ec.db.IsEmoteBlocked(channelUserID, emoteId, dto.REWARD_SEVENTV) {
		return dto.EMOTE_ADD_ADD, "", errors.New("Emote is blocked")
	}

	nextEmote, err := ec.sevenTvClient.GetEmote(emoteId)
	if err != nil {
		return
	}

	user, err := ec.sevenTvClient.GetUser(channelUserID)
	if err != nil {
		return
	}

	for _, emote := range user.Emotes {
		if emote.Code == nextEmote.Code {
			return dto.EMOTE_ADD_ADD, "", fmt.Errorf("Emote code \"%s\" already added", nextEmote.Code)
		}
	}
	log.Infof("Current 7tv emotes: %d/%d", len(user.Emotes), user.EmoteSlots)

	emotesAdded := ec.db.GetEmoteAdded(channelUserID, dto.REWARD_SEVENTV, slots)
	log.Infof("Total Previous emotes %d in %s", len(emotesAdded), channelUserID)

	if len(emotesAdded) > 0 {
		oldestEmote := emotesAdded[len(emotesAdded)-1]
		if !oldestEmote.Blocked {
			for _, sharedEmote := range user.Emotes {
				if oldestEmote.EmoteID == sharedEmote.ID {
					removalTargetEmoteId = oldestEmote.EmoteID
					log.Infof("Found removal target %s in %s", removalTargetEmoteId, channelUserID)
				}
			}
		} else {
			log.Infof("Removal target %s is already blocked, so already removed, skipping removal", oldestEmote.EmoteID)
		}
	}

	emoteAddType = dto.EMOTE_ADD_REMOVED_PREVIOUS
	if removalTargetEmoteId == "" && len(user.Emotes) >= user.EmoteSlots {
		if len(user.Emotes) == 0 {
			return dto.EMOTE_ADD_ADD, "", errors.New("emotes limit reached and can't find amount of emotes added to choose random")
		}

		emoteAddType = dto.EMOTE_ADD_REMOVED_RANDOM
		log.Infof("Didn't find previous emote history of %d emotes and limit reached, choosing random in %s", slots, channelUserID)
		removalTargetEmoteId = user.Emotes[rand.Intn(len(user.Emotes))].ID
	}

	return
}

func (ec *EmoteChief) setSevenTvEmote(channelUserID, emoteId, channel, redeemedByUsername string, slots int) (addedEmoteId string, removedEmoteID string, err error) {
	emoteAddType, removalTargetEmoteId, err := ec.VerifySetSevenTvEmote(channelUserID, emoteId, channel, redeemedByUsername, slots)
	if err != nil {
		return "", "", err
	}

	// do we need to remove the emote?
	if removalTargetEmoteId != "" {
		err := ec.sevenTvClient.RemoveEmote(channelUserID, removalTargetEmoteId)
		if err != nil {
			return "", "", err
		}

		ec.db.CreateEmoteAdd(channelUserID, dto.REWARD_SEVENTV, removalTargetEmoteId, emoteAddType)
	}

	err = ec.sevenTvClient.AddEmote(channelUserID, emoteId)
	if err != nil {
		return "", removalTargetEmoteId, err
	}

	ec.db.CreateEmoteAdd(channelUserID, dto.REWARD_SEVENTV, emoteId, dto.EMOTE_ADD_ADD)

	return emoteId, removalTargetEmoteId, nil
}

func GetSevenTvEmoteId(message string) (string, error) {
	matches := sevenTvRegex.FindAllStringSubmatch(message, -1)

	if len(matches) == 1 && len(matches[0]) == 2 {
		return matches[0][1], nil
	}

	return "", errors.New("no 7tv emote link found")
}

func (ec *EmoteChief) VerifySeventvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent) bool {
	opts := channelpoint.UnmarshallSevenTvAdditionalOptions(reward.AdditionalOptions)

	emoteID, err := GetSevenTvEmoteId(redemption.UserInput)
	if err == nil {
		_, _, err := ec.VerifySetSevenTvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, redemption.UserLogin, opts.Slots)
		if err != nil {
			log.Warnf("7tv error %s %s", redemption.BroadcasterUserLogin, err)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s error: %s", redemption.UserName, err.Error()))
			return false
		}

		return true
	}

	ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s error: %s", redemption.UserName, err.Error()))
	return false
}

func (ec *EmoteChief) HandleSeventvRedemption(reward store.ChannelPointReward, redemption helix.EventSubChannelPointsCustomRewardRedemptionEvent, updateStatus bool) {
	opts := channelpoint.UnmarshallSevenTvAdditionalOptions(reward.AdditionalOptions)
	success := false

	emoteID, err := GetSevenTvEmoteId(redemption.UserInput)
	if err == nil {
		added, removed, settingErr := ec.setSevenTvEmote(redemption.BroadcasterUserID, emoteID, redemption.BroadcasterUserLogin, redemption.UserName, opts.Slots)
		addedEmote, err := ec.sevenTvClient.GetEmote(added)
		if err != nil {
			log.Error("Error fetching added emote: " + err.Error())
		}
		removedEmote, err := ec.sevenTvClient.GetEmote(removed)
		if err != nil {
			log.Error("Error fetching removed emote: " + err.Error())
		}

		if settingErr != nil {
			log.Warnf("7tv error %s %s", redemption.BroadcasterUserLogin, settingErr)
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s %s", redemption.UserName, settingErr.Error()))
		} else if addedEmote.Code != "" && removedEmote.Code != "" {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote %s redeemed by @%s removed %s", addedEmote.Code, redemption.UserName, removedEmote.Code))
		} else if addedEmote.Code != "" {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote %s redeemed by @%s", addedEmote.Code, redemption.UserName))
		} else {
			success = true
			ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("✅ Added new 7tv emote [unknown] redeemed by @%s", redemption.UserName))
		}
	} else {
		ec.chatClient.Say(redemption.BroadcasterUserLogin, fmt.Sprintf("⚠ Failed to add 7tv emote from @%s %s", redemption.UserName, err.Error()))
	}

	if redemption.UserID == dto.GEMPIR_USER_ID {
		return
	}

	if updateStatus {
		err := ec.helixClient.UpdateRedemptionStatus(redemption.BroadcasterUserID, redemption.Reward.ID, redemption.ID, success)
		if err != nil {
			log.Errorf("Failed to update redemption status %s", err.Error())
			return
		}
	}
}
<|endoftext|>


<commit_before>package overview

import (
	"context"
	"fmt"
	"reflect"

	"github.com/heptio/developer-dash/internal/cluster"
	"github.com/heptio/developer-dash/internal/content"
	"github.com/heptio/developer-dash/internal/printers"
	"github.com/heptio/developer-dash/internal/view"
	"github.com/pkg/errors"
	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/client-go/kubernetes/scheme"
	kprinters "k8s.io/kubernetes/pkg/printers"
	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
)

type ObjectTransformFunc func(namespace, prefix string, contents *[]content.Content) func(*metav1beta1.Table) error

type DescriberOptions struct {
	Cache  Cache
	Fields map[string]string
}

// Describer creates content.
type Describer interface {
	Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error)
	PathFilters() []pathFilter
}

type baseDescriber struct{}

func newBaseDescriber() *baseDescriber {
	return &baseDescriber{}
}

func (d *baseDescriber) clock() clock.Clock {
	return &clock.RealClock{}
}

type ListDescriber struct {
	*baseDescriber

	path                string
	title               string
	listType            func() interface{}
	objectType          func() interface{}
	cacheKey            CacheKey
	objectTransformFunc ObjectTransformFunc
}

func NewListDescriber(p, title string, cacheKey CacheKey, listType, objectType func() interface{}, otf ObjectTransformFunc) *ListDescriber {
	return &ListDescriber{
		path:                p,
		title:               title,
		baseDescriber:       newBaseDescriber(),
		cacheKey:            cacheKey,
		listType:            listType,
		objectType:          objectType,
		objectTransformFunc: otf,
	}
}

// Describe creates content.
func (d *ListDescriber) Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error) {
	var contents []content.Content

	objects, err := loadObjects(ctx, options.Cache, namespace, options.Fields, []CacheKey{d.cacheKey})
	if err != nil {
		return emptyContentResponse, err
	}

	list := d.listType()

	v := reflect.ValueOf(list)
	f := reflect.Indirect(v).FieldByName("Items")

	for _, object := range objects {
		item := d.objectType()
		err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, item)
		if err != nil {
			return emptyContentResponse, err
		}

		setItemName(item, object.GetName())

		newSlice := reflect.Append(f, reflect.ValueOf(item).Elem())
		f.Set(newSlice)
	}

	listObject, ok := list.(runtime.Object)
	if !ok {
		return emptyContentResponse, errors.Errorf("expected list to be a runtime object. It was a %T",
			list)
	}

	otf := d.objectTransformFunc(namespace, prefix, &contents)
	if err := printObject(listObject, otf); err != nil {
		return emptyContentResponse, err
	}

	return ContentResponse{
		Contents: contents,
		Title:    d.title,
	}, nil
}

func (d *ListDescriber) PathFilters() []pathFilter {
	return []pathFilter{
		*newPathFilter(d.path, d),
	}
}

type ObjectDescriber struct {
	*baseDescriber

	path                string
	baseTitle           string
	objectType          func() interface{}
	cacheKey            CacheKey
	objectTransformFunc ObjectTransformFunc
	views               []view.View
}

func NewObjectDescriber(p, baseTitle string, cacheKey CacheKey, objectType func() interface{}, otf ObjectTransformFunc, views []view.View) *ObjectDescriber {
	return &ObjectDescriber{
		path:                p,
		baseTitle:           baseTitle,
		baseDescriber:       newBaseDescriber(),
		cacheKey:            cacheKey,
		objectType:          objectType,
		objectTransformFunc: otf,
		views:               views,
	}
}

func (d *ObjectDescriber) Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error) {
	objects, err := loadObjects(ctx, options.Cache, namespace, options.Fields, []CacheKey{d.cacheKey})
	if err != nil {
		return emptyContentResponse, err
	}

	var contents []content.Content

	if len(objects) != 1 {
		return emptyContentResponse, errors.Errorf("expected exactly one object")
	}

	object := objects[0]

	item := d.objectType()
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, item)
	if err != nil {
		return emptyContentResponse, err
	}

	objectName := object.GetName()
	setItemName(item, objectName)

	var title string

	if objectName == "" {
		title = d.baseTitle
	} else {
		title = fmt.Sprintf("%s: %s", d.baseTitle, objectName)
	}

	newObject, ok := item.(runtime.Object)
	if !ok {
		return emptyContentResponse, errors.Errorf("expected item to be a runtime object. It was a %T",
			item)
	}

	otf := d.objectTransformFunc(namespace, prefix, &contents)
	if err := printObject(newObject, otf); err != nil {
		return emptyContentResponse, err
	}

	// TODO should show parents here
	// TODO will need to register a map of object transformers?

	for _, v := range d.views {
		viewContent, err := v.Content(ctx, newObject, nil)
		if err != nil {
			return emptyContentResponse, err
		}

		contents = append(contents, viewContent...)
	}

	eventsTable, err := eventsForObject(object, options.Cache, prefix, namespace, d.clock())
	if err != nil {
		return emptyContentResponse, err
	}

	contents = append(contents, eventsTable)

	return ContentResponse{
		Contents: contents,
		Title:    title,
	}, nil
}

func (d *ObjectDescriber) PathFilters() []pathFilter {
	return []pathFilter{
		*newPathFilter(d.path, d),
	}
}

func setItemName(item interface{}, name string) {
	setNameVal := reflect.ValueOf(item).MethodByName("SetName")
	setNameIface := setNameVal.Interface()
	setName := setNameIface.(func(string))
	setName(name)
}

func printObject(object runtime.Object, transformFunc func(*metav1beta1.Table) error) error {
	options := kprinters.PrintOptions{
		Wide:       true,
		ShowLabels: true,
		WithKind:   true,
	}

	decoder := scheme.Codecs.UniversalDecoder()
	p := printers.NewHumanReadablePrinter(decoder, options)

	printersinternal.AddHandlers(p)

	tbl, err := p.PrintTable(object, options)
	if err != nil {
		return err
	}

	if transformFunc != nil {
		return transformFunc(tbl)
	}

	return nil
}

func printContentTable(title, namespace, prefix string, tbl *metav1beta1.Table, m map[string]lookupFunc) (*content.Table, error) {
	contentTable := content.NewTable(title)

	headers := make(map[int]string)

	for i, column := range tbl.ColumnDefinitions {

		headers[i] = column.Name

		contentTable.Columns = append(contentTable.Columns, content.TableColumn{
			Name:     column.Name,
			Accessor: column.Name,
		})
	}

	transforms := buildTransforms(m)

	for _, row := range tbl.Rows {
		contentRow := content.TableRow{}

		for pos, header := range headers {
			cell := row.Cells[pos]

			c, ok := transforms[header]
			if !ok {
				contentRow[header] = content.NewStringText(fmt.Sprintf("%v", cell))
			} else {
				contentRow[header] = c(namespace, prefix, cell)
			}
		}

		contentTable.AddRow(contentRow)
	}

	return &contentTable, nil
}

// SectionDescriber is a wrapper to combine content from multiple describers.
type SectionDescriber struct {
	path       string
	title      string
	describers []Describer
}

// NewSectionDescriber creates a SectionDescriber.
func NewSectionDescriber(p, title string, describers ...Describer) *SectionDescriber {
	return &SectionDescriber{
		path:       p,
		title:      title,
		describers: describers,
	}
}

// Describe generates content.
func (d *SectionDescriber) Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error) {
	var contents []content.Content

	for _, child := range d.describers {
		cResponse, err := child.Describe(ctx, prefix, namespace, clusterClient, options)
		if err != nil {
			return emptyContentResponse, err
		}

		for _, childContent := range cResponse.Contents {
			if !childContent.IsEmpty() {
				contents = append(contents, childContent)
			}
		}
	}

	return ContentResponse{
		Contents: contents,
		Title:    d.title,
	}, nil
}

func (d *SectionDescriber) PathFilters() []pathFilter {
	pathFilters := []pathFilter{
		*newPathFilter(d.path, d),
	}

	for _, child := range d.describers {
		pathFilters = append(pathFilters, child.PathFilters()...)
	}

	return pathFilters
}
<commit_msg>copy object meta to new object<commit_after>package overview

import (
	"context"
	"fmt"
	"reflect"

	"github.com/heptio/developer-dash/internal/cluster"
	"github.com/heptio/developer-dash/internal/content"
	"github.com/heptio/developer-dash/internal/printers"
	"github.com/heptio/developer-dash/internal/view"
	"github.com/pkg/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/client-go/kubernetes/scheme"
	kprinters "k8s.io/kubernetes/pkg/printers"
	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
)

type ObjectTransformFunc func(namespace, prefix string, contents *[]content.Content) func(*metav1beta1.Table) error

type DescriberOptions struct {
	Cache  Cache
	Fields map[string]string
}

// Describer creates content.
type Describer interface {
	Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error)
	PathFilters() []pathFilter
}

type baseDescriber struct{}

func newBaseDescriber() *baseDescriber {
	return &baseDescriber{}
}

func (d *baseDescriber) clock() clock.Clock {
	return &clock.RealClock{}
}

type ListDescriber struct {
	*baseDescriber

	path                string
	title               string
	listType            func() interface{}
	objectType          func() interface{}
	cacheKey            CacheKey
	objectTransformFunc ObjectTransformFunc
}

func NewListDescriber(p, title string, cacheKey CacheKey, listType, objectType func() interface{}, otf ObjectTransformFunc) *ListDescriber {
	return &ListDescriber{
		path:                p,
		title:               title,
		baseDescriber:       newBaseDescriber(),
		cacheKey:            cacheKey,
		listType:            listType,
		objectType:          objectType,
		objectTransformFunc: otf,
	}
}

// Describe creates content.
func (d *ListDescriber) Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error) {
	var contents []content.Content

	objects, err := loadObjects(ctx, options.Cache, namespace, options.Fields, []CacheKey{d.cacheKey})
	if err != nil {
		return emptyContentResponse, err
	}

	list := d.listType()

	v := reflect.ValueOf(list)
	f := reflect.Indirect(v).FieldByName("Items")

	for _, object := range objects {
		item := d.objectType()
		err := runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, item)
		if err != nil {
			return emptyContentResponse, err
		}

		copyObjectMeta(item, object)

		newSlice := reflect.Append(f, reflect.ValueOf(item).Elem())
		f.Set(newSlice)
	}

	listObject, ok := list.(runtime.Object)
	if !ok {
		return emptyContentResponse, errors.Errorf("expected list to be a runtime object. It was a %T",
			list)
	}

	otf := d.objectTransformFunc(namespace, prefix, &contents)
	if err := printObject(listObject, otf); err != nil {
		return emptyContentResponse, err
	}

	return ContentResponse{
		Contents: contents,
		Title:    d.title,
	}, nil
}

func (d *ListDescriber) PathFilters() []pathFilter {
	return []pathFilter{
		*newPathFilter(d.path, d),
	}
}

type ObjectDescriber struct {
	*baseDescriber

	path                string
	baseTitle           string
	objectType          func() interface{}
	cacheKey            CacheKey
	objectTransformFunc ObjectTransformFunc
	views               []view.View
}

func NewObjectDescriber(p, baseTitle string, cacheKey CacheKey, objectType func() interface{}, otf ObjectTransformFunc, views []view.View) *ObjectDescriber {
	return &ObjectDescriber{
		path:                p,
		baseTitle:           baseTitle,
		baseDescriber:       newBaseDescriber(),
		cacheKey:            cacheKey,
		objectType:          objectType,
		objectTransformFunc: otf,
		views:               views,
	}
}

func (d *ObjectDescriber) Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error) {
	objects, err := loadObjects(ctx, options.Cache, namespace, options.Fields, []CacheKey{d.cacheKey})
	if err != nil {
		return emptyContentResponse, err
	}

	var contents []content.Content

	if len(objects) != 1 {
		return emptyContentResponse, errors.Errorf("expected exactly one object")
	}

	object := objects[0]

	item := d.objectType()
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(object.Object, item)
	if err != nil {
		return emptyContentResponse, err
	}

	copyObjectMeta(item, object)

	objectName := object.GetName()

	var title string

	if objectName == "" {
		title = d.baseTitle
	} else {
		title = fmt.Sprintf("%s: %s", d.baseTitle, objectName)
	}

	newObject, ok := item.(runtime.Object)
	if !ok {
		return emptyContentResponse, errors.Errorf("expected item to be a runtime object. It was a %T",
			item)
	}

	otf := d.objectTransformFunc(namespace, prefix, &contents)
	if err := printObject(newObject, otf); err != nil {
		return emptyContentResponse, err
	}

	// TODO should show parents here
	// TODO will need to register a map of object transformers?

	for _, v := range d.views {
		viewContent, err := v.Content(ctx, newObject, nil)
		if err != nil {
			return emptyContentResponse, err
		}

		contents = append(contents, viewContent...)
	}

	eventsTable, err := eventsForObject(object, options.Cache, prefix, namespace, d.clock())
	if err != nil {
		return emptyContentResponse, err
	}

	contents = append(contents, eventsTable)

	return ContentResponse{
		Contents: contents,
		Title:    title,
	}, nil
}

func (d *ObjectDescriber) PathFilters() []pathFilter {
	return []pathFilter{
		*newPathFilter(d.path, d),
	}
}

func setItemName(item interface{}, name string) {
	setNameVal := reflect.ValueOf(item).MethodByName("SetName")
	setNameIface := setNameVal.Interface()
	setName := setNameIface.(func(string))
	setName(name)
}

func copyObjectMeta(to interface{}, from *unstructured.Unstructured) error {
	object, ok := to.(metav1.Object)
	if !ok {
		return errors.Errorf("%T is not an object", to)
	}

	typeMeta := metav1.TypeMeta{
		Kind:       from.GetKind(),
		APIVersion: from.GetAPIVersion(),
	}

	reflect.ValueOf(object).Elem().FieldByName("TypeMeta").Set(reflect.ValueOf(typeMeta))

	object.SetNamespace(from.GetNamespace())
	object.SetName(from.GetName())
	object.SetGenerateName(from.GetGenerateName())
	object.SetUID(from.GetUID())
	object.SetResourceVersion(from.GetResourceVersion())
	object.SetGeneration(from.GetGeneration())
	object.SetSelfLink(from.GetSelfLink())
	object.SetCreationTimestamp(from.GetCreationTimestamp())
	object.SetDeletionTimestamp(from.GetDeletionTimestamp())
	object.SetDeletionGracePeriodSeconds(from.GetDeletionGracePeriodSeconds())
	object.SetLabels(from.GetLabels())
	object.SetAnnotations(from.GetAnnotations())
	object.SetInitializers(from.GetInitializers())
	object.SetOwnerReferences(from.GetOwnerReferences())
	object.SetClusterName(from.GetClusterName())
	object.SetFinalizers(from.GetFinalizers())

	return nil
}

func printObject(object runtime.Object, transformFunc func(*metav1beta1.Table) error) error {
	options := kprinters.PrintOptions{
		Wide:       true,
		ShowLabels: true,
		WithKind:   true,
	}

	decoder := scheme.Codecs.UniversalDecoder()
	p := printers.NewHumanReadablePrinter(decoder, options)

	printersinternal.AddHandlers(p)

	tbl, err := p.PrintTable(object, options)
	if err != nil {
		return err
	}

	if transformFunc != nil {
		return transformFunc(tbl)
	}

	return nil
}

func printContentTable(title, namespace, prefix string, tbl *metav1beta1.Table, m map[string]lookupFunc) (*content.Table, error) {
	contentTable := content.NewTable(title)

	headers := make(map[int]string)

	for i, column := range tbl.ColumnDefinitions {

		headers[i] = column.Name

		contentTable.Columns = append(contentTable.Columns, content.TableColumn{
			Name:     column.Name,
			Accessor: column.Name,
		})
	}

	transforms := buildTransforms(m)

	for _, row := range tbl.Rows {
		contentRow := content.TableRow{}

		for pos, header := range headers {
			cell := row.Cells[pos]

			c, ok := transforms[header]
			if !ok {
				contentRow[header] = content.NewStringText(fmt.Sprintf("%v", cell))
			} else {
				contentRow[header] = c(namespace, prefix, cell)
			}
		}

		contentTable.AddRow(contentRow)
	}

	return &contentTable, nil
}

// SectionDescriber is a wrapper to combine content from multiple describers.
type SectionDescriber struct {
	path       string
	title      string
	describers []Describer
}

// NewSectionDescriber creates a SectionDescriber.
func NewSectionDescriber(p, title string, describers ...Describer) *SectionDescriber {
	return &SectionDescriber{
		path:       p,
		title:      title,
		describers: describers,
	}
}

// Describe generates content.
func (d *SectionDescriber) Describe(ctx context.Context, prefix, namespace string, clusterClient cluster.ClientInterface, options DescriberOptions) (ContentResponse, error) {
	var contents []content.Content

	for _, child := range d.describers {
		cResponse, err := child.Describe(ctx, prefix, namespace, clusterClient, options)
		if err != nil {
			return emptyContentResponse, err
		}

		for _, childContent := range cResponse.Contents {
			if !childContent.IsEmpty() {
				contents = append(contents, childContent)
			}
		}
	}

	return ContentResponse{
		Contents: contents,
		Title:    d.title,
	}, nil
}

func (d *SectionDescriber) PathFilters() []pathFilter {
	pathFilters := []pathFilter{
		*newPathFilter(d.path, d),
	}

	for _, child := range d.describers {
		pathFilters = append(pathFilters, child.PathFilters()...)
	}

	return pathFilters
}
<|endoftext|>


<commit_before>package report_test

import (
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/kr/pretty"
	"github.com/rafaeljusto/toglacier/internal/cloud"
	"github.com/rafaeljusto/toglacier/internal/report"
)

func TestBuild(t *testing.T) {
	date := time.Date(2017, 3, 10, 14, 10, 46, 0, time.UTC)

	scenarios := []struct {
		description   string
		reports       []report.Report
		expected      string
		expectedError error
	}{
		{
			description: "it should build correctly all types of reports",
			reports: []report.Report{
				func() report.Report {
					r := report.NewSendBackup()
					r.CreatedAt = date
					r.Backup = cloud.Backup{
						ID:        "AWSID123",
						CreatedAt: date.Add(-time.Second),
						VaultName: "vault",
						Checksum:  "cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705",
					}
					r.Paths = []string{"/data/important-files"}
					r.Durations.Build = 2 * time.Second
					r.Durations.Encrypt = 6 * time.Second
					r.Durations.Send = 6 * time.Minute
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
				func() report.Report {
					r := report.NewListBackups()
					r.CreatedAt = date
					r.Durations.List = 6 * time.Hour
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
				func() report.Report {
					r := report.NewRemoveOldBackups()
					r.CreatedAt = date
					r.Backups = []cloud.Backup{
						{
							ID:        "AWSID123",
							CreatedAt: date.Add(-time.Second),
							VaultName: "vault",
							Checksum:  "cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705",
						},
					}
					r.Durations.List = 6 * time.Hour
					r.Durations.Remove = 2 * time.Second
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
				func() report.Report {
					r := report.NewTest()
					r.CreatedAt = date
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
			},
			expected: `[2017-03-10 14:10:46] Backups Sent

  Backup
  ------

    ID:          AWSID123
    Date:        2017-03-10 14:10:45
    Vault:       vault
    Checksum:    cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705
    Paths:       /data/important-files

  Durations
  ---------

    Build:       2s
    Encrypt:     6s
    Send:        6m0s

  Errors
  ------

    * timeout connecting to aws

[2017-03-10 14:10:46] List Backup

  Durations
  ---------

    List:        6h0m0s

  Errors
  ------

    * timeout connecting to aws

[2017-03-10 14:10:46] Remove Old Backups

  Backups
  -------

    * ID:        AWSID123
      Date:      2017-03-10 14:10:45
      Vault:     vault
      Checksum:  cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705

  Durations
  ---------

    List:        6h0m0s
    Remove:      2s

  Errors
  ------

    * timeout connecting to aws

[2017-03-10 14:10:46] Test report

  Testing the notification mechanisms.

  Errors
  ------

    * timeout connecting to aws`,
		},
	}

	for _, scenario := range scenarios {
		t.Run(scenario.description, func(t *testing.T) {
			for _, r := range scenario.reports {
				report.Add(r)
			}

			output, err := report.Build()
			output = strings.TrimSpace(output)

			outputLines := strings.Split(output, "\n")
			for i := range outputLines {
				outputLines[i] = strings.TrimSpace(outputLines[i])
			}

			scenario.expected = strings.TrimSpace(scenario.expected)
			expectedLines := strings.Split(scenario.expected, "\n")
			for i := range expectedLines {
				expectedLines[i] = strings.TrimSpace(expectedLines[i])
			}

			if !reflect.DeepEqual(expectedLines, outputLines) {
				t.Errorf("output don't match.\n%s", pretty.Diff(expectedLines, outputLines))
			}

			if !reflect.DeepEqual(scenario.expectedError, err) {
				t.Errorf("errors don't match. expected “%v” and got “%v”", scenario.expectedError, err)
			}
		})
	}
}
<commit_msg>Improve test coverage in report package<commit_after>package report_test

import (
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/kr/pretty"
	"github.com/rafaeljusto/toglacier/internal/cloud"
	"github.com/rafaeljusto/toglacier/internal/report"
)

func TestBuild(t *testing.T) {
	date := time.Date(2017, 3, 10, 14, 10, 46, 0, time.UTC)

	scenarios := []struct {
		description   string
		reports       []report.Report
		expected      string
		expectedError error
	}{
		{
			description: "it should build correctly all types of reports",
			reports: []report.Report{
				func() report.Report {
					r := report.NewSendBackup()
					r.CreatedAt = date
					r.Backup = cloud.Backup{
						ID:        "AWSID123",
						CreatedAt: date.Add(-time.Second),
						VaultName: "vault",
						Checksum:  "cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705",
					}
					r.Paths = []string{"/data/important-files"}
					r.Durations.Build = 2 * time.Second
					r.Durations.Encrypt = 6 * time.Second
					r.Durations.Send = 6 * time.Minute
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
				func() report.Report {
					r := report.NewListBackups()
					r.CreatedAt = date
					r.Durations.List = 6 * time.Hour
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
				func() report.Report {
					r := report.NewRemoveOldBackups()
					r.CreatedAt = date
					r.Backups = []cloud.Backup{
						{
							ID:        "AWSID123",
							CreatedAt: date.Add(-time.Second),
							VaultName: "vault",
							Checksum:  "cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705",
						},
					}
					r.Durations.List = 6 * time.Hour
					r.Durations.Remove = 2 * time.Second
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
				func() report.Report {
					r := report.NewTest()
					r.CreatedAt = date
					r.Errors = append(r.Errors, errors.New("timeout connecting to aws"))
					return r
				}(),
			},
			expected: `[2017-03-10 14:10:46] Backups Sent

  Backup
  ------

    ID:          AWSID123
    Date:        2017-03-10 14:10:45
    Vault:       vault
    Checksum:    cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705
    Paths:       /data/important-files

  Durations
  ---------

    Build:       2s
    Encrypt:     6s
    Send:        6m0s

  Errors
  ------

    * timeout connecting to aws

[2017-03-10 14:10:46] List Backup

  Durations
  ---------

    List:        6h0m0s

  Errors
  ------

    * timeout connecting to aws

[2017-03-10 14:10:46] Remove Old Backups

  Backups
  -------

    * ID:        AWSID123
      Date:      2017-03-10 14:10:45
      Vault:     vault
      Checksum:  cb63324d2c35cdfcb4521e15ca4518bd0ed9dc2364a9f47de75151b3f9b4b705

  Durations
  ---------

    List:        6h0m0s
    Remove:      2s

  Errors
  ------

    * timeout connecting to aws

[2017-03-10 14:10:46] Test report

  Testing the notification mechanisms.

  Errors
  ------

    * timeout connecting to aws`,
		},
		{
			description: "it should detect an error while building a report",
			reports: []report.Report{
				reportMock{
					mockBuild: func() (string, error) {
						return "", errors.New("error generating report")
					},
				},
			},
			expectedError: errors.New("error generating report"),
		},
	}

	for _, scenario := range scenarios {
		t.Run(scenario.description, func(t *testing.T) {
			for _, r := range scenario.reports {
				report.Add(r)
			}

			output, err := report.Build()
			output = strings.TrimSpace(output)

			outputLines := strings.Split(output, "\n")
			for i := range outputLines {
				outputLines[i] = strings.TrimSpace(outputLines[i])
			}

			scenario.expected = strings.TrimSpace(scenario.expected)
			expectedLines := strings.Split(scenario.expected, "\n")
			for i := range expectedLines {
				expectedLines[i] = strings.TrimSpace(expectedLines[i])
			}

			if !reflect.DeepEqual(expectedLines, outputLines) {
				t.Errorf("output don't match.\n%s", pretty.Diff(expectedLines, outputLines))
			}

			if !reflect.DeepEqual(scenario.expectedError, err) {
				t.Errorf("errors don't match. expected “%v” and got “%v”", scenario.expectedError, err)
			}
		})
	}
}

type reportMock struct {
	mockBuild func() (string, error)
}

func (r reportMock) Build() (string, error) {
	return r.mockBuild()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testutil

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

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/xunit"
)

const (
	numAttempts = 3
)

// vanadiumBootstrap runs a test of Vanadium bootstrapping.
func vanadiumBootstrap(ctx *tool.Context, testName string, _ ...TestOpt) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Create a new temporary V23_ROOT.
	oldRoot := os.Getenv("V23_ROOT")
	defer collect.Error(func() error { return os.Setenv("V23_ROOT", oldRoot) }, &e)
	tmpDir, err := ctx.Run().TempDir("", "")
	if err != nil {
		return nil, internalTestError{err, "TempDir"}
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpDir) }, &e)

	root := filepath.Join(tmpDir, "root")
	if err := os.Setenv("V23_ROOT", root); err != nil {
		return nil, internalTestError{err, "Setenv"}
	}

	// Run the setup script.
	var out bytes.Buffer
	opts := ctx.Run().Opts()
	opts.Stdout = io.MultiWriter(opts.Stdout, &out)
	opts.Stderr = io.MultiWriter(opts.Stderr, &out)
	opts.Env["PATH"] = strings.Replace(os.Getenv("PATH"), filepath.Join(oldRoot, "devtools", "bin"), "", -1)
	for i := 1; i <= numAttempts; i++ {
		if i > 1 {
			fmt.Fprintf(ctx.Stdout(), "Attempt %d/%d:\n", i, numAttempts)
		}
		if err = ctx.Run().CommandWithOpts(opts, filepath.Join(oldRoot, "scripts", "setup", "bootstrap")); err == nil {
			break
		}
	}
	if err != nil {
		// Create xUnit report.
		if err := xunit.CreateFailureReport(ctx, testName, "VanadiumGo", "bootstrap", "Vanadium bootstrapping failed", out.String()); err != nil {
			return nil, err
		}
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}
<commit_msg>TBR: devtools/testutil: temporary fix for bootstrap test.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testutil

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

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/xunit"
)

const (
	numAttempts = 3
)

// vanadiumBootstrap runs a test of Vanadium bootstrapping.
func vanadiumBootstrap(ctx *tool.Context, testName string, _ ...TestOpt) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, nil)
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Run v23 update with private manifest to pull in scripts repo.
	if err := ctx.Run().Command("v23", "update", "-manifest=private"); err != nil {
		return nil, internalTestError{err, "Update with private manifest"}
	}

	// Create a new temporary V23_ROOT.
	oldRoot := os.Getenv("V23_ROOT")
	defer collect.Error(func() error { return os.Setenv("V23_ROOT", oldRoot) }, &e)
	tmpDir, err := ctx.Run().TempDir("", "")
	if err != nil {
		return nil, internalTestError{err, "TempDir"}
	}
	defer collect.Error(func() error { return ctx.Run().RemoveAll(tmpDir) }, &e)

	root := filepath.Join(tmpDir, "root")
	if err := os.Setenv("V23_ROOT", root); err != nil {
		return nil, internalTestError{err, "Setenv"}
	}

	// Run the setup script.
	var out bytes.Buffer
	opts := ctx.Run().Opts()
	opts.Stdout = io.MultiWriter(opts.Stdout, &out)
	opts.Stderr = io.MultiWriter(opts.Stderr, &out)
	opts.Env["PATH"] = strings.Replace(os.Getenv("PATH"), filepath.Join(oldRoot, "devtools", "bin"), "", -1)
	for i := 1; i <= numAttempts; i++ {
		if i > 1 {
			fmt.Fprintf(ctx.Stdout(), "Attempt %d/%d:\n", i, numAttempts)
		}
		if err = ctx.Run().CommandWithOpts(opts, filepath.Join(oldRoot, "scripts", "setup", "bootstrap")); err == nil {
			break
		}
	}
	if err != nil {
		// Create xUnit report.
		if err := xunit.CreateFailureReport(ctx, testName, "VanadiumGo", "bootstrap", "Vanadium bootstrapping failed", out.String()); err != nil {
			return nil, err
		}
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}
<|endoftext|>


<commit_before>package hm

import "github.com/pkg/errors"

// Unify unifies the two types.
// These are the rules:
//
// Type Constants and Type Constants
//
// Type constants (atomic types) have no substitution
//		c ~ c : []
//
// Type Variables and Type Variables
//
// Type variables have no substitutions if there are no instances:
// 		a ~ a : []
//
// Default Unification
//
// if type variable 'a' is not in 'T', then unification is simple: replace all instances of 'a' with 'T'
// 		     a ∉ T
//		---------------
//		 a ~ T : [a/T]
//
// The more complicated constructor unification and arrow unification isn't quite covered yet.
func Unify(t1, t2 Type) (retVal1, retVal2 Type, replacements map[TypeVariable]Type, err error) {
	logf("Unifying %#v and %#v", t1, t2)
	enterLoggingContext()
	defer leaveLoggingContext()
	a := Prune(t1)
	b := Prune(t2)

	switch at := a.(type) {
	case TypeVariable:
		if retVal1, retVal2, err = UnifyVar(at, b); err != nil {
			return
		}
		if replacements == nil {
			replacements = make(map[TypeVariable]Type)
		}

		replacements[at] = retVal1
	case TypeOp:
		switch bt := b.(type) {
		case TypeVariable:
			// note the order change
			if retVal2, retVal1, err = UnifyVar(bt, at); err != nil {
				return
			}

			if replacements == nil {
				replacements = make(map[TypeVariable]Type)
			}

			replacements[bt] = retVal2
		case TypeOp:
			atypes := at.Types()
			btypes := bt.Types()
			if at.Name() != bt.Name() || len(atypes) != len(btypes) {
				err = errors.Errorf(typeMismatch, a, b)
				return
			}

			enterLoggingContext()
			var t_a, t_b Type
			for i := 0; i < len(atypes); i++ {
				t_a = atypes[i]
				t_b = btypes[i]

				logf("Unifying recursively %v and %v", t_a, t_b)
				var t_a2, t_b2 Type
				var r2 map[TypeVariable]Type
				if t_a2, t_b2, r2, err = Unify(t_a, t_b); err != nil {
					return
				}

				if replacements == nil {
					replacements = r2
				} else {
					for k, v := range r2 {
						replacements[k] = v
					}
				}
				logf("r: %v", replacements)

				pt_a2 := Prune(t_a2)
				pt_b2 := Prune(t_b2)

				logf("Replacing %v with %v in %v", t_a, pt_a2, at)
				logf("Replacing %v with %v in %v", t_b, pt_b2, bt)

				at = at.Replace(t_a, pt_a2)
				bt = bt.Replace(t_b, pt_b2)

				logf("Replacing replacement map : %v", replacements)
				for k, v := range replacements {
					at = at.Replace(k, v)
					bt = bt.Replace(k, v)
				}

				logf("at: %v", at)
				logf("bt: %v", bt)

				if tv, ok := t_a.(TypeVariable); ok {
					replacements[tv] = pt_a2
				}

				if tv, ok := t_b.(TypeVariable); ok {
					replacements[tv] = pt_b2
				}

				atypes = at.Types()
				btypes = bt.Types()
			}
			leaveLoggingContext()

			retVal1 = at
			retVal2 = bt
			return
		default:
			err = errors.Errorf(nyi, "Unify of TypeOp ", b, b)
			return
		}

	default:
		err = errors.Errorf(nu, t1, t2)
		return
	}
	return
}

// UnifyVar unifies a TypeVariable and a Type.
func UnifyVar(tv TypeVariable, t Type) (ret1, ret2 Type, err error) {
	if tv.IsEmpty() {
		err = errors.Errorf(undefinedTV)
		return
	}
	ret1 = tv
	ret2 = t
	var unioned *TypeClassSet
	if ttv, ok := t.(TypeVariable); ok {
		if ttv.IsEmpty() {
			return
		}

		if t.Eq(ttv) {
			if tv.constraints == nil {
			}
			unioned = tv.constraints.Union(ttv.constraints)

			tv.constraints = unioned
			ttv.constraints = unioned
			ret2 = ttv
		}

	}

	if ret2.Contains(tv) {
		err = errors.Errorf(recursiveUnification, tv, t)
		return
	}

	tv.instance = ret2
	ret1 = tv
	return
}

// Prune returns the defining instance of T
func Prune(t Type) Type {
	if tv, ok := t.(TypeVariable); ok {
		if tv.instance != nil {
			return Prune(tv.instance)
		}
	}
	return t
}
<commit_msg>Cleaned up stuff<commit_after>package hm

import "github.com/pkg/errors"

// Unify unifies the two types.
// These are the rules:
//
// Type Constants and Type Constants
//
// Type constants (atomic types) have no substitution
//		c ~ c : []
//
// Type Variables and Type Variables
//
// Type variables have no substitutions if there are no instances:
// 		a ~ a : []
//
// Default Unification
//
// if type variable 'a' is not in 'T', then unification is simple: replace all instances of 'a' with 'T'
// 		     a ∉ T
//		---------------
//		 a ~ T : [a/T]
//
// The more complicated constructor unification and arrow unification isn't quite covered yet.
func Unify(t1, t2 Type) (retVal1, retVal2 Type, replacements map[TypeVariable]Type, err error) {
	a := Prune(t1)
	b := Prune(t2)

	switch at := a.(type) {
	case TypeVariable:
		if retVal1, retVal2, err = UnifyVar(at, b); err != nil {
			return
		}
		if replacements == nil {
			replacements = make(map[TypeVariable]Type)
		}

		replacements[at] = retVal1
	case TypeOp:
		switch bt := b.(type) {
		case TypeVariable:
			// note the order change
			if retVal2, retVal1, err = UnifyVar(bt, at); err != nil {
				return
			}

			if replacements == nil {
				replacements = make(map[TypeVariable]Type)
			}

			replacements[bt] = retVal2
		case TypeOp:
			atypes := at.Types()
			btypes := bt.Types()
			if at.Name() != bt.Name() || len(atypes) != len(btypes) {
				err = errors.Errorf(typeMismatch, a, b)
				return
			}

			var t_a, t_b Type
			for i := 0; i < len(atypes); i++ {
				t_a = atypes[i]
				t_b = btypes[i]

				var t_a2, t_b2 Type
				var r2 map[TypeVariable]Type
				if t_a2, t_b2, r2, err = Unify(t_a, t_b); err != nil {
					return
				}

				if replacements == nil {
					replacements = r2
				} else {
					for k, v := range r2 {
						replacements[k] = v
					}
				}

				pt_a2 := Prune(t_a2)
				pt_b2 := Prune(t_b2)

				at = at.Replace(t_a, pt_a2)
				bt = bt.Replace(t_b, pt_b2)

				for k, v := range replacements {
					at = at.Replace(k, v)
					bt = bt.Replace(k, v)
				}

				if tv, ok := t_a.(TypeVariable); ok {
					replacements[tv] = pt_a2
				}

				if tv, ok := t_b.(TypeVariable); ok {
					replacements[tv] = pt_b2
				}

				atypes = at.Types()
				btypes = bt.Types()
			}

			retVal1 = at
			retVal2 = bt
			return
		default:
			err = errors.Errorf(nyi, "Unify of TypeOp ", b, b)
			return
		}

	default:
		err = errors.Errorf(nu, t1, t2)
		return
	}
	return
}

// UnifyVar unifies a TypeVariable and a Type.
func UnifyVar(tv TypeVariable, t Type) (ret1, ret2 Type, err error) {
	if tv.IsEmpty() {
		err = errors.Errorf(undefinedTV)
		return
	}
	ret1 = tv
	ret2 = t
	var unioned *TypeClassSet
	if ttv, ok := t.(TypeVariable); ok {
		if ttv.IsEmpty() {
			return
		}

		if t.Eq(ttv) {
			if tv.constraints == nil {
			}
			unioned = tv.constraints.Union(ttv.constraints)

			tv.constraints = unioned
			ttv.constraints = unioned
			ret2 = ttv
		}

	}

	if ret2.Contains(tv) {
		err = errors.Errorf(recursiveUnification, tv, t)
		return
	}

	tv.instance = ret2
	ret1 = tv
	return
}

// Prune returns the defining instance of T
func Prune(t Type) Type {
	if tv, ok := t.(TypeVariable); ok {
		if tv.instance != nil {
			return Prune(tv.instance)
		}
	}
	return t
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 statserver_test

import (
	"bytes"
	"encoding/gob"
	"net/url"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/gorilla/websocket"
	"github.com/knative/serving/pkg/autoscaler"
	stats "github.com/knative/serving/pkg/autoscaler/statserver"
	"go.uber.org/zap"
)

const testAddress = "127.0.0.1:0"

func TestServerLifecycle(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.New(testAddress, statsCh, zap.NewNop().Sugar())

	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		err := server.ListenAndServe()
		if err != nil {
			t.Fatal("ListenAndServe failed.", err)
		}
	}()

	server.Shutdown(time.Second)

	wg.Wait()
}

func TestStatsReceived(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.NewTestServer(statsCh)

	defer server.Shutdown(0)
	go server.ListenAndServe()

	statSink := dialOk(server.ListenAddr(), t)

	assertReceivedOk(newStatMessage("test-namespace/test-revision", "pod1", 2.1, 51), statSink, statsCh, t)
	assertReceivedOk(newStatMessage("test-namespace/test-revision2", "pod2", 2.2, 30), statSink, statsCh, t)

	closeSink(statSink, t)
}

func TestServerShutdown(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.NewTestServer(statsCh)

	go server.ListenAndServe()

	listenAddr := server.ListenAddr()
	statSink := dialOk(listenAddr, t)

	assertReceivedOk(newStatMessage("test-namespace/test-revision", "pod1", 2.1, 51), statSink, statsCh, t)

	server.Shutdown(time.Second)

	// Send a statistic to the server
	send(statSink, newStatMessage("test-namespace/test-revision2", "pod2", 2.2, 30), t)

	// Check the statistic was not received
	_, ok := <-statsCh
	if ok {
		t.Fatal("Received statistic after shutdown")
	}

	// Check connection has been closed with a close control message with a "service restart" close code
	if _, _, err := statSink.NextReader(); err == nil {
		t.Fatal("Connection not closed")
	} else {
		err, ok := err.(*websocket.CloseError)
		if !ok {
			t.Fatal("CloseError not received")
		}
		if err.Code != 1012 {
			t.Fatalf("CloseError with unexpected close code %d received", err.Code)
		}
	}

	// Check that new connections are refused with some error
	if _, err := dial(listenAddr, t); err == nil {
		t.Fatal("Connection not refused")
	}

	closeSink(statSink, t)
}

func TestServerDoesNotLeakGoroutines(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.NewTestServer(statsCh)

	go server.ListenAndServe()

	originalGoroutines := runtime.NumGoroutine()

	listenAddr := server.ListenAddr()
	statSink := dialOk(listenAddr, t)

	assertReceivedOk(newStatMessage("test-namespace/test-revision", "pod1", 2.1, 51), statSink, statsCh, t)

	closeSink(statSink, t)

	// Check the number of goroutines eventually reduces to the number there were before the connection was created
	for i := 1000; i >= 0; i-- {
		currentGoRoutines := runtime.NumGoroutine()
		if currentGoRoutines == originalGoroutines {
			break
		}
		time.Sleep(5 * time.Millisecond)
		if i == 0 {
			t.Fatalf("Current number of goroutines %d is not equal to the original number %d", currentGoRoutines, originalGoroutines)
		}
	}

	server.Shutdown(time.Second)
}

func newStatMessage(revKey string, podName string, averageConcurrentRequests float64, requestCount int32) *autoscaler.StatMessage {
	now := time.Now()
	return &autoscaler.StatMessage{
		revKey,
		autoscaler.Stat{
			&now,
			podName,
			averageConcurrentRequests,
			requestCount,
		},
	}
}

func assertReceivedOk(sm *autoscaler.StatMessage, statSink *websocket.Conn, statsCh <-chan *autoscaler.StatMessage, t *testing.T) bool {
	send(statSink, sm, t)
	recv, ok := <-statsCh
	if !ok {
		t.Fatalf("statistic not received")
	}
	if !cmp.Equal(sm, recv) {
		t.Fatalf("Expected and actual stats messages are not equal: %s", cmp.Diff(sm, recv))
	}
	return true
}

func dialOk(serverURL string, t *testing.T) *websocket.Conn {
	statSink, err := dial(serverURL, t)
	if err != nil {
		t.Fatalf("Dial failed: %v", zap.Error(err))
	}
	return statSink
}

func dial(serverURL string, t *testing.T) (*websocket.Conn, error) {
	u, err := url.Parse(serverURL)
	if err != nil {
		t.Fatal(err)
	}
	u.Scheme = "ws"

	dialer := &websocket.Dialer{
		HandshakeTimeout: time.Second,
	}
	statSink, _, err := dialer.Dial(u.String(), nil)
	return statSink, err
}

func send(statSink *websocket.Conn, sm *autoscaler.StatMessage, t *testing.T) {
	var b bytes.Buffer
	enc := gob.NewEncoder(&b)
	err := enc.Encode(sm)
	if err != nil {
		t.Fatal("Failed to encode data from stats channel", zap.Error(err))
	}
	err = statSink.WriteMessage(websocket.BinaryMessage, b.Bytes())
	if err != nil {
		t.Fatal("Failed to write to stat sink.", zap.Error(err))
	}
}

func closeSink(statSink *websocket.Conn, t *testing.T) {
	if err := statSink.Close(); err != nil {
		t.Fatal("Failed to close", err)
	}
}
<commit_msg>Accept goroutines less than original as success. (#1786)<commit_after>/*
Copyright 2018 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 statserver_test

import (
	"bytes"
	"encoding/gob"
	"net/url"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/gorilla/websocket"
	"github.com/knative/serving/pkg/autoscaler"
	stats "github.com/knative/serving/pkg/autoscaler/statserver"
	"go.uber.org/zap"
)

const testAddress = "127.0.0.1:0"

func TestServerLifecycle(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.New(testAddress, statsCh, zap.NewNop().Sugar())

	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		defer wg.Done()
		err := server.ListenAndServe()
		if err != nil {
			t.Fatal("ListenAndServe failed.", err)
		}
	}()

	server.Shutdown(time.Second)

	wg.Wait()
}

func TestStatsReceived(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.NewTestServer(statsCh)

	defer server.Shutdown(0)
	go server.ListenAndServe()

	statSink := dialOk(server.ListenAddr(), t)

	assertReceivedOk(newStatMessage("test-namespace/test-revision", "pod1", 2.1, 51), statSink, statsCh, t)
	assertReceivedOk(newStatMessage("test-namespace/test-revision2", "pod2", 2.2, 30), statSink, statsCh, t)

	closeSink(statSink, t)
}

func TestServerShutdown(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.NewTestServer(statsCh)

	go server.ListenAndServe()

	listenAddr := server.ListenAddr()
	statSink := dialOk(listenAddr, t)

	assertReceivedOk(newStatMessage("test-namespace/test-revision", "pod1", 2.1, 51), statSink, statsCh, t)

	server.Shutdown(time.Second)

	// Send a statistic to the server
	send(statSink, newStatMessage("test-namespace/test-revision2", "pod2", 2.2, 30), t)

	// Check the statistic was not received
	_, ok := <-statsCh
	if ok {
		t.Fatal("Received statistic after shutdown")
	}

	// Check connection has been closed with a close control message with a "service restart" close code
	if _, _, err := statSink.NextReader(); err == nil {
		t.Fatal("Connection not closed")
	} else {
		err, ok := err.(*websocket.CloseError)
		if !ok {
			t.Fatal("CloseError not received")
		}
		if err.Code != 1012 {
			t.Fatalf("CloseError with unexpected close code %d received", err.Code)
		}
	}

	// Check that new connections are refused with some error
	if _, err := dial(listenAddr, t); err == nil {
		t.Fatal("Connection not refused")
	}

	closeSink(statSink, t)
}

func TestServerDoesNotLeakGoroutines(t *testing.T) {
	statsCh := make(chan *autoscaler.StatMessage)
	server := stats.NewTestServer(statsCh)

	go server.ListenAndServe()

	originalGoroutines := runtime.NumGoroutine()

	listenAddr := server.ListenAddr()
	statSink := dialOk(listenAddr, t)

	assertReceivedOk(newStatMessage("test-namespace/test-revision", "pod1", 2.1, 51), statSink, statsCh, t)

	closeSink(statSink, t)

	// Check the number of goroutines eventually reduces to the number there were before the connection was created
	for i := 1000; i >= 0; i-- {
		currentGoRoutines := runtime.NumGoroutine()
		if currentGoRoutines <= originalGoroutines {
			break
		}
		time.Sleep(5 * time.Millisecond)
		if i == 0 {
			t.Fatalf("Current number of goroutines %d is not equal to the original number %d", currentGoRoutines, originalGoroutines)
		}
	}

	server.Shutdown(time.Second)
}

func newStatMessage(revKey string, podName string, averageConcurrentRequests float64, requestCount int32) *autoscaler.StatMessage {
	now := time.Now()
	return &autoscaler.StatMessage{
		revKey,
		autoscaler.Stat{
			&now,
			podName,
			averageConcurrentRequests,
			requestCount,
		},
	}
}

func assertReceivedOk(sm *autoscaler.StatMessage, statSink *websocket.Conn, statsCh <-chan *autoscaler.StatMessage, t *testing.T) bool {
	send(statSink, sm, t)
	recv, ok := <-statsCh
	if !ok {
		t.Fatalf("statistic not received")
	}
	if !cmp.Equal(sm, recv) {
		t.Fatalf("Expected and actual stats messages are not equal: %s", cmp.Diff(sm, recv))
	}
	return true
}

func dialOk(serverURL string, t *testing.T) *websocket.Conn {
	statSink, err := dial(serverURL, t)
	if err != nil {
		t.Fatalf("Dial failed: %v", zap.Error(err))
	}
	return statSink
}

func dial(serverURL string, t *testing.T) (*websocket.Conn, error) {
	u, err := url.Parse(serverURL)
	if err != nil {
		t.Fatal(err)
	}
	u.Scheme = "ws"

	dialer := &websocket.Dialer{
		HandshakeTimeout: time.Second,
	}
	statSink, _, err := dialer.Dial(u.String(), nil)
	return statSink, err
}

func send(statSink *websocket.Conn, sm *autoscaler.StatMessage, t *testing.T) {
	var b bytes.Buffer
	enc := gob.NewEncoder(&b)
	err := enc.Encode(sm)
	if err != nil {
		t.Fatal("Failed to encode data from stats channel", zap.Error(err))
	}
	err = statSink.WriteMessage(websocket.BinaryMessage, b.Bytes())
	if err != nil {
		t.Fatal("Failed to write to stat sink.", zap.Error(err))
	}
}

func closeSink(statSink *websocket.Conn, t *testing.T) {
	if err := statSink.Close(); err != nil {
		t.Fatal("Failed to close", err)
	}
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	"io"

	"github.com/spf13/cobra"

	kerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

	"github.com/openshift/origin/pkg/network"
	"github.com/openshift/origin/pkg/oc/cli/util/clientcmd"
)

const IsolateProjectsNetworkCommandName = "isolate-projects"

var (
	isolateProjectsNetworkLong = templates.LongDesc(`
		Isolate project network

		Allows projects to isolate their network from other projects when using the %[1]s network plugin.`)

	isolateProjectsNetworkExample = templates.Examples(`
		# Provide isolation for project p1
		%[1]s <p1>

		# Allow all projects with label name=top-secret to have their own isolated project network
		%[1]s --selector='name=top-secret'`)
)

type IsolateOptions struct {
	Options *ProjectOptions
}

func NewCmdIsolateProjectsNetwork(commandName, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	opts := &ProjectOptions{}
	isolateOp := &IsolateOptions{Options: opts}

	cmd := &cobra.Command{
		Use:     commandName,
		Short:   "Isolate project network",
		Long:    fmt.Sprintf(isolateProjectsNetworkLong, network.MultiTenantPluginName),
		Example: fmt.Sprintf(isolateProjectsNetworkExample, fullName),
		Run: func(c *cobra.Command, args []string) {
			if err := opts.Complete(f, c, args, out); err != nil {
				kcmdutil.CheckErr(err)
			}
			opts.CheckSelector = c.Flag("selector").Changed
			if err := opts.Validate(); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error()))
			}

			err := isolateOp.Run()
			kcmdutil.CheckErr(err)
		},
	}
	flags := cmd.Flags()

	// Common optional params
	flags.StringVar(&opts.Selector, "selector", "", "Label selector to filter projects. Either pass one/more projects as arguments or use this project selector")

	return cmd
}

func (i *IsolateOptions) Run() error {
	projects, err := i.Options.GetProjects()
	if err != nil {
		return err
	}

	errList := []error{}
	for _, project := range projects {
		if err = i.Options.UpdatePodNetwork(project.Name, network.IsolatePodNetwork, ""); err != nil {
			errList = append(errList, fmt.Errorf("Network isolation for project %q failed, error: %v", project.Name, err))
		}
	}
	return kerrors.NewAggregate(errList)
}
<commit_msg>oc adm client should forbid isolation for 'default' project<commit_after>package network

import (
	"fmt"
	"io"

	"github.com/spf13/cobra"

	kerrors "k8s.io/apimachinery/pkg/util/errors"
	kapi "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

	"github.com/openshift/origin/pkg/network"
	"github.com/openshift/origin/pkg/oc/cli/util/clientcmd"
)

const IsolateProjectsNetworkCommandName = "isolate-projects"

var (
	isolateProjectsNetworkLong = templates.LongDesc(`
		Isolate project network

		Allows projects to isolate their network from other projects when using the %[1]s network plugin.`)

	isolateProjectsNetworkExample = templates.Examples(`
		# Provide isolation for project p1
		%[1]s <p1>

		# Allow all projects with label name=top-secret to have their own isolated project network
		%[1]s --selector='name=top-secret'`)
)

type IsolateOptions struct {
	Options *ProjectOptions
}

func NewCmdIsolateProjectsNetwork(commandName, fullName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
	opts := &ProjectOptions{}
	isolateOp := &IsolateOptions{Options: opts}

	cmd := &cobra.Command{
		Use:     commandName,
		Short:   "Isolate project network",
		Long:    fmt.Sprintf(isolateProjectsNetworkLong, network.MultiTenantPluginName),
		Example: fmt.Sprintf(isolateProjectsNetworkExample, fullName),
		Run: func(c *cobra.Command, args []string) {
			if err := opts.Complete(f, c, args, out); err != nil {
				kcmdutil.CheckErr(err)
			}
			opts.CheckSelector = c.Flag("selector").Changed
			if err := opts.Validate(); err != nil {
				kcmdutil.CheckErr(kcmdutil.UsageErrorf(c, err.Error()))
			}

			err := isolateOp.Run()
			kcmdutil.CheckErr(err)
		},
	}
	flags := cmd.Flags()

	// Common optional params
	flags.StringVar(&opts.Selector, "selector", "", "Label selector to filter projects. Either pass one/more projects as arguments or use this project selector")

	return cmd
}

func (i *IsolateOptions) Run() error {
	projects, err := i.Options.GetProjects()
	if err != nil {
		return err
	}

	errList := []error{}
	for _, project := range projects {
		if project.Name == kapi.NamespaceDefault {
			errList = append(errList, fmt.Errorf("network isolation for project %q is forbidden", project.Name))
			continue
		}
		if err = i.Options.UpdatePodNetwork(project.Name, network.IsolatePodNetwork, ""); err != nil {
			errList = append(errList, fmt.Errorf("Network isolation for project %q failed, error: %v", project.Name, err))
		}
	}
	return kerrors.NewAggregate(errList)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/gob"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime/debug"
	"strings"
	"time"

	"github.com/1lann/airlift/airlift"
	"github.com/1lann/airlift/fs"

	"github.com/gin-gonic/contrib/renders/multitemplate"
	"github.com/gin-gonic/contrib/secure"
	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
)

var packagePath = os.Getenv("GOPATH") + "/src/github.com/1lann/airlift"
var viewsPath = packagePath + "/views"

var registers []func(r *gin.RouterGroup, t multitemplate.Render)

func main() {
	err := airlift.Connect(dbConnectOpts)
	if err != nil {
		log.Fatal("failed to connect to database: ", err)
	}
	err = fs.Connect(minioAddr, minioAccessKey, minioSecretKey)
	if err != nil {
		log.Fatal("failed to connect to object store: ", err)
	}

	r := gin.Default()
	t := multitemplate.New()

	registerBaseHandlers(r, t)

	store := sessions.NewCookieStore([]byte(sessionSecret))
	store.Options(sessionOpts)
	r.Use(sessions.Sessions("airlift", store))

	rg := r.Group("/")
	rg.Use(authMiddleware)

	t.AddFromFiles("not-found", viewsPath+"/not-found.tmpl",
		viewsPath+"/components/base.tmpl")

	r.NoRoute(func(c *gin.Context) {
		c.HTML(http.StatusNotFound, "not-found", nil)
	})

	for _, registerFunc := range registers {
		registerFunc(rg, t)
	}
	r.HTMLRender = t
	r.Run()
}

func authMiddleware(c *gin.Context) {
	if strings.HasPrefix(c.Request.URL.Path, "/static") ||
		c.Request.URL.Path == "/favicon.ico" {
		return
	}

	session := sessions.Default(c)
	username, ok := session.Get("username").(string)
	if !ok && c.Request.URL.Path == "/" {
		c.Next()
		return
	}

	if !ok {
		session.AddFlash(showMessage{
			Title:   "You aren't logged in",
			Message: "You need to log in first before you can see this content.",
			Type:    "error",
		}, "login")
		session.Save()
		c.Redirect(http.StatusSeeOther, "/")
		c.Abort()
		return
	}

	if c.Request.URL.Path == "/" {
		c.Redirect(http.StatusSeeOther, "/schedule")
		c.Abort()
		return
	}

	user, err := airlift.GetUser(username)
	if err == airlift.ErrNotFound {
		session.AddFlash(showMessage{
			Title:   "Your account has gone missing",
			Message: "Was it deleted? Contact Chuie for help.",
			Type:    "error",
		}, "login")
		session.Delete("username")
		session.Save()
		c.Redirect(http.StatusSeeOther, "/")
		c.Abort()
		return
	} else if err != nil {
		panic(err)
	}

	c.Set("user", user)
}

func registerBaseHandlers(r *gin.Engine, t multitemplate.Render) {
	r.Use(secure.Secure(secureOpts))

	r.Use(func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Recover from panic
				stackTrace := strings.Replace(string(debug.Stack()),
					os.Getenv("GOPATH")+"/src/", "", -1)
				log.Println("Recovered from panic:", fmt.Sprintf("%v\n", err)+stackTrace)
				c.HTML(http.StatusInternalServerError, "error", gin.H{
					"StackTrace": fmt.Sprintf("%v\n", err) + stackTrace,
				})
			}
		}()

		c.Next()
	})
}

func getGreeting() string {
	hour := time.Now().Hour()
	if hour < 5 {
		return "evening"
	} else if hour < 12 {
		return "morning"
	} else if hour < 18 {
		return "afternoon"
	}

	return "evening"
}

func htmlOK(c *gin.Context, template string, h map[string]interface{}) {
	h["User"] = c.MustGet("user")
	h["Greeting"] = getGreeting()
	c.HTML(http.StatusOK, template, h)
}

func init() {
	gob.Register(showMessage{})

	registers = append(registers, func(r *gin.RouterGroup, t multitemplate.Render) {
		rg := r.Group("/static")
		rg.Use(func(c *gin.Context) {
			c.Header("Cache-Control", "max-age=86400")
		})
		rg.Static("/static", packagePath+"/static")
	})

	registers = append(registers, func(r *gin.RouterGroup, t multitemplate.Render) {
		t.AddFromFiles("error", viewsPath+"/error.tmpl",
			viewsPath+"/components/base.tmpl")
	})
}
<commit_msg>Fix static asset loading<commit_after>package main

import (
	"encoding/gob"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime/debug"
	"strings"
	"time"

	"github.com/1lann/airlift/airlift"
	"github.com/1lann/airlift/fs"

	"github.com/gin-gonic/contrib/renders/multitemplate"
	"github.com/gin-gonic/contrib/secure"
	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
)

var packagePath = os.Getenv("GOPATH") + "/src/github.com/1lann/airlift"
var viewsPath = packagePath + "/views"

var registers []func(r *gin.RouterGroup, t multitemplate.Render)

func main() {
	err := airlift.Connect(dbConnectOpts)
	if err != nil {
		log.Fatal("failed to connect to database: ", err)
	}
	err = fs.Connect(minioAddr, minioAccessKey, minioSecretKey)
	if err != nil {
		log.Fatal("failed to connect to object store: ", err)
	}

	r := gin.Default()
	t := multitemplate.New()

	registerBaseHandlers(r, t)

	store := sessions.NewCookieStore([]byte(sessionSecret))
	store.Options(sessionOpts)
	r.Use(sessions.Sessions("airlift", store))

	rg := r.Group("/")
	rg.Use(authMiddleware)

	t.AddFromFiles("not-found", viewsPath+"/not-found.tmpl",
		viewsPath+"/components/base.tmpl")

	r.NoRoute(func(c *gin.Context) {
		c.HTML(http.StatusNotFound, "not-found", nil)
	})

	for _, registerFunc := range registers {
		registerFunc(rg, t)
	}
	r.HTMLRender = t
	r.Run()
}

func authMiddleware(c *gin.Context) {
	if strings.HasPrefix(c.Request.URL.Path, "/static") ||
		c.Request.URL.Path == "/favicon.ico" {
		return
	}

	session := sessions.Default(c)
	username, ok := session.Get("username").(string)
	if !ok && c.Request.URL.Path == "/" {
		c.Next()
		return
	}

	if !ok {
		session.AddFlash(showMessage{
			Title:   "You aren't logged in",
			Message: "You need to log in first before you can see this content.",
			Type:    "error",
		}, "login")
		session.Save()
		c.Redirect(http.StatusSeeOther, "/")
		c.Abort()
		return
	}

	if c.Request.URL.Path == "/" {
		c.Redirect(http.StatusSeeOther, "/schedule")
		c.Abort()
		return
	}

	user, err := airlift.GetUser(username)
	if err == airlift.ErrNotFound {
		session.AddFlash(showMessage{
			Title:   "Your account has gone missing",
			Message: "Was it deleted? Contact Chuie for help.",
			Type:    "error",
		}, "login")
		session.Delete("username")
		session.Save()
		c.Redirect(http.StatusSeeOther, "/")
		c.Abort()
		return
	} else if err != nil {
		panic(err)
	}

	c.Set("user", user)
}

func registerBaseHandlers(r *gin.Engine, t multitemplate.Render) {
	r.Use(secure.Secure(secureOpts))

	r.Use(func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Recover from panic
				stackTrace := strings.Replace(string(debug.Stack()),
					os.Getenv("GOPATH")+"/src/", "", -1)
				log.Println("Recovered from panic:", fmt.Sprintf("%v\n", err)+stackTrace)
				c.HTML(http.StatusInternalServerError, "error", gin.H{
					"StackTrace": fmt.Sprintf("%v\n", err) + stackTrace,
				})
			}
		}()

		c.Next()
	})
}

func getGreeting() string {
	hour := time.Now().Hour()
	if hour < 5 {
		return "evening"
	} else if hour < 12 {
		return "morning"
	} else if hour < 18 {
		return "afternoon"
	}

	return "evening"
}

func htmlOK(c *gin.Context, template string, h map[string]interface{}) {
	h["User"] = c.MustGet("user")
	h["Greeting"] = getGreeting()
	c.HTML(http.StatusOK, template, h)
}

func init() {
	gob.Register(showMessage{})

	registers = append(registers, func(r *gin.RouterGroup, t multitemplate.Render) {
		rg := r.Group("/static")
		rg.Use(func(c *gin.Context) {
			c.Header("Cache-Control", "max-age=86400")
		})
		rg.Static("/", packagePath+"/static")
	})

	registers = append(registers, func(r *gin.RouterGroup, t multitemplate.Render) {
		t.AddFromFiles("error", viewsPath+"/error.tmpl",
			viewsPath+"/components/base.tmpl")
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 custom

import (
	"context"
	"io/ioutil"
	"os/exec"
	"testing"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/GoogleContainerTools/skaffold/testutil"
)

func TestRetrieveEnv(t *testing.T) {
	tests := []struct {
		description   string
		tag           string
		pushImages    bool
		buildContext  string
		additionalEnv []string
		environ       []string
		expected      []string
	}{

		{
			description:  "make sure tags are correct",
			tag:          "gcr.io/image/tag:mytag",
			environ:      nil,
			buildContext: "/some/path",
			expected:     []string{"IMAGES=gcr.io/image/tag:mytag", "PUSH_IMAGE=false", "BUILD_CONTEXT=/some/path"},
		}, {
			description:  "make sure environ is correctly applied",
			tag:          "gcr.io/image/tag:anothertag",
			environ:      []string{"PATH=/path", "HOME=/root"},
			buildContext: "/some/path",
			expected:     []string{"IMAGES=gcr.io/image/tag:anothertag", "PUSH_IMAGE=false", "BUILD_CONTEXT=/some/path", "PATH=/path", "HOME=/root"},
		}, {
			description: "push image is true",
			tag:         "gcr.io/image/push:tag",
			pushImages:  true,
			expected:    []string{"IMAGES=gcr.io/image/push:tag", "PUSH_IMAGE=true", "BUILD_CONTEXT="},
		}, {
			description:   "add additional env",
			tag:           "gcr.io/image/push:tag",
			pushImages:    true,
			additionalEnv: []string{"KUBECONTEXT=mycluster"},
			expected:      []string{"IMAGES=gcr.io/image/push:tag", "PUSH_IMAGE=true", "BUILD_CONTEXT=", "KUBECONTEXT=mycluster"},
		},
	}
	for _, test := range tests {
		testutil.Run(t, test.description, func(t *testutil.T) {
			t.Override(&util.OSEnviron, func() []string { return test.environ })
			t.Override(&buildContext, func(string) (string, error) { return test.buildContext, nil })

			artifactBuilder := NewArtifactBuilder(test.pushImages, test.additionalEnv)
			actual, err := artifactBuilder.retrieveEnv(&latest.Artifact{}, test.tag)

			t.CheckNoError(err)
			t.CheckDeepEqual(test.expected, actual)
		})
	}
}

func TestRetrieveCmd(t *testing.T) {
	tests := []struct {
		description string
		artifact    *latest.Artifact
		tag         string
		expected    *exec.Cmd
	}{
		{
			description: "artifact with workspace set",
			artifact: &latest.Artifact{
				Workspace: "workspace",
				ArtifactType: latest.ArtifactType{
					CustomArtifact: &latest.CustomArtifact{
						BuildCommand: "./build.sh",
					},
				},
			},
			tag:      "image:tag",
			expected: expectedCmd("./build.sh", "workspace", nil, []string{"IMAGES=image:tag", "PUSH_IMAGE=false", "BUILD_CONTEXT=workspace"}),
		}, {
			description: "buildcommand with multiple args",
			artifact: &latest.Artifact{
				ArtifactType: latest.ArtifactType{
					CustomArtifact: &latest.CustomArtifact{
						BuildCommand: "./build.sh --flag --anotherflag",
					},
				},
			},
			tag:      "image:tag",
			expected: expectedCmd("./build.sh", "", []string{"--flag", "--anotherflag"}, []string{"IMAGES=image:tag", "PUSH_IMAGE=false", "BUILD_CONTEXT="}),
		},
	}
	for _, test := range tests {
		testutil.Run(t, test.description, func(t *testutil.T) {
			t.Override(&util.OSEnviron, func() []string { return nil })
			t.Override(&buildContext, func(string) (string, error) { return test.artifact.Workspace, nil })

			builder := NewArtifactBuilder(false, nil)
			cmd, err := builder.retrieveCmd(context.Background(), ioutil.Discard, test.artifact, test.tag)

			t.CheckNoError(err)
			t.CheckDeepEqual(test.expected.Args, cmd.Args)
			t.CheckDeepEqual(test.expected.Dir, cmd.Dir)
			t.CheckDeepEqual(test.expected.Env, cmd.Env)
		})
	}
}

func expectedCmd(buildCommand, dir string, args, env []string) *exec.Cmd {
	cmd := exec.CommandContext(context.Background(), buildCommand, args...)
	cmd.Dir = dir
	cmd.Env = env
	return cmd
}
<commit_msg>Fix tests<commit_after>/*
Copyright 2019 The Skaffold 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 custom

import (
	"io/ioutil"
	"os/exec"
	"testing"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/GoogleContainerTools/skaffold/testutil"
)

func TestRetrieveEnv(t *testing.T) {
	tests := []struct {
		description   string
		tag           string
		pushImages    bool
		buildContext  string
		additionalEnv []string
		environ       []string
		expected      []string
	}{

		{
			description:  "make sure tags are correct",
			tag:          "gcr.io/image/tag:mytag",
			environ:      nil,
			buildContext: "/some/path",
			expected:     []string{"IMAGES=gcr.io/image/tag:mytag", "PUSH_IMAGE=false", "BUILD_CONTEXT=/some/path"},
		}, {
			description:  "make sure environ is correctly applied",
			tag:          "gcr.io/image/tag:anothertag",
			environ:      []string{"PATH=/path", "HOME=/root"},
			buildContext: "/some/path",
			expected:     []string{"IMAGES=gcr.io/image/tag:anothertag", "PUSH_IMAGE=false", "BUILD_CONTEXT=/some/path", "PATH=/path", "HOME=/root"},
		}, {
			description: "push image is true",
			tag:         "gcr.io/image/push:tag",
			pushImages:  true,
			expected:    []string{"IMAGES=gcr.io/image/push:tag", "PUSH_IMAGE=true", "BUILD_CONTEXT="},
		}, {
			description:   "add additional env",
			tag:           "gcr.io/image/push:tag",
			pushImages:    true,
			additionalEnv: []string{"KUBECONTEXT=mycluster"},
			expected:      []string{"IMAGES=gcr.io/image/push:tag", "PUSH_IMAGE=true", "BUILD_CONTEXT=", "KUBECONTEXT=mycluster"},
		},
	}
	for _, test := range tests {
		testutil.Run(t, test.description, func(t *testutil.T) {
			t.Override(&util.OSEnviron, func() []string { return test.environ })
			t.Override(&buildContext, func(string) (string, error) { return test.buildContext, nil })

			artifactBuilder := NewArtifactBuilder(test.pushImages, test.additionalEnv)
			actual, err := artifactBuilder.retrieveEnv(&latest.Artifact{}, test.tag)

			t.CheckNoError(err)
			t.CheckDeepEqual(test.expected, actual)
		})
	}
}

func TestRetrieveCmd(t *testing.T) {
	tests := []struct {
		description string
		artifact    *latest.Artifact
		tag         string
		expected    *exec.Cmd
	}{
		{
			description: "artifact with workspace set",
			artifact: &latest.Artifact{
				Workspace: "workspace",
				ArtifactType: latest.ArtifactType{
					CustomArtifact: &latest.CustomArtifact{
						BuildCommand: "./build.sh",
					},
				},
			},
			tag:      "image:tag",
			expected: expectedCmd("./build.sh", "workspace", nil, []string{"IMAGES=image:tag", "PUSH_IMAGE=false", "BUILD_CONTEXT=workspace"}),
		}, {
			description: "buildcommand with multiple args",
			artifact: &latest.Artifact{
				ArtifactType: latest.ArtifactType{
					CustomArtifact: &latest.CustomArtifact{
						BuildCommand: "./build.sh --flag --anotherflag",
					},
				},
			},
			tag:      "image:tag",
			expected: expectedCmd("./build.sh", "", []string{"--flag", "--anotherflag"}, []string{"IMAGES=image:tag", "PUSH_IMAGE=false", "BUILD_CONTEXT="}),
		},
	}
	for _, test := range tests {
		testutil.Run(t, test.description, func(t *testutil.T) {
			t.Override(&util.OSEnviron, func() []string { return nil })
			t.Override(&buildContext, func(string) (string, error) { return test.artifact.Workspace, nil })

			builder := NewArtifactBuilder(false, nil)
			cmd, err := builder.retrieveCmd(ioutil.Discard, test.artifact, test.tag)

			t.CheckNoError(err)
			t.CheckDeepEqual(test.expected.Args, cmd.Args)
			t.CheckDeepEqual(test.expected.Dir, cmd.Dir)
			t.CheckDeepEqual(test.expected.Env, cmd.Env)
		})
	}
}

func expectedCmd(buildCommand, dir string, args, env []string) *exec.Cmd {
	cmd := exec.Command(buildCommand, args...)
	cmd.Dir = dir
	cmd.Env = env
	return cmd
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	textTempl "text/template"
)

// type sourceConfig struct {
// 	name           string            "json:name"
// 	upstreamDriver string            "json:type"
// 	upstream       upstreamTarget "json:upstream"
// 	output         []outputType      "json:specFile"
// }

// type upstreamTarget interface {
// 	check() error
// 	version() string
// 	download() error
// }

type versionType struct {
	fmt  string   `json:"fmt"`
	cur  string   `json:"cur"`
	past []string `json:"past"`
}

func (v versionType) nextPossible() (possible []string ) {
	fmtChunks := strings.Split(v.fmt, "%v")
	remainingChunks := fmtChunks

	var possibleSubs []string
	for len(fmtChunks) > 0 {
		prefix := strings.TrimSuffix(v.cur, fmtChunks[len(fmtChunks)-1])

	}
}

type outputType struct {
	active      bool   `json:"active"`
	gitRepo     string `json:"gitRepo"`
	location    string `json:"location"`
	outputID    int    `json:"outputID"`
	outputPrune int    `json:"outputPrune"`
}

type httpResponseTracker struct {
	version versionType `json:"version"`
	driver string `json:"driver"`
	url    string `json:"url"`
}

// func (upstream httpResponseTracker) check() error {

// 	req, _ := http.NewRequest("GET", upstream.Replace(upstream.url), nil)

// }<commit_msg>added a few helper functions that imma need<commit_after>package main

import (
	"net/http"
	textTempl "text/template"
	"strconv"
)

// type sourceConfig struct {
// 	name           string            "json:name"
// 	upstreamDriver string            "json:type"
// 	upstream       upstreamTarget "json:upstream"
// 	output         []outputType      "json:specFile"
// }

// type upstreamTarget interface {
// 	check() error
// 	version() string
// 	download() error
// }

type versionType struct {
	fmt  string   `json:"fmt"`
	cur  string   `json:"cur"`
	past []string `json:"past"`
}

func stringSubtraction(s string, chunks []string) ([]string, error) {
	var result []string
	remaining := strings.TrimLeft(chunks[0], s)

	for i := 1; i < len(chunks) ; i++{
		if !strings.Contains(remaining, chunks[i]) {
			return []string{}, errors.New(`chunk [%q] not found in [%q]
			original string [%q]
			cut chunks %#v
			parts gotten %#v`,
			chunks[i], remaining, s, chunks, result)
		}
		parts := strings.SplitN(remaining, chunks[i], 2)
		result = append(result, parts[0])
		remaining = parts[1]
	}

	if remaining != "" {
		result = append(result, remaining)
	}

	return result, nil
}

func stringsToInts(s []string) ([]int, error) {
	var results []int
	for _, s := range strings {
		i, err := strconv.Atoi(s)
		if err != nil {
			return []int{}, errors.New("failed - string [%q] is not an int - %v", s, err)
		}
		results = append(results, i)
	}
	return results
}

func (v versionType) nextPossible() (possible []string ) {
	fmtChunks := strings.Split(v.fmt, "%v")
	unprocessedPrefix := v.cur
	var processedSuffix []string

	for len(fmtChunks) > 0 {
		suffix := fmtChunks[len(fmtChunks)-1]
		prefix := strings.TrimSuffix(v.cur, fmtChunks[len(fmtChunks)-1])
		parts := string.Split(prefix, fmtChunks[len(fmtChunks)-2])
		versionNumber := parts[len(parts)-1]

	}
}

type outputType struct {
	active      bool   `json:"active"`
	gitRepo     string `json:"gitRepo"`
	location    string `json:"location"`
	outputID    int    `json:"outputID"`
	outputPrune int    `json:"outputPrune"`
}

type httpResponseTracker struct {
	version versionType `json:"version"`
	driver string `json:"driver"`
	url    string `json:"url"`
}

// func (upstream httpResponseTracker) check() error {

// 	req, _ := http.NewRequest("GET", upstream.Replace(upstream.url), nil)

// }<|endoftext|>


<commit_before>//
// Copyright © 2019-2020 Guy M. Allard
//
// 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, 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 stompngo

import "log"

type eltd struct {
	ens int64 // nanoseconds
	ec  int64 // count
}

type eltmets struct {

	// Reader overall
	rov eltd
	// Reader command
	rcmd eltd
	// Reader individual headers
	rivh eltd
	// Reader - until null
	run eltd
	// Reader - Body
	rbdy eltd

	// Writer overall
	wov eltd
	// Writer command
	wcmd eltd
	// Writer individual headers
	wivh eltd
	// Writer - Body
	wbdy eltd
}

func (c *Connection) ShowEltd(ll *log.Logger) {
	if c.eltd == nil {
		return
	}
	//
	ll.Println("Reader Elapsed Time Information")
	//
	ll.Printf("Overall - ns %d count %d\n",
		c.eltd.rov.ens, c.eltd.rov.ec)
	//
	ll.Printf("Command - ns %d count %d\n",
		c.eltd.rcmd.ens, c.eltd.rcmd.ec)
	//
	ll.Printf("Individual Headers - ns %d count %d\n",
		c.eltd.rivh.ens, c.eltd.rivh.ec)
	//
	ll.Printf("Until Null - ns %d count %d\n",
		c.eltd.run.ens, c.eltd.run.ec)
	//
	ll.Printf("Body - ns %d count %d\n",
		c.eltd.rbdy.ens, c.eltd.rbdy.ec)

	//
	ll.Println("Writer Elapsed Time Information")
	//
	ll.Printf("Overall - ns %d count %d\n",
		c.eltd.wov.ens, c.eltd.wov.ec)
	//
	ll.Printf("Command - ns %d count %d\n",
		c.eltd.wcmd.ens, c.eltd.wcmd.ec)
	//
	ll.Printf("Individual Headers - ns %d count %d\n",
		c.eltd.wivh.ens, c.eltd.wivh.ec)
	//
	ll.Printf("Body - ns %d count %d\n",
		c.eltd.wbdy.ens, c.eltd.wbdy.ec)
}
<commit_msg>Add CSV display of elapsed times.<commit_after>//
// Copyright © 2019-2020 Guy M. Allard
//
// 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, 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 stompngo

import (
	"fmt"
	"log"
)

type eltd struct {
	ens int64 // nanoseconds
	ec  int64 // count
}

type eltmets struct {

	// Reader overall
	rov eltd
	// Reader command
	rcmd eltd
	// Reader individual headers
	rivh eltd
	// Reader - until null
	run eltd
	// Reader - Body
	rbdy eltd

	// Writer overall
	wov eltd
	// Writer command
	wcmd eltd
	// Writer individual headers
	wivh eltd
	// Writer - Body
	wbdy eltd
}

func (c *Connection) ShowEltd(ll *log.Logger) {
	if c.eltd == nil {
		return
	}
	//
	ll.Println("Reader Elapsed Time Information")
	//
	ll.Printf("Overall - ns %d count %d\n",
		c.eltd.rov.ens, c.eltd.rov.ec)
	//
	ll.Printf("Command - ns %d count %d\n",
		c.eltd.rcmd.ens, c.eltd.rcmd.ec)
	//
	ll.Printf("Individual Headers - ns %d count %d\n",
		c.eltd.rivh.ens, c.eltd.rivh.ec)
	//
	ll.Printf("Until Null - ns %d count %d\n",
		c.eltd.run.ens, c.eltd.run.ec)
	//
	ll.Printf("Body - ns %d count %d\n",
		c.eltd.rbdy.ens, c.eltd.rbdy.ec)

	//
	ll.Println("Writer Elapsed Time Information")
	//
	ll.Printf("Overall - ns %d count %d\n",
		c.eltd.wov.ens, c.eltd.wov.ec)
	//
	ll.Printf("Command - ns %d count %d\n",
		c.eltd.wcmd.ens, c.eltd.wcmd.ec)
	//
	ll.Printf("Individual Headers - ns %d count %d\n",
		c.eltd.wivh.ens, c.eltd.wivh.ec)
	//
	ll.Printf("Body - ns %d count %d\n",
		c.eltd.wbdy.ens, c.eltd.wbdy.ec)
}

func (c *Connection) ShowEltdCsv() {
	//
	fmt.Println("SECTION,ELTNS,COUNT")
	//
	fmt.Printf("ROV,%d,%d\n",
		c.eltd.rov.ens, c.eltd.rov.ec)
	//
	fmt.Printf("RCMD,%d,%d\n",
		c.eltd.rcmd.ens, c.eltd.rcmd.ec)
	//
	fmt.Printf("RIVH,%d,%d\n",
		c.eltd.rivh.ens, c.eltd.rivh.ec)
	//
	fmt.Printf("RUN,%d,%d\n",
		c.eltd.run.ens, c.eltd.run.ec)
	//
	fmt.Printf("RBDY,%d,%d\n",
		c.eltd.rbdy.ens, c.eltd.rbdy.ec)

	//
	fmt.Printf("WOV,%d,%d\n",
		c.eltd.wov.ens, c.eltd.wov.ec)
	//
	fmt.Printf("WCMD,%d,%d\n",
		c.eltd.wcmd.ens, c.eltd.wcmd.ec)
	//
	fmt.Printf("WIVH,%d,%d\n",
		c.eltd.wivh.ens, c.eltd.wivh.ec)
	//
	fmt.Printf("WBDY,%d,%d\n",
		c.eltd.wbdy.ens, c.eltd.wbdy.ec)
}
<|endoftext|>


<commit_before>package versionconstants

import (
	"fmt"
	"github.com/drud/ddev/pkg/globalconfig"
	"github.com/drud/ddev/pkg/nodeps"
)

// DdevVersion is the current version of ddev, by default the git committish (should be current git tag)
var DdevVersion = "v0.0.0-overridden-by-make" // Note that this is overridden by make

// SegmentKey is the ddev-specific key for Segment service
// Compiled with link-time variables
var SegmentKey = ""

// WebImg defines the default web image used for applications.
var WebImg = "drud/ddev-webserver"

// WebTag defines the default web image tag
var WebTag = "20220808_yarn_crash" // Note that this can be overridden by make

// DBImg defines the default db image used for applications.
var DBImg = "drud/ddev-dbserver"

// BaseDBTag is the main tag, DBTag is constructed from it
var BaseDBTag = "v1.20.0"

// DBAImg defines the default phpmyadmin image tag used for applications.
var DBAImg = "phpmyadmin"

// DBATag defines the default phpmyadmin image tag used for applications.
var DBATag = "5" // Note that this can be overridden by make

// RouterImage defines the image used for the router.
var RouterImage = "drud/ddev-router"

// RouterTag defines the tag used for the router.
var RouterTag = "v1.20.0" // Note that this can be overridden by make

// SSHAuthImage is image for agent
// var SSHAuthImage = "drud/ddev-ssh-agent"
var SSHAuthImage = "drud/ddev-ssh-agent"

// SSHAuthTag is ssh-agent auth tag
// var SSHAuthTag = "v1.19.0"
var SSHAuthTag = "v1.20.0"

// Busybox is used a couple of places for a quick-pull
var BusyboxImage = "busybox:stable"

// BUILDINFO is information with date and context, supplied by make
var BUILDINFO = "BUILDINFO should have new info"

// MutagenVersion is filled with the version we find for mutagen in use
var MutagenVersion = ""

const RequiredMutagenVersion = "0.15.0"

// GetWebImage returns the correctly formatted web image:tag reference
func GetWebImage() string {
	fullWebImg := WebImg
	if globalconfig.DdevGlobalConfig.UseHardenedImages {
		fullWebImg = fullWebImg + "-prod"
	}
	return fmt.Sprintf("%s:%s", fullWebImg, WebTag)
}

// GetDBImage returns the correctly formatted db image:tag reference
func GetDBImage(dbType string, dbVersion ...string) string {
	v := nodeps.MariaDBDefaultVersion
	if len(dbVersion) > 0 {
		v = dbVersion[0]
	}
	switch dbType {
	case nodeps.Postgres:
		return fmt.Sprintf("%s:%s", dbType, v)
	case nodeps.MySQL:
		fallthrough
	case nodeps.MariaDB:
		fallthrough
	default:
		return fmt.Sprintf("%s-%s-%s:%s", DBImg, dbType, v, BaseDBTag)
	}
}

// GetDBAImage returns the correctly formatted dba image:tag reference
func GetDBAImage() string {
	return fmt.Sprintf("%s:%s", DBAImg, DBATag)
}

// GetSSHAuthImage returns the correctly formatted sshauth image:tag reference
func GetSSHAuthImage() string {
	return fmt.Sprintf("%s:%s", SSHAuthImage, SSHAuthTag)
}

// GetRouterImage returns the correctly formatted router image:tag reference
func GetRouterImage() string {
	return fmt.Sprintf("%s:%s", RouterImage, RouterTag)
}
<commit_msg>Bump mutagen to 0.15.1 (#4121) [skip ci]<commit_after>package versionconstants

import (
	"fmt"
	"github.com/drud/ddev/pkg/globalconfig"
	"github.com/drud/ddev/pkg/nodeps"
)

// DdevVersion is the current version of ddev, by default the git committish (should be current git tag)
var DdevVersion = "v0.0.0-overridden-by-make" // Note that this is overridden by make

// SegmentKey is the ddev-specific key for Segment service
// Compiled with link-time variables
var SegmentKey = ""

// WebImg defines the default web image used for applications.
var WebImg = "drud/ddev-webserver"

// WebTag defines the default web image tag
var WebTag = "20220808_yarn_crash" // Note that this can be overridden by make

// DBImg defines the default db image used for applications.
var DBImg = "drud/ddev-dbserver"

// BaseDBTag is the main tag, DBTag is constructed from it
var BaseDBTag = "v1.20.0"

// DBAImg defines the default phpmyadmin image tag used for applications.
var DBAImg = "phpmyadmin"

// DBATag defines the default phpmyadmin image tag used for applications.
var DBATag = "5" // Note that this can be overridden by make

// RouterImage defines the image used for the router.
var RouterImage = "drud/ddev-router"

// RouterTag defines the tag used for the router.
var RouterTag = "v1.20.0" // Note that this can be overridden by make

// SSHAuthImage is image for agent
// var SSHAuthImage = "drud/ddev-ssh-agent"
var SSHAuthImage = "drud/ddev-ssh-agent"

// SSHAuthTag is ssh-agent auth tag
// var SSHAuthTag = "v1.19.0"
var SSHAuthTag = "v1.20.0"

// Busybox is used a couple of places for a quick-pull
var BusyboxImage = "busybox:stable"

// BUILDINFO is information with date and context, supplied by make
var BUILDINFO = "BUILDINFO should have new info"

// MutagenVersion is filled with the version we find for mutagen in use
var MutagenVersion = ""

const RequiredMutagenVersion = "0.15.1"

// GetWebImage returns the correctly formatted web image:tag reference
func GetWebImage() string {
	fullWebImg := WebImg
	if globalconfig.DdevGlobalConfig.UseHardenedImages {
		fullWebImg = fullWebImg + "-prod"
	}
	return fmt.Sprintf("%s:%s", fullWebImg, WebTag)
}

// GetDBImage returns the correctly formatted db image:tag reference
func GetDBImage(dbType string, dbVersion ...string) string {
	v := nodeps.MariaDBDefaultVersion
	if len(dbVersion) > 0 {
		v = dbVersion[0]
	}
	switch dbType {
	case nodeps.Postgres:
		return fmt.Sprintf("%s:%s", dbType, v)
	case nodeps.MySQL:
		fallthrough
	case nodeps.MariaDB:
		fallthrough
	default:
		return fmt.Sprintf("%s-%s-%s:%s", DBImg, dbType, v, BaseDBTag)
	}
}

// GetDBAImage returns the correctly formatted dba image:tag reference
func GetDBAImage() string {
	return fmt.Sprintf("%s:%s", DBAImg, DBATag)
}

// GetSSHAuthImage returns the correctly formatted sshauth image:tag reference
func GetSSHAuthImage() string {
	return fmt.Sprintf("%s:%s", SSHAuthImage, SSHAuthTag)
}

// GetRouterImage returns the correctly formatted router image:tag reference
func GetRouterImage() string {
	return fmt.Sprintf("%s:%s", RouterImage, RouterTag)
}
<|endoftext|>