<commit_before>// Copyright 2018 Google Inc.
//
// 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 termdash implements a terminal based dashboard.

While running, the terminal dashboard performs the following:
  - Periodic redrawing of the canvas and all the widgets.
  - Event based redrawing of the widgets (i.e. on Keyboard or Mouse events).
  - Forwards input events to widgets and optional subscribers.
  - Handles terminal resize events.
*/
package termdash

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/mum4k/termdash/container"
	"github.com/mum4k/termdash/event"
	"github.com/mum4k/termdash/terminalapi"
)

// DefaultRedrawInterval is the default for the RedrawInterval option.
const DefaultRedrawInterval = 250 * time.Millisecond

// Option is used to provide options.
type Option interface {
	// set sets the provided option.
	set(td *termdash)
}

// option implements Option.
type option func(td *termdash)

// set implements Option.set.
func (o option) set(td *termdash) {
	o(td)
}

// RedrawInterval sets how often termdash redraws the container and all the widgets.
// Defaults to DefaultRedrawInterval. Use the controller to disable the
// periodic redraw.
func RedrawInterval(t time.Duration) Option {
	return option(func(td *termdash) {
		td.redrawInterval = t
	})
}

// ErrorHandler is used to provide a function that will be called with all
// errors that occur while the dashboard is running. If not provided, any
// errors panic the application.
func ErrorHandler(f func(error)) Option {
	return option(func(td *termdash) {
		td.errorHandler = f
	})
}

// KeyboardSubscriber registers a subscriber for Keyboard events. Each
// keyboard event is forwarded to the container and the registered subscriber.
// The provided function must be non-blocking, ideally just storing the value
// and returning as termdash blocks on each subscriber.
func KeyboardSubscriber(f func(*terminalapi.Keyboard)) Option {
	return option(func(td *termdash) {
		td.keyboardSubscriber = f
	})
}

// MouseSubscriber registers a subscriber for Mouse events. Each mouse event
// is forwarded to the container and the registered subscriber.
// The provided function must be non-blocking, ideally just storing the value
// and returning as termdash blocks on each subscriber.
func MouseSubscriber(f func(*terminalapi.Mouse)) Option {
	return option(func(td *termdash) {
		td.mouseSubscriber = f
	})
}

// Run runs the terminal dashboard with the provided container on the terminal.
// Redraws the terminal periodically. If you prefer a manual redraw, use the
// Controller instead.
// Blocks until the context expires.
func Run(ctx context.Context, t terminalapi.Terminal, c *container.Container, opts ...Option) error {
	td := newTermdash(t, c, opts...)

	err := td.start(ctx)
	// Only return the status (error or nil) after the termdash event
	// processing goroutine actually exits.
	td.stop()
	return err
}

// Controller controls a termdash instance.
// The controller instance is only valid until Close() is called.
// The controller is not thread-safe.
type Controller struct {
	td     *termdash
	cancel context.CancelFunc
}

// NewController initializes termdash and returns an instance of the controller.
// Periodic redrawing is disabled when using the controller, the RedrawInterval
// option is ignored.
// Close the controller when it isn't needed anymore.
func NewController(t terminalapi.Terminal, c *container.Container, opts ...Option) (*Controller, error) {
	ctx, cancel := context.WithCancel(context.Background())
	ctrl := &Controller{
		td:     newTermdash(t, c, opts...),
		cancel: cancel,
	}

	// stops when Close() is called.
	go ctrl.td.processEvents(ctx)
	if err := ctrl.td.periodicRedraw(); err != nil {
		return nil, err
	}
	return ctrl, nil
}

// Redraw triggers redraw of the terminal.
func (c *Controller) Redraw() error {
	if c.td == nil {
		return errors.New("the termdash instance is no longer running, this controller is now invalid")
	}

	c.td.mu.Lock()
	defer c.td.mu.Unlock()
	return c.td.redraw()
}

// Close closes the Controller and its termdash instance.
func (c *Controller) Close() {
	c.cancel()
	c.td.stop()
	c.td = nil
}

// termdash is a terminal based dashboard.
// This object is thread-safe.
type termdash struct {
	// term is the terminal the dashboard runs on.
	term terminalapi.Terminal

	// container maintains terminal splits and places widgets.
	container *container.Container

	// eds distributes input events to subscribers.
	eds *event.DistributionSystem

	// closeCh gets closed when Stop() is called, which tells the event
	// collecting goroutine to exit.
	closeCh chan struct{}
	// exitCh gets closed when the event collecting goroutine actually exits.
	exitCh chan struct{}

	// clearNeeded indicates if the terminal needs to be cleared next time
	// we're drawing it. Terminal needs to be cleared if its sized changed.
	clearNeeded bool

	// mu protects termdash.
	mu sync.Mutex

	// Options.
	redrawInterval     time.Duration
	errorHandler       func(error)
	mouseSubscriber    func(*terminalapi.Mouse)
	keyboardSubscriber func(*terminalapi.Keyboard)
}

// newTermdash creates a new termdash.
func newTermdash(t terminalapi.Terminal, c *container.Container, opts ...Option) *termdash {
	td := &termdash{
		term:           t,
		container:      c,
		eds:            event.NewDistributionSystem(),
		closeCh:        make(chan struct{}),
		exitCh:         make(chan struct{}),
		redrawInterval: DefaultRedrawInterval,
	}

	for _, opt := range opts {
		opt.set(td)
	}
	td.subscribers()
	c.Subscribe(td.eds)
	return td
}

// subscribers subscribes event receivers that live in this package to EDS.
func (td *termdash) subscribers() {
	// Handler for all errors that occur during input event processing.
	td.eds.Subscribe([]terminalapi.Event{terminalapi.NewError("")}, func(ev terminalapi.Event) {
		td.handleError(ev.(*terminalapi.Error).Error())
	})

	// Handles terminal resize events.
	td.eds.Subscribe([]terminalapi.Event{&terminalapi.Resize{}}, func(terminalapi.Event) {
		td.setClearNeeded()
	})

	// Redraws the screen on Keyboard and Mouse events.
	// These events very likely change the content of the widgets (e.g. zooming
	// a LineChart) so a redraw is needed to make that visible.
	td.eds.Subscribe([]terminalapi.Event{
		&terminalapi.Keyboard{},
		&terminalapi.Mouse{},
	}, func(terminalapi.Event) {
		td.evRedraw()
	}, event.MaxRepetitive(0)) // No repetitive events that cause terminal redraw.

	// Keyboard and Mouse subscribers specified via options.
	if td.keyboardSubscriber != nil {
		td.eds.Subscribe([]terminalapi.Event{&terminalapi.Keyboard{}}, func(ev terminalapi.Event) {
			td.keyboardSubscriber(ev.(*terminalapi.Keyboard))
		})
	}
	if td.mouseSubscriber != nil {
		td.eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
			td.mouseSubscriber(ev.(*terminalapi.Mouse))
		})
	}
}

// handleError forwards the error to the error handler if one was
// provided or panics.
func (td *termdash) handleError(err error) {
	if td.errorHandler != nil {
		td.errorHandler(err)
	} else {
		panic(err)
	}
}

// setClearNeeded flags that the terminal needs to be cleared next time we're
// drawing it.
func (td *termdash) setClearNeeded() {
	td.mu.Lock()
	defer td.mu.Unlock()
	td.clearNeeded = true
}

// redraw redraws the container and its widgets.
// The caller must hold td.mu.
func (td *termdash) redraw() error {
	if td.clearNeeded {
		if err := td.term.Clear(); err != nil {
			return fmt.Errorf("term.Clear => error: %v", err)
		}
		td.clearNeeded = false
	}

	if err := td.container.Draw(); err != nil {
		return fmt.Errorf("container.Draw => error: %v", err)
	}

	if err := td.term.Flush(); err != nil {
		return fmt.Errorf("term.Flush => error: %v", err)
	}
	return nil
}

// evRedraw redraws the container and its widgets.
func (td *termdash) evRedraw() error {
	td.mu.Lock()
	defer td.mu.Unlock()

	// Don't redraw immediately, give widgets that are performing enough time
	// to update.
	// We don't want to actually synchronize until all widgets update, we are
	// purposefully leaving slow widgets behind.
	time.Sleep(25 * time.Millisecond)
	return td.redraw()
}

// periodicRedraw is called once each RedrawInterval.
func (td *termdash) periodicRedraw() error {
	td.mu.Lock()
	defer td.mu.Unlock()
	return td.redraw()
}

// processEvents processes terminal input events.
// This is the body of the event collecting goroutine.
func (td *termdash) processEvents(ctx context.Context) {
	defer close(td.exitCh)

	for {
		ev := td.term.Event(ctx)
		if ev != nil {
			td.eds.Event(ev)
		}

		select {
		case <-ctx.Done():
			return
		default:
		}
	}
}

// start starts the terminal dashboard. Blocks until the context expires or
// until stop() is called.
func (td *termdash) start(ctx context.Context) error {
	redrawTimer := time.NewTicker(td.redrawInterval)
	defer redrawTimer.Stop()

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	// stops when stop() is called or the context expires.
	go td.processEvents(ctx)

	for {
		select {
		case <-redrawTimer.C:
			if err := td.periodicRedraw(); err != nil {
				return err
			}

		case <-ctx.Done():
			return nil

		case <-td.closeCh:
			return nil
		}
	}
}

// stop tells the event collecting goroutine to stop.
// Blocks until it exits.
func (td *termdash) stop() {
	close(td.closeCh)
	<-td.exitCh
}
<commit_msg>Updating comments for subscribers, since we now have the EDS.<commit_after>// Copyright 2018 Google Inc.
//
// 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 termdash implements a terminal based dashboard.

While running, the terminal dashboard performs the following:
  - Periodic redrawing of the canvas and all the widgets.
  - Event based redrawing of the widgets (i.e. on Keyboard or Mouse events).
  - Forwards input events to widgets and optional subscribers.
  - Handles terminal resize events.
*/
package termdash

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/mum4k/termdash/container"
	"github.com/mum4k/termdash/event"
	"github.com/mum4k/termdash/terminalapi"
)

// DefaultRedrawInterval is the default for the RedrawInterval option.
const DefaultRedrawInterval = 250 * time.Millisecond

// Option is used to provide options.
type Option interface {
	// set sets the provided option.
	set(td *termdash)
}

// option implements Option.
type option func(td *termdash)

// set implements Option.set.
func (o option) set(td *termdash) {
	o(td)
}

// RedrawInterval sets how often termdash redraws the container and all the widgets.
// Defaults to DefaultRedrawInterval. Use the controller to disable the
// periodic redraw.
func RedrawInterval(t time.Duration) Option {
	return option(func(td *termdash) {
		td.redrawInterval = t
	})
}

// ErrorHandler is used to provide a function that will be called with all
// errors that occur while the dashboard is running. If not provided, any
// errors panic the application.
func ErrorHandler(f func(error)) Option {
	return option(func(td *termdash) {
		td.errorHandler = f
	})
}

// KeyboardSubscriber registers a subscriber for Keyboard events. Each
// keyboard event is forwarded to the container and the registered subscriber.
// The provided function must be thread-safe.
func KeyboardSubscriber(f func(*terminalapi.Keyboard)) Option {
	return option(func(td *termdash) {
		td.keyboardSubscriber = f
	})
}

// MouseSubscriber registers a subscriber for Mouse events. Each mouse event
// is forwarded to the container and the registered subscriber.
// The provided function must be thread-safe.
func MouseSubscriber(f func(*terminalapi.Mouse)) Option {
	return option(func(td *termdash) {
		td.mouseSubscriber = f
	})
}

// Run runs the terminal dashboard with the provided container on the terminal.
// Redraws the terminal periodically. If you prefer a manual redraw, use the
// Controller instead.
// Blocks until the context expires.
func Run(ctx context.Context, t terminalapi.Terminal, c *container.Container, opts ...Option) error {
	td := newTermdash(t, c, opts...)

	err := td.start(ctx)
	// Only return the status (error or nil) after the termdash event
	// processing goroutine actually exits.
	td.stop()
	return err
}

// Controller controls a termdash instance.
// The controller instance is only valid until Close() is called.
// The controller is not thread-safe.
type Controller struct {
	td     *termdash
	cancel context.CancelFunc
}

// NewController initializes termdash and returns an instance of the controller.
// Periodic redrawing is disabled when using the controller, the RedrawInterval
// option is ignored.
// Close the controller when it isn't needed anymore.
func NewController(t terminalapi.Terminal, c *container.Container, opts ...Option) (*Controller, error) {
	ctx, cancel := context.WithCancel(context.Background())
	ctrl := &Controller{
		td:     newTermdash(t, c, opts...),
		cancel: cancel,
	}

	// stops when Close() is called.
	go ctrl.td.processEvents(ctx)
	if err := ctrl.td.periodicRedraw(); err != nil {
		return nil, err
	}
	return ctrl, nil
}

// Redraw triggers redraw of the terminal.
func (c *Controller) Redraw() error {
	if c.td == nil {
		return errors.New("the termdash instance is no longer running, this controller is now invalid")
	}

	c.td.mu.Lock()
	defer c.td.mu.Unlock()
	return c.td.redraw()
}

// Close closes the Controller and its termdash instance.
func (c *Controller) Close() {
	c.cancel()
	c.td.stop()
	c.td = nil
}

// termdash is a terminal based dashboard.
// This object is thread-safe.
type termdash struct {
	// term is the terminal the dashboard runs on.
	term terminalapi.Terminal

	// container maintains terminal splits and places widgets.
	container *container.Container

	// eds distributes input events to subscribers.
	eds *event.DistributionSystem

	// closeCh gets closed when Stop() is called, which tells the event
	// collecting goroutine to exit.
	closeCh chan struct{}
	// exitCh gets closed when the event collecting goroutine actually exits.
	exitCh chan struct{}

	// clearNeeded indicates if the terminal needs to be cleared next time
	// we're drawing it. Terminal needs to be cleared if its sized changed.
	clearNeeded bool

	// mu protects termdash.
	mu sync.Mutex

	// Options.
	redrawInterval     time.Duration
	errorHandler       func(error)
	mouseSubscriber    func(*terminalapi.Mouse)
	keyboardSubscriber func(*terminalapi.Keyboard)
}

// newTermdash creates a new termdash.
func newTermdash(t terminalapi.Terminal, c *container.Container, opts ...Option) *termdash {
	td := &termdash{
		term:           t,
		container:      c,
		eds:            event.NewDistributionSystem(),
		closeCh:        make(chan struct{}),
		exitCh:         make(chan struct{}),
		redrawInterval: DefaultRedrawInterval,
	}

	for _, opt := range opts {
		opt.set(td)
	}
	td.subscribers()
	c.Subscribe(td.eds)
	return td
}

// subscribers subscribes event receivers that live in this package to EDS.
func (td *termdash) subscribers() {
	// Handler for all errors that occur during input event processing.
	td.eds.Subscribe([]terminalapi.Event{terminalapi.NewError("")}, func(ev terminalapi.Event) {
		td.handleError(ev.(*terminalapi.Error).Error())
	})

	// Handles terminal resize events.
	td.eds.Subscribe([]terminalapi.Event{&terminalapi.Resize{}}, func(terminalapi.Event) {
		td.setClearNeeded()
	})

	// Redraws the screen on Keyboard and Mouse events.
	// These events very likely change the content of the widgets (e.g. zooming
	// a LineChart) so a redraw is needed to make that visible.
	td.eds.Subscribe([]terminalapi.Event{
		&terminalapi.Keyboard{},
		&terminalapi.Mouse{},
	}, func(terminalapi.Event) {
		td.evRedraw()
	}, event.MaxRepetitive(0)) // No repetitive events that cause terminal redraw.

	// Keyboard and Mouse subscribers specified via options.
	if td.keyboardSubscriber != nil {
		td.eds.Subscribe([]terminalapi.Event{&terminalapi.Keyboard{}}, func(ev terminalapi.Event) {
			td.keyboardSubscriber(ev.(*terminalapi.Keyboard))
		})
	}
	if td.mouseSubscriber != nil {
		td.eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
			td.mouseSubscriber(ev.(*terminalapi.Mouse))
		})
	}
}

// handleError forwards the error to the error handler if one was
// provided or panics.
func (td *termdash) handleError(err error) {
	if td.errorHandler != nil {
		td.errorHandler(err)
	} else {
		panic(err)
	}
}

// setClearNeeded flags that the terminal needs to be cleared next time we're
// drawing it.
func (td *termdash) setClearNeeded() {
	td.mu.Lock()
	defer td.mu.Unlock()
	td.clearNeeded = true
}

// redraw redraws the container and its widgets.
// The caller must hold td.mu.
func (td *termdash) redraw() error {
	if td.clearNeeded {
		if err := td.term.Clear(); err != nil {
			return fmt.Errorf("term.Clear => error: %v", err)
		}
		td.clearNeeded = false
	}

	if err := td.container.Draw(); err != nil {
		return fmt.Errorf("container.Draw => error: %v", err)
	}

	if err := td.term.Flush(); err != nil {
		return fmt.Errorf("term.Flush => error: %v", err)
	}
	return nil
}

// evRedraw redraws the container and its widgets.
func (td *termdash) evRedraw() error {
	td.mu.Lock()
	defer td.mu.Unlock()

	// Don't redraw immediately, give widgets that are performing enough time
	// to update.
	// We don't want to actually synchronize until all widgets update, we are
	// purposefully leaving slow widgets behind.
	time.Sleep(25 * time.Millisecond)
	return td.redraw()
}

// periodicRedraw is called once each RedrawInterval.
func (td *termdash) periodicRedraw() error {
	td.mu.Lock()
	defer td.mu.Unlock()
	return td.redraw()
}

// processEvents processes terminal input events.
// This is the body of the event collecting goroutine.
func (td *termdash) processEvents(ctx context.Context) {
	defer close(td.exitCh)

	for {
		ev := td.term.Event(ctx)
		if ev != nil {
			td.eds.Event(ev)
		}

		select {
		case <-ctx.Done():
			return
		default:
		}
	}
}

// start starts the terminal dashboard. Blocks until the context expires or
// until stop() is called.
func (td *termdash) start(ctx context.Context) error {
	redrawTimer := time.NewTicker(td.redrawInterval)
	defer redrawTimer.Stop()

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	// stops when stop() is called or the context expires.
	go td.processEvents(ctx)

	for {
		select {
		case <-redrawTimer.C:
			if err := td.periodicRedraw(); err != nil {
				return err
			}

		case <-ctx.Done():
			return nil

		case <-td.closeCh:
			return nil
		}
	}
}

// stop tells the event collecting goroutine to stop.
// Blocks until it exits.
func (td *termdash) stop() {
	close(td.closeCh)
	<-td.exitCh
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"strconv"
	"strings"
)

/*****************************

FooEnum - bit flags

******************************/

type FooEnum struct{ value_4v1mir4wruhy uint8 }

var Foo = struct {
	Bar FooEnum
	Baz FooEnum
	Buz FooEnum
}{
	Bar: FooEnum{value_4v1mir4wruhy: 1},
	Baz: FooEnum{value_4v1mir4wruhy: 2},
	Buz: FooEnum{value_4v1mir4wruhy: 4},
}

// Used to iterate in range loops
var foobar = [...]FooEnum{
	Foo.Bar, Foo.Baz, Foo.Buz,
}

// Get the integer value of the enum variant
func (self FooEnum) Value() uint8 {
	return self.value_4v1mir4wruhy
}

func (self FooEnum) IntValue() int {
	return int(self.value_4v1mir4wruhy)
}

// Get the string representation of the enum variant
func (self FooEnum) String() string {
	switch self.value_4v1mir4wruhy {
	case 1:
		return "bar"
	case 2:
		return "baz"
	case 4:
		return "Buz"
	}

	if self.value_4v1mir4wruhy == 0 {
		return ""
	}

	var vals = make([]string, 0, 3/2)

	for _, item := range foobar {
		if self.value_4v1mir4wruhy&item.value_4v1mir4wruhy == item.value_4v1mir4wruhy {
			vals = append(vals, item.String())
		}
	}
	return strings.Join(vals, ",")
}

// Get the string description of the enum variant
func (self FooEnum) Description() string {
	switch self.value_4v1mir4wruhy {
	case 1:
		return "bar"
	case 2:
		return "This is the description"
	case 4:
		return "Buz"
	}
	return ""
}

// XML marshaling methods to come
// Bitflag enum methods
func (self FooEnum) Add(v FooEnum) FooEnum {
	self.value_4v1mir4wruhy |= v.value_4v1mir4wruhy
	return self
}

func (self FooEnum) AddAll(v ...FooEnum) FooEnum {
	for _, item := range v {
		self.value_4v1mir4wruhy |= item.value_4v1mir4wruhy
	}
	return self
}

func (self FooEnum) Remove(v FooEnum) FooEnum {
	self.value_4v1mir4wruhy &^= v.value_4v1mir4wruhy
	return self
}

func (self FooEnum) RemoveAll(v ...FooEnum) FooEnum {
	for _, item := range v {
		self.value_4v1mir4wruhy &^= item.value_4v1mir4wruhy
	}
	return self
}

func (self FooEnum) Has(v FooEnum) bool {
	return self.value_4v1mir4wruhy&v.value_4v1mir4wruhy == v.value_4v1mir4wruhy
}

func (self FooEnum) HasAny(v ...FooEnum) bool {
	for _, item := range v {
		if self.value_4v1mir4wruhy&item.value_4v1mir4wruhy == item.value_4v1mir4wruhy {
			return true
		}
	}
	return false
}

func (self FooEnum) HasAll(v ...FooEnum) bool {
	for _, item := range v {
		if self.value_4v1mir4wruhy&item.value_4v1mir4wruhy != item.value_4v1mir4wruhy {
			return false
		}
	}
	return true
}

/*****************************

OofEnum

******************************/

type OofEnum struct{ value_n09eoz2ee3ka uint8 }

var Oof = struct {
	Bar OofEnum
	Baz OofEnum
	Buz OofEnum
}{
	Bar: OofEnum{value_n09eoz2ee3ka: 1},
	Baz: OofEnum{value_n09eoz2ee3ka: 123},
	Buz: OofEnum{value_n09eoz2ee3ka: 3},
}

// Used to iterate in range loops
var OofValues = [...]OofEnum{
	Oof.Bar, Oof.Baz, Oof.Buz,
}

// Get the integer value of the enum variant
func (self OofEnum) Value() uint8 {
	return self.value_n09eoz2ee3ka
}

func (self OofEnum) IntValue() int {
	return int(self.value_n09eoz2ee3ka)
}

// Get the string representation of the enum variant
func (self OofEnum) String() string {
	switch self.value_n09eoz2ee3ka {
	case 1:
		return "bar"
	case 123:
		return "Baz"
	case 3:
		return "Buz"
	}

	return ""
}

// Get the string description of the enum variant
func (self OofEnum) Description() string {
	switch self.value_n09eoz2ee3ka {
	case 1:
		return "bar"
	case 123:
		return "Baz"
	case 3:
		return "Some description"
	}
	return ""
}

// JSON marshaling methods
func (self OofEnum) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Itoa(self.IntValue())), nil
}

func (self *OofEnum) UnmarshalJSON(b []byte) error {
	var n, err = strconv.ParseUint(string(b), 10, 64)
	if err != nil {
		return err
	}
	self.value_n09eoz2ee3ka = uint8(n)
	return nil
}

// XML marshaling methods to come

/*****************************

AnimalEnum

******************************/

type AnimalEnum struct{ value_16myto1zex81q uint8 }

var Animal = struct {
	Dog   AnimalEnum
	Cat   AnimalEnum
	Horse AnimalEnum
}{
	Dog:   AnimalEnum{value_16myto1zex81q: 1},
	Cat:   AnimalEnum{value_16myto1zex81q: 2},
	Horse: AnimalEnum{value_16myto1zex81q: 3},
}

// Used to iterate in range loops
var AnimalValues = [...]AnimalEnum{
	Animal.Dog, Animal.Cat, Animal.Horse,
}

// Get the integer value of the enum variant
func (self AnimalEnum) Value() uint8 {
	return self.value_16myto1zex81q
}

func (self AnimalEnum) IntValue() int {
	return int(self.value_16myto1zex81q)
}

// Get the string representation of the enum variant
func (self AnimalEnum) String() string {
	switch self.value_16myto1zex81q {
	case 1:
		return "doggy"
	case 2:
		return "kitty"
	case 3:
		return "horsie"
	}

	return ""
}

// Get the string description of the enum variant
func (self AnimalEnum) Description() string {
	switch self.value_16myto1zex81q {
	case 1:
		return "Your best friend, and you know it."
	case 2:
		return "Your best friend, but doesn't always show it."
	case 3:
		return "Everyone loves horses."
	}
	return ""
}

// JSON marshaling methods
func (self AnimalEnum) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Quote(self.String())), nil
}

func (self *AnimalEnum) UnmarshalJSON(b []byte) error {
	var s, err = strconv.Unquote(string(b))
	if err != nil {
		return err
	}

	if len(s) == 0 {
		return nil
	}

	switch s {
	case "doggy":
		self.value_16myto1zex81q = 1
		return nil
	case "kitty":
		self.value_16myto1zex81q = 2
		return nil
	case "horsie":
		self.value_16myto1zex81q = 3
		return nil
	default:
		log.Printf("Unexpected value: %q while unmarshaling AnimalEnum\n", s)
	}

	return nil
}

// XML marshaling methods to come
<commit_msg>regenerated<commit_after>package main

import (
	"log"
	"strconv"
	"strings"
)

/*****************************

FooEnum - bit flags

******************************/

type FooEnum struct{ value_hb36h415ag9i uint8 }

var Foo = struct {
	Bar FooEnum
	Baz FooEnum
	Buz FooEnum
}{
	Bar: FooEnum{value_hb36h415ag9i: 1},
	Baz: FooEnum{value_hb36h415ag9i: 2},
	Buz: FooEnum{value_hb36h415ag9i: 4},
}

// Used to iterate in range loops
var foobar = [...]FooEnum{
	Foo.Bar, Foo.Baz, Foo.Buz,
}

// Get the integer value of the enum variant
func (Fe FooEnum) Value() uint8 {
	return Fe.value_hb36h415ag9i
}

func (Fe FooEnum) IntValue() int {
	return int(Fe.value_hb36h415ag9i)
}

// Get the string representation of the enum variant
func (Fe FooEnum) String() string {
	switch Fe.value_hb36h415ag9i {
	case 1:
		return "bar"
	case 2:
		return "baz"
	case 4:
		return "Buz"
	}

	if Fe.value_hb36h415ag9i == 0 {
		return ""
	}

	var vals = make([]string, 0, 3/2)

	for _, item := range foobar {
		if Fe.value_hb36h415ag9i&item.value_hb36h415ag9i == item.value_hb36h415ag9i {
			vals = append(vals, item.String())
		}
	}
	return strings.Join(vals, ",")
}

// Get the string description of the enum variant
func (Fe FooEnum) Description() string {
	switch Fe.value_hb36h415ag9i {
	case 1:
		return "bar"
	case 2:
		return "This is the description"
	case 4:
		return "Buz"
	}
	return ""
}

// JSON marshaling methods
func (Fe FooEnum) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Quote(Fe.String())), nil
}

func (Fe *FooEnum) UnmarshalJSON(b []byte) error {
	var s, err = strconv.Unquote(string(b))
	if err != nil {
		return err
	}

	if len(s) == 0 {
		return nil
	}

	switch s {
	case "bar":
		Fe.value_hb36h415ag9i = 1
		return nil
	case "baz":
		Fe.value_hb36h415ag9i = 2
		return nil
	case "Buz":
		Fe.value_hb36h415ag9i = 4
		return nil
	}

	var val = 0

	for _, part := range strings.Split(string(b), ",") {
		switch part {
		case "bar":
			val |= 1
		case "baz":
			val |= 2
		case "Buz":
			val |= 4
		default:
			log.Printf("Unexpected value: %q while unmarshaling FooEnum\n", part)
		}
	}

	Fe.value_hb36h415ag9i = uint8(val)
	return nil
}

// XML marshaling methods to come
// Bitflag enum methods
func (Fe FooEnum) Add(v FooEnum) FooEnum {
	Fe.value_hb36h415ag9i |= v.value_hb36h415ag9i
	return Fe
}

func (Fe FooEnum) AddAll(v ...FooEnum) FooEnum {
	for _, item := range v {
		Fe.value_hb36h415ag9i |= item.value_hb36h415ag9i
	}
	return Fe
}

func (Fe FooEnum) Remove(v FooEnum) FooEnum {
	Fe.value_hb36h415ag9i &^= v.value_hb36h415ag9i
	return Fe
}

func (Fe FooEnum) RemoveAll(v ...FooEnum) FooEnum {
	for _, item := range v {
		Fe.value_hb36h415ag9i &^= item.value_hb36h415ag9i
	}
	return Fe
}

func (Fe FooEnum) Has(v FooEnum) bool {
	return Fe.value_hb36h415ag9i&v.value_hb36h415ag9i == v.value_hb36h415ag9i
}

func (Fe FooEnum) HasAny(v ...FooEnum) bool {
	for _, item := range v {
		if Fe.value_hb36h415ag9i&item.value_hb36h415ag9i == item.value_hb36h415ag9i {
			return true
		}
	}
	return false
}

func (Fe FooEnum) HasAll(v ...FooEnum) bool {
	for _, item := range v {
		if Fe.value_hb36h415ag9i&item.value_hb36h415ag9i != item.value_hb36h415ag9i {
			return false
		}
	}
	return true
}

/*****************************

OofEnum

******************************/

type OofEnum struct{ value_148yiuu7sigrx uint8 }

var Oof = struct {
	Bar OofEnum
	Baz OofEnum
	Buz OofEnum
}{
	Bar: OofEnum{value_148yiuu7sigrx: 1},
	Baz: OofEnum{value_148yiuu7sigrx: 123},
	Buz: OofEnum{value_148yiuu7sigrx: 3},
}

// Used to iterate in range loops
var OofValues = [...]OofEnum{
	Oof.Bar, Oof.Baz, Oof.Buz,
}

// Get the integer value of the enum variant
func (Oe OofEnum) Value() uint8 {
	return Oe.value_148yiuu7sigrx
}

func (Oe OofEnum) IntValue() int {
	return int(Oe.value_148yiuu7sigrx)
}

// Get the string representation of the enum variant
func (Oe OofEnum) String() string {
	switch Oe.value_148yiuu7sigrx {
	case 1:
		return "bar"
	case 123:
		return "Baz"
	case 3:
		return "Buz"
	}

	return ""
}

// Get the string description of the enum variant
func (Oe OofEnum) Description() string {
	switch Oe.value_148yiuu7sigrx {
	case 1:
		return "bar"
	case 123:
		return "Baz"
	case 3:
		return "Some description"
	}
	return ""
}

// JSON marshaling methods
func (Oe OofEnum) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Itoa(Oe.IntValue())), nil
}

func (Oe *OofEnum) UnmarshalJSON(b []byte) error {
	var n, err = strconv.ParseUint(string(b), 10, 64)
	if err != nil {
		return err
	}
	Oe.value_148yiuu7sigrx = uint8(n)
	return nil
}

// XML marshaling methods to come

/*****************************

AnimalEnum

******************************/

type AnimalEnum struct{ value_18hsg8max2cj2 uint8 }

var Animal = struct {
	Dog   AnimalEnum
	Cat   AnimalEnum
	Horse AnimalEnum
}{
	Dog:   AnimalEnum{value_18hsg8max2cj2: 1},
	Cat:   AnimalEnum{value_18hsg8max2cj2: 2},
	Horse: AnimalEnum{value_18hsg8max2cj2: 3},
}

// Used to iterate in range loops
var AnimalValues = [...]AnimalEnum{
	Animal.Dog, Animal.Cat, Animal.Horse,
}

// Get the integer value of the enum variant
func (Ae AnimalEnum) Value() uint8 {
	return Ae.value_18hsg8max2cj2
}

func (Ae AnimalEnum) IntValue() int {
	return int(Ae.value_18hsg8max2cj2)
}

// Get the string representation of the enum variant
func (Ae AnimalEnum) String() string {
	switch Ae.value_18hsg8max2cj2 {
	case 1:
		return "doggy"
	case 2:
		return "kitty"
	case 3:
		return "horsie"
	}

	return ""
}

// Get the string description of the enum variant
func (Ae AnimalEnum) Description() string {
	switch Ae.value_18hsg8max2cj2 {
	case 1:
		return "Your best friend, and you know it."
	case 2:
		return "Your best friend, but doesn't always show it."
	case 3:
		return "Everyone loves horses."
	}
	return ""
}

// JSON marshaling methods
func (Ae AnimalEnum) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Quote(Ae.String())), nil
}

func (Ae *AnimalEnum) UnmarshalJSON(b []byte) error {
	var s, err = strconv.Unquote(string(b))
	if err != nil {
		return err
	}

	if len(s) == 0 {
		return nil
	}

	switch s {
	case "doggy":
		Ae.value_18hsg8max2cj2 = 1
		return nil
	case "kitty":
		Ae.value_18hsg8max2cj2 = 2
		return nil
	case "horsie":
		Ae.value_18hsg8max2cj2 = 3
		return nil
	default:
		log.Printf("Unexpected value: %q while unmarshaling AnimalEnum\n", s)
	}

	return nil
}

// XML marshaling methods to come
<|endoftext|>


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

// Garbage collection benchmark: parse Go packages repeatedly.

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

func isGoFile(dir *os.FileInfo) bool {
	return dir.IsRegular() &&
		!strings.HasPrefix(dir.Name, ".") && // ignore .files
		path.Ext(dir.Name) == ".go"
}

func isPkgFile(dir *os.FileInfo) bool {
	return isGoFile(dir) &&
		!strings.HasSuffix(dir.Name, "_test.go") // ignore test files
}

func pkgName(filename string) string {
	file, err := parser.ParseFile(filename, nil, nil, parser.PackageClauseOnly)
	if err != nil || file == nil {
		return ""
	}
	return file.Name.Name()
}

func parseDir(dirpath string) map[string]*ast.Package {
	// the package name is the directory name within its parent
	// (use dirname instead of path because dirname is clean; i.e. has no trailing '/')
	_, pkgname := path.Split(dirpath)

	// filter function to select the desired .go files
	filter := func(d *os.FileInfo) bool {
		if isPkgFile(d) {
			// Some directories contain main packages: Only accept
			// files that belong to the expected package so that
			// parser.ParsePackage doesn't return "multiple packages
			// found" errors.
			// Additionally, accept the special package name
			// fakePkgName if we are looking at cmd documentation.
			name := pkgName(dirpath + "/" + d.Name)
			return name == pkgname
		}
		return false
	}

	// get package AST
	pkgs, err := parser.ParseDir(dirpath, filter, parser.ParseComments)
	if err != nil {
		println("parse", dirpath, err.String())
		panic("fail")
	}
	return pkgs
}

func main() {
	st := &runtime.MemStats
	n := flag.Int("n", 4, "iterations")
	p := flag.Int("p", len(packages), "# of packages to keep in memory")
	flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
	flag.Parse()

	var t0 int64
	pkgroot := runtime.GOROOT() + "/src/pkg/"
	for pass := 0; pass < 2; pass++ {
		// Once the heap is grown to full size, reset counters.
		// This hides the start-up pauses, which are much smaller
		// than the normal pauses and would otherwise make
		// the average look much better than it actually is.
		st.NumGC = 0
		st.PauseNs = 0
		t0 = time.Nanoseconds()

		for i := 0; i < *n; i++ {
			parsed := make([]map[string]*ast.Package, *p)
			for j := range parsed {
				parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
			}
		}
		runtime.GC()
	}
	t1 := time.Nanoseconds()

	fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
		st.Alloc, st.TotalAlloc,
		st.Sys,
		st.Mallocs, float64(st.PauseNs)/1e9,
		st.NumGC, float64(st.PauseNs)/1e9/float64(st.NumGC))

	fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
	for _, s := range st.BySize {
		fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
	}

	// Standard gotest benchmark output, collected by build dashboard.
	fmt.Printf("garbage.BenchmarkParser %d %d ns/op\n", *n, (t1-t0)/int64(*n))
	fmt.Printf("garbage.BenchmarkParserPause %d %d ns/op\n", st.NumGC, int64(st.PauseNs)/int64(st.NumGC))
}

var packages = []string{
	"archive/tar",
	"asn1",
	"big",
	"bufio",
	"bytes",
	"cmath",
	"compress/flate",
	"compress/gzip",
	"compress/zlib",
	"container/heap",
	"container/list",
	"container/ring",
	"container/vector",
	"crypto/aes",
	"crypto/block",
	"crypto/blowfish",
	"crypto/hmac",
	"crypto/md4",
	"crypto/md5",
	"crypto/rand",
	"crypto/rc4",
	"crypto/rsa",
	"crypto/sha1",
	"crypto/sha256",
	"crypto/sha512",
	"crypto/subtle",
	"crypto/tls",
	"crypto/x509",
	"crypto/xtea",
	"debug/dwarf",
	"debug/macho",
	"debug/elf",
	"debug/gosym",
	"debug/proc",
	"ebnf",
	"encoding/ascii85",
	"encoding/base64",
	"encoding/binary",
	"encoding/git85",
	"encoding/hex",
	"encoding/pem",
	"exec",
	"exp/datafmt",
	"exp/draw",
	"exp/eval",
	"exp/iterable",
	"expvar",
	"flag",
	"fmt",
	"go/ast",
	"go/doc",
	"go/parser",
	"go/printer",
	"go/scanner",
	"go/token",
	"gob",
	"hash",
	"hash/adler32",
	"hash/crc32",
	"hash/crc64",
	"http",
	"image",
	"image/jpeg",
	"image/png",
	"io",
	"io/ioutil",
	"json",
	"log",
	"math",
	"mime",
	"net",
	"nntp",
	"os",
	"os/signal",
	"patch",
	"path",
	"rand",
	"reflect",
	"regexp",
	"rpc",
	"runtime",
	"scanner",
	"sort",
	"strconv",
	"strings",
	"sync",
	"syscall",
	"syslog",
	"tabwriter",
	"template",
	"testing",
	"testing/iotest",
	"testing/quick",
	"testing/script",
	"time",
	"unicode",
	"utf8",
	"utf16",
	"websocket",
	"xml",
}
<commit_msg>test/garbage/parser: sync with recent parser changes<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Garbage collection benchmark: parse Go packages repeatedly.

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

func isGoFile(dir *os.FileInfo) bool {
	return dir.IsRegular() &&
		!strings.HasPrefix(dir.Name, ".") && // ignore .files
		path.Ext(dir.Name) == ".go"
}

func isPkgFile(dir *os.FileInfo) bool {
	return isGoFile(dir) &&
		!strings.HasSuffix(dir.Name, "_test.go") // ignore test files
}

func pkgName(filename string) string {
	file, err := parser.ParseFile(filename, nil, parser.PackageClauseOnly)
	if err != nil || file == nil {
		return ""
	}
	return file.Name.Name
}

func parseDir(dirpath string) map[string]*ast.Package {
	// the package name is the directory name within its parent
	// (use dirname instead of path because dirname is clean; i.e. has no trailing '/')
	_, pkgname := path.Split(dirpath)

	// filter function to select the desired .go files
	filter := func(d *os.FileInfo) bool {
		if isPkgFile(d) {
			// Some directories contain main packages: Only accept
			// files that belong to the expected package so that
			// parser.ParsePackage doesn't return "multiple packages
			// found" errors.
			// Additionally, accept the special package name
			// fakePkgName if we are looking at cmd documentation.
			name := pkgName(dirpath + "/" + d.Name)
			return name == pkgname
		}
		return false
	}

	// get package AST
	pkgs, err := parser.ParseDir(dirpath, filter, parser.ParseComments)
	if err != nil {
		println("parse", dirpath, err.String())
		panic("fail")
	}
	return pkgs
}

func main() {
	st := &runtime.MemStats
	n := flag.Int("n", 4, "iterations")
	p := flag.Int("p", len(packages), "# of packages to keep in memory")
	flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
	flag.Parse()

	var t0 int64
	pkgroot := runtime.GOROOT() + "/src/pkg/"
	for pass := 0; pass < 2; pass++ {
		// Once the heap is grown to full size, reset counters.
		// This hides the start-up pauses, which are much smaller
		// than the normal pauses and would otherwise make
		// the average look much better than it actually is.
		st.NumGC = 0
		st.PauseNs = 0
		t0 = time.Nanoseconds()

		for i := 0; i < *n; i++ {
			parsed := make([]map[string]*ast.Package, *p)
			for j := range parsed {
				parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
			}
		}
		runtime.GC()
	}
	t1 := time.Nanoseconds()

	fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
		st.Alloc, st.TotalAlloc,
		st.Sys,
		st.Mallocs, float64(st.PauseNs)/1e9,
		st.NumGC, float64(st.PauseNs)/1e9/float64(st.NumGC))

	fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
	for _, s := range st.BySize {
		fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
	}

	// Standard gotest benchmark output, collected by build dashboard.
	fmt.Printf("garbage.BenchmarkParser %d %d ns/op\n", *n, (t1-t0)/int64(*n))
	fmt.Printf("garbage.BenchmarkParserPause %d %d ns/op\n", st.NumGC, int64(st.PauseNs)/int64(st.NumGC))
}

var packages = []string{
	"archive/tar",
	"asn1",
	"big",
	"bufio",
	"bytes",
	"cmath",
	"compress/flate",
	"compress/gzip",
	"compress/zlib",
	"container/heap",
	"container/list",
	"container/ring",
	"container/vector",
	"crypto/aes",
	"crypto/block",
	"crypto/blowfish",
	"crypto/hmac",
	"crypto/md4",
	"crypto/md5",
	"crypto/rand",
	"crypto/rc4",
	"crypto/rsa",
	"crypto/sha1",
	"crypto/sha256",
	"crypto/sha512",
	"crypto/subtle",
	"crypto/tls",
	"crypto/x509",
	"crypto/xtea",
	"debug/dwarf",
	"debug/macho",
	"debug/elf",
	"debug/gosym",
	"debug/proc",
	"ebnf",
	"encoding/ascii85",
	"encoding/base64",
	"encoding/binary",
	"encoding/git85",
	"encoding/hex",
	"encoding/pem",
	"exec",
	"exp/datafmt",
	"exp/draw",
	"exp/eval",
	"exp/iterable",
	"expvar",
	"flag",
	"fmt",
	"go/ast",
	"go/doc",
	"go/parser",
	"go/printer",
	"go/scanner",
	"go/token",
	"gob",
	"hash",
	"hash/adler32",
	"hash/crc32",
	"hash/crc64",
	"http",
	"image",
	"image/jpeg",
	"image/png",
	"io",
	"io/ioutil",
	"json",
	"log",
	"math",
	"mime",
	"net",
	"nntp",
	"os",
	"os/signal",
	"patch",
	"path",
	"rand",
	"reflect",
	"regexp",
	"rpc",
	"runtime",
	"scanner",
	"sort",
	"strconv",
	"strings",
	"sync",
	"syscall",
	"syslog",
	"tabwriter",
	"template",
	"testing",
	"testing/iotest",
	"testing/quick",
	"testing/script",
	"time",
	"unicode",
	"utf8",
	"utf16",
	"websocket",
	"xml",
}
<|endoftext|>


<commit_before>// +build windows

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 config

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows/registry"

	"github.com/aws/amazon-ecs-agent/agent/statemanager/dependencies"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/cihub/seelog"
)

const (
	// envSkipDomainJoinCheck is an environment setting that can be used to skip
	// domain join check validation. This is useful for integration and
	// functional-tests but should not be set for any non-test use-case.
	envSkipDomainJoinCheck = "ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION"

	ReleaseId2004SAC        = "2004"
	ReleaseId1909SAC        = "1909"
	WindowsServer2019       = "Windows Server 2019"
	WindowsServer2016       = "Windows Server 2016"
	WindowsServerDataCenter = "Windows Server Datacenter"
	InstallationTypeCore    = "Server Core"
	InstallationTypeFull    = "Server"
	UnknownOSType           = "unknown"
	osTypeFormat            = "WINDOWS_SERVER_%s_%s"
	ecsWinRegistryRootKey   = registry.LOCAL_MACHINE
	ecsWinRegistryRootPath  = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
)

var winRegistry dependencies.WindowsRegistry

func init() {
	winRegistry = dependencies.StdRegistry{}
}

func setWinRegistry(mockRegistry dependencies.WindowsRegistry) {
	winRegistry = mockRegistry
}

func getInstallationType(installationType string) (string, error) {
	if InstallationTypeFull == installationType {
		return "FULL", nil
	} else if InstallationTypeCore == installationType {
		return "CORE", nil
	} else {
		err := seelog.Errorf("Unsupported Installation type:%s", installationType)
		return "", err
	}
}

func getReleaseIdForSACReleases(productName string) (string, error) {
	if strings.HasPrefix(productName, WindowsServer2019) {
		return "2019", nil
	} else if strings.HasPrefix(productName, WindowsServer2016) {
		return "2016", nil
	}
	err := seelog.Errorf("Unsupported productName:%s for Windows SAC Release", productName)
	return "", err
}

func getReleaseIdForLTSCReleases(releaseId string) (string, error) {
	if ReleaseId2004SAC == releaseId {
		return ReleaseId2004SAC, nil
	} else if ReleaseId1909SAC == releaseId {
		return ReleaseId1909SAC, nil
	}
	err := seelog.Errorf("Unsupported ReleaseId:%s for Windows LTSC Release", releaseId)
	return "", err
}

func GetOperatingSystemFamily() string {
	key, err := winRegistry.OpenKey(ecsWinRegistryRootKey, ecsWinRegistryRootPath, registry.QUERY_VALUE)
	if err != nil {
		seelog.Errorf("Unable to open Windows registry key to determine Windows version: %v", err)
		return UnknownOSType
	}
	defer key.Close()

	productName, _, err := key.GetStringValue("ProductName")
	if err != nil {
		seelog.Errorf("Unable to read registry key, ProductName: %v", err)
		return UnknownOSType
	}
	installationType, _, err := key.GetStringValue("InstallationType")
	if err != nil {
		seelog.Errorf("Unable to read registry key, InstallationType: %v", err)
		return UnknownOSType
	}
	iType, err := getInstallationType(installationType)
	if err != nil {
		seelog.Errorf("Invalid Installation type found: %v", err)
		return UnknownOSType
	}

	releaseId := ""
	if strings.HasPrefix(productName, WindowsServerDataCenter) {
		releaseIdFromRegistry, _, err := key.GetStringValue("ReleaseId")
		if err != nil {
			seelog.Errorf("Unable to read registry key, ReleaseId: %v", err)
			return UnknownOSType
		}

		releaseId, err = getReleaseIdForLTSCReleases(releaseIdFromRegistry)
		if err != nil {
			seelog.Errorf("Failed to construct releaseId for Windows LTSC, Error: %v", err)
			return UnknownOSType
		}
	} else {
		releaseId, err = getReleaseIdForSACReleases(productName)
		if err != nil {
			seelog.Errorf("Failed to construct releaseId for Windows SAC, Error: %v", err)
			return UnknownOSType
		}
	}
	return fmt.Sprintf(osTypeFormat, releaseId, iType)
}

// parseGMSACapability is used to determine if gMSA support can be enabled
func parseGMSACapability() bool {
	envStatus := utils.ParseBool(os.Getenv("ECS_GMSA_SUPPORTED"), true)
	return checkDomainJoinWithEnvOverride(envStatus)
}

// parseFSxWindowsFileServerCapability is used to determine if fsxWindowsFileServer support can be enabled
func parseFSxWindowsFileServerCapability() bool {
	// fsxwindowsfileserver is not supported on Windows 2016 and non-domain-joined container instances
	status, err := isWindows2016()
	if err != nil || status == true {
		return false
	}

	envStatus := utils.ParseBool(os.Getenv("ECS_FSX_WINDOWS_FILE_SERVER_SUPPORTED"), true)
	return checkDomainJoinWithEnvOverride(envStatus)
}

func checkDomainJoinWithEnvOverride(envStatus bool) bool {
	if envStatus {
		// Check if domain join check override is present
		skipDomainJoinCheck := utils.ParseBool(os.Getenv(envSkipDomainJoinCheck), false)
		if skipDomainJoinCheck {
			seelog.Debug("Skipping domain join validation based on environment override")
			return true
		}
		// check if container instance is domain joined.
		// If container instance is not domain joined, explicitly disable feature configuration.
		status, err := isDomainJoined()
		if err == nil && status == true {
			return true
		}
		seelog.Errorf("Unable to determine valid domain join: %v", err)
	}

	return false
}

// isDomainJoined is used to validate if container instance is part of a valid active directory.
// Reference: https://golang.org/src/os/user/lookup_windows.go
func isDomainJoined() (bool, error) {
	var domain *uint16
	var status uint32

	err := syscall.NetGetJoinInformation(nil, &domain, &status)
	if err != nil {
		return false, err
	}

	err = syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain)))
	if err != nil {
		return false, err
	}

	return status == syscall.NetSetupDomainName, nil
}

// isWindows2016 is used to check if container instance is versioned Windows 2016
// Reference: https://godoc.org/golang.org/x/sys/windows/registry
var isWindows2016 = func() (bool, error) {
	key, err := winRegistry.OpenKey(ecsWinRegistryRootKey, ecsWinRegistryRootPath, registry.QUERY_VALUE)

	if err != nil {
		seelog.Errorf("Unable to open Windows registry key to determine Windows version: %v", err)
		return false, err
	}
	defer key.Close()

	version, _, err := key.GetStringValue("ProductName")
	if err != nil {
		seelog.Errorf("Unable to read current version from Windows registry: %v", err)
		return false, err
	}

	if strings.HasPrefix(version, "Windows Server 2016") {
		return true, nil
	}

	return false, nil
}
<commit_msg>Revert "Changes to advertise OSType while registering the container instance with cluster"<commit_after>// +build windows

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 config

import (
	"fmt"
	"golang.org/x/sys/windows/registry"
	"os"
	"strings"
	"syscall"
	"unsafe"

	"github.com/aws/amazon-ecs-agent/agent/statemanager/dependencies"
	"github.com/aws/amazon-ecs-agent/agent/utils"
	"github.com/cihub/seelog"
)

const (
	// envSkipDomainJoinCheck is an environment setting that can be used to skip
	// domain join check validation. This is useful for integration and
	// functional-tests but should not be set for any non-test use-case.
	envSkipDomainJoinCheck = "ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION"

	ReleaseId2004SAC        = "2004"
	ReleaseId1909SAC        = "1909"
	WindowsServer2019       = "Windows Server 2019"
	WindowsServer2016       = "Windows Server 2016"
	WindowsServerDataCenter = "Windows Server Datacenter"
	InstallationTypeCore    = "Server Core"
	InstallationTypeFull    = "Server"
	UnknownOSType           = "unknown"
	osTypeFormat            = "WINDOWS_SERVER_%s_%s"
	ecsWinRegistryRootKey   = registry.LOCAL_MACHINE
	ecsWinRegistryRootPath  = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
)

var winRegistry dependencies.WindowsRegistry

func init() {
	winRegistry = dependencies.StdRegistry{}
}

func setWinRegistry(mockRegistry dependencies.WindowsRegistry) {
	winRegistry = mockRegistry
}

func getInstallationType(installationType string) (string, error) {
	if InstallationTypeFull == installationType {
		return "FULL", nil
	} else if InstallationTypeCore == installationType {
		return "CORE", nil
	} else {
		err := seelog.Errorf("Unsupported Installation type:%s", installationType)
		return "", err
	}
}

func getReleaseIdForSACReleases(productName string) (string, error) {
	if strings.HasPrefix(productName, WindowsServer2019) {
		return "2019", nil
	} else if strings.HasPrefix(productName, WindowsServer2016) {
		return "2016", nil
	}
	err := seelog.Errorf("Unsupported productName:%s for Windows SAC Release", productName)
	return "", err
}

func getReleaseIdForLTSCReleases(releaseId string) (string, error) {
	if ReleaseId2004SAC == releaseId {
		return ReleaseId2004SAC, nil
	} else if ReleaseId1909SAC == releaseId {
		return ReleaseId1909SAC, nil
	}
	err := seelog.Errorf("Unsupported ReleaseId:%s for Windows LTSC Release", releaseId)
	return "", err
}

func GetOperatingSystemFamily() string {
	key, err := winRegistry.OpenKey(ecsWinRegistryRootKey, ecsWinRegistryRootPath, registry.QUERY_VALUE)
	if err != nil {
		seelog.Errorf("Unable to open Windows registry key to determine Windows version: %v", err)
		return UnknownOSType
	}
	defer key.Close()

	productName, _, err := key.GetStringValue("ProductName")
	if err != nil {
		seelog.Errorf("Unable to read registry key, ProductName: %v", err)
		return UnknownOSType
	}
	installationType, _, err := key.GetStringValue("InstallationType")
	if err != nil {
		seelog.Errorf("Unable to read registry key, InstallationType: %v", err)
		return UnknownOSType
	}
	iType, err := getInstallationType(installationType)
	if err != nil {
		seelog.Errorf("Invalid Installation type found: %v", err)
		return UnknownOSType
	}

	releaseId := ""
	if strings.HasPrefix(productName, WindowsServerDataCenter) {
		releaseIdFromRegistry, _, err := key.GetStringValue("ReleaseId")
		if err != nil {
			seelog.Errorf("Unable to read registry key, ReleaseId: %v", err)
			return UnknownOSType
		}

		releaseId, err = getReleaseIdForLTSCReleases(releaseIdFromRegistry)
		if err != nil {
			seelog.Errorf("Failed to construct releaseId for Windows LTSC, Error: %v", err)
			return UnknownOSType
		}
	} else {
		releaseId, err = getReleaseIdForSACReleases(productName)
		if err != nil {
			seelog.Errorf("Failed to construct releaseId for Windows SAC, Error: %v", err)
			return UnknownOSType
		}
	}
	return fmt.Sprintf(osTypeFormat, releaseId, iType)
}

// parseGMSACapability is used to determine if gMSA support can be enabled
func parseGMSACapability() bool {
	envStatus := utils.ParseBool(os.Getenv("ECS_GMSA_SUPPORTED"), true)
	return checkDomainJoinWithEnvOverride(envStatus)
}

// parseFSxWindowsFileServerCapability is used to determine if fsxWindowsFileServer support can be enabled
func parseFSxWindowsFileServerCapability() bool {
	// fsxwindowsfileserver is not supported on Windows 2016 and non-domain-joined container instances
	status, err := isWindows2016()
	if err != nil || status == true {
		return false
	}

	envStatus := utils.ParseBool(os.Getenv("ECS_FSX_WINDOWS_FILE_SERVER_SUPPORTED"), true)
	return checkDomainJoinWithEnvOverride(envStatus)
}

func checkDomainJoinWithEnvOverride(envStatus bool) bool {
	if envStatus {
		// Check if domain join check override is present
		skipDomainJoinCheck := utils.ParseBool(os.Getenv(envSkipDomainJoinCheck), false)
		if skipDomainJoinCheck {
			seelog.Debug("Skipping domain join validation based on environment override")
			return true
		}
		// check if container instance is domain joined.
		// If container instance is not domain joined, explicitly disable feature configuration.
		status, err := isDomainJoined()
		if err == nil && status == true {
			return true
		}
		seelog.Errorf("Unable to determine valid domain join: %v", err)
	}

	return false
}

// isDomainJoined is used to validate if container instance is part of a valid active directory.
// Reference: https://golang.org/src/os/user/lookup_windows.go
func isDomainJoined() (bool, error) {
	var domain *uint16
	var status uint32

	err := syscall.NetGetJoinInformation(nil, &domain, &status)
	if err != nil {
		return false, err
	}

	err = syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain)))
	if err != nil {
		return false, err
	}

	return status == syscall.NetSetupDomainName, nil
}

// isWindows2016 is used to check if container instance is versioned Windows 2016
// Reference: https://godoc.org/golang.org/x/sys/windows/registry
var isWindows2016 = func() (bool, error) {
	key, err := winRegistry.OpenKey(ecsWinRegistryRootKey, ecsWinRegistryRootPath, registry.QUERY_VALUE)

	if err != nil {
		seelog.Errorf("Unable to open Windows registry key to determine Windows version: %v", err)
		return false, err
	}
	defer key.Close()

	version, _, err := key.GetStringValue("ProductName")
	if err != nil {
		seelog.Errorf("Unable to read current version from Windows registry: %v", err)
		return false, err
	}

	if strings.HasPrefix(version, "Windows Server 2016") {
		return true, nil
	}

	return false, nil
}
<|endoftext|>


<commit_before>// +build !ent
package consul

import "github.com/hashicorp/consul/agent/consul/autopilot"

func (s *Server) initAutopilot(config *Config) {
	apDelegate := &AutopilotDelegate{s}
	s.autopilot = autopilot.NewAutopilot(s.logger, apDelegate, config.AutopilotInterval, config.ServerHealthInterval)
}
<commit_msg>Fix vet error<commit_after>// +build !ent

package consul

import "github.com/hashicorp/consul/agent/consul/autopilot"

func (s *Server) initAutopilot(config *Config) {
	apDelegate := &AutopilotDelegate{s}
	s.autopilot = autopilot.NewAutopilot(s.logger, apDelegate, config.AutopilotInterval, config.ServerHealthInterval)
}
<|endoftext|>


<commit_before>package goregexp

import (
	"regexp"
	"testing"
	. "github.com/smartystreets/goconvey/convey"
)

func TestProject(t *testing.T) {
	Convey("Test new regexp res container", t, func() {

		Convey("A Regexp res container can be build", func() {
			r := regexp.MustCompile("test")
			rx := NewReres("test", r)
			So(rx.String(), ShouldEqual, "Regexp res for 'test': (0-0; len 4) [[0 4]]")
		})

		Convey("A Regexp res container can display the string on which the regexp is applied", func() {
			r := regexp.MustCompile("test")
			rx := NewReres("test2", r)
			So(rx.Text(), ShouldEqual, "test2")
		})

		Convey("A Regexp res container knows if it has any match", func() {
			r := regexp.MustCompile("test")
			rx := NewReres("test3", r)
			So(rx.HasAnyMatch(), ShouldBeTrue)
			rx = NewReres("aaa", r)
			So(rx.HasAnyMatch(), ShouldBeFalse)
		})

		Convey("A Regexp res can reference groups", func() {
			r := regexp.MustCompile("(test)+")
			rx := NewReres("testatest", r)
			So(rx.HasNext(), ShouldBeTrue)
			rx.Next()
			So(rx.HasNext(), ShouldBeFalse)
			rx.ResetNext()
			So(rx.HasNext(), ShouldBeTrue)
		})

		Convey("A Regexp res can get prefix and suffix", func() {
			r := regexp.MustCompile("(test)+")
			rx := NewReres("aaatesttestbbb", r)
			So(rx.Prefix(), ShouldEqual, "aaa")
			So(rx.Suffix(), ShouldEqual, "bbb")
		})

		Convey("A Regexp res can get the first char of the current match", func() {
			r := regexp.MustCompile("(.est)")
			rx := NewReres("aaaTestcccUestbbb", r)
			So(rx.FirstChar(), ShouldEqual, 'T')
			So(rx.HasNext(), ShouldBeTrue)
			rx.Next()
			So(rx.FirstChar(), ShouldEqual, 'U')
		})

		Convey("A Regexp res can detect if the first char of the current match is \\", func() {
			r := regexp.MustCompile("(.est)")
			rx := NewReres("aaaTestccc\\estbbb", r)
			So(rx.IsEscaped(), ShouldBeFalse)
			rx.Next()
			So(rx.IsEscaped(), ShouldBeTrue)
		})

		Convey("A Regexp res can get full match", func() {
			r := regexp.MustCompile("(.est)")
			rx := NewReres("aaa1Testccc2Uestbbb3", r)
			So(rx.FullMatch(), ShouldEqual, "Test")
			rx.Next()
			So(rx.FullMatch(), ShouldEqual, "Uest")
		})

		Convey("A Regexp res can check if a group is matched", func() {
			r := regexp.MustCompile("(Test)(b?)(c)")
			rx := NewReres("Testc", r)
			// fmt.Println(rx.matches)
			So(rx.HasGroup(1), ShouldBeTrue)
			So(rx.HasGroup(2), ShouldBeFalse)
			So(rx.HasGroup(3), ShouldBeTrue)
		})

		Convey("A Regexp res get the group (string)", func() {
			r := regexp.MustCompile("(TesT)(a?)(d)")
			rx := NewReres("TesTd", r)
			// fmt.Println(rx.matches)
			So(rx.Group(1), ShouldEqual, "TesT")
			So(rx.Group(2), ShouldEqual, "")
			So(rx.Group(3), ShouldEqual, "d")
		})

	})
}
<commit_msg>Complete prefix-suffix test<commit_after>package goregexp

import (
	"regexp"
	"testing"
	. "github.com/smartystreets/goconvey/convey"
)

func TestProject(t *testing.T) {
	Convey("Test new regexp res container", t, func() {

		Convey("A Regexp res container can be build", func() {
			r := regexp.MustCompile("test")
			rx := NewReres("test", r)
			So(rx.String(), ShouldEqual, "Regexp res for 'test': (0-0; len 4) [[0 4]]")
		})

		Convey("A Regexp res container can display the string on which the regexp is applied", func() {
			r := regexp.MustCompile("test")
			rx := NewReres("test2", r)
			So(rx.Text(), ShouldEqual, "test2")
		})

		Convey("A Regexp res container knows if it has any match", func() {
			r := regexp.MustCompile("test")
			rx := NewReres("test3", r)
			So(rx.HasAnyMatch(), ShouldBeTrue)
			rx = NewReres("aaa", r)
			So(rx.HasAnyMatch(), ShouldBeFalse)
		})

		Convey("A Regexp res can reference groups", func() {
			r := regexp.MustCompile("(test)+")
			rx := NewReres("testatest", r)
			So(rx.HasNext(), ShouldBeTrue)
			rx.Next()
			So(rx.HasNext(), ShouldBeFalse)
			rx.ResetNext()
			So(rx.HasNext(), ShouldBeTrue)
		})

		Convey("A Regexp res can get prefix and suffix", func() {
			r := regexp.MustCompile("(test)+")
			rx := NewReres("aaatesttestbbb", r)
			So(rx.Prefix(), ShouldEqual, "aaa")
			So(rx.Suffix(), ShouldEqual, "bbb")
			So(rx.HasNext(), ShouldBeFalse)
		})

		Convey("A Regexp res can get the first char of the current match", func() {
			r := regexp.MustCompile("(.est)")
			rx := NewReres("aaaTestcccUestbbb", r)
			So(rx.FirstChar(), ShouldEqual, 'T')
			So(rx.HasNext(), ShouldBeTrue)
			rx.Next()
			So(rx.FirstChar(), ShouldEqual, 'U')
		})

		Convey("A Regexp res can detect if the first char of the current match is \\", func() {
			r := regexp.MustCompile("(.est)")
			rx := NewReres("aaaTestccc\\estbbb", r)
			So(rx.IsEscaped(), ShouldBeFalse)
			rx.Next()
			So(rx.IsEscaped(), ShouldBeTrue)
		})

		Convey("A Regexp res can get full match", func() {
			r := regexp.MustCompile("(.est)")
			rx := NewReres("aaa1Testccc2Uestbbb3", r)
			So(rx.FullMatch(), ShouldEqual, "Test")
			rx.Next()
			So(rx.FullMatch(), ShouldEqual, "Uest")
		})

		Convey("A Regexp res can check if a group is matched", func() {
			r := regexp.MustCompile("(Test)(b?)(c)")
			rx := NewReres("Testc", r)
			// fmt.Println(rx.matches)
			So(rx.HasGroup(1), ShouldBeTrue)
			So(rx.HasGroup(2), ShouldBeFalse)
			So(rx.HasGroup(3), ShouldBeTrue)
		})

		Convey("A Regexp res get the group (string)", func() {
			r := regexp.MustCompile("(TesT)(a?)(d)")
			rx := NewReres("TesTd", r)
			// fmt.Println(rx.matches)
			So(rx.Group(1), ShouldEqual, "TesT")
			So(rx.Group(2), ShouldEqual, "")
			So(rx.Group(3), ShouldEqual, "d")
		})

	})
}
<|endoftext|>


<commit_before>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

var gothicRand *rand.Rand

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
	gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	for i := 0; i < 64; i++ {
		nonceBytes[i] = byte(gothicRand.Int63() % 256)
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// get all the used providers
	providers := goth.GetProviders()

	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
	for _, provider := range providers {
		p := provider.Name()
		session, _ := Store.Get(req, p+SessionName)
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.Get(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<commit_msg>Add code comments<commit_after>/*
Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
package gothic

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/markbates/goth"
)

// SessionName is the key used to access the session store.
const SessionName = "_gothic_session"

// Store can/should be set by applications using gothic. The default is a cookie store.
var Store sessions.Store
var defaultStore sessions.Store

var keySet = false

var gothicRand *rand.Rand

func init() {
	key := []byte(os.Getenv("SESSION_SECRET"))
	keySet = len(key) != 0

	cookieStore := sessions.NewCookieStore([]byte(key))
	cookieStore.Options.HttpOnly = true
	Store = cookieStore
	defaultStore = Store
	gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
}

/*
BeginAuthHandler is a convenience handler for starting the authentication process.
It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

BeginAuthHandler will redirect the user to the appropriate authentication end-point
for the requested provider.

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
	url, err := GetAuthURL(res, req)
	if err != nil {
		res.WriteHeader(http.StatusBadRequest)
		fmt.Fprintln(res, err)
		return
	}

	http.Redirect(res, req, url, http.StatusTemporaryRedirect)
}

// SetState sets the state string associated with the given request.
// If no state string is associated with the request, one will be generated.
// This state is sent to the provider and can be retrieved during the
// callback.
var SetState = func(req *http.Request) string {
	state := req.URL.Query().Get("state")
	if len(state) > 0 {
		return state
	}

	// If a state query param is not passed in, generate a random
	// base64-encoded nonce so that the state on the auth URL
	// is unguessable, preventing CSRF attacks, as described in
	//
	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
	nonceBytes := make([]byte, 64)
	for i := 0; i < 64; i++ {
		nonceBytes[i] = byte(gothicRand.Int63() % 256)
	}
	return base64.URLEncoding.EncodeToString(nonceBytes)
}

// GetState gets the state returned by the provider during the callback.
// This is used to prevent CSRF attacks, see
// http://tools.ietf.org/html/rfc6749#section-10.12
var GetState = func(req *http.Request) string {
	return req.URL.Query().Get("state")
}

/*
GetAuthURL starts the authentication process with the requested provided.
It will return a URL that should be used to send users to.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

I would recommend using the BeginAuthHandler instead of doing all of these steps
yourself, but that's entirely up to you.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return "", err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return "", err
	}
	sess, err := provider.BeginAuth(SetState(req))
	if err != nil {
		return "", err
	}

	url, err := sess.GetAuthURL()
	if err != nil {
		return "", err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return "", err
	}

	return url, err
}

/*
CompleteUserAuth does what it says on the tin. It completes the authentication
process and fetches all of the basic information about the user from the provider.

It expects to be able to get the name of the provider from the query parameters
as either "provider" or ":provider".

See https://github.com/markbates/goth/examples/main.go to see this in action.
*/
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
	defer Logout(res, req)
	if !keySet && defaultStore == Store {
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
	}

	providerName, err := GetProviderName(req)
	if err != nil {
		return goth.User{}, err
	}

	provider, err := goth.GetProvider(providerName)
	if err != nil {
		return goth.User{}, err
	}

	value, err := GetFromSession(providerName, req)
	if err != nil {
		return goth.User{}, err
	}

	sess, err := provider.UnmarshalSession(value)
	if err != nil {
		return goth.User{}, err
	}

	err = validateState(req, sess)
	if err != nil {
		return goth.User{}, err
	}

	user, err := provider.FetchUser(sess)
	if err == nil {
		// user can be found with existing session data
		return user, err
	}

	// get new token and retry fetch
	_, err = sess.Authorize(provider, req.URL.Query())
	if err != nil {
		return goth.User{}, err
	}

	err = StoreInSession(providerName, sess.Marshal(), req, res)

	if err != nil {
		return goth.User{}, err
	}

	gu, err := provider.FetchUser(sess)
	return gu, err
}

// validateState ensures that the state token param from the original
// AuthURL matches the one included in the current (callback) request.
func validateState(req *http.Request, sess goth.Session) error {
	rawAuthURL, err := sess.GetAuthURL()
	if err != nil {
		return err
	}

	authURL, err := url.Parse(rawAuthURL)
	if err != nil {
		return err
	}

	originalState := authURL.Query().Get("state")
	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
		return errors.New("state token mismatch")
	}
	return nil
}

// Logout invalidates a user session.
func Logout(res http.ResponseWriter, req *http.Request) error {
	session, err := Store.Get(req, SessionName)
	if err != nil {
		return err
	}
	session.Options.MaxAge = -1
	session.Values = make(map[interface{}]interface{})
	err = session.Save(req, res)
	if err != nil {
		return errors.New("Could not delete user session ")
	}
	return nil
}

// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

func getProviderName(req *http.Request) (string, error) {

	// get all the used providers
	providers := goth.GetProviders()

	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
	for _, provider := range providers {
		p := provider.Name()
		session, _ := Store.Get(req, p+SessionName)
		value := session.Values[p]
		if _, ok := value.(string); ok {
			return p, nil
		}
	}

	// try to get it from the url param "provider"
	if p := req.URL.Query().Get("provider"); p != "" {
		return p, nil
	}

	// try to get it from the url param ":provider"
	if p := req.URL.Query().Get(":provider"); p != "" {
		return p, nil
	}

	// try to get it from the context's value of "provider" key
	if p, ok := mux.Vars(req)["provider"]; ok {
		return p, nil
	}

	//  try to get it from the go-context's value of "provider" key
	if p, ok := req.Context().Value("provider").(string); ok {
		return p, nil
	}

	// if not found then return an empty string with the corresponding error
	return "", errors.New("you must select a provider")
}

// StoreInSession stores a specified key/value pair in the session.
func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
	session, _ := Store.Get(req, SessionName)

	if err := updateSessionValue(session, key, value); err != nil {
		return err
	}

	return session.Save(req, res)
}

// GetFromSession retrieves a previously-stored value from the session.
// If no value has previously been stored at the specified key, it will return an error.
func GetFromSession(key string, req *http.Request) (string, error) {
	session, _ := Store.Get(req, SessionName)
	value, err := getSessionValue(session, key)
	if err != nil {
		return "", errors.New("could not find a matching session for this request")
	}

	return value, nil
}

func getSessionValue(session *sessions.Session, key string) (string, error) {
	value := session.Values[key]
	if value == nil {
		return "", fmt.Errorf("could not find a matching session for this request")
	}

	rdata := strings.NewReader(value.(string))
	r, err := gzip.NewReader(rdata)
	if err != nil {
		return "", err
	}
	s, err := ioutil.ReadAll(r)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func updateSessionValue(session *sessions.Session, key, value string) error {
	var b bytes.Buffer
	gz := gzip.NewWriter(&b)
	if _, err := gz.Write([]byte(value)); err != nil {
		return err
	}
	if err := gz.Flush(); err != nil {
		return err
	}
	if err := gz.Close(); err != nil {
		return err
	}

	session.Values[key] = b.String()
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2020-2022 Buf Technologies, Inc.
//
// 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 push

import (
	"archive/tar"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"strings"
	"sync"
	"testing"

	"github.com/bufbuild/buf/private/buf/cmd/buf/internal/internaltesting"
	"github.com/bufbuild/buf/private/bufpkg/buftransport"
	"github.com/bufbuild/buf/private/gen/proto/connect/buf/alpha/registry/v1alpha1/registryv1alpha1connect"
	registryv1alpha1 "github.com/bufbuild/buf/private/gen/proto/go/buf/alpha/registry/v1alpha1"
	"github.com/bufbuild/buf/private/pkg/app"
	"github.com/bufbuild/buf/private/pkg/app/appcmd"
	"github.com/bufbuild/buf/private/pkg/app/appflag"
	connect_go "github.com/bufbuild/connect-go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type mockPushService struct {
	t         *testing.T
	lock      sync.Mutex
	callbacks map[string]func(*registryv1alpha1.PushRequest)
	called    map[string]struct{}
	resp      map[string]*registryv1alpha1.PushResponse
}

var _ registryv1alpha1connect.PushServiceHandler = (*mockPushService)(nil)

func newMockPushService(t *testing.T) *mockPushService {
	return &mockPushService{
		t:         t,
		callbacks: make(map[string]func(*registryv1alpha1.PushRequest)),
		called:    make(map[string]struct{}),
		resp:      make(map[string]*registryv1alpha1.PushResponse),
	}
}

// Push pushes.
func (m *mockPushService) Push(
	_ context.Context,
	req *connect_go.Request[registryv1alpha1.PushRequest],
) (*connect_go.Response[registryv1alpha1.PushResponse], error) {
	m.lock.Lock()
	defer m.lock.Unlock()
	owner := req.Msg.Owner
	cb, ok := m.callbacks[owner]
	if ok {
		cb(req.Msg)
		m.called[owner] = struct{}{}
	}
	resp := m.resp[owner]
	if resp != nil {
		return &connect_go.Response[registryv1alpha1.PushResponse]{
			Msg: resp,
		}, nil
	}
	return nil, errors.New("bad request")
}

func (m *mockPushService) respond(owner string, resp *registryv1alpha1.PushResponse) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.resp[owner] = resp
}

func (m *mockPushService) callback(
	owner string,
	cb func(*registryv1alpha1.PushRequest),
) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.callbacks[owner] = cb
}

func (m *mockPushService) assertAllCallbacksCalled() {
	m.lock.Lock()
	defer m.lock.Unlock()
	for k := range m.callbacks {
		_, ok := m.called[k]
		assert.True(m.t, ok)
	}
}

func TestPush(t *testing.T) {
	server, mock := pushService(t)
	t.Cleanup(func() {
		server.Close()
		mock.assertAllCallbacksCalled()
	})

	testPush(
		t,
		"success",
		server.URL,
		mock,
		&registryv1alpha1.PushResponse{
			LocalModulePin: &registryv1alpha1.LocalModulePin{},
		},
		"",
	)
	testPush(
		t,
		"missing local module pin",
		server.URL,
		mock,
		&registryv1alpha1.PushResponse{
			LocalModulePin: nil,
		},
		"Missing local module pin",
	)
	testPush(
		t,
		"registry error",
		server.URL,
		mock,
		nil,
		"bad request",
	)
}

func pushService(t *testing.T) (*httptest.Server, *mockPushService) {
	mock := newMockPushService(t)
	mux := http.NewServeMux()
	mux.Handle(
		registryv1alpha1connect.NewPushServiceHandler(mock),
	)
	return httptest.NewServer(mux), mock
}

func testPush(
	t *testing.T,
	desc string,
	URL string,
	mock *mockPushService,
	resp *registryv1alpha1.PushResponse,
	errorMsg string,
) {
	t.Helper()
	owner := strings.ReplaceAll(desc, " ", "_")
	mock.respond(owner, resp)
	mock.callback(owner, func(req *registryv1alpha1.PushRequest) {
		assert.NotNil(t, req.Module, "missing module")
	})
	t.Run(desc, func(t *testing.T) {
		t.Parallel()
		appName := "test"
		err := appcmd.Run(
			context.Background(),
			app.NewContainer(
				ammendEnv(
					internaltesting.NewEnvFunc(t),
					func(env map[string]string) map[string]string {
						env["BUF_TOKEN"] = "invalid"
						buftransport.SetDisableAPISubdomain(env)
						injectConfig(t, appName, env)
						return env
					},
				)(appName),
				tarball(map[string][]byte{
					"buf.yaml":  bufYAML(t, URL, owner, "repo"),
					"foo.proto": nil,
					"bar.proto": nil,
				}),
				os.Stdout,
				os.Stderr,
				appName,        // push ran as appName, aka "test"
				"-#format=tar", // using stdin as a tar
			),
			NewCommand(
				appName,
				appflag.NewBuilder(appName),
			),
		)
		if errorMsg == "" {
			assert.NoError(t, err)
		} else {
			assert.ErrorContains(t, err, errorMsg)
		}
	})
}

// tarball returns a tar stream of files[path] = content.
func tarball(files map[string][]byte) io.ReadCloser {
	pr, pw := io.Pipe()
	go tarballWriter(files, pw)
	return pr
}

func tarballWriter(files map[string][]byte, out io.WriteCloser) {
	tw := tar.NewWriter(out)
	defer tw.Close()
	defer out.Close()
	for name, content := range files {
		hdr := &tar.Header{
			Name: name,
			Mode: 0666,
			Size: int64(len(content)),
		}
		if err := tw.WriteHeader(hdr); err != nil {
			return
		}
		if _, err := tw.Write(content); err != nil {
			return
		}
	}
}

// bufYAML returns a buf.yaml content for a given remote URL, org, and repo.
func bufYAML(t *testing.T, remoteURL, org, repo string) []byte {
	remote, err := url.Parse(remoteURL)
	require.NoError(t, err)
	conf := "version: v1\n"
	conf += fmt.Sprintf("name: %s/%s/%s\n", remote.Host, org, repo)
	return []byte(conf)
}

// injectConfig writes an app's config.yaml that disables TLS.
func injectConfig(t *testing.T, appName string, env map[string]string) {
	configDir := env[strings.ToUpper(appName)+"_CONFIG_DIR"]
	confFile, err := os.Create(path.Join(configDir, "config.yaml"))
	require.NoError(t, err)
	defer confFile.Close()
	_, err = io.WriteString(confFile, `
version: v1
tls:
  use: false
`)
	require.NoError(t, err)
}

// ammendEnv calls sideEffects after the env generator function constructs an
// environment. The environment from the last sideEffect call is returned.
func ammendEnv(
	envGen func(string) map[string]string,
	sideEffects ...func(map[string]string) map[string]string,
) func(string) map[string]string {
	return func(use string) map[string]string {
		env := envGen(use)
		for _, sideEffect := range sideEffects {
			env = sideEffect(env)
		}
		return env
	}
}
<commit_msg>push: tweak testing (#1571)<commit_after>// Copyright 2020-2022 Buf Technologies, Inc.
//
// 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 push

import (
	"archive/tar"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"path"
	"strings"
	"sync"
	"testing"

	"github.com/bufbuild/buf/private/buf/cmd/buf/internal/internaltesting"
	"github.com/bufbuild/buf/private/bufpkg/buftransport"
	"github.com/bufbuild/buf/private/gen/proto/connect/buf/alpha/registry/v1alpha1/registryv1alpha1connect"
	registryv1alpha1 "github.com/bufbuild/buf/private/gen/proto/go/buf/alpha/registry/v1alpha1"
	"github.com/bufbuild/buf/private/pkg/app"
	"github.com/bufbuild/buf/private/pkg/app/appcmd"
	"github.com/bufbuild/buf/private/pkg/app/appflag"
	connect_go "github.com/bufbuild/connect-go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type mockPushService struct {
	t         *testing.T
	lock      sync.Mutex
	callbacks map[string]func(*registryv1alpha1.PushRequest)
	called    map[string]struct{}
	resp      map[string]*registryv1alpha1.PushResponse
}

var _ registryv1alpha1connect.PushServiceHandler = (*mockPushService)(nil)

func newMockPushService(t *testing.T) *mockPushService {
	return &mockPushService{
		t:         t,
		callbacks: make(map[string]func(*registryv1alpha1.PushRequest)),
		called:    make(map[string]struct{}),
		resp:      make(map[string]*registryv1alpha1.PushResponse),
	}
}

// Push pushes.
func (m *mockPushService) Push(
	_ context.Context,
	req *connect_go.Request[registryv1alpha1.PushRequest],
) (*connect_go.Response[registryv1alpha1.PushResponse], error) {
	m.lock.Lock()
	defer m.lock.Unlock()
	owner := req.Msg.Owner
	cb, ok := m.callbacks[owner]
	if ok {
		cb(req.Msg)
		m.called[owner] = struct{}{}
	}
	resp := m.resp[owner]
	if resp == nil {
		return nil, errors.New("bad request")
	}
	return &connect_go.Response[registryv1alpha1.PushResponse]{
		Msg: resp,
	}, nil
}

func (m *mockPushService) respond(owner string, resp *registryv1alpha1.PushResponse) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.resp[owner] = resp
}

func (m *mockPushService) callback(
	owner string,
	cb func(*registryv1alpha1.PushRequest),
) {
	m.lock.Lock()
	defer m.lock.Unlock()
	m.callbacks[owner] = cb
}

func (m *mockPushService) assertAllCallbacksCalled() {
	m.lock.Lock()
	defer m.lock.Unlock()
	for k := range m.callbacks {
		_, ok := m.called[k]
		assert.True(m.t, ok)
	}
}

func TestPush(t *testing.T) {
	server, mock := pushService(t)
	t.Cleanup(func() {
		server.Close()
		mock.assertAllCallbacksCalled()
	})

	testPush(
		t,
		"success",
		server.URL,
		mock,
		&registryv1alpha1.PushResponse{
			LocalModulePin: &registryv1alpha1.LocalModulePin{},
		},
		"",
	)
	testPush(
		t,
		"missing local module pin",
		server.URL,
		mock,
		&registryv1alpha1.PushResponse{
			LocalModulePin: nil,
		},
		"Missing local module pin",
	)
	testPush(
		t,
		"registry error",
		server.URL,
		mock,
		nil,
		"bad request",
	)
}

func pushService(t *testing.T) (*httptest.Server, *mockPushService) {
	mock := newMockPushService(t)
	mux := http.NewServeMux()
	mux.Handle(
		registryv1alpha1connect.NewPushServiceHandler(mock),
	)
	return httptest.NewServer(mux), mock
}

func testPush(
	t *testing.T,
	desc string,
	URL string,
	mock *mockPushService,
	resp *registryv1alpha1.PushResponse,
	errorMsg string,
) {
	t.Helper()
	owner := strings.ReplaceAll(desc, " ", "_")
	mock.respond(owner, resp)
	mock.callback(owner, func(req *registryv1alpha1.PushRequest) {
		assert.NotNil(t, req.Module, "missing module")
	})
	t.Run(desc, func(t *testing.T) {
		t.Parallel()
		const appName = "test"
		err := appcmd.Run(
			context.Background(),
			app.NewContainer(
				ammendEnv(
					internaltesting.NewEnvFunc(t),
					func(env map[string]string) map[string]string {
						env["BUF_TOKEN"] = "invalid"
						buftransport.SetDisableAPISubdomain(env)
						injectConfig(t, appName, env)
						return env
					},
				)(appName),
				tarball(map[string][]byte{
					"buf.yaml":  bufYAML(t, URL, owner, "repo"),
					"foo.proto": nil,
					"bar.proto": nil,
				}),
				os.Stdout,
				os.Stderr,
				appName,        // push ran as appName, aka "test"
				"-#format=tar", // using stdin as a tar
			),
			NewCommand(
				appName,
				appflag.NewBuilder(appName),
			),
		)
		if errorMsg == "" {
			assert.NoError(t, err)
		} else {
			assert.ErrorContains(t, err, errorMsg)
		}
	})
}

// tarball returns a tar stream of files[path] = content.
func tarball(files map[string][]byte) io.ReadCloser {
	pr, pw := io.Pipe()
	go tarballWriter(files, pw)
	return pr
}

func tarballWriter(files map[string][]byte, out io.WriteCloser) {
	tw := tar.NewWriter(out)
	defer tw.Close()
	defer out.Close()
	for name, content := range files {
		hdr := &tar.Header{
			Name: name,
			Mode: 0666,
			Size: int64(len(content)),
		}
		if err := tw.WriteHeader(hdr); err != nil {
			return
		}
		if _, err := tw.Write(content); err != nil {
			return
		}
	}
}

// bufYAML returns a buf.yaml content for a given remote URL, org, and repo.
func bufYAML(t *testing.T, remoteURL, org, repo string) []byte {
	remote, err := url.Parse(remoteURL)
	require.NoError(t, err)
	conf := "version: v1\n"
	conf += fmt.Sprintf("name: %s/%s/%s\n", remote.Host, org, repo)
	return []byte(conf)
}

// injectConfig writes an app's config.yaml that disables TLS.
func injectConfig(t *testing.T, appName string, env map[string]string) {
	configDir := env[strings.ToUpper(appName)+"_CONFIG_DIR"]
	confFile, err := os.Create(path.Join(configDir, "config.yaml"))
	require.NoError(t, err)
	defer confFile.Close()
	_, err = io.WriteString(confFile, `
version: v1
tls:
  use: false
`)
	require.NoError(t, err)
}

// ammendEnv calls sideEffects after the env generator function constructs an
// environment. The environment from the last sideEffect call is returned.
func ammendEnv(
	envGen func(string) map[string]string,
	sideEffects ...func(map[string]string) map[string]string,
) func(string) map[string]string {
	return func(use string) map[string]string {
		env := envGen(use)
		for _, sideEffect := range sideEffects {
			env = sideEffect(env)
		}
		return env
	}
}
<|endoftext|>


<commit_before>package reactor

import (
	"fmt"
	"github.com/yanc0/greedee/events"
	"github.com/yanc0/greedee/plugins"
	"log"
	"time"
)

type Reactor struct {
	EventPlugin plugins.EventPlugin
}

func (r *Reactor) Launch() {
	c := time.Tick(time.Second * 5)
	for range c {
		evts, err := r.EventPlugin.GetExpiredAndNotProcessed()
		if err != nil {
			log.Println("[WARN]", err.Error())
		} else {
			for _, e := range evts {
				// Make new Event for expired event
				newEv := events.Event{
					Source:         "greedee reactor",
					CreatedAt:      time.Now(),
					AuthUserSource: e.AuthUserSource,
					Name:           e.Name,
					Description:    fmt.Sprintf("%s (%s)", e.Description, "expired"),
				}
				newEv.Fail()
				newEv.Gen256Sum()
				r.EventPlugin.Send(newEv)
				r.EventPlugin.Process(e, true) // tag event as processed in plugin
				log.Println("[INFO] Event created for", e.Name, "expired at", e.ExpiresAt)
			}
		}
	}
}
<commit_msg>fix mysql 5.7 strict mode date<commit_after>package reactor

import (
	"fmt"
	"github.com/yanc0/greedee/events"
	"github.com/yanc0/greedee/plugins"
	"log"
	"time"
)

type Reactor struct {
	EventPlugin plugins.EventPlugin
}

func (r *Reactor) Launch() {
	c := time.Tick(time.Second * 5)
	for range c {
		evts, err := r.EventPlugin.GetExpiredAndNotProcessed()
		if err != nil {
			log.Println("[WARN]", err.Error())
		} else {
			for _, e := range evts {
				// Make new Event for expired event
				newEv := events.Event{
					Source:         "greedee reactor",
					CreatedAt:      time.Now(),
					ExpiresAt:      time.Now(),
					AuthUserSource: e.AuthUserSource,
					Name:           e.Name,
					Description:    fmt.Sprintf("%s (%s)", e.Description, "expired"),
				}
				newEv.Fail()
				newEv.Gen256Sum()
				r.EventPlugin.Send(newEv)
				r.EventPlugin.Process(e, true) // tag event as processed in plugin
				log.Println("[INFO] Event created for", e.Name, "expired at", e.ExpiresAt)
			}
		}
	}
}
<|endoftext|>