<commit_before>package journey

import (
	"context"
	"os"
	"os/signal"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"

	"github.com/db-journey/migrate"
	"github.com/db-journey/migrate/file"
	"github.com/urfave/cli"
)

var MigrateFlags = []cli.Flag{}

//Commands returns the application cli commands:
//create <name>  Create a new migration
//up             Apply all -up- migrations
//down           Apply all -down- migrations
//reset          Down followed by Up
//redo           Roll back most recent migration, then apply it again
//version        Show current migration version
//migrate <n>    Apply migrations -n|+n
//goto <v>       Migrate to version v
func MigrateCommands() cli.Commands {
	return cli.Commands{
		createCommand,
		upCommand,
		downCommand,
		resetCommand,
		redoCommand,
		versionCommand,
		migrateCommand,
		applyCommand,
		rollbackCommand,
		gotoCommand,
	}
}

var createCommand = cli.Command{
	Name:      "create",
	Aliases:   []string{"c"},
	Usage:     "Create a new migration",
	ArgsUsage: "<name>",
	Flags:     MigrateFlags,
	Action: func(ctx *cli.Context) error {
		name := ctx.Args().First()
		if name == "" {
			log.Fatal("Please specify a name for the new migration")
		}
		// if more than one param is passed, create a concat name
		if ctx.NArg() != 1 {
			name = strings.Join(ctx.Args(), "_")
		}

		migrate, _, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()

		migrationFile, err := migrate.Create(name)
		if err != nil {
			logErr(err).Fatal("Migration failed")
		}

		log.WithFields(log.Fields{
			"up":   migrationFile.UpFile.FileName,
			"down": migrationFile.DownFile.FileName,
		}).Infof("Version %v migration files created in %v:\n", migrationFile.Version, ctx.GlobalString("path"))
		return nil
	},
}

var upCommand = cli.Command{
	Name:  "up",
	Usage: "Apply all -up- migrations",
	Flags: MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Applying all -up- migrations")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Up(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to apply all -up- migrations")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var downCommand = cli.Command{
	Name:  "down",
	Usage: "Apply all -down- migrations",
	Flags: MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Applying all -down- migrations")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Down(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to apply all -down- migrations")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var redoCommand = cli.Command{
	Name:    "redo",
	Aliases: []string{"r"},
	Usage:   "Roll back most recent migration, then apply it again",
	Flags:   MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Redoing last migration")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Redo(mctx)
		logErr(err).Fatal("Failed to redo last migration")
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var versionCommand = cli.Command{
	Name:    "version",
	Aliases: []string{"v"},
	Usage:   "Show current migration version",
	Flags:   MigrateFlags,
	Action: func(ctx *cli.Context) error {
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		version, err := migrate.Version(mctx)
		if err != nil {
			logErr(err).Fatal("Unable to fetch version")
		}

		log.Infof("Current version: %d", version)
		return nil
	},
}

var resetCommand = cli.Command{
	Name:  "reset",
	Usage: "Down followed by Up",
	Flags: MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Reseting database")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Redo(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to reset database")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var migrateCommand = cli.Command{
	Name:            "migrate",
	Aliases:         []string{"m"},
	Usage:           "Apply migrations -n|+n",
	ArgsUsage:       "<n>",
	Flags:           MigrateFlags,
	SkipFlagParsing: true,
	Action: func(ctx *cli.Context) error {
		relativeN := ctx.Args().First()
		relativeNInt, err := strconv.Atoi(relativeN)
		if err != nil {
			logErr(err).Fatal("Unable to parse param <n>")
		}

		log.Infof("Applying %d migrations", relativeNInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err = migrate.Migrate(mctx, relativeNInt)
		if err != nil {
			logErr(err).Fatalf("Failed to apply %d migrations", relativeNInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var applyCommand = cli.Command{
	Name:            "apply",
	Aliases:         []string{"a"},
	Usage:           "Run up migration for specific version",
	ArgsUsage:       "<version>",
	Flags:           MigrateFlags,
	SkipFlagParsing: true,
	Action: func(ctx *cli.Context) error {
		version := ctx.Args().First()
		versionInt, err := strconv.Atoi(version)
		if err != nil {
			logErr(err).Fatal("Unable to parse param <n>")
		}

		log.Infof("Applying version %d", versionInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err = migrate.ApplyVersion(mctx, file.Version(versionInt))
		if err != nil {
			logErr(err).Fatalf("Failed to apply version %d", versionInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var rollbackCommand = cli.Command{
	Name:            "rollback",
	Aliases:         []string{"r"},
	Usage:           "Run down migration for specific version",
	ArgsUsage:       "<version>",
	Flags:           MigrateFlags,
	SkipFlagParsing: true,
	Action: func(ctx *cli.Context) error {
		version := ctx.Args().First()
		versionInt, err := strconv.Atoi(version)
		if err != nil {
			logErr(err).Fatal("Unable to parse param <n>")
		}

		log.Infof("Applying version %d", versionInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err = migrate.RollbackVersion(mctx, file.Version(versionInt))
		if err != nil {
			logErr(err).Fatalf("Failed to rollback version %d", versionInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var gotoCommand = cli.Command{
	Name:      "goto",
	Aliases:   []string{"g"},
	Usage:     "Migrate to version <v>",
	ArgsUsage: "<v>",
	Flags:     MigrateFlags,
	Action: func(ctx *cli.Context) error {
		toVersion := ctx.Args().First()
		toVersionInt, err := strconv.Atoi(toVersion)
		if err != nil || toVersionInt < 0 {
			logErr(err).Fatal("Unable to parse param <v>")
		}

		log.Infof("Migrating to version %d", toVersionInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		currentVersion, err := migrate.Version(mctx)
		if err != nil {
			logErr(err).Fatalf("failed to migrate to version %d", toVersionInt)
		}

		relativeNInt := toVersionInt - int(currentVersion)

		err = migrate.Migrate(mctx, relativeNInt)
		if err != nil {
			logErr(err).Fatalf("Failed to migrate to vefrsion %d", toVersionInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

func newMigrateWithCtx(url, migrationsPath string) (*migrate.Handle, context.Context, func()) {
	done := make(chan struct{})
	m, err := migrate.Open(url, migrationsPath, migrate.WithHooks(
		func(f file.File) error {
			log.Infof("Applying %s migration for version %d (%s)", f.Direction, f.Version, f.Name)
			return nil
		},
		func(f file.File) error {
			done <- struct{}{}
			return nil
		},
	))
	if err != nil {
		log.Fatalf("Initialization failed: %s", err)
	}
	ctx, cancel := newOsInterruptCtx(done)
	return m, ctx, cancel
}

// newOsInterruptCtx returns new context that will be cancelled
// on os.Interrupt signal.
func newOsInterruptCtx(done <-chan struct{}) (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		exit := false
		for loop := true; loop; {
			select {
			case <-done:
				if exit {
					loop = false
				}
			case <-c:
				if exit {
					os.Exit(5)
				}
				cancel()
				exit = true
				log.Info("Aborting after this migration... Hit again to force quit.")
			}
		}
		signal.Stop(c)
	}()
	return ctx, cancel
}

func logErr(err error) *log.Entry {
	return log.WithError(err)
}

func logCurrentVersion(ctx context.Context, migrate *migrate.Handle) {
	version, err := migrate.Version(ctx)
	if err != nil {
		logErr(err).Fatal("Unable to fetch version")
	}
	log.WithField("current-version", version).Info("done")
}
<commit_msg>Fix: do not log error for redo, when is nil<commit_after>package journey

import (
	"context"
	"os"
	"os/signal"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"

	"github.com/db-journey/migrate"
	"github.com/db-journey/migrate/file"
	"github.com/urfave/cli"
)

var MigrateFlags = []cli.Flag{}

//Commands returns the application cli commands:
//create <name>  Create a new migration
//up             Apply all -up- migrations
//down           Apply all -down- migrations
//reset          Down followed by Up
//redo           Roll back most recent migration, then apply it again
//version        Show current migration version
//migrate <n>    Apply migrations -n|+n
//goto <v>       Migrate to version v
func MigrateCommands() cli.Commands {
	return cli.Commands{
		createCommand,
		upCommand,
		downCommand,
		resetCommand,
		redoCommand,
		versionCommand,
		migrateCommand,
		applyCommand,
		rollbackCommand,
		gotoCommand,
	}
}

var createCommand = cli.Command{
	Name:      "create",
	Aliases:   []string{"c"},
	Usage:     "Create a new migration",
	ArgsUsage: "<name>",
	Flags:     MigrateFlags,
	Action: func(ctx *cli.Context) error {
		name := ctx.Args().First()
		if name == "" {
			log.Fatal("Please specify a name for the new migration")
		}
		// if more than one param is passed, create a concat name
		if ctx.NArg() != 1 {
			name = strings.Join(ctx.Args(), "_")
		}

		migrate, _, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()

		migrationFile, err := migrate.Create(name)
		if err != nil {
			logErr(err).Fatal("Migration failed")
		}

		log.WithFields(log.Fields{
			"up":   migrationFile.UpFile.FileName,
			"down": migrationFile.DownFile.FileName,
		}).Infof("Version %v migration files created in %v:\n", migrationFile.Version, ctx.GlobalString("path"))
		return nil
	},
}

var upCommand = cli.Command{
	Name:  "up",
	Usage: "Apply all -up- migrations",
	Flags: MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Applying all -up- migrations")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Up(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to apply all -up- migrations")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var downCommand = cli.Command{
	Name:  "down",
	Usage: "Apply all -down- migrations",
	Flags: MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Applying all -down- migrations")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Down(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to apply all -down- migrations")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var redoCommand = cli.Command{
	Name:    "redo",
	Aliases: []string{"r"},
	Usage:   "Roll back most recent migration, then apply it again",
	Flags:   MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Redoing last migration")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Redo(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to redo last migration")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var versionCommand = cli.Command{
	Name:    "version",
	Aliases: []string{"v"},
	Usage:   "Show current migration version",
	Flags:   MigrateFlags,
	Action: func(ctx *cli.Context) error {
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		version, err := migrate.Version(mctx)
		if err != nil {
			logErr(err).Fatal("Unable to fetch version")
		}

		log.Infof("Current version: %d", version)
		return nil
	},
}

var resetCommand = cli.Command{
	Name:  "reset",
	Usage: "Down followed by Up",
	Flags: MigrateFlags,
	Action: func(ctx *cli.Context) error {
		log.Info("Reseting database")
		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err := migrate.Redo(mctx)
		if err != nil {
			logErr(err).Fatal("Failed to reset database")
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var migrateCommand = cli.Command{
	Name:            "migrate",
	Aliases:         []string{"m"},
	Usage:           "Apply migrations -n|+n",
	ArgsUsage:       "<n>",
	Flags:           MigrateFlags,
	SkipFlagParsing: true,
	Action: func(ctx *cli.Context) error {
		relativeN := ctx.Args().First()
		relativeNInt, err := strconv.Atoi(relativeN)
		if err != nil {
			logErr(err).Fatal("Unable to parse param <n>")
		}

		log.Infof("Applying %d migrations", relativeNInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err = migrate.Migrate(mctx, relativeNInt)
		if err != nil {
			logErr(err).Fatalf("Failed to apply %d migrations", relativeNInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var applyCommand = cli.Command{
	Name:            "apply",
	Aliases:         []string{"a"},
	Usage:           "Run up migration for specific version",
	ArgsUsage:       "<version>",
	Flags:           MigrateFlags,
	SkipFlagParsing: true,
	Action: func(ctx *cli.Context) error {
		version := ctx.Args().First()
		versionInt, err := strconv.Atoi(version)
		if err != nil {
			logErr(err).Fatal("Unable to parse param <n>")
		}

		log.Infof("Applying version %d", versionInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err = migrate.ApplyVersion(mctx, file.Version(versionInt))
		if err != nil {
			logErr(err).Fatalf("Failed to apply version %d", versionInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var rollbackCommand = cli.Command{
	Name:            "rollback",
	Aliases:         []string{"r"},
	Usage:           "Run down migration for specific version",
	ArgsUsage:       "<version>",
	Flags:           MigrateFlags,
	SkipFlagParsing: true,
	Action: func(ctx *cli.Context) error {
		version := ctx.Args().First()
		versionInt, err := strconv.Atoi(version)
		if err != nil {
			logErr(err).Fatal("Unable to parse param <n>")
		}

		log.Infof("Applying version %d", versionInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		err = migrate.RollbackVersion(mctx, file.Version(versionInt))
		if err != nil {
			logErr(err).Fatalf("Failed to rollback version %d", versionInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

var gotoCommand = cli.Command{
	Name:      "goto",
	Aliases:   []string{"g"},
	Usage:     "Migrate to version <v>",
	ArgsUsage: "<v>",
	Flags:     MigrateFlags,
	Action: func(ctx *cli.Context) error {
		toVersion := ctx.Args().First()
		toVersionInt, err := strconv.Atoi(toVersion)
		if err != nil || toVersionInt < 0 {
			logErr(err).Fatal("Unable to parse param <v>")
		}

		log.Infof("Migrating to version %d", toVersionInt)

		migrate, mctx, cancel := newMigrateWithCtx(ctx.GlobalString("url"), ctx.GlobalString("path"))
		defer cancel()
		currentVersion, err := migrate.Version(mctx)
		if err != nil {
			logErr(err).Fatalf("failed to migrate to version %d", toVersionInt)
		}

		relativeNInt := toVersionInt - int(currentVersion)

		err = migrate.Migrate(mctx, relativeNInt)
		if err != nil {
			logErr(err).Fatalf("Failed to migrate to vefrsion %d", toVersionInt)
		}
		logCurrentVersion(mctx, migrate)
		return nil
	},
}

func newMigrateWithCtx(url, migrationsPath string) (*migrate.Handle, context.Context, func()) {
	done := make(chan struct{})
	m, err := migrate.Open(url, migrationsPath, migrate.WithHooks(
		func(f file.File) error {
			log.Infof("Applying %s migration for version %d (%s)", f.Direction, f.Version, f.Name)
			return nil
		},
		func(f file.File) error {
			done <- struct{}{}
			return nil
		},
	))
	if err != nil {
		log.Fatalf("Initialization failed: %s", err)
	}
	ctx, cancel := newOsInterruptCtx(done)
	return m, ctx, cancel
}

// newOsInterruptCtx returns new context that will be cancelled
// on os.Interrupt signal.
func newOsInterruptCtx(done <-chan struct{}) (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		exit := false
		for loop := true; loop; {
			select {
			case <-done:
				if exit {
					loop = false
				}
			case <-c:
				if exit {
					os.Exit(5)
				}
				cancel()
				exit = true
				log.Info("Aborting after this migration... Hit again to force quit.")
			}
		}
		signal.Stop(c)
	}()
	return ctx, cancel
}

func logErr(err error) *log.Entry {
	return log.WithError(err)
}

func logCurrentVersion(ctx context.Context, migrate *migrate.Handle) {
	version, err := migrate.Version(ctx)
	if err != nil {
		logErr(err).Fatal("Unable to fetch version")
	}
	log.WithField("current-version", version).Info("done")
}
<|endoftext|>


<commit_before>package action

import (
	"bytes"

	"github.com/zyedidia/micro/v2/internal/buffer"
	"github.com/zyedidia/micro/v2/internal/config"
	"github.com/zyedidia/micro/v2/internal/display"
	"github.com/zyedidia/micro/v2/internal/info"
	"github.com/zyedidia/micro/v2/internal/util"
	"github.com/zyedidia/tcell/v2"
)

type InfoKeyAction func(*InfoPane)

var InfoBindings *KeyTree
var InfoBufBindings *KeyTree

func init() {
	InfoBindings = NewKeyTree()
	InfoBufBindings = NewKeyTree()
}

func InfoMapEvent(k Event, action string) {
	config.Bindings["command"][k.Name()] = action

	switch e := k.(type) {
	case KeyEvent, KeySequenceEvent, RawEvent:
		infoMapKey(e, action)
	case MouseEvent:
		infoMapMouse(e, action)
	}
}

func infoMapKey(k Event, action string) {
	if f, ok := InfoKeyActions[action]; ok {
		InfoBindings.RegisterKeyBinding(k, InfoKeyActionGeneral(f))
	} else if f, ok := BufKeyActions[action]; ok {
		InfoBufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(f))
	}
}

func infoMapMouse(k MouseEvent, action string) {
	// TODO: map mouse
	if f, ok := BufMouseActions[action]; ok {
		InfoBufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
	} else {
		infoMapKey(k, action)
	}
}

func InfoKeyActionGeneral(a InfoKeyAction) PaneKeyAction {
	return func(p Pane) bool {
		a(p.(*InfoPane))
		return true
	}
}

type InfoPane struct {
	*BufPane
	*info.InfoBuf
}

func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane {
	ip := new(InfoPane)
	ip.InfoBuf = ib
	ip.BufPane = NewBufPane(ib.Buffer, w, tab)
	ip.BufPane.bindings = InfoBufBindings

	return ip
}

func NewInfoBar() *InfoPane {
	ib := info.NewBuffer()
	w := display.NewInfoWindow(ib)
	return NewInfoPane(ib, w, nil)
}

func (h *InfoPane) Close() {
	h.InfoBuf.Close()
	h.BufPane.Close()
}

func (h *InfoPane) HandleEvent(event tcell.Event) {
	switch e := event.(type) {
	case *tcell.EventKey:
		ke := KeyEvent{
			code: e.Key(),
			mod:  metaToAlt(e.Modifiers()),
			r:    e.Rune(),
		}

		done := h.DoKeyEvent(ke)
		hasYN := h.HasYN
		if e.Key() == tcell.KeyRune && hasYN {
			if e.Rune() == 'y' && hasYN {
				h.YNResp = true
				h.DonePrompt(false)
			} else if e.Rune() == 'n' && hasYN {
				h.YNResp = false
				h.DonePrompt(false)
			}
		}
		if e.Key() == tcell.KeyRune && !done && !hasYN {
			h.DoRuneInsert(e.Rune())
			done = true
		}
		if done && h.HasPrompt && !hasYN {
			resp := string(h.LineBytes(0))
			hist := h.History[h.PromptType]
			hist[h.HistoryNum] = resp
			if h.EventCallback != nil {
				h.EventCallback(resp)
			}
		}
	default:
		h.BufPane.HandleEvent(event)
	}
}

// DoKeyEvent executes a key event for the command bar, doing any overridden actions
func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
	action, more := InfoBindings.NextEvent(e, nil)
	if action != nil && !more {
		action(h)
		InfoBindings.ResetEvents()

		return true
	} else if action == nil && !more {
		InfoBindings.ResetEvents()
		// return false //TODO:?
	}

	if !more {
		action, more = InfoBufBindings.NextEvent(e, nil)
		if action != nil && !more {
			done := action(h.BufPane)
			InfoBufBindings.ResetEvents()
			return done
		} else if action == nil && !more {
			InfoBufBindings.ResetEvents()
		}
	}

	return more
}

// HistoryUp cycles history up
func (h *InfoPane) HistoryUp() {
	h.UpHistory(h.History[h.PromptType])
}

// HistoryDown cycles history down
func (h *InfoPane) HistoryDown() {
	h.DownHistory(h.History[h.PromptType])
}

// Autocomplete begins autocompletion
func (h *InfoPane) CommandComplete() {
	b := h.Buf
	if b.HasSuggestions {
		b.CycleAutocomplete(true)
		return
	}

	c := b.GetActiveCursor()
	l := b.LineBytes(0)
	l = util.SliceStart(l, c.X)

	args := bytes.Split(l, []byte{' '})
	cmd := string(args[0])

	if h.PromptType == "Command" {
		if len(args) == 1 {
			b.Autocomplete(CommandComplete)
		} else if action, ok := commands[cmd]; ok {
			if action.completer != nil {
				b.Autocomplete(action.completer)
			}
		}
	} else {
		// by default use filename autocompletion
		b.Autocomplete(buffer.FileComplete)
	}
}

// ExecuteCommand completes the prompt
func (h *InfoPane) ExecuteCommand() {
	if !h.HasYN {
		h.DonePrompt(false)
	}
}

// AbortCommand cancels the prompt
func (h *InfoPane) AbortCommand() {
	h.DonePrompt(true)
}

// InfoKeyActions contains the list of all possible key actions the infopane could execute
var InfoKeyActions = map[string]InfoKeyAction{
	"HistoryUp":       (*InfoPane).HistoryUp,
	"HistoryDown":     (*InfoPane).HistoryDown,
	"CommandComplete": (*InfoPane).CommandComplete,
	"ExecuteCommand":  (*InfoPane).ExecuteCommand,
	"AbortCommand":    (*InfoPane).AbortCommand,
}
<commit_msg>Make yes/no prompts case-insensitive (#2182)<commit_after>package action

import (
	"bytes"

	"github.com/zyedidia/micro/v2/internal/buffer"
	"github.com/zyedidia/micro/v2/internal/config"
	"github.com/zyedidia/micro/v2/internal/display"
	"github.com/zyedidia/micro/v2/internal/info"
	"github.com/zyedidia/micro/v2/internal/util"
	"github.com/zyedidia/tcell/v2"
)

type InfoKeyAction func(*InfoPane)

var InfoBindings *KeyTree
var InfoBufBindings *KeyTree

func init() {
	InfoBindings = NewKeyTree()
	InfoBufBindings = NewKeyTree()
}

func InfoMapEvent(k Event, action string) {
	config.Bindings["command"][k.Name()] = action

	switch e := k.(type) {
	case KeyEvent, KeySequenceEvent, RawEvent:
		infoMapKey(e, action)
	case MouseEvent:
		infoMapMouse(e, action)
	}
}

func infoMapKey(k Event, action string) {
	if f, ok := InfoKeyActions[action]; ok {
		InfoBindings.RegisterKeyBinding(k, InfoKeyActionGeneral(f))
	} else if f, ok := BufKeyActions[action]; ok {
		InfoBufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(f))
	}
}

func infoMapMouse(k MouseEvent, action string) {
	// TODO: map mouse
	if f, ok := BufMouseActions[action]; ok {
		InfoBufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
	} else {
		infoMapKey(k, action)
	}
}

func InfoKeyActionGeneral(a InfoKeyAction) PaneKeyAction {
	return func(p Pane) bool {
		a(p.(*InfoPane))
		return true
	}
}

type InfoPane struct {
	*BufPane
	*info.InfoBuf
}

func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane {
	ip := new(InfoPane)
	ip.InfoBuf = ib
	ip.BufPane = NewBufPane(ib.Buffer, w, tab)
	ip.BufPane.bindings = InfoBufBindings

	return ip
}

func NewInfoBar() *InfoPane {
	ib := info.NewBuffer()
	w := display.NewInfoWindow(ib)
	return NewInfoPane(ib, w, nil)
}

func (h *InfoPane) Close() {
	h.InfoBuf.Close()
	h.BufPane.Close()
}

func (h *InfoPane) HandleEvent(event tcell.Event) {
	switch e := event.(type) {
	case *tcell.EventKey:
		ke := KeyEvent{
			code: e.Key(),
			mod:  metaToAlt(e.Modifiers()),
			r:    e.Rune(),
		}

		done := h.DoKeyEvent(ke)
		hasYN := h.HasYN
		if e.Key() == tcell.KeyRune && hasYN {
			if (e.Rune() == 'y' || e.Rune() == 'Y') && hasYN {
				h.YNResp = true
				h.DonePrompt(false)
			} else if (e.Rune() == 'n' || e.Rune() == 'N') && hasYN {
				h.YNResp = false
				h.DonePrompt(false)
			}
		}
		if e.Key() == tcell.KeyRune && !done && !hasYN {
			h.DoRuneInsert(e.Rune())
			done = true
		}
		if done && h.HasPrompt && !hasYN {
			resp := string(h.LineBytes(0))
			hist := h.History[h.PromptType]
			hist[h.HistoryNum] = resp
			if h.EventCallback != nil {
				h.EventCallback(resp)
			}
		}
	default:
		h.BufPane.HandleEvent(event)
	}
}

// DoKeyEvent executes a key event for the command bar, doing any overridden actions
func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
	action, more := InfoBindings.NextEvent(e, nil)
	if action != nil && !more {
		action(h)
		InfoBindings.ResetEvents()

		return true
	} else if action == nil && !more {
		InfoBindings.ResetEvents()
		// return false //TODO:?
	}

	if !more {
		action, more = InfoBufBindings.NextEvent(e, nil)
		if action != nil && !more {
			done := action(h.BufPane)
			InfoBufBindings.ResetEvents()
			return done
		} else if action == nil && !more {
			InfoBufBindings.ResetEvents()
		}
	}

	return more
}

// HistoryUp cycles history up
func (h *InfoPane) HistoryUp() {
	h.UpHistory(h.History[h.PromptType])
}

// HistoryDown cycles history down
func (h *InfoPane) HistoryDown() {
	h.DownHistory(h.History[h.PromptType])
}

// Autocomplete begins autocompletion
func (h *InfoPane) CommandComplete() {
	b := h.Buf
	if b.HasSuggestions {
		b.CycleAutocomplete(true)
		return
	}

	c := b.GetActiveCursor()
	l := b.LineBytes(0)
	l = util.SliceStart(l, c.X)

	args := bytes.Split(l, []byte{' '})
	cmd := string(args[0])

	if h.PromptType == "Command" {
		if len(args) == 1 {
			b.Autocomplete(CommandComplete)
		} else if action, ok := commands[cmd]; ok {
			if action.completer != nil {
				b.Autocomplete(action.completer)
			}
		}
	} else {
		// by default use filename autocompletion
		b.Autocomplete(buffer.FileComplete)
	}
}

// ExecuteCommand completes the prompt
func (h *InfoPane) ExecuteCommand() {
	if !h.HasYN {
		h.DonePrompt(false)
	}
}

// AbortCommand cancels the prompt
func (h *InfoPane) AbortCommand() {
	h.DonePrompt(true)
}

// InfoKeyActions contains the list of all possible key actions the infopane could execute
var InfoKeyActions = map[string]InfoKeyAction{
	"HistoryUp":       (*InfoPane).HistoryUp,
	"HistoryDown":     (*InfoPane).HistoryDown,
	"CommandComplete": (*InfoPane).CommandComplete,
	"ExecuteCommand":  (*InfoPane).ExecuteCommand,
	"AbortCommand":    (*InfoPane).AbortCommand,
}
<|endoftext|>


<commit_before>// Copyright 2016 The Minimal Configuration Manager 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 catpogs provides Go struct equivalents of the Cap'n Proto
// catalog.  These are primarily intended for constructing test inputs.
package catpogs

import (
	"github.com/zombiezen/mcm/catalog"
	"github.com/zombiezen/mcm/third_party/golang/capnproto"
	"github.com/zombiezen/mcm/third_party/golang/capnproto/pogs"
)

type Catalog struct {
	Resources []*Resource
}

func (c *Catalog) ToCapnp() (catalog.Catalog, error) {
	_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
	if err != nil {
		return catalog.Catalog{}, err
	}
	root, err := catalog.NewRootCatalog(seg)
	if err != nil {
		return catalog.Catalog{}, err
	}
	err = pogs.Insert(catalog.Catalog_TypeID, root.Struct, c)
	return root, err
}

type Resource struct {
	ID      uint64 `capnp:"id"`
	Comment string
	Deps    []uint64 `capnp:"dependencies"`

	Which catalog.Resource_Which
	File  *File
	Exec  *Exec
}

type File struct {
	Path string

	Which catalog.File_Which
	Plain struct {
		Content []byte
	}
	Directory struct{}
	Symlink   struct {
		Target string
	}
}

func PlainFile(path string, content []byte) *File {
	f := &File{
		Path:  path,
		Which: catalog.File_Which_plain,
	}
	f.Plain.Content = content
	return f
}

func SymlinkFile(oldname, newname string) *File {
	f := &File{
		Path:  newname,
		Which: catalog.File_Which_symlink,
	}
	f.Symlink.Target = oldname
	return f
}

type Exec struct {
	Command   *Command
	Condition ExecCondition
}

type ExecCondition struct {
	Which         catalog.Exec_condition_Which
	OnlyIf        *Command
	Unless        *Command
	FileAbsent    string
	IfDepsChanged []uint64
}

type Command struct {
	Which catalog.Exec_Command_Which
	Argv  []string
	Bash  string

	Env []EnvVar `capnp:"environment"`
	Dir string   `capnp:"workingDirectory"`
}

type EnvVar struct {
	Name, Value string
}
<commit_msg>internal/catpogs: add file mode structs<commit_after>// Copyright 2016 The Minimal Configuration Manager 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 catpogs provides Go struct equivalents of the Cap'n Proto
// catalog.  These are primarily intended for constructing test inputs.
package catpogs

import (
	"github.com/zombiezen/mcm/catalog"
	"github.com/zombiezen/mcm/third_party/golang/capnproto"
	"github.com/zombiezen/mcm/third_party/golang/capnproto/pogs"
)

type Catalog struct {
	Resources []*Resource
}

func (c *Catalog) ToCapnp() (catalog.Catalog, error) {
	_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
	if err != nil {
		return catalog.Catalog{}, err
	}
	root, err := catalog.NewRootCatalog(seg)
	if err != nil {
		return catalog.Catalog{}, err
	}
	err = pogs.Insert(catalog.Catalog_TypeID, root.Struct, c)
	return root, err
}

type Resource struct {
	ID      uint64 `capnp:"id"`
	Comment string
	Deps    []uint64 `capnp:"dependencies"`

	Which catalog.Resource_Which
	File  *File
	Exec  *Exec
}

type File struct {
	Path string

	Which catalog.File_Which
	Plain struct {
		Content []byte
		Mode    *FileMode
	}
	Directory struct {
		Mode *FileMode
	}
	Symlink struct {
		Target string
	}
}

func PlainFile(path string, content []byte) *File {
	f := &File{
		Path:  path,
		Which: catalog.File_Which_plain,
	}
	f.Plain.Content = content
	return f
}

func SymlinkFile(oldname, newname string) *File {
	f := &File{
		Path:  newname,
		Which: catalog.File_Which_symlink,
	}
	f.Symlink.Target = oldname
	return f
}

type FileMode struct {
	Bits  uint16
	User  *UserRef
	Group *GroupRef
}

// FileMode.Bits constants.
const (
	ModeUnset  = catalog.File_Mode_unset
	ModePerm   = catalog.File_Mode_permMask
	ModeSticky = catalog.File_Mode_sticky
	ModeSetuid = catalog.File_Mode_setuid
	ModeSetgid = catalog.File_Mode_setgid
)

type UserRef struct {
	Which catalog.UserRef_Which
	ID    int32 `capnp:"id"`
	Name  string
}

func UserIDRef(uid int) *UserRef {
	return &UserRef{Which: catalog.UserRef_Which_ID, ID: int32(uid)}
}

func UserNameRef(name string) *UserRef {
	return &UserRef{Which: catalog.UserRef_Which_name, Name: name}
}

type GroupRef struct {
	Which catalog.GroupRef_Which
	ID    int32 `capnp:"id"`
	Name  string
}

func GroupIDRef(gid int) *GroupRef {
	return &GroupRef{Which: catalog.GroupRef_Which_ID, ID: int32(gid)}
}

func GroupNameRef(name string) *GroupRef {
	return &GroupRef{Which: catalog.GroupRef_Which_name, Name: name}
}

type Exec struct {
	Command   *Command
	Condition ExecCondition
}

type ExecCondition struct {
	Which         catalog.Exec_condition_Which
	OnlyIf        *Command
	Unless        *Command
	FileAbsent    string
	IfDepsChanged []uint64
}

type Command struct {
	Which catalog.Exec_Command_Which
	Argv  []string
	Bash  string

	Env []EnvVar `capnp:"environment"`
	Dir string   `capnp:"workingDirectory"`
}

type EnvVar struct {
	Name, Value string
}
<|endoftext|>


<commit_before>package subv

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gocql/gocql"
	git "github.com/libgit2/git2go"
	"github.com/skeswa/gophr/common"
	"github.com/skeswa/gophr/common/config"
	"github.com/skeswa/gophr/common/github"
	"github.com/skeswa/gophr/common/models"
	"github.com/skeswa/gophr/common/verdeps"
)

var (
	folderName         string
	folderPath         string
	commitAuthor       = "gophrpm"
	commitAuthorEmail  = "gophr.pm@gmail.com"
	gitHubRemoteOrigin = "git@github.com:gophr-packages/%s.git"
)

// SubVersionPackageModel creates a github repo for the packageModel on github.com/gophr/gophr-packages
// versioned a the speicifed ref.
func SubVersionPackageModel(
	conf *config.Config,
	session *gocql.Session,
	credentials *config.Credentials,
	packageModel *models.PackageModel,
	ref string,
	fileDir string) error {
	// If the given ref is empty or refers to 'master' then we need to grab the current master SHA
	log.Printf("Preparing to sub-version %s/%s@%s \n", *packageModel.Author, *packageModel.Repo, ref)
	if len(ref) == 0 || ref == "master" {
		log.Println("Ref is empty or is 'master', fetching current master SHA")
		curretRef, err := common.FetchRefs(*packageModel.Author, *packageModel.Repo)
		if err != nil || len(curretRef.MasterRefHash) == 0 {
			return fmt.Errorf(
				"Error could not retrieve master ref of %s/%s, or packageModel does not exist \n",
				*packageModel.Author,
				*packageModel.Repo,
			)
		}
		ref = curretRef.MasterRefHash
	}

	// First check if this ref has already been versioned for this packageModel
	log.Printf("Checking if ref %s has been versioned before \n", ref)
	exists, err := github.CheckIfRefExists(*packageModel.Author, *packageModel.Repo, ref)
	if exists == true && err == nil {
		log.Println("That ref has already been versioned")
		// Since we wouldn't have gotten this far if this were already recorded,
		// make sure that we record it now.
		go recordPackageArchival(
			session,
			*packageModel.Author,
			*packageModel.Repo,
			ref)

		return nil
	}
	if err != nil {
		return fmt.Errorf("Error occurred in checking if ref exists. %s", err)
	}

	log.Printf("%s/%s@%s has not been versioned yet",
		github.GitHubGophrPackageOrgName,
		github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo),
		github.BuildGitHubBranch(ref),
	)

	// Set working folderName and folderPath for package
	folderName = github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo)
	folderPath = filepath.Join(fileDir, folderName)

	// Fetch ref archive
	refZipURL := fmt.Sprintf("https://github.com/%s/%s/archive/%s.zip", *packageModel.Author, *packageModel.Repo, ref)
	resp, err := http.Get(refZipURL)
	if err != nil || resp.StatusCode == 404 {
		// TODO:(Shikkic) Better error description here
		return fmt.Errorf("Error 404, could not find ref archive for %s. %v \n", refZipURL, err)
	}
	defer resp.Body.Close()

	// Write Archive to filepath
	zipFilePath := fmt.Sprintf("%s/%s.zip", fileDir, ref)
	out, err := os.Create(zipFilePath)
	if err != nil {
		if deletionErr := deleteAchriveFile(zipFilePath); deletionErr != nil {
			return fmt.Errorf("Error, could not write ref archive to file system or delete archive. %v, %v \n", err, deletionErr)
		}
		return fmt.Errorf("Error, could not write ref archive to file system. %v \n", err)
	}
	defer out.Close()
	io.Copy(out, resp.Body)

	// Unzip files
	if err = unzip(zipFilePath, fileDir); err != nil {
		if deletionErr := deleteAchriveFile(zipFilePath); deletionErr != nil {
			return fmt.Errorf("Error, could not unzip ref archive or delete it. %v, %v. \n", err, deletionErr)
		}
		return fmt.Errorf("Error, could not unzip ref archive. %v \n", err)
	}

	// Delete The Archive File
	if deletionErr := deleteAchriveFile(zipFilePath); deletionErr != nil {
		return deletionErr
	}

	// Move files around
	targetFolder := fmt.Sprintf("%s/%s-%s", fileDir, *packageModel.Repo, ref)
	newTargetFolder := fmt.Sprintf("%s/%s", fileDir, github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo))
	if err = os.Rename(targetFolder, newTargetFolder); err != nil {
		if deletionErr := deleteFolder(newTargetFolder); deletionErr != nil {
			return fmt.Errorf("Error, could not rename archive folder to target folder or delete it. %v %v. \n", err, deletionErr)
		}
		return fmt.Errorf("Error, could not rename archive folder to target folder. %v \n", err)
	}

	// Git init
	repo, err := git.InitRepository(newTargetFolder, false)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not initialize new repository or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not initialize new repository. %v", err)
	}

	// Instantiate New Github Request Service
	gitHubRequestService := github.NewRequestService(
		github.RequestServiceParams{
			ForIndexer: false,
			Conf:       conf,
			Session:    session,
		},
	)

	// Prepare to Create a new Github repo for packageModel if DNE
	err = gitHubRequestService.CreateNewGitHubRepo(*packageModel)
	// TODO:(Shikkic) figure out error handling here

	// Fetch the timestamp of the ref commit
	commitDate, err := gitHubRequestService.FetchCommitTimestamp(packageModel, ref)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error could not fetch commit timestamp or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error could not fetch commit timestamp %s \n", err)
	}

	// Version lock all of the Github dependencies in the packageModel
	if err = verdeps.VersionDeps(
		verdeps.VersionDepsArgs{
			SHA:           ref,
			Path:          folderPath,
			Date:          commitDate,
			Model:         packageModel,
			GithubService: gitHubRequestService,
		}); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error could not version deps properly or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error could not version deps properly. %v \n", err)
	}

	// Git add all
	index, err := repo.Index()
	if err = index.AddAll([]string{}, git.IndexAddDefault, nil); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not add files to git repo or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not add files to git repo. %v \n", err)
	}

	// Write tree
	treeID, err := index.WriteTreeTo(repo)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not write tree or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not write tree. %v \n", err)
	}

	// Write the index
	if err = index.Write(); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not write index or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not write index. %v \n", err)
	}
	// TODO(Shikkic): is this necessary here?
	tree, err := repo.LookupTree(treeID)

	// Create commit Signature
	sig := &git.Signature{
		Name:  commitAuthor,
		Email: commitAuthorEmail,
		When:  time.Now(),
	}

	// Create commit
	// TODO(Shikkic): is commitID necessary here?
	// We dont use it
	commitID, err := repo.CreateCommit(
		"HEAD",
		sig,
		sig,
		fmt.Sprintf("Gophr versioned repo of %s/%s@%s",
			*packageModel.Author,
			*packageModel.Repo,
			ref,
		),
		tree,
	)
	log.Println("Created commit", commitID)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not commit data or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not commit data. %v \n", err)
	}

	// Lookup Current Commit
	// TODO(Shikkic): dont think this is necessary
	head, err := repo.Head()
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not look up repo HEAD or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not look up repo HEAD. %v \n", err)
	}
	headCommit, err := repo.LookupCommit(head.Target())
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not get HEAD commit or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not get HEAD commit. %v \n", err)
	}

	// Creating branch
	log.Println("Building the github branch")
	branchName := github.BuildGitHubBranch(ref)
	branch, err := repo.CreateBranch(branchName, headCommit, false)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not create branch or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not create branch. %v \n", err)
	}

	log.Println("Setting the upstream")
	if err = branch.SetUpstream(branchName); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not set upstream branch or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not set upstream branch. %v \n", err)
	}

	_, err = repo.References.CreateSymbolic("HEAD", fmt.Sprintf("refs/heads/%s", branchName), true, "headOne")
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not create symbolic ref or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not create symbolic ref. %v \n", err)
	}

	// Check out Branch
	opts := &git.CheckoutOpts{
		Strategy: git.CheckoutSafe | git.CheckoutRecreateMissing,
	}
	if err = repo.CheckoutHead(opts); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not checkout branch or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not checkout branch. %v \n", err)
	}

	// Creating remote origin
	remote, err := repo.Remotes.Create(
		"origin",
		fmt.Sprintf(
			"https://%s/%s.git",
			os.Getenv("DEPOT_SVC_SERVICE_HOST"),
			github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo),
		),
	)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not create remote origin or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not create remote origin. %v \n", err)
	}

	// Define push options
	pushOptions := &git.PushOptions{
		RemoteCallbacks: git.RemoteCallbacks{
			// TODO(skeswa): optimize out this closure.
			CredentialsCallback: func(url string, username string, allowedTypes git.CredType) (git.ErrorCode, *git.Cred) {
				// TODO figure out how to get ssh working
				//ret, cred := git.NewCredSshKey("git", "/Users/shikkic/.ssh/id_rsa.pub", "/Users/shikkic/.ssh/id_rsa", "")
				ret, cred := git.NewCredUserpassPlaintext(
					credentials.GithubPush.User,
					credentials.GithubPush.Pass)

				return git.ErrorCode(ret), &cred
			},
			CertificateCheckCallback: certificateCheckCallback,
		},
	}

	log.Println("Doing the push")
	if err = remote.Push([]string{"refs/heads/" + branchName + ":refs/heads/" + branchName}, pushOptions); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error,could not push to remote or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not push to remote. %v \n", err)
	}

	// Delete work dir before returning
	log.Println("Deleting the folder")
	if deletionErr := deleteFolder(folderPath); deletionErr != nil {
		return fmt.Errorf("Error, could not delete repo folder and clean work dir. %v \n", deletionErr)
	}

	// Record that this package has been archived.
	go recordPackageArchival(
		session,
		*(packageModel.Author),
		*(packageModel.Repo),
		ref)

	return nil
}

func recordPackageArchival(
	session *gocql.Session,
	author string,
	repo string,
	ref string) {
	// Use the package archive model to record this in the database.
	if err := models.RecordPackageArchival(
		session,
		author,
		repo,
		ref,
	); err != nil {
		log.Println("Failed to record package archival:", err)
	}
}
<commit_msg>added internal http port<commit_after>package subv

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gocql/gocql"
	git "github.com/libgit2/git2go"
	"github.com/skeswa/gophr/common"
	"github.com/skeswa/gophr/common/config"
	"github.com/skeswa/gophr/common/github"
	"github.com/skeswa/gophr/common/models"
	"github.com/skeswa/gophr/common/verdeps"
)

var (
	folderName         string
	folderPath         string
	commitAuthor       = "gophrpm"
	commitAuthorEmail  = "gophr.pm@gmail.com"
	gitHubRemoteOrigin = "git@github.com:gophr-packages/%s.git"
)

// SubVersionPackageModel creates a github repo for the packageModel on github.com/gophr/gophr-packages
// versioned a the speicifed ref.
func SubVersionPackageModel(
	conf *config.Config,
	session *gocql.Session,
	credentials *config.Credentials,
	packageModel *models.PackageModel,
	ref string,
	fileDir string) error {
	// If the given ref is empty or refers to 'master' then we need to grab the current master SHA
	log.Printf("Preparing to sub-version %s/%s@%s \n", *packageModel.Author, *packageModel.Repo, ref)
	if len(ref) == 0 || ref == "master" {
		log.Println("Ref is empty or is 'master', fetching current master SHA")
		curretRef, err := common.FetchRefs(*packageModel.Author, *packageModel.Repo)
		if err != nil || len(curretRef.MasterRefHash) == 0 {
			return fmt.Errorf(
				"Error could not retrieve master ref of %s/%s, or packageModel does not exist \n",
				*packageModel.Author,
				*packageModel.Repo,
			)
		}
		ref = curretRef.MasterRefHash
	}

	// First check if this ref has already been versioned for this packageModel
	log.Printf("Checking if ref %s has been versioned before \n", ref)
	exists, err := github.CheckIfRefExists(*packageModel.Author, *packageModel.Repo, ref)
	if exists == true && err == nil {
		log.Println("That ref has already been versioned")
		// Since we wouldn't have gotten this far if this were already recorded,
		// make sure that we record it now.
		go recordPackageArchival(
			session,
			*packageModel.Author,
			*packageModel.Repo,
			ref)

		return nil
	}
	if err != nil {
		return fmt.Errorf("Error occurred in checking if ref exists. %s", err)
	}

	log.Printf("%s/%s@%s has not been versioned yet",
		github.GitHubGophrPackageOrgName,
		github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo),
		github.BuildGitHubBranch(ref),
	)

	// Set working folderName and folderPath for package
	folderName = github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo)
	folderPath = filepath.Join(fileDir, folderName)

	// Fetch ref archive
	refZipURL := fmt.Sprintf("https://github.com/%s/%s/archive/%s.zip", *packageModel.Author, *packageModel.Repo, ref)
	resp, err := http.Get(refZipURL)
	if err != nil || resp.StatusCode == 404 {
		// TODO:(Shikkic) Better error description here
		return fmt.Errorf("Error 404, could not find ref archive for %s. %v \n", refZipURL, err)
	}
	defer resp.Body.Close()

	// Write Archive to filepath
	zipFilePath := fmt.Sprintf("%s/%s.zip", fileDir, ref)
	out, err := os.Create(zipFilePath)
	if err != nil {
		if deletionErr := deleteAchriveFile(zipFilePath); deletionErr != nil {
			return fmt.Errorf("Error, could not write ref archive to file system or delete archive. %v, %v \n", err, deletionErr)
		}
		return fmt.Errorf("Error, could not write ref archive to file system. %v \n", err)
	}
	defer out.Close()
	io.Copy(out, resp.Body)

	// Unzip files
	if err = unzip(zipFilePath, fileDir); err != nil {
		if deletionErr := deleteAchriveFile(zipFilePath); deletionErr != nil {
			return fmt.Errorf("Error, could not unzip ref archive or delete it. %v, %v. \n", err, deletionErr)
		}
		return fmt.Errorf("Error, could not unzip ref archive. %v \n", err)
	}

	// Delete The Archive File
	if deletionErr := deleteAchriveFile(zipFilePath); deletionErr != nil {
		return deletionErr
	}

	// Move files around
	targetFolder := fmt.Sprintf("%s/%s-%s", fileDir, *packageModel.Repo, ref)
	newTargetFolder := fmt.Sprintf("%s/%s", fileDir, github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo))
	if err = os.Rename(targetFolder, newTargetFolder); err != nil {
		if deletionErr := deleteFolder(newTargetFolder); deletionErr != nil {
			return fmt.Errorf("Error, could not rename archive folder to target folder or delete it. %v %v. \n", err, deletionErr)
		}
		return fmt.Errorf("Error, could not rename archive folder to target folder. %v \n", err)
	}

	// Git init
	repo, err := git.InitRepository(newTargetFolder, false)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not initialize new repository or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not initialize new repository. %v", err)
	}

	// Instantiate New Github Request Service
	gitHubRequestService := github.NewRequestService(
		github.RequestServiceParams{
			ForIndexer: false,
			Conf:       conf,
			Session:    session,
		},
	)

	// Prepare to Create a new Github repo for packageModel if DNE
	err = gitHubRequestService.CreateNewGitHubRepo(*packageModel)
	// TODO:(Shikkic) figure out error handling here

	// Fetch the timestamp of the ref commit
	commitDate, err := gitHubRequestService.FetchCommitTimestamp(packageModel, ref)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error could not fetch commit timestamp or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error could not fetch commit timestamp %s \n", err)
	}

	// Version lock all of the Github dependencies in the packageModel
	if err = verdeps.VersionDeps(
		verdeps.VersionDepsArgs{
			SHA:           ref,
			Path:          folderPath,
			Date:          commitDate,
			Model:         packageModel,
			GithubService: gitHubRequestService,
		}); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error could not version deps properly or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error could not version deps properly. %v \n", err)
	}

	// Git add all
	index, err := repo.Index()
	if err = index.AddAll([]string{}, git.IndexAddDefault, nil); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not add files to git repo or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not add files to git repo. %v \n", err)
	}

	// Write tree
	treeID, err := index.WriteTreeTo(repo)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not write tree or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not write tree. %v \n", err)
	}

	// Write the index
	if err = index.Write(); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not write index or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not write index. %v \n", err)
	}
	// TODO(Shikkic): is this necessary here?
	tree, err := repo.LookupTree(treeID)

	// Create commit Signature
	sig := &git.Signature{
		Name:  commitAuthor,
		Email: commitAuthorEmail,
		When:  time.Now(),
	}

	// Create commit
	// TODO(Shikkic): is commitID necessary here?
	// We dont use it
	commitID, err := repo.CreateCommit(
		"HEAD",
		sig,
		sig,
		fmt.Sprintf("Gophr versioned repo of %s/%s@%s",
			*packageModel.Author,
			*packageModel.Repo,
			ref,
		),
		tree,
	)
	log.Println("Created commit", commitID)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not commit data or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not commit data. %v \n", err)
	}

	// Lookup Current Commit
	// TODO(Shikkic): dont think this is necessary
	head, err := repo.Head()
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not look up repo HEAD or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not look up repo HEAD. %v \n", err)
	}
	headCommit, err := repo.LookupCommit(head.Target())
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not get HEAD commit or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not get HEAD commit. %v \n", err)
	}

	// Creating branch
	log.Println("Building the github branch")
	branchName := github.BuildGitHubBranch(ref)
	branch, err := repo.CreateBranch(branchName, headCommit, false)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not create branch or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not create branch. %v \n", err)
	}

	log.Println("Setting the upstream")
	if err = branch.SetUpstream(branchName); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not set upstream branch or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not set upstream branch. %v \n", err)
	}

	_, err = repo.References.CreateSymbolic("HEAD", fmt.Sprintf("refs/heads/%s", branchName), true, "headOne")
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not create symbolic ref or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not create symbolic ref. %v \n", err)
	}

	// Check out Branch
	opts := &git.CheckoutOpts{
		Strategy: git.CheckoutSafe | git.CheckoutRecreateMissing,
	}
	if err = repo.CheckoutHead(opts); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not checkout branch or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not checkout branch. %v \n", err)
	}

	// Creating remote origin
	remote, err := repo.Remotes.Create(
		"origin",
		fmt.Sprintf(
			"https://%s:%s/%s.git",
			os.Getenv("DEPOT_SVC_SERVICE_HOST"),
			os.Getenv("DEPOT_SVC_SERVICE_PORT_INTERNAL_HTTP"),
			github.BuildNewGitHubRepoName(*packageModel.Author, *packageModel.Repo),
		),
	)
	if err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error, could not create remote origin or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not create remote origin. %v \n", err)
	}

	// Define push options
	pushOptions := &git.PushOptions{
		RemoteCallbacks: git.RemoteCallbacks{
			// TODO(skeswa): optimize out this closure.
			CredentialsCallback: func(url string, username string, allowedTypes git.CredType) (git.ErrorCode, *git.Cred) {
				// TODO figure out how to get ssh working
				//ret, cred := git.NewCredSshKey("git", "/Users/shikkic/.ssh/id_rsa.pub", "/Users/shikkic/.ssh/id_rsa", "")
				ret, cred := git.NewCredUserpassPlaintext(
					credentials.GithubPush.User,
					credentials.GithubPush.Pass)

				return git.ErrorCode(ret), &cred
			},
			CertificateCheckCallback: certificateCheckCallback,
		},
	}

	log.Println("Doing the push")
	if err = remote.Push([]string{"refs/heads/" + branchName + ":refs/heads/" + branchName}, pushOptions); err != nil {
		if deletionErr := deleteFolder(folderPath); deletionErr != nil {
			return fmt.Errorf("Error,could not push to remote or delete repo folder. %v, %v \n", deletionErr, err)
		}
		return fmt.Errorf("Error, could not push to remote. %v \n", err)
	}

	// Delete work dir before returning
	log.Println("Deleting the folder")
	if deletionErr := deleteFolder(folderPath); deletionErr != nil {
		return fmt.Errorf("Error, could not delete repo folder and clean work dir. %v \n", deletionErr)
	}

	// Record that this package has been archived.
	go recordPackageArchival(
		session,
		*(packageModel.Author),
		*(packageModel.Repo),
		ref)

	return nil
}

func recordPackageArchival(
	session *gocql.Session,
	author string,
	repo string,
	ref string) {
	// Use the package archive model to record this in the database.
	if err := models.RecordPackageArchival(
		session,
		author,
		repo,
		ref,
	); err != nil {
		log.Println("Failed to record package archival:", err)
	}
}
<|endoftext|>


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

package api

import (
	"errors"
	"fmt"
	"testing"

	"github.com/gorilla/securecookie"

	"github.com/strukturag/spreed-webrtc/go/buffercache"
	"github.com/strukturag/spreed-webrtc/go/channelling"
)

type fakeClient struct {
}

func (fake *fakeClient) Index() uint64 {
	return 0
}

func (fake *fakeClient) Send(_ buffercache.Buffer) {
}

type fakeRoomManager struct {
	joinedRoomID string
	leftRoomID   string
	roomUsers    []*channelling.DataSession
	joinedID     string
	joinError    error
	leftID       string
	broadcasts   []interface{}
	updatedRoom  *channelling.DataRoom
	updateError  error
}

func (fake *fakeRoomManager) RoomUsers(session *channelling.Session) []*channelling.DataSession {
	return fake.roomUsers
}

func (fake *fakeRoomManager) JoinRoom(id, roomName, roomType string, _ *channelling.DataRoomCredentials, session *channelling.Session, sessionAuthenticated bool, _ channelling.Sender) (*channelling.DataRoom, error) {
	fake.joinedID = id
	return &channelling.DataRoom{Name: roomName, Type: roomType}, fake.joinError
}

func (fake *fakeRoomManager) LeaveRoom(roomID, sessionID string) {
	fake.leftID = roomID
}

func (fake *fakeRoomManager) Broadcast(_, _ string, outgoing *channelling.DataOutgoing) {
	fake.broadcasts = append(fake.broadcasts, outgoing.Data)
}

func (fake *fakeRoomManager) UpdateRoom(_ *channelling.Session, _ *channelling.DataRoom) (*channelling.DataRoom, error) {
	return fake.updatedRoom, fake.updateError
}

func (fake *fakeRoomManager) MakeRoomID(roomName, roomType string) string {
	if roomType == "" {
		roomType = "Room"
	}
	return fmt.Sprintf("%s:%s", roomType, roomName)
}

func NewTestChannellingAPI() (channelling.ChannellingAPI, *fakeClient, *channelling.Session, *fakeRoomManager) {
	apiConsumer := channelling.NewChannellingAPIConsumer()
	client, roomManager := &fakeClient{}, &fakeRoomManager{}
	sessionNonces := securecookie.New(securecookie.GenerateRandomKey(64), nil)
	session := channelling.NewSession(nil, nil, roomManager, roomManager, nil, sessionNonces, "", "")
	busManager := channelling.NewBusManager(apiConsumer, "", false, "")
	api := New(nil, roomManager, nil, nil, nil, nil, nil, nil, busManager, nil)
	apiConsumer.SetChannellingAPI(api)
	return api, client, session, roomManager
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing.T) {
	roomID, roomName, ua := "Room:foobar", "foobar", "unit tests"
	api, client, session, roomManager := NewTestChannellingAPI()

	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName, Ua: ua}})

	if roomManager.joinedID != roomID {
		t.Errorf("Expected to have joined room %v, but got %v", roomID, roomManager.joinedID)
	}

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 1 {
		t.Fatalf("Expected 1 broadcast, but got %d", broadcastCount)
	}

	dataSession, ok := roomManager.broadcasts[0].(*channelling.DataSession)
	if !ok {
		t.Fatal("Expected a session data broadcast")
	}

	if dataSession.Ua != ua {
		t.Errorf("Expected to have broadcasted a user agent of %v, but was %v", ua, dataSession.Ua)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms(t *testing.T) {
	roomID, roomName := "Room:foobar", "foobar"
	api, client, session, roomManager := NewTestChannellingAPI()

	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: "baz"}})

	if roomManager.leftID != roomID {
		t.Errorf("Expected to have left room %v, but got %v", roomID, roomManager.leftID)
	}

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 3 {
		t.Fatalf("Expected 3 broadcasts, but got %d", broadcastCount)
	}

	dataSession, ok := roomManager.broadcasts[1].(*channelling.DataSession)
	if !ok {
		t.Fatal("Expected a session data broadcast")
	}

	if status := "soft"; dataSession.Status != status {
		t.Errorf("Expected to have broadcast a leave status of of %v, but was %v", status, dataSession.Status)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *testing.T) {
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.joinError = errors.New("Can't enter this room")

	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{}})

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 0 {
		t.Fatalf("Expected no broadcasts, but got %d", broadcastCount)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing.T) {
	roomID := "a-room"
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.roomUsers = []*channelling.DataSession{&channelling.DataSession{}}

	reply, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomID}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	welcome, ok := reply.(*channelling.DataWelcome)
	if !ok {
		t.Fatalf("Expected response %#v to be a Welcome", reply)
	}

	if welcome.Type != "Welcome" {
		t.Error("Message did not have the correct type")
	}

	if welcome.Room == nil || welcome.Room.Name != roomID {
		t.Errorf("Expected room with name %v, but got %#v", roomID, welcome.Room)
	}

	if len(welcome.Users) != len(roomManager.roomUsers) {
		t.Errorf("Expected to get users %#v, but was %#v", roomManager.roomUsers, welcome.Users)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.joinError = channelling.NewDataError("bad_join", "")

	_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{}})

	assertDataError(t, err, "bad_join")
}

func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpdatedRoom(t *testing.T) {
	roomName := "foo"
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.updatedRoom = &channelling.DataRoom{Name: "FOO"}

	_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	reply, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Room", Room: &channelling.DataRoom{Name: roomName}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	room, ok := reply.(*channelling.DataRoom)
	if !ok {
		t.Fatalf("Expected response message to be a Room")
	}

	if room.Name != roomManager.updatedRoom.Name {
		t.Errorf("Expected updated room with name %v, but got %#v", roomManager.updatedRoom, room)
	}

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 2 {
		t.Fatalf("Expected 1 broadcasts, but got %d", broadcastCount)
	}

	if _, ok := roomManager.broadcasts[1].(*channelling.DataRoom); !ok {
		t.Fatal("Expected a room data broadcast")
	}
}

func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) {
	roomName := "foo"
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.updateError = channelling.NewDataError("a_room_error", "")

	_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}
	_, err = api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Room", Room: &channelling.DataRoom{Name: roomName}})

	assertDataError(t, err, "a_room_error")
}

func assertDataError(t *testing.T, err error, code string) {
	if err == nil {
		t.Error("Expected an error, but none was returned")
		return
	}

	dataError, ok := err.(*channelling.DataError)
	if !ok {
		t.Errorf("Expected error %#v to be a *DataError", err)
		return
	}

	if code != dataError.Code {
		t.Errorf("Expected error code to be %v, but was %v", code, dataError.Code)
	}
}
<commit_msg>Implement new API for tests.<commit_after>/*
 * Spreed WebRTC.
 * Copyright (C) 2013-2015 struktur AG
 *
 * This file is part of Spreed WebRTC.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package api

import (
	"errors"
	"fmt"
	"testing"

	"github.com/gorilla/securecookie"

	"github.com/strukturag/spreed-webrtc/go/buffercache"
	"github.com/strukturag/spreed-webrtc/go/channelling"
)

type fakeClient struct {
}

func (fake *fakeClient) Index() uint64 {
	return 0
}

func (fake *fakeClient) Send(_ buffercache.Buffer) {
}

type fakeRoomManager struct {
	joinedRoomID string
	leftRoomID   string
	roomUsers    []*channelling.DataSession
	joinedID     string
	joinError    error
	leftID       string
	broadcasts   []interface{}
	updatedRoom  *channelling.DataRoom
	updateError  error
}

func (fake *fakeRoomManager) RoomUsers(session *channelling.Session) []*channelling.DataSession {
	return fake.roomUsers
}

func (fake *fakeRoomManager) JoinRoom(id, roomName, roomType string, _ *channelling.DataRoomCredentials, session *channelling.Session, sessionAuthenticated bool, _ channelling.Sender) (*channelling.DataRoom, error) {
	fake.joinedID = id
	return &channelling.DataRoom{Name: roomName, Type: roomType}, fake.joinError
}

func (fake *fakeRoomManager) LeaveRoom(roomID, sessionID string) {
	fake.leftID = roomID
}

func (fake *fakeRoomManager) Broadcast(_, _ string, outgoing *channelling.DataOutgoing) {
	fake.broadcasts = append(fake.broadcasts, outgoing.Data)
}

func (fake *fakeRoomManager) UpdateRoom(_ *channelling.Session, _ *channelling.DataRoom) (*channelling.DataRoom, error) {
	return fake.updatedRoom, fake.updateError
}

func (fake *fakeRoomManager) MakeRoomID(roomName, roomType string) string {
	if roomType == "" {
		roomType = "Room"
	}
	return fmt.Sprintf("%s:%s", roomType, roomName)
}

func (fake *fakeRoomManager) Get(roomID string) (room channelling.RoomWorker, ok bool) {
	return nil, false
}

func NewTestChannellingAPI() (channelling.ChannellingAPI, *fakeClient, *channelling.Session, *fakeRoomManager) {
	apiConsumer := channelling.NewChannellingAPIConsumer()
	client, roomManager := &fakeClient{}, &fakeRoomManager{}
	sessionNonces := securecookie.New(securecookie.GenerateRandomKey(64), nil)
	session := channelling.NewSession(nil, nil, roomManager, roomManager, nil, sessionNonces, "", "")
	busManager := channelling.NewBusManager(apiConsumer, "", false, "")
	api := New(nil, roomManager, nil, nil, nil, nil, nil, nil, busManager, nil)
	apiConsumer.SetChannellingAPI(api)
	return api, client, session, roomManager
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing.T) {
	roomID, roomName, ua := "Room:foobar", "foobar", "unit tests"
	api, client, session, roomManager := NewTestChannellingAPI()

	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName, Ua: ua}})

	if roomManager.joinedID != roomID {
		t.Errorf("Expected to have joined room %v, but got %v", roomID, roomManager.joinedID)
	}

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 1 {
		t.Fatalf("Expected 1 broadcast, but got %d", broadcastCount)
	}

	dataSession, ok := roomManager.broadcasts[0].(*channelling.DataSession)
	if !ok {
		t.Fatal("Expected a session data broadcast")
	}

	if dataSession.Ua != ua {
		t.Errorf("Expected to have broadcasted a user agent of %v, but was %v", ua, dataSession.Ua)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms(t *testing.T) {
	roomID, roomName := "Room:foobar", "foobar"
	api, client, session, roomManager := NewTestChannellingAPI()

	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: "baz"}})

	if roomManager.leftID != roomID {
		t.Errorf("Expected to have left room %v, but got %v", roomID, roomManager.leftID)
	}

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 3 {
		t.Fatalf("Expected 3 broadcasts, but got %d", broadcastCount)
	}

	dataSession, ok := roomManager.broadcasts[1].(*channelling.DataSession)
	if !ok {
		t.Fatal("Expected a session data broadcast")
	}

	if status := "soft"; dataSession.Status != status {
		t.Errorf("Expected to have broadcast a leave status of of %v, but was %v", status, dataSession.Status)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *testing.T) {
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.joinError = errors.New("Can't enter this room")

	api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{}})

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 0 {
		t.Fatalf("Expected no broadcasts, but got %d", broadcastCount)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing.T) {
	roomID := "a-room"
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.roomUsers = []*channelling.DataSession{&channelling.DataSession{}}

	reply, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomID}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	welcome, ok := reply.(*channelling.DataWelcome)
	if !ok {
		t.Fatalf("Expected response %#v to be a Welcome", reply)
	}

	if welcome.Type != "Welcome" {
		t.Error("Message did not have the correct type")
	}

	if welcome.Room == nil || welcome.Room.Name != roomID {
		t.Errorf("Expected room with name %v, but got %#v", roomID, welcome.Room)
	}

	if len(welcome.Users) != len(roomManager.roomUsers) {
		t.Errorf("Expected to get users %#v, but was %#v", roomManager.roomUsers, welcome.Users)
	}
}

func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.joinError = channelling.NewDataError("bad_join", "")

	_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{}})

	assertDataError(t, err, "bad_join")
}

func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpdatedRoom(t *testing.T) {
	roomName := "foo"
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.updatedRoom = &channelling.DataRoom{Name: "FOO"}

	_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	reply, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Room", Room: &channelling.DataRoom{Name: roomName}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}

	room, ok := reply.(*channelling.DataRoom)
	if !ok {
		t.Fatalf("Expected response message to be a Room")
	}

	if room.Name != roomManager.updatedRoom.Name {
		t.Errorf("Expected updated room with name %v, but got %#v", roomManager.updatedRoom, room)
	}

	if broadcastCount := len(roomManager.broadcasts); broadcastCount != 2 {
		t.Fatalf("Expected 1 broadcasts, but got %d", broadcastCount)
	}

	if _, ok := roomManager.broadcasts[1].(*channelling.DataRoom); !ok {
		t.Fatal("Expected a room data broadcast")
	}
}

func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) {
	roomName := "foo"
	api, client, session, roomManager := NewTestChannellingAPI()
	roomManager.updateError = channelling.NewDataError("a_room_error", "")

	_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
	if err != nil {
		t.Fatalf("Unexpected error %v", err)
	}
	_, err = api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Room", Room: &channelling.DataRoom{Name: roomName}})

	assertDataError(t, err, "a_room_error")
}

func assertDataError(t *testing.T, err error, code string) {
	if err == nil {
		t.Error("Expected an error, but none was returned")
		return
	}

	dataError, ok := err.(*channelling.DataError)
	if !ok {
		t.Errorf("Expected error %#v to be a *DataError", err)
		return
	}

	if code != dataError.Code {
		t.Errorf("Expected error code to be %v, but was %v", code, dataError.Code)
	}
}
<|endoftext|>


<commit_before>// Package common provides functions used by multiple commands.
package common

import (
	"flag"
	"fmt"
	"path"
	"strings"

	"github.com/google/go-genproto/googleapis/rpc/code"
	genomics "google.golang.org/api/genomics/v2alpha1"
)

// ExpandOperationName adds the project and operations prefixes to name (if
// they are not already present).
func ExpandOperationName(project, name string) string {
	if !strings.HasPrefix(name, "projects/") {
		if !strings.HasPrefix(name, "operations/") {
			name = path.Join("operations/", name)
		}
		name = path.Join("projects", project, name)
	}
	return name
}

// ParseFlags calls parse on flags and collects non-flag arguments until there
// are no non-flag arguments remaining.  This makes it possible to handle mixed
// flag and non-flag arguments.
func ParseFlags(flags *flag.FlagSet, arguments []string) []string {
	var nonFlags []string
	for {
		flags.Parse(arguments)
		if flags.NArg() == 0 {
			return nonFlags
		}
		nonFlags = append(nonFlags, flags.Arg(0))
		arguments = flags.Args()[1:]
	}
}

type MapFlagValue struct {
	Values map[string]string
}

func (m *MapFlagValue) String() string {
	return fmt.Sprintf("%v", m.Values)
}

func (m *MapFlagValue) Set(input string) error {
	if i := strings.Index(input, "="); i >= 0 {
		m.Values[input[0:i]] = input[i+1:]
	} else {
		m.Values[input] = ""
	}
	return nil
}

// PipelineExecutionError is an error returned by the Genomics API
// during a pipeline execution
type PipelineExecutionError genomics.Status

func (err PipelineExecutionError) Error() string {
	reason := code.Code_name[int32(err.Code)]
	if reason == "" {
		reason = fmt.Sprintf("unknown error code %d", err.Code)
	}
	return fmt.Sprintf("executing pipeline: %s (reason: %s)", err.Message, reason)
}

var fatalErrorCodes = map[code.Code]bool{
	code.Code_FAILED_PRECONDITION: true,
	code.Code_INVALID_ARGUMENT:    true,
	code.Code_ALREADY_EXISTS:      true,
	code.Code_NOT_FOUND:           true,
	code.Code_OUT_OF_RANGE:        true,
	code.Code_PERMISSION_DENIED:   true,
	code.Code_UNAUTHENTICATED:     true,
	code.Code_UNIMPLEMENTED:       true,
}

// IsRetriable indicates if the user should retry the operation after receiving
// the current error.
func (err PipelineExecutionError) IsRetriable() bool {
	return !fatalErrorCodes[code.Code(err.Code)]
}
<commit_msg>Change import path for rpc/code<commit_after>// Package common provides functions used by multiple commands.
package common

import (
	"flag"
	"fmt"
	"path"
	"strings"

	genomics "google.golang.org/api/genomics/v2alpha1"
	"google.golang.org/genproto/googleapis/rpc/code"
)

// ExpandOperationName adds the project and operations prefixes to name (if
// they are not already present).
func ExpandOperationName(project, name string) string {
	if !strings.HasPrefix(name, "projects/") {
		if !strings.HasPrefix(name, "operations/") {
			name = path.Join("operations/", name)
		}
		name = path.Join("projects", project, name)
	}
	return name
}

// ParseFlags calls parse on flags and collects non-flag arguments until there
// are no non-flag arguments remaining.  This makes it possible to handle mixed
// flag and non-flag arguments.
func ParseFlags(flags *flag.FlagSet, arguments []string) []string {
	var nonFlags []string
	for {
		flags.Parse(arguments)
		if flags.NArg() == 0 {
			return nonFlags
		}
		nonFlags = append(nonFlags, flags.Arg(0))
		arguments = flags.Args()[1:]
	}
}

type MapFlagValue struct {
	Values map[string]string
}

func (m *MapFlagValue) String() string {
	return fmt.Sprintf("%v", m.Values)
}

func (m *MapFlagValue) Set(input string) error {
	if i := strings.Index(input, "="); i >= 0 {
		m.Values[input[0:i]] = input[i+1:]
	} else {
		m.Values[input] = ""
	}
	return nil
}

// PipelineExecutionError is an error returned by the Genomics API
// during a pipeline execution
type PipelineExecutionError genomics.Status

func (err PipelineExecutionError) Error() string {
	reason := code.Code_name[int32(err.Code)]
	if reason == "" {
		reason = fmt.Sprintf("unknown error code %d", err.Code)
	}
	return fmt.Sprintf("executing pipeline: %s (reason: %s)", err.Message, reason)
}

var fatalErrorCodes = map[code.Code]bool{
	code.Code_FAILED_PRECONDITION: true,
	code.Code_INVALID_ARGUMENT:    true,
	code.Code_ALREADY_EXISTS:      true,
	code.Code_NOT_FOUND:           true,
	code.Code_OUT_OF_RANGE:        true,
	code.Code_PERMISSION_DENIED:   true,
	code.Code_UNAUTHENTICATED:     true,
	code.Code_UNIMPLEMENTED:       true,
}

// IsRetriable indicates if the user should retry the operation after receiving
// the current error.
func (err PipelineExecutionError) IsRetriable() bool {
	return !fatalErrorCodes[code.Code(err.Code)]
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package custom_metrics

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/flowcontrol"
	"k8s.io/metrics/pkg/apis/custom_metrics/v1alpha1"
)

type customMetricsClient struct {
	client rest.Interface
	mapper meta.RESTMapper
}

func New(client rest.Interface) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
	}
}

func NewForConfig(c *rest.Config) (CustomMetricsClient, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}
	configShallowCopy.APIPath = "/apis"
	if configShallowCopy.UserAgent == "" {
		configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
	}
	configShallowCopy.GroupVersion = &v1alpha1.SchemeGroupVersion
	configShallowCopy.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}

	client, err := rest.RESTClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return New(client), nil
}

func NewForConfigOrDie(c *rest.Config) CustomMetricsClient {
	client, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return client
}

// NewForMapper constucts the client with a RESTMapper, which allows more
// accurate translation from GroupVersionKind to GroupVersionResource.
func NewForMapper(client rest.Interface, mapper meta.RESTMapper) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
		mapper: mapper,
	}
}

func (c *customMetricsClient) RootScopedMetrics() MetricsInterface {
	return &rootScopedMetrics{c}
}

func (c *customMetricsClient) NamespacedMetrics(namespace string) MetricsInterface {
	return &namespacedMetrics{
		client:    c,
		namespace: namespace,
	}
}

func (c *customMetricsClient) qualResourceForKind(groupKind schema.GroupKind) (string, error) {
	if c.mapper == nil {
		// the version doesn't matter
		gvk := groupKind.WithVersion("")
		gvr, _ := meta.UnsafeGuessKindToResource(gvk)
		gr := gvr.GroupResource()
		return gr.String(), nil
	}

	// use the mapper if it's available
	mapping, err := c.mapper.RESTMapping(groupKind)
	if err != nil {
		return "", fmt.Errorf("unable to map kind %s to resource: %v", groupKind.String(), err)
	}

	groupResource := schema.GroupResource{
		Group:    mapping.GroupVersionKind.Group,
		Resource: mapping.Resource,
	}
	return groupResource.String(), nil
}

type rootScopedMetrics struct {
	client *customMetricsClient
}

func (m *rootScopedMetrics) getForNamespace(namespace string, metricName string) (*v1alpha1.MetricValue, error) {
	res := &v1alpha1.MetricValueList{}
	err := m.client.client.Get().
		Resource("metrics").
		Namespace(namespace).
		Name(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1alpha1.MetricValue, error) {
	// handle namespace separately
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return m.getForNamespace(name, metricName)
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1alpha1.MetricValueList, error) {
	// we can't wildcard-fetch for namespaces
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return nil, fmt.Errorf("cannot fetch metrics for multiple namespaces at once")
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(v1alpha1.AllObjects).
		SubResource(metricName).
		LabelsSelectorParam(selector).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}

type namespacedMetrics struct {
	client    *customMetricsClient
	namespace string
}

func (m *namespacedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1alpha1.MetricValue, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one")
	}

	return &res.Items[0], nil
}

func (m *namespacedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1alpha1.MetricValueList, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(v1alpha1.AllObjects).
		SubResource(metricName).
		LabelsSelectorParam(selector).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}
<commit_msg>missing argument for Errorf(%v): format reads<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package custom_metrics

import (
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/flowcontrol"
	"k8s.io/metrics/pkg/apis/custom_metrics/v1alpha1"
)

type customMetricsClient struct {
	client rest.Interface
	mapper meta.RESTMapper
}

func New(client rest.Interface) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
	}
}

func NewForConfig(c *rest.Config) (CustomMetricsClient, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}
	configShallowCopy.APIPath = "/apis"
	if configShallowCopy.UserAgent == "" {
		configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
	}
	configShallowCopy.GroupVersion = &v1alpha1.SchemeGroupVersion
	configShallowCopy.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}

	client, err := rest.RESTClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return New(client), nil
}

func NewForConfigOrDie(c *rest.Config) CustomMetricsClient {
	client, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return client
}

// NewForMapper constucts the client with a RESTMapper, which allows more
// accurate translation from GroupVersionKind to GroupVersionResource.
func NewForMapper(client rest.Interface, mapper meta.RESTMapper) CustomMetricsClient {
	return &customMetricsClient{
		client: client,
		mapper: mapper,
	}
}

func (c *customMetricsClient) RootScopedMetrics() MetricsInterface {
	return &rootScopedMetrics{c}
}

func (c *customMetricsClient) NamespacedMetrics(namespace string) MetricsInterface {
	return &namespacedMetrics{
		client:    c,
		namespace: namespace,
	}
}

func (c *customMetricsClient) qualResourceForKind(groupKind schema.GroupKind) (string, error) {
	if c.mapper == nil {
		// the version doesn't matter
		gvk := groupKind.WithVersion("")
		gvr, _ := meta.UnsafeGuessKindToResource(gvk)
		gr := gvr.GroupResource()
		return gr.String(), nil
	}

	// use the mapper if it's available
	mapping, err := c.mapper.RESTMapping(groupKind)
	if err != nil {
		return "", fmt.Errorf("unable to map kind %s to resource: %v", groupKind.String(), err)
	}

	groupResource := schema.GroupResource{
		Group:    mapping.GroupVersionKind.Group,
		Resource: mapping.Resource,
	}
	return groupResource.String(), nil
}

type rootScopedMetrics struct {
	client *customMetricsClient
}

func (m *rootScopedMetrics) getForNamespace(namespace string, metricName string) (*v1alpha1.MetricValue, error) {
	res := &v1alpha1.MetricValueList{}
	err := m.client.client.Get().
		Resource("metrics").
		Namespace(namespace).
		Name(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1alpha1.MetricValue, error) {
	// handle namespace separately
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return m.getForNamespace(name, metricName)
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *rootScopedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1alpha1.MetricValueList, error) {
	// we can't wildcard-fetch for namespaces
	if groupKind.Kind == "Namespace" && groupKind.Group == "" {
		return nil, fmt.Errorf("cannot fetch metrics for multiple namespaces at once")
	}

	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Name(v1alpha1.AllObjects).
		SubResource(metricName).
		LabelsSelectorParam(selector).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}

type namespacedMetrics struct {
	client    *customMetricsClient
	namespace string
}

func (m *namespacedMetrics) GetForObject(groupKind schema.GroupKind, name string, metricName string) (*v1alpha1.MetricValue, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(name).
		SubResource(metricName).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	if len(res.Items) != 1 {
		return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items))
	}

	return &res.Items[0], nil
}

func (m *namespacedMetrics) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string) (*v1alpha1.MetricValueList, error) {
	resourceName, err := m.client.qualResourceForKind(groupKind)
	if err != nil {
		return nil, err
	}

	res := &v1alpha1.MetricValueList{}
	err = m.client.client.Get().
		Resource(resourceName).
		Namespace(m.namespace).
		Name(v1alpha1.AllObjects).
		SubResource(metricName).
		LabelsSelectorParam(selector).
		Do().
		Into(res)

	if err != nil {
		return nil, err
	}

	return res, nil
}
<|endoftext|>


<commit_before>package util

import (
	"sync"
)

// A mostly for read map, which can thread-safely
// initialize the map entries.
type ConcurrentReadMap struct {
	sync.RWMutex

	items map[string]interface{}
}

func NewConcurrentReadMap() *ConcurrentReadMap {
	return &ConcurrentReadMap{items: make(map[string]interface{})}
}

func (m *ConcurrentReadMap) initMapEntry(key string, newEntry func() interface{}) (value interface{}) {
	m.Lock()
	defer m.Unlock()
	if value, ok := m.items[key]; ok {
		return value
	}
	value = newEntry()
	m.items[key] = value
	return value
}

func (m *ConcurrentReadMap) Get(key string, newEntry func() interface{}) interface{} {
	m.RLock()
	if value, ok := m.items[key]; ok {
		m.RUnlock()
		return value
	}
	m.RUnlock()
	return m.initMapEntry(key, newEntry)
}

func (m *ConcurrentReadMap) Find(key string) (interface{}, bool) {
	m.RLock()
	value, ok := m.items[key]
	m.RUnlock()
	return value, ok
}

func (m *ConcurrentReadMap) Items() (itemsCopy []interface{}) {
	m.RLock()
	for _, i := range m.items {
		itemsCopy = append(itemsCopy, i)
	}
	return itemsCopy
}

func (m *ConcurrentReadMap) Delete(key string) {
	m.Lock()
	delete(m.items, key)
	m.Unlock()
}
<commit_msg>add missing unlock()!!!!!<commit_after>package util

import (
	"sync"
)

// A mostly for read map, which can thread-safely
// initialize the map entries.
type ConcurrentReadMap struct {
	sync.RWMutex

	items map[string]interface{}
}

func NewConcurrentReadMap() *ConcurrentReadMap {
	return &ConcurrentReadMap{items: make(map[string]interface{})}
}

func (m *ConcurrentReadMap) initMapEntry(key string, newEntry func() interface{}) (value interface{}) {
	m.Lock()
	defer m.Unlock()
	if value, ok := m.items[key]; ok {
		return value
	}
	value = newEntry()
	m.items[key] = value
	return value
}

func (m *ConcurrentReadMap) Get(key string, newEntry func() interface{}) interface{} {
	m.RLock()
	if value, ok := m.items[key]; ok {
		m.RUnlock()
		return value
	}
	m.RUnlock()
	return m.initMapEntry(key, newEntry)
}

func (m *ConcurrentReadMap) Find(key string) (interface{}, bool) {
	m.RLock()
	value, ok := m.items[key]
	m.RUnlock()
	return value, ok
}

func (m *ConcurrentReadMap) Items() (itemsCopy []interface{}) {
	m.RLock()
	for _, i := range m.items {
		itemsCopy = append(itemsCopy, i)
	}
	m.RUnlock()
	return itemsCopy
}

func (m *ConcurrentReadMap) Delete(key string) {
	m.Lock()
	delete(m.items, key)
	m.Unlock()
}
<|endoftext|>