<commit_before>// Copyright (c) 2017 Magnus Bäck <magnus@noun.se>

package logstash

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/blang/semver"
	"github.com/magnusbaeck/logstash-filter-verifier/testhelpers"
)

func TestArgs(t *testing.T) {
	tinv, err := createTestInvocation(semver.MustParse("6.0.0"))
	if err != nil {
		t.Fatalf("%s", err)
	}

	args, err := tinv.Inv.Args("input", "output")
	if err != nil {
		t.Fatalf("Error generating args: %s", err)
	}
	options := simpleOptionParser(args)
	configOption, exists := options["-f"]
	if !exists {
		t.Fatalf("no -f option found")
	}
	configDir, err := os.Open(configOption)
	if err != nil {
		t.Fatalf("Error opening configuration file directory: %s", err)
	}

	files, err := configDir.Readdirnames(0)
	if err != nil {
		t.Fatalf("Error reading configuration file directory: %s", err)
	}

	// Three aspects of the pipeline config file directory concern us:
	//   - The file that normally contains filters exists and has the
	//     expected contents.
	//   - The file with the inputs and outputs exists and has the
	//     expected contents.
	//   - No other files are present.
	var filterOk bool
	var ioOk bool
	for _, file := range files {
		buf, err := ioutil.ReadFile(filepath.Join(configOption, file))
		if err != nil {
			t.Errorf("Error reading configuration file: %s", err)
			continue
		}
		fileContents := string(buf)

		// Filter configuration file.
		if file == tinv.configFile {
			if fileContents != tinv.configContents {
				t.Errorf("Filter configuration file didn't contain the expected data.\nExpected: %q\nGot: %q", tinv.configContents, fileContents)
			}
			filterOk = true
			continue
		}

		// Input/Output configuration file.
		if file == filepath.Base(tinv.Inv.ioConfig) {
			expectedIoConfig := "input\noutput"
			if fileContents != expectedIoConfig {
				t.Errorf("Input/output configuration file didn't contain the expected data.\nExpected: %q\nGot: %q",
					expectedIoConfig, fileContents)
			}
			ioOk = true
			continue
		}

		// We should never get here.
		t.Errorf("Unexpected file found: %s", file)
	}

	if !filterOk {
		t.Errorf("No filter configuration file found in %s: %v", configOption, files)
	}
	if !ioOk {
		t.Errorf("No input/output configuration file found in %s: %v", configOption, files)
	}
}

func TestNewInvocation(t *testing.T) {
	cases := []struct {
		version     string
		optionTests func(options map[string]string) error
	}{
		// Logstash 2.4 gets a regular file as a log file argument.
		{
			"2.4.0",
			func(options map[string]string) error {
				logOption, exists := options["-l"]
				if !exists {
					return errors.New("no logfile option found")
				}
				fi, err := os.Stat(logOption)
				if err != nil {
					return fmt.Errorf("could not stat logfile: %s", err)
				}
				if !fi.Mode().IsRegular() {
					return fmt.Errorf("log path not a regular file: %s", fi.Name())
				}

				if _, exists = options["--path.settings"]; exists {
					return errors.New("unsupported --path.settings option provided")
				}
				return nil
			},
		},
		// Logstash 5.0 gets a directory as a log file
		// argument and --path.settings pointing to a
		// directory with the expected files.
		{
			"5.0.0",
			func(options map[string]string) error {
				logOption, exists := options["-l"]
				if !exists {
					return errors.New("no logfile option found")
				}
				fi, err := os.Stat(logOption)
				if err != nil {
					return fmt.Errorf("could not stat logfile: %s", err)
				}
				if fi.Mode().IsRegular() {
					return fmt.Errorf("log path not a regular file: %s", fi.Name())
				}

				pathOption, exists := options["--path.settings"]
				if !exists {
					return errors.New("--path.settings option missing")
				}
				requiredFiles := []string{
					"jvm.options",
					"log4j2.properties",
					"logstash.yml",
				}
				if !allFilesExist(pathOption, requiredFiles) {
					return fmt.Errorf("Not all required files found in %q: %v",
						pathOption, requiredFiles)
				}

				return nil
			},
		},
	}
	for i, c := range cases {
		tinv, err := createTestInvocation(semver.MustParse(c.version))
		if err != nil {
			t.Errorf("Test %d: %s", i, err)
		}
		defer tinv.Release()

		args, err := tinv.Inv.Args("input", "output")
		if err != nil {
			t.Errorf("Test %d: Error generating args: %s", i, err)
		}
		err = c.optionTests(simpleOptionParser(args))
		if err != nil {
			t.Errorf("Test %d: Command option test failed for %v: %s", i, args, err)
		}
	}

}

type testInvocation struct {
	Inv            *Invocation
	tempdir        string
	configFile     string
	configContents string
}

func createTestInvocation(version semver.Version) (*testInvocation, error) {
	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		return nil, fmt.Errorf("Unexpected error when creating temp dir: %s", err)
	}

	files := []testhelpers.FileWithMode{
		{"bin", os.ModeDir | 0755, ""},
		{"bin/logstash", 0755, ""},
		{"config", os.ModeDir | 0755, ""},
		{"config/jvm.options", 0644, ""},
		{"config/log4j2.properties", 0644, ""},
	}
	for _, fwm := range files {
		if err = fwm.Create(tempdir); err != nil {
			return nil, fmt.Errorf("Unexpected error when creating test file: %s", err)
		}
	}

	configFile := filepath.Join(tempdir, "configfile.conf")
	configContents := ""
	if err = ioutil.WriteFile(configFile, []byte(configContents), 0644); err != nil {
		return nil, fmt.Errorf("Unexpected error when creating dummy configuration file: %s", err)
	}
	logstashPath := filepath.Join(tempdir, "bin/logstash")
	inv, err := NewInvocation(logstashPath, []string{}, &version, configFile)
	if err != nil {
		return nil, fmt.Errorf("Unexpected error when creating Invocation: %s", err)
	}

	return &testInvocation{inv, tempdir, filepath.Base(configFile), configContents}, nil
}

func (ti *testInvocation) Release() {
	ti.Inv.Release()
	_ = os.RemoveAll(ti.tempdir)
}

// simpleOptionParser is a super-simple command line option parser
// that just builds a map of all the options and their values. For
// options not taking any arguments the option's value will be an
// empty string.
func simpleOptionParser(args []string) map[string]string {
	result := map[string]string{}
	for i := 0; i < len(args); i++ {
		if args[i][0] != '-' {
			continue
		}

		if i+1 < len(args) && args[i+1][0] != '-' {
			result[args[i]] = args[i+1]
			i++
		} else {
			result[args[i]] = ""
		}
	}
	return result
}
<commit_msg>logstash: Test harness setup failure must abort current testcase<commit_after>// Copyright (c) 2017 Magnus Bäck <magnus@noun.se>

package logstash

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/blang/semver"
	"github.com/magnusbaeck/logstash-filter-verifier/testhelpers"
)

func TestArgs(t *testing.T) {
	tinv, err := createTestInvocation(semver.MustParse("6.0.0"))
	if err != nil {
		t.Fatalf("%s", err)
	}

	args, err := tinv.Inv.Args("input", "output")
	if err != nil {
		t.Fatalf("Error generating args: %s", err)
	}
	options := simpleOptionParser(args)
	configOption, exists := options["-f"]
	if !exists {
		t.Fatalf("no -f option found")
	}
	configDir, err := os.Open(configOption)
	if err != nil {
		t.Fatalf("Error opening configuration file directory: %s", err)
	}

	files, err := configDir.Readdirnames(0)
	if err != nil {
		t.Fatalf("Error reading configuration file directory: %s", err)
	}

	// Three aspects of the pipeline config file directory concern us:
	//   - The file that normally contains filters exists and has the
	//     expected contents.
	//   - The file with the inputs and outputs exists and has the
	//     expected contents.
	//   - No other files are present.
	var filterOk bool
	var ioOk bool
	for _, file := range files {
		buf, err := ioutil.ReadFile(filepath.Join(configOption, file))
		if err != nil {
			t.Errorf("Error reading configuration file: %s", err)
			continue
		}
		fileContents := string(buf)

		// Filter configuration file.
		if file == tinv.configFile {
			if fileContents != tinv.configContents {
				t.Errorf("Filter configuration file didn't contain the expected data.\nExpected: %q\nGot: %q", tinv.configContents, fileContents)
			}
			filterOk = true
			continue
		}

		// Input/Output configuration file.
		if file == filepath.Base(tinv.Inv.ioConfig) {
			expectedIoConfig := "input\noutput"
			if fileContents != expectedIoConfig {
				t.Errorf("Input/output configuration file didn't contain the expected data.\nExpected: %q\nGot: %q",
					expectedIoConfig, fileContents)
			}
			ioOk = true
			continue
		}

		// We should never get here.
		t.Errorf("Unexpected file found: %s", file)
	}

	if !filterOk {
		t.Errorf("No filter configuration file found in %s: %v", configOption, files)
	}
	if !ioOk {
		t.Errorf("No input/output configuration file found in %s: %v", configOption, files)
	}
}

func TestNewInvocation(t *testing.T) {
	cases := []struct {
		version     string
		optionTests func(options map[string]string) error
	}{
		// Logstash 2.4 gets a regular file as a log file argument.
		{
			"2.4.0",
			func(options map[string]string) error {
				logOption, exists := options["-l"]
				if !exists {
					return errors.New("no logfile option found")
				}
				fi, err := os.Stat(logOption)
				if err != nil {
					return fmt.Errorf("could not stat logfile: %s", err)
				}
				if !fi.Mode().IsRegular() {
					return fmt.Errorf("log path not a regular file: %s", fi.Name())
				}

				if _, exists = options["--path.settings"]; exists {
					return errors.New("unsupported --path.settings option provided")
				}
				return nil
			},
		},
		// Logstash 5.0 gets a directory as a log file
		// argument and --path.settings pointing to a
		// directory with the expected files.
		{
			"5.0.0",
			func(options map[string]string) error {
				logOption, exists := options["-l"]
				if !exists {
					return errors.New("no logfile option found")
				}
				fi, err := os.Stat(logOption)
				if err != nil {
					return fmt.Errorf("could not stat logfile: %s", err)
				}
				if fi.Mode().IsRegular() {
					return fmt.Errorf("log path not a regular file: %s", fi.Name())
				}

				pathOption, exists := options["--path.settings"]
				if !exists {
					return errors.New("--path.settings option missing")
				}
				requiredFiles := []string{
					"jvm.options",
					"log4j2.properties",
					"logstash.yml",
				}
				if !allFilesExist(pathOption, requiredFiles) {
					return fmt.Errorf("Not all required files found in %q: %v",
						pathOption, requiredFiles)
				}

				return nil
			},
		},
	}
	for i, c := range cases {
		tinv, err := createTestInvocation(semver.MustParse(c.version))
		if err != nil {
			t.Errorf("Test %d: Error unexpectedly returned: %s", i, err)
			continue
		}
		defer tinv.Release()

		args, err := tinv.Inv.Args("input", "output")
		if err != nil {
			t.Errorf("Test %d: Error generating args: %s", i, err)
		}
		err = c.optionTests(simpleOptionParser(args))
		if err != nil {
			t.Errorf("Test %d: Command option test failed for %v: %s", i, args, err)
		}
	}

}

type testInvocation struct {
	Inv            *Invocation
	tempdir        string
	configFile     string
	configContents string
}

func createTestInvocation(version semver.Version) (*testInvocation, error) {
	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		return nil, fmt.Errorf("Unexpected error when creating temp dir: %s", err)
	}

	files := []testhelpers.FileWithMode{
		{"bin", os.ModeDir | 0755, ""},
		{"bin/logstash", 0755, ""},
		{"config", os.ModeDir | 0755, ""},
		{"config/jvm.options", 0644, ""},
		{"config/log4j2.properties", 0644, ""},
	}
	for _, fwm := range files {
		if err = fwm.Create(tempdir); err != nil {
			return nil, fmt.Errorf("Unexpected error when creating test file: %s", err)
		}
	}

	configFile := filepath.Join(tempdir, "configfile.conf")
	configContents := ""
	if err = ioutil.WriteFile(configFile, []byte(configContents), 0644); err != nil {
		return nil, fmt.Errorf("Unexpected error when creating dummy configuration file: %s", err)
	}
	logstashPath := filepath.Join(tempdir, "bin/logstash")
	inv, err := NewInvocation(logstashPath, []string{}, &version, configFile)
	if err != nil {
		return nil, fmt.Errorf("Unexpected error when creating Invocation: %s", err)
	}

	return &testInvocation{inv, tempdir, filepath.Base(configFile), configContents}, nil
}

func (ti *testInvocation) Release() {
	ti.Inv.Release()
	_ = os.RemoveAll(ti.tempdir)
}

// simpleOptionParser is a super-simple command line option parser
// that just builds a map of all the options and their values. For
// options not taking any arguments the option's value will be an
// empty string.
func simpleOptionParser(args []string) map[string]string {
	result := map[string]string{}
	for i := 0; i < len(args); i++ {
		if args[i][0] != '-' {
			continue
		}

		if i+1 < len(args) && args[i+1][0] != '-' {
			result[args[i]] = args[i+1]
			i++
		} else {
			result[args[i]] = ""
		}
	}
	return result
}
<|endoftext|>


<commit_before>/*
Package lmdbsync provides advanced synchronization for LMDB environments at the
cost of performance.  The package provides a drop-in replacement for *lmdb.Env
that can be used in situations where the database may be resized or where the
flag lmdb.NoLock is used.

Bypassing an Env's methods to access the underlying lmdb.Env is not safe.  The
severity of such usage depends such behavior should be strictly avoided as it
may produce undefined behavior from the LMDB C library.

Resizing the environment

The Env type synchronizes all calls to Env.SetMapSize so that it may be safely
called in the presence of concurrent transactinos after an environment has been
opened.  All running transactions complete before the method is called on the
underlying lmdb.Env.

However, applications are recommended against attempting to change the memory
map size for an open database.  It requires careful synchronization by all
processes accessing the database file.  And, a large memory map will not affect
disk usage on operating systems that support sparse files (e.g. Linux, not OS
X).

Multi-processing (MapResized)

Using the Handler interface provided by the package MapResizedHandler can be
used to automatically resize an enviornment when a MapResized error is
encountered.  Usage of the MapResizedHandler puts important caveats on how one
can safely work with transactions.  See the function documentation for more
detailed information.

See mdb_txn_begin and MDB_MAP_RESIZED.

MapFull

Similar to the MapResizedHandler the MapFullHandler will automatically resize
the map and retry transactions when a MapFull error is encountered.  Usage of
the MapFullHandler puts important caveats on how one can safely work with
transactions.  See the function documentation for more detailed information.

See mdb_env_set_mapsize and MDB_MAP_FULL.

NoLock

When the lmdb.NoLock flag is set on an environment Env handles all transaction
synchronization using Go structures and is an experimental feature.  It is
unclear what benefits this provides.

See mdb_env_open and MDB_NOLOCK.
*/
package lmdbsync

import (
	"os"
	"sync"
	"syscall"
	"time"

	"github.com/bmatsuo/lmdb-go/lmdb"
)

type envBagKey int

func BagEnv(b Bag) *Env {
	env, _ := b.Value(envBagKey(0)).(*Env)
	return env
}

func bagWithEnv(b Bag, env *Env) Bag {
	return BagWith(b, envBagKey(0), env)
}

// Env wraps an *lmdb.Env, receiving all the same methods and proxying some to
// provide transaction management.  Transactions run by an Env handle
// lmdb.MapResized error transparently through additional synchronization.
// Additionally, Env is safe to use on environments setting the lmdb.NoLock
// flag.  When in NoLock mode write transactions block all read transactions
// from running (in addition to blocking other write transactions like a normal
// lmdb.Env would).
//
// Env proxies several methods to provide synchronization required for safe
// operation in some scenarios.  It is important not byprass proxies and call
// the methods directly on the underlying lmdb.Env or synchronization may be
// interfered with.  Calling proxied methods directly on the lmdb.Env may
// result in poor transaction performance or unspecified behavior in from the C
// library.
type Env struct {
	*lmdb.Env
	Handlers HandlerChain
	bag      Bag
	noLock   bool
	txnlock  sync.RWMutex
}

// NewEnv returns an newly allocated Env that wraps env.  If env is nil then
// lmdb.NewEnv() will be called to allocate an lmdb.Env.
func NewEnv(env *lmdb.Env, h ...Handler) (*Env, error) {
	var err error
	if env == nil {
		env, err = lmdb.NewEnv()
		if err != nil {
			return nil, err
		}
	}

	flags, err := env.Flags()
	if lmdb.IsErrnoSys(err, syscall.EINVAL) {
		err = nil
	} else if err != nil {
		return nil, err
	}
	noLock := flags&lmdb.NoLock != 0

	chain := append(HandlerChain(nil), h...)

	_env := &Env{
		Env:      env,
		Handlers: chain,
		noLock:   noLock,
		bag:      Background(),
	}
	return _env, nil
}

// Open is a proxy for r.Env.Open() that detects the lmdb.NoLock flag to
// properly manage transaction synchronization.
func (r *Env) Open(path string, flags uint, mode os.FileMode) error {
	err := r.Env.Open(path, flags, mode)
	if err != nil {
		// no update to flags occurred
		return err
	}

	if flags&lmdb.NoLock != 0 {
		r.noLock = true
	}

	return nil
}

// SetFlags is a proxy for r.Env.SetFlags() that detects the lmdb.NoLock flag
// to properly manage transaction synchronization.
func (r *Env) SetFlags(flags uint) error {
	err := r.Env.SetFlags(flags)
	if err != nil {
		// no update to flags occurred
		return err
	}

	if flags&lmdb.NoLock != 0 {
		r.noLock = true
	}

	return nil
}

// UnsetFlags is a proxy for r.Env.UnsetFlags() that detects the lmdb.NoLock flag
// to properly manage transaction synchronization.
func (r *Env) UnsetFlags(flags uint) error {
	err := r.Env.UnsetFlags(flags)
	if err != nil {
		// no update to flags occurred
		return err
	}

	if flags&lmdb.NoLock != 0 {
		r.noLock = false
	}

	return nil
}

// SetMapSize is a proxy for r.Env.SetMapSize() that blocks while concurrent
// transactions are in progress.
func (r *Env) SetMapSize(size int64) error {
	r.txnlock.Lock()
	err := r.setMapSize(size, 0)
	r.txnlock.Unlock()
	return err
}

func (r *Env) setMapSize(size int64, delay time.Duration) error {
	r.txnlock.Lock()
	if delay > 0 {
		// wait before adopting a map size set from another process. hold on to
		// the transaction lock so that other transactions don't attempt to
		// begin while waiting.
		time.Sleep(delay)
	}
	err := r.Env.SetMapSize(size)
	r.txnlock.Unlock()
	return err
}

// RunTxn is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then RunTxn will block while other updates
// are in progress, regardless of flags.
//
// If RunTxn returns MapResized it means another process(es) was writing too
// fast to the database and the calling process could not get a valid
// transaction handle.
func (r *Env) RunTxn(flags uint, op lmdb.TxnOp) (err error) {
	readonly := flags&lmdb.Readonly != 0
	return r.runHandler(readonly, func() error { return r.Env.RunTxn(flags, op) }, r.Handlers)
}

// View is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then View will block until any running update
// completes.
//
// If View returns MapResized it means another process(es) was writing too fast
// to the database and the calling process could not get a valid transaction
// handle.
func (r *Env) View(op lmdb.TxnOp) error {
	return r.runHandler(true, func() error { return r.Env.View(op) }, r.Handlers)
}

// Update is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then Update blocks until all other
// transactions have terminated and blocks all other transactions from running
// while in progress (including readonly transactions).
//
// If Update returns MapResized it means another process(es) was writing too
// fast to the database and the calling process could not get a valid
// transaction handle.
func (r *Env) Update(op lmdb.TxnOp) error {
	return r.runHandler(false, func() error { return r.Env.Update(op) }, r.Handlers)
}

// UpdateLocked is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then UpdateLocked blocks until all other
// transactions have terminated and blocks all other transactions from running
// while in progress (including readonly transactions).
//
// If UpdateLocked returns MapResized it means another process(es) was writing
// too fast to the database and the calling process could not get a valid
// transaction handle.
func (r *Env) UpdateLocked(op lmdb.TxnOp) error {
	return r.runHandler(false, func() error { return r.Env.UpdateLocked(op) }, r.Handlers)
}

// WithHandler returns a TxnRunner than handles transaction errors r.Handlers
// chained with h.
func (r *Env) WithHandler(h Handler) TxnRunner {
	return &handlerRunner{
		env: r,
		h:   r.Handlers.Append(h),
	}
}

func (r *Env) runHandler(readonly bool, fn func() error, h Handler) error {
	b := bagWithEnv(r.bag, r)
	for {
		err := r.run(readonly, fn)
		b, err = h.HandleTxnErr(b, err)
		if err != RetryTxn {
			return err
		}
	}
}
func (r *Env) run(readonly bool, fn func() error) error {
	var err error
	if r.noLock && !readonly {
		r.txnlock.Lock()
		err = fn()
		r.txnlock.Unlock()
	} else {
		r.txnlock.RLock()
		err = fn()
		r.txnlock.RUnlock()
	}
	return err
}
<commit_msg>clarify lmdbsync requirements on transactino independence<commit_after>/*
Package lmdbsync provides advanced synchronization for LMDB environments at the
cost of performance.  The package provides a drop-in replacement for *lmdb.Env
that can be used in situations where the database may be resized or where the
flag lmdb.NoLock is used.

Bypassing an Env's methods to access the underlying lmdb.Env is not safe.  The
severity of such usage depends such behavior should be strictly avoided as it
may produce undefined behavior from the LMDB C library.

Resizing the environment

The Env type synchronizes all calls to Env.SetMapSize so that it may, with some
caveats, be safely called in the presence of concurrent transactions after an
environment has been opened.  All running transactions must complete before the
method will be called on the underlying lmdb.Env.

If an open transaction depends on a change in map size then the database will
deadlock and block all future transactions in the environment.  Put simply, all
transactions must terminate independently of other transactions.

In the simplest example, a function in view transaction that attempts an update
will deadlock database if the map is full and an increase of the map size is
attempted so the transaction can be retried.  Instead the update should be
prepared inside the view and then executed following the termination of the
view.

The developers of LMDB officially recommend against applications changing the
memory map size for an open database.  It requires careful synchronization by
all processes accessing the database file.  And, a large memory map will not
affect disk usage on operating systems that support sparse files (e.g. Linux,
not OS X).

See mdb_env_set_mapsize.

Multi-processing (MapResized)

Using the Handler interface provided by the package MapResizedHandler can be
used to automatically resize an enviornment when a lmdb.MapResized error is
encountered.  Usage of the MapResizedHandler puts important caveats on how one
can safely work with transactions.  See the function documentation for more
detailed information.

When other processes may change an environment's map size it is extremely
important to ensure that transactions terminate independent of all other
transactions.  The MapResized error may be returned at the beginning of any
transaction.

See mdb_txn_begin and MDB_MAP_RESIZED.

MapFull

Similar to the MapResizedHandler the MapFullHandler will automatically resize
the map and retry transactions when a MapFull error is encountered.  Usage of
the MapFullHandler puts important caveats on how one can safely work with
transactions.  See the function documentation for more detailed information.

The caveats on transactions are lessened if lmdb.MapFull is the only error
being handled (when multi-processing is not a concern).  The only requirement
then is that view transactions not depend on the termination of updates
transactions.

See mdb_env_set_mapsize and MDB_MAP_FULL.

NoLock

When the lmdb.NoLock flag is set on an environment Env handles all transaction
synchronization using Go structures and is an experimental feature.  It is
unclear what benefits this provides.

Usage of lmdb.NoLock requires that update transactions acquire an exclusive
lock on the environment.  In such cases it is required that view transactions
execute independently of update transactions, a requirement more strict than
that from handling MapFull.

See mdb_env_open and MDB_NOLOCK.
*/
package lmdbsync

import (
	"os"
	"sync"
	"syscall"
	"time"

	"github.com/bmatsuo/lmdb-go/lmdb"
)

type envBagKey int

func BagEnv(b Bag) *Env {
	env, _ := b.Value(envBagKey(0)).(*Env)
	return env
}

func bagWithEnv(b Bag, env *Env) Bag {
	return BagWith(b, envBagKey(0), env)
}

// Env wraps an *lmdb.Env, receiving all the same methods and proxying some to
// provide transaction management.  Transactions run by an Env handle
// lmdb.MapResized error transparently through additional synchronization.
// Additionally, Env is safe to use on environments setting the lmdb.NoLock
// flag.  When in NoLock mode write transactions block all read transactions
// from running (in addition to blocking other write transactions like a normal
// lmdb.Env would).
//
// Env proxies several methods to provide synchronization required for safe
// operation in some scenarios.  It is important not byprass proxies and call
// the methods directly on the underlying lmdb.Env or synchronization may be
// interfered with.  Calling proxied methods directly on the lmdb.Env may
// result in poor transaction performance or unspecified behavior in from the C
// library.
type Env struct {
	*lmdb.Env
	Handlers HandlerChain
	bag      Bag
	noLock   bool
	txnlock  sync.RWMutex
}

// NewEnv returns an newly allocated Env that wraps env.  If env is nil then
// lmdb.NewEnv() will be called to allocate an lmdb.Env.
func NewEnv(env *lmdb.Env, h ...Handler) (*Env, error) {
	var err error
	if env == nil {
		env, err = lmdb.NewEnv()
		if err != nil {
			return nil, err
		}
	}

	flags, err := env.Flags()
	if lmdb.IsErrnoSys(err, syscall.EINVAL) {
		err = nil
	} else if err != nil {
		return nil, err
	}
	noLock := flags&lmdb.NoLock != 0

	chain := append(HandlerChain(nil), h...)

	_env := &Env{
		Env:      env,
		Handlers: chain,
		noLock:   noLock,
		bag:      Background(),
	}
	return _env, nil
}

// Open is a proxy for r.Env.Open() that detects the lmdb.NoLock flag to
// properly manage transaction synchronization.
func (r *Env) Open(path string, flags uint, mode os.FileMode) error {
	err := r.Env.Open(path, flags, mode)
	if err != nil {
		// no update to flags occurred
		return err
	}

	if flags&lmdb.NoLock != 0 {
		r.noLock = true
	}

	return nil
}

// SetFlags is a proxy for r.Env.SetFlags() that detects the lmdb.NoLock flag
// to properly manage transaction synchronization.
func (r *Env) SetFlags(flags uint) error {
	err := r.Env.SetFlags(flags)
	if err != nil {
		// no update to flags occurred
		return err
	}

	if flags&lmdb.NoLock != 0 {
		r.noLock = true
	}

	return nil
}

// UnsetFlags is a proxy for r.Env.UnsetFlags() that detects the lmdb.NoLock flag
// to properly manage transaction synchronization.
func (r *Env) UnsetFlags(flags uint) error {
	err := r.Env.UnsetFlags(flags)
	if err != nil {
		// no update to flags occurred
		return err
	}

	if flags&lmdb.NoLock != 0 {
		r.noLock = false
	}

	return nil
}

// SetMapSize is a proxy for r.Env.SetMapSize() that blocks while concurrent
// transactions are in progress.
func (r *Env) SetMapSize(size int64) error {
	r.txnlock.Lock()
	err := r.setMapSize(size, 0)
	r.txnlock.Unlock()
	return err
}

func (r *Env) setMapSize(size int64, delay time.Duration) error {
	r.txnlock.Lock()
	if delay > 0 {
		// wait before adopting a map size set from another process. hold on to
		// the transaction lock so that other transactions don't attempt to
		// begin while waiting.
		time.Sleep(delay)
	}
	err := r.Env.SetMapSize(size)
	r.txnlock.Unlock()
	return err
}

// RunTxn is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then RunTxn will block while other updates
// are in progress, regardless of flags.
//
// If RunTxn returns MapResized it means another process(es) was writing too
// fast to the database and the calling process could not get a valid
// transaction handle.
func (r *Env) RunTxn(flags uint, op lmdb.TxnOp) (err error) {
	readonly := flags&lmdb.Readonly != 0
	return r.runHandler(readonly, func() error { return r.Env.RunTxn(flags, op) }, r.Handlers)
}

// View is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then View will block until any running update
// completes.
//
// If View returns MapResized it means another process(es) was writing too fast
// to the database and the calling process could not get a valid transaction
// handle.
func (r *Env) View(op lmdb.TxnOp) error {
	return r.runHandler(true, func() error { return r.Env.View(op) }, r.Handlers)
}

// Update is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then Update blocks until all other
// transactions have terminated and blocks all other transactions from running
// while in progress (including readonly transactions).
//
// If Update returns MapResized it means another process(es) was writing too
// fast to the database and the calling process could not get a valid
// transaction handle.
func (r *Env) Update(op lmdb.TxnOp) error {
	return r.runHandler(false, func() error { return r.Env.Update(op) }, r.Handlers)
}

// UpdateLocked is a proxy for r.Env.RunTxn().
//
// If lmdb.NoLock is set on r.Env then UpdateLocked blocks until all other
// transactions have terminated and blocks all other transactions from running
// while in progress (including readonly transactions).
//
// If UpdateLocked returns MapResized it means another process(es) was writing
// too fast to the database and the calling process could not get a valid
// transaction handle.
func (r *Env) UpdateLocked(op lmdb.TxnOp) error {
	return r.runHandler(false, func() error { return r.Env.UpdateLocked(op) }, r.Handlers)
}

// WithHandler returns a TxnRunner than handles transaction errors r.Handlers
// chained with h.
func (r *Env) WithHandler(h Handler) TxnRunner {
	return &handlerRunner{
		env: r,
		h:   r.Handlers.Append(h),
	}
}

func (r *Env) runHandler(readonly bool, fn func() error, h Handler) error {
	b := bagWithEnv(r.bag, r)
	for {
		err := r.run(readonly, fn)
		b, err = h.HandleTxnErr(b, err)
		if err != RetryTxn {
			return err
		}
	}
}
func (r *Env) run(readonly bool, fn func() error) error {
	var err error
	if r.noLock && !readonly {
		r.txnlock.Lock()
		err = fn()
		r.txnlock.Unlock()
	} else {
		r.txnlock.RLock()
		err = fn()
		r.txnlock.RUnlock()
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 sdb

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"strings"
	"testing"
)

func TestValidate(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ValidateTest struct {
}

func init() { RegisterTestSuite(&ValidateTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ValidateTest) EmptyString() {
	err := validateValue("")

	ExpectEq(nil, err)
}

func (t *ValidateTest) LongStrings() {
	var err error

	// Just short enough
	err = validateValue(strings.Repeat("x", 1024))
	ExpectEq(nil, err)

	// Too long
	err = validateValue(strings.Repeat("x", 1025))

	ExpectThat(err, Error(HasSubstr("1024")))
	ExpectThat(err, Error(HasSubstr("bytes")))
}

func (t *ValidateTest) InvalidUtf8() {
	err := validateValue("taco\x80\x81\x82burrito")

	ExpectThat(err, Error(HasSubstr("valid")))
	ExpectThat(err, Error(HasSubstr("UTF-8")))
}

func (t *ValidateTest) LegalCharacters() {
	ExpectEq("TODO", "")
}

func (t *ValidateTest) NullByte() {
	ExpectEq("TODO", "")
}

func (t *ValidateTest) ControlCharacter() {
	ExpectEq("TODO", "")
}
<commit_msg>ValidateTest.LegalCharacters<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 sdb

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"strings"
	"testing"
)

func TestValidate(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ValidateTest struct {
}

func init() { RegisterTestSuite(&ValidateTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ValidateTest) EmptyString() {
	err := validateValue("")

	ExpectEq(nil, err)
}

func (t *ValidateTest) LongStrings() {
	var err error

	// Just short enough
	err = validateValue(strings.Repeat("x", 1024))
	ExpectEq(nil, err)

	// Too long
	err = validateValue(strings.Repeat("x", 1025))

	ExpectThat(err, Error(HasSubstr("1024")))
	ExpectThat(err, Error(HasSubstr("bytes")))
}

func (t *ValidateTest) InvalidUtf8() {
	err := validateValue("taco\x80\x81\x82burrito")

	ExpectThat(err, Error(HasSubstr("valid")))
	ExpectThat(err, Error(HasSubstr("UTF-8")))
}

func (t *ValidateTest) LegalCharacters() {
	s := "\x09 \x0a \x0d \x20 \x25 \x30 abcd ü 타코"
	err := validateValue(s)

	ExpectEq(nil, err)
}

func (t *ValidateTest) NullByte() {
	ExpectEq("TODO", "")
}

func (t *ValidateTest) ControlCharacter() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>package data_types

import "encoding/json"
import "fmt"
import "strings"

type AddressFamily string

const (
  AddressFamily_IPv4 AddressFamily = "ipv4"
  AddressFamily_IPv6 AddressFamily = "ipv6"
)

type Fragment string

const (
  Fragment_Unsupported Fragment = "unsupported"
  Fragment_V4Fragment  Fragment = "v4-fragment"
  Fragment_V6Fragment  Fragment = "v6-fragment"
)

type ForwardingAction string

const (
  ForwardingAction_Accept ForwardingAction = "accept"
  ForwardingAction_Drop   ForwardingAction = "drop"
  ForwardingAction_Reject ForwardingAction = "reject"
)

type ACLType string

const (
  ACLType_IPv4ACLType             ACLType = "ipv4-acl-type"
  ACLType_IPv6ACLType             ACLType = "ipv6-acl-type"
  ACLType_EthACLType              ACLType = "eth-acl-type"
  ACLType_MixedEthIPv4ACLType     ACLType = "mixed-eth-ipv4-acl-type"
  ACLType_MixedEthIPv6ACLType     ACLType = "mixed-eth-ipv6-acl-type"
  ACLType_MixedEthIPv4IPv6ACLType ACLType = "mixed-eth-ipv4-ipv6-acl-type"
)

type ActivationType string

const (
  ActivationType_ActivateWhenMitigating ActivationType = "activate-when-mitigating"
  ActivationType_Immediate              ActivationType = "immediate"
)

type Operator string

const (
  Operator_LTE Operator = "lte"
  Operator_GTE Operator = "gte"
  Operator_EQ  Operator = "eq"
  Operator_NEQ Operator = "neq"
)

type IPv4Flag string
type IPv4Flags []IPv4Flag

const (
  IPv4Flag_Reserved IPv4Flag = "reserved"
  IPv4Flag_Fragment IPv4Flag = "fragment"
  IPv4Flag_More     IPv4Flag = "more"
)

type TCPFlag string
type TCPFlags []TCPFlag

const (
  TCPFlag_CWR TCPFlag = "cwr"
  TCPFlag_ECE TCPFlag = "ece"
  TCPFlag_URG TCPFlag = "urg"
  TCPFlag_ACK TCPFlag = "ack"
  TCPFlag_PSH TCPFlag = "psh"
  TCPFlag_RST TCPFlag = "rst"
  TCPFlag_SYN TCPFlag = "syn"
  TCPFlag_FIN TCPFlag = "fin"
)

func (e AddressFamily) String() string {
  return string(e)
}

func (e AddressFamily) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *AddressFamily) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(AddressFamily_IPv4):
    *p = AddressFamily_IPv4
    return nil
  case string(AddressFamily_IPv6):
    *p = AddressFamily_IPv6
    return nil
  default:
    return fmt.Errorf("Unexpected AddressFamily: %v", s)
  }
}

func (e Fragment) String() string {
  return string(e)
}

func (e Fragment) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *Fragment) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(Fragment_Unsupported):
    *p = Fragment_Unsupported
    return nil
  case string(Fragment_V4Fragment):
    *p = Fragment_V4Fragment
    return nil
  case string(Fragment_V6Fragment):
    *p = Fragment_V6Fragment
    return nil
  default:
    return fmt.Errorf("Unexpected Fragment: %v", s)
  }
}

func (e ForwardingAction) String() string {
  return string(e)
}

func (e ForwardingAction) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *ForwardingAction) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(ForwardingAction_Accept):
    *p = ForwardingAction_Accept
    return nil
  case string(ForwardingAction_Drop):
    *p = ForwardingAction_Drop
    return nil
  case string(ForwardingAction_Reject):
    *p = ForwardingAction_Reject
    return nil
  default:
    return fmt.Errorf("Unexpected ForwardingAction: %v", s)
  }
}

func (e ACLType) String() string {
  return string(e)
}

func (e ACLType) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *ACLType) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  r := strings.TrimPrefix(s, "ietf-acl:")

  switch r {
  case string(ACLType_IPv4ACLType):
    *p = ACLType_IPv4ACLType
    return nil
  case string(ACLType_IPv6ACLType):
    *p = ACLType_IPv6ACLType
    return nil
  case string(ACLType_EthACLType):
    *p = ACLType_EthACLType
    return nil
  case string(ACLType_MixedEthIPv4ACLType):
    *p = ACLType_MixedEthIPv4ACLType
    return nil
  case string(ACLType_MixedEthIPv6ACLType):
    *p = ACLType_MixedEthIPv6ACLType
    return nil
  case string(ACLType_MixedEthIPv4IPv6ACLType):
    *p = ACLType_MixedEthIPv4IPv6ACLType
    return nil
  default:
    return fmt.Errorf("Unexpected ACLType: %v", s)
  }
}

func (e ActivationType) String() string {
  return string(e)
}

func (e ActivationType) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *ActivationType) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(ActivationType_ActivateWhenMitigating):
    *p = ActivationType_ActivateWhenMitigating
    return nil
  case string(ActivationType_Immediate):
    *p = ActivationType_Immediate
    return nil
  default:
    return fmt.Errorf("Unexpected ActivationType: %v", s)
  }
}

func (e Operator) String() string {
  return string(e)
}

func (e Operator) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *Operator) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(Operator_LTE):
    *p = Operator_LTE
    return nil
  case string(Operator_GTE):
    *p = Operator_GTE
    return nil
  case string(Operator_EQ):
    *p = Operator_EQ
    return nil
  case string(Operator_NEQ):
    *p = Operator_NEQ
    return nil
  default:
    return fmt.Errorf("Unexpected Operator: %v", s)
  }
}

func (e IPv4Flag) String() string {
  return string(e)
}

func (e IPv4Flags) String() string {
  a := make([]string, len(e))
  for i, v := range e {
    a[i] = v.String()
  }
  return strings.Join(a, " ")
}

func (e IPv4Flags) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *IPv4Flags) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  m := make(map[IPv4Flag]bool)
  for _, v := range strings.Split(s, " ") {
    switch v {
    case "":
    case string(IPv4Flag_Reserved):
      m[IPv4Flag_Reserved] = true
    case string(IPv4Flag_Fragment):
      m[IPv4Flag_Fragment] = true
    case string(IPv4Flag_More):
      m[IPv4Flag_More] = true
    default:
      return fmt.Errorf("Unexpected IPv4Flag: %v", v)
    }
  }

  r := make(IPv4Flags, len(m))
  i := 0
  for k := range m {
    r[i] = k
    i++
  }
  *p = r
  return nil
}

func (e TCPFlag) String() string {
  return string(e)
}

func (e TCPFlags) String() string {
  a := make([]string, len(e))
  for i, v := range e {
    a[i] = v.String()
  }
  return strings.Join(a, " ")
}

func (p *TCPFlags) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  m := make(map[TCPFlag]bool)
  for _, v := range strings.Split(s, " ") {
    switch v {
    case "":
    case string(TCPFlag_CWR):
      m[TCPFlag_CWR] = true
    case string(TCPFlag_ECE):
      m[TCPFlag_ECE] = true
    case string(TCPFlag_URG):
      m[TCPFlag_URG] = true
    case string(TCPFlag_ACK):
      m[TCPFlag_ACK] = true
    case string(TCPFlag_PSH):
      m[TCPFlag_PSH] = true
    case string(TCPFlag_RST):
      m[TCPFlag_RST] = true
    case string(TCPFlag_SYN):
      m[TCPFlag_SYN] = true
    case string(TCPFlag_FIN):
      m[TCPFlag_FIN] = true
    default:
      return fmt.Errorf("Unexpected TCPFlag: %v", v)
    }
  }

  r := make(TCPFlags, len(m))
  i := 0
  for k := range m {
    r[i] = k
    i++
  }
  *p = r
  return nil
}
<commit_msg>Fix dev#83 handle flags as string not array<commit_after>package data_types

import "encoding/json"
import "fmt"
import "strings"

type AddressFamily string

const (
  AddressFamily_IPv4 AddressFamily = "ipv4"
  AddressFamily_IPv6 AddressFamily = "ipv6"
)

type Fragment string

const (
  Fragment_Unsupported Fragment = "unsupported"
  Fragment_V4Fragment  Fragment = "v4-fragment"
  Fragment_V6Fragment  Fragment = "v6-fragment"
)

type ForwardingAction string

const (
  ForwardingAction_Accept ForwardingAction = "accept"
  ForwardingAction_Drop   ForwardingAction = "drop"
  ForwardingAction_Reject ForwardingAction = "reject"
)

type ACLType string

const (
  ACLType_IPv4ACLType             ACLType = "ipv4-acl-type"
  ACLType_IPv6ACLType             ACLType = "ipv6-acl-type"
  ACLType_EthACLType              ACLType = "eth-acl-type"
  ACLType_MixedEthIPv4ACLType     ACLType = "mixed-eth-ipv4-acl-type"
  ACLType_MixedEthIPv6ACLType     ACLType = "mixed-eth-ipv6-acl-type"
  ACLType_MixedEthIPv4IPv6ACLType ACLType = "mixed-eth-ipv4-ipv6-acl-type"
)

type ActivationType string

const (
  ActivationType_ActivateWhenMitigating ActivationType = "activate-when-mitigating"
  ActivationType_Immediate              ActivationType = "immediate"
)

type Operator string

const (
  Operator_LTE Operator = "lte"
  Operator_GTE Operator = "gte"
  Operator_EQ  Operator = "eq"
  Operator_NEQ Operator = "neq"
)

type IPv4Flag string
type IPv4Flags string

const (
  IPv4Flag_Reserved IPv4Flag = "reserved"
  IPv4Flag_Fragment IPv4Flag = "fragment"
  IPv4Flag_More     IPv4Flag = "more"
)

type TCPFlag string
type TCPFlags string

const (
  TCPFlag_CWR TCPFlag = "cwr"
  TCPFlag_ECE TCPFlag = "ece"
  TCPFlag_URG TCPFlag = "urg"
  TCPFlag_ACK TCPFlag = "ack"
  TCPFlag_PSH TCPFlag = "psh"
  TCPFlag_RST TCPFlag = "rst"
  TCPFlag_SYN TCPFlag = "syn"
  TCPFlag_FIN TCPFlag = "fin"
)

func (e AddressFamily) String() string {
  return string(e)
}

func (e AddressFamily) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *AddressFamily) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(AddressFamily_IPv4):
    *p = AddressFamily_IPv4
    return nil
  case string(AddressFamily_IPv6):
    *p = AddressFamily_IPv6
    return nil
  default:
    return fmt.Errorf("Unexpected AddressFamily: %v", s)
  }
}

func (e Fragment) String() string {
  return string(e)
}

func (e Fragment) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *Fragment) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(Fragment_Unsupported):
    *p = Fragment_Unsupported
    return nil
  case string(Fragment_V4Fragment):
    *p = Fragment_V4Fragment
    return nil
  case string(Fragment_V6Fragment):
    *p = Fragment_V6Fragment
    return nil
  default:
    return fmt.Errorf("Unexpected Fragment: %v", s)
  }
}

func (e ForwardingAction) String() string {
  return string(e)
}

func (e ForwardingAction) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *ForwardingAction) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(ForwardingAction_Accept):
    *p = ForwardingAction_Accept
    return nil
  case string(ForwardingAction_Drop):
    *p = ForwardingAction_Drop
    return nil
  case string(ForwardingAction_Reject):
    *p = ForwardingAction_Reject
    return nil
  default:
    return fmt.Errorf("Unexpected ForwardingAction: %v", s)
  }
}

func (e ACLType) String() string {
  return string(e)
}

func (e ACLType) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *ACLType) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  r := strings.TrimPrefix(s, "ietf-acl:")

  switch r {
  case string(ACLType_IPv4ACLType):
    *p = ACLType_IPv4ACLType
    return nil
  case string(ACLType_IPv6ACLType):
    *p = ACLType_IPv6ACLType
    return nil
  case string(ACLType_EthACLType):
    *p = ACLType_EthACLType
    return nil
  case string(ACLType_MixedEthIPv4ACLType):
    *p = ACLType_MixedEthIPv4ACLType
    return nil
  case string(ACLType_MixedEthIPv6ACLType):
    *p = ACLType_MixedEthIPv6ACLType
    return nil
  case string(ACLType_MixedEthIPv4IPv6ACLType):
    *p = ACLType_MixedEthIPv4IPv6ACLType
    return nil
  default:
    return fmt.Errorf("Unexpected ACLType: %v", s)
  }
}

func (e ActivationType) String() string {
  return string(e)
}

func (e ActivationType) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *ActivationType) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(ActivationType_ActivateWhenMitigating):
    *p = ActivationType_ActivateWhenMitigating
    return nil
  case string(ActivationType_Immediate):
    *p = ActivationType_Immediate
    return nil
  default:
    return fmt.Errorf("Unexpected ActivationType: %v", s)
  }
}

func (e Operator) String() string {
  return string(e)
}

func (e Operator) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *Operator) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  switch s {
  case string(Operator_LTE):
    *p = Operator_LTE
    return nil
  case string(Operator_GTE):
    *p = Operator_GTE
    return nil
  case string(Operator_EQ):
    *p = Operator_EQ
    return nil
  case string(Operator_NEQ):
    *p = Operator_NEQ
    return nil
  default:
    return fmt.Errorf("Unexpected Operator: %v", s)
  }
}

func (e IPv4Flag) String() string {
  return string(e)
}

func (e IPv4Flags) String() string {
  return string(e)
}

func (e IPv4Flags) MarshalJSON() ([]byte, error) {
  return json.Marshal(e.String())
}

func (p *IPv4Flags) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  m := make(map[IPv4Flag]bool)
  for _, v := range strings.Split(s, " ") {
    switch v {
    case "":
    case string(IPv4Flag_Reserved):
      m[IPv4Flag_Reserved] = true
    case string(IPv4Flag_Fragment):
      m[IPv4Flag_Fragment] = true
    case string(IPv4Flag_More):
      m[IPv4Flag_More] = true
    default:
      return fmt.Errorf("Unexpected IPv4Flag: %v", v)
    }
  }

  *p = IPv4Flags(s)
  return nil
}

func (e TCPFlag) String() string {
  return string(e)
}

func (e TCPFlags) String() string {
  return string(e)
}

func (p *TCPFlags) UnmarshalJSON(data []byte) error {
  var s string
  err := json.Unmarshal(data, &s)
  if err != nil {
    return fmt.Errorf("Could not unmarshal as string: %v", data)
  }

  m := make(map[TCPFlag]bool)
  for _, v := range strings.Split(s, " ") {
    switch v {
    case "":
    case string(TCPFlag_CWR):
      m[TCPFlag_CWR] = true
    case string(TCPFlag_ECE):
      m[TCPFlag_ECE] = true
    case string(TCPFlag_URG):
      m[TCPFlag_URG] = true
    case string(TCPFlag_ACK):
      m[TCPFlag_ACK] = true
    case string(TCPFlag_PSH):
      m[TCPFlag_PSH] = true
    case string(TCPFlag_RST):
      m[TCPFlag_RST] = true
    case string(TCPFlag_SYN):
      m[TCPFlag_SYN] = true
    case string(TCPFlag_FIN):
      m[TCPFlag_FIN] = true
    default:
      return fmt.Errorf("Unexpected TCPFlag: %v", v)
    }
  }

  *p = TCPFlags(s)
  return nil
}
<|endoftext|>


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

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

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

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

package addons

import (
	"runtime"
	"strconv"

	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/kubeconfig"
	"k8s.io/minikube/pkg/minikube/mustload"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
	"k8s.io/minikube/pkg/minikube/sysinit"
)

// enableOrDisableAutoPause enables the service after the config was copied by generic enble
func enableOrDisableAutoPause(cc *config.ClusterConfig, name string, val string) error {
	enable, err := strconv.ParseBool(val)
	if err != nil {
		return errors.Wrapf(err, "parsing bool: %s", name)
	}
	out.Infof("auto-pause addon is an alpha feature and still in early development. Please file issues to help us make it better.")
	out.Infof("https://github.com/kubernetes/minikube/labels/co/auto-pause")

	if cc.KubernetesConfig.ContainerRuntime != "docker" || runtime.GOARCH != "amd64" {
		exit.Message(reason.Usage, `auto-pause currently is only supported on docker runtime and amd64. Track progress of others here: https://github.com/kubernetes/minikube/issues/10601`)
	}
	co := mustload.Running(cc.Name)
	if enable {
		if err := sysinit.New(co.CP.Runner).EnableNow("auto-pause"); err != nil {
			klog.ErrorS(err, "failed to enable", "service", "auto-pause")
		}
	}

	port := co.CP.Port // api server port
	if enable {        // if enable then need to calculate the forwarded port
		port = constants.AutoPauseProxyPort
		if driver.NeedsPortForward(cc.Driver) {
			port, err = oci.ForwardedPort(cc.Driver, cc.Name, port)
			if err != nil {
				klog.ErrorS(err, "failed to get forwarded port for", "auto-pause port", port)
			}
		}
	}

	updated, err := kubeconfig.UpdateEndpoint(cc.Name, co.CP.Hostname, port, kubeconfig.PathFromEnv(), kubeconfig.NewExtension())
	if err != nil {
		klog.ErrorS(err, "failed to update kubeconfig", "auto-pause proxy endpoint")
		return err
	}
	if updated {
		klog.Infof("%s context has been updated to point to auto-pause proxy %s:%s", cc.Name, co.CP.Hostname, co.CP.Port)
	} else {
		klog.Info("no need to update kube-context for auto-pause proxy")
	}

	return nil
}
<commit_msg>enable auto-puase on arm64<commit_after>/*
Copyright 2021 The Kubernetes Authors All rights reserved.

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

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

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

package addons

import (
	"strconv"

	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/kubeconfig"
	"k8s.io/minikube/pkg/minikube/mustload"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
	"k8s.io/minikube/pkg/minikube/sysinit"
)

// enableOrDisableAutoPause enables the service after the config was copied by generic enble
func enableOrDisableAutoPause(cc *config.ClusterConfig, name string, val string) error {
	enable, err := strconv.ParseBool(val)
	if err != nil {
		return errors.Wrapf(err, "parsing bool: %s", name)
	}
	out.Infof("auto-pause addon is an alpha feature and still in early development. Please file issues to help us make it better.")
	out.Infof("https://github.com/kubernetes/minikube/labels/co/auto-pause")

	if cc.KubernetesConfig.ContainerRuntime != "docker" {
		exit.Message(reason.Usage, `auto-pause currently is only supported on docker runtime. Track progress of others here: https://github.com/kubernetes/minikube/issues/10601`)
	}
	co := mustload.Running(cc.Name)
	if enable {
		if err := sysinit.New(co.CP.Runner).EnableNow("auto-pause"); err != nil {
			klog.ErrorS(err, "failed to enable", "service", "auto-pause")
		}
	}

	port := co.CP.Port // api server port
	if enable {        // if enable then need to calculate the forwarded port
		port = constants.AutoPauseProxyPort
		if driver.NeedsPortForward(cc.Driver) {
			port, err = oci.ForwardedPort(cc.Driver, cc.Name, port)
			if err != nil {
				klog.ErrorS(err, "failed to get forwarded port for", "auto-pause port", port)
			}
		}
	}

	updated, err := kubeconfig.UpdateEndpoint(cc.Name, co.CP.Hostname, port, kubeconfig.PathFromEnv(), kubeconfig.NewExtension())
	if err != nil {
		klog.ErrorS(err, "failed to update kubeconfig", "auto-pause proxy endpoint")
		return err
	}
	if updated {
		klog.Infof("%s context has been updated to point to auto-pause proxy %s:%s", cc.Name, co.CP.Hostname, co.CP.Port)
	} else {
		klog.Info("no need to update kube-context for auto-pause proxy")
	}

	return nil
}
<|endoftext|>


<commit_before>package device

import (
	"fmt"
	"net"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/network"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/logger"
)

// deviceCommon represents the common struct for all devices.
type deviceCommon struct {
	logger      logger.Logger
	inst        instance.Instance
	name        string
	config      deviceConfig.Device
	state       *state.State
	volatileGet func() map[string]string
	volatileSet func(map[string]string) error
}

// init stores the Instance, daemon state, device name and config into device.
// It also needs to be provided with volatile get and set functions for the device to allow
// persistent data to be accessed. This is implemented as part of deviceCommon so that the majority
// of devices don't need to implement it and can just embed deviceCommon.
func (d *deviceCommon) init(inst instance.Instance, state *state.State, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) {
	logCtx := logger.Ctx{"driver": conf["type"], "device": name}
	if inst != nil {
		logCtx["project"] = inst.Project()
		logCtx["instance"] = inst.Name()
	}

	d.logger = logger.AddContext(logger.Log, logCtx)
	d.inst = inst
	d.name = name
	d.config = conf
	d.state = state
	d.volatileGet = volatileGet
	d.volatileSet = volatileSet
}

// Name returns the name of the device.
func (d *deviceCommon) Name() string {
	return d.name
}

// Config returns the config for the device.
func (d *deviceCommon) Config() deviceConfig.Device {
	return d.config
}

// Add returns nil error as majority of devices don't need to do any host-side setup.
func (d *deviceCommon) Add() error {
	return nil
}

// Register returns nil error as majority of devices don't need to do any event registration.
func (d *deviceCommon) Register() error {
	return nil
}

// CanHotPlug returns whether the device can be managed whilst the instance is running,
// Returns true if instance type is container, as majority of devices can be started/stopped when
// instance is running. If instance type is VM then returns false as this is not currently supported.
func (d *deviceCommon) CanHotPlug() bool {
	return d.inst.Type() == instancetype.Container
}

// CanMigrate returns whether the device can be migrated to any other cluster member.
func (d *deviceCommon) CanMigrate() bool {
	return false
}

// UpdatableFields returns an empty list of updatable fields as most devices do not support updates.
func (d *deviceCommon) UpdatableFields(oldDevice Type) []string {
	return []string{}
}

// PreStartCheck indicates if the device is available for starting.
func (d *deviceCommon) PreStartCheck() error {
	return nil
}

// Update returns an ErrCannotUpdate error as most devices do not support updates.
func (d *deviceCommon) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
	return ErrCannotUpdate
}

// Remove returns nil error as majority of devices don't need to do any host-side cleanup on delete.
func (d *deviceCommon) Remove() error {
	return nil
}

// generateHostName generates the name to use for the host side NIC interface based on the
// instances.nic.host_name setting.
// Accepts prefix argument to use with random interface generation.
// Accepts optional hwaddr MAC address to use for generating the interface name in mac mode.
// In mac mode the interface prefix is always "lxd".
func (d *deviceCommon) generateHostName(prefix string, hwaddr string) (string, error) {
	hostNameMode := d.state.GlobalConfig.InstancesNICHostname()

	// Handle instances.nic.host_name mac mode if a MAC address has been supplied.
	if hostNameMode == "mac" && hwaddr != "" {
		mac, err := net.ParseMAC(hwaddr)
		if err != nil {
			return "", fmt.Errorf("Failed parsing MAC address %q: %w", hwaddr, err)
		}

		return network.MACDevName(mac), nil
	}

	// Handle instances.nic.host_name random mode or where no MAC address supplied.
	return network.RandomDevName(prefix), nil
}
<commit_msg>lxd/device/device/common: Fix project name in device logger<commit_after>package device

import (
	"fmt"
	"net"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/network"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/logger"
)

// deviceCommon represents the common struct for all devices.
type deviceCommon struct {
	logger      logger.Logger
	inst        instance.Instance
	name        string
	config      deviceConfig.Device
	state       *state.State
	volatileGet func() map[string]string
	volatileSet func(map[string]string) error
}

// init stores the Instance, daemon state, device name and config into device.
// It also needs to be provided with volatile get and set functions for the device to allow
// persistent data to be accessed. This is implemented as part of deviceCommon so that the majority
// of devices don't need to implement it and can just embed deviceCommon.
func (d *deviceCommon) init(inst instance.Instance, state *state.State, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) {
	logCtx := logger.Ctx{"driver": conf["type"], "device": name}
	if inst != nil {
		logCtx["project"] = inst.Project().Name
		logCtx["instance"] = inst.Name()
	}

	d.logger = logger.AddContext(logger.Log, logCtx)
	d.inst = inst
	d.name = name
	d.config = conf
	d.state = state
	d.volatileGet = volatileGet
	d.volatileSet = volatileSet
}

// Name returns the name of the device.
func (d *deviceCommon) Name() string {
	return d.name
}

// Config returns the config for the device.
func (d *deviceCommon) Config() deviceConfig.Device {
	return d.config
}

// Add returns nil error as majority of devices don't need to do any host-side setup.
func (d *deviceCommon) Add() error {
	return nil
}

// Register returns nil error as majority of devices don't need to do any event registration.
func (d *deviceCommon) Register() error {
	return nil
}

// CanHotPlug returns whether the device can be managed whilst the instance is running,
// Returns true if instance type is container, as majority of devices can be started/stopped when
// instance is running. If instance type is VM then returns false as this is not currently supported.
func (d *deviceCommon) CanHotPlug() bool {
	return d.inst.Type() == instancetype.Container
}

// CanMigrate returns whether the device can be migrated to any other cluster member.
func (d *deviceCommon) CanMigrate() bool {
	return false
}

// UpdatableFields returns an empty list of updatable fields as most devices do not support updates.
func (d *deviceCommon) UpdatableFields(oldDevice Type) []string {
	return []string{}
}

// PreStartCheck indicates if the device is available for starting.
func (d *deviceCommon) PreStartCheck() error {
	return nil
}

// Update returns an ErrCannotUpdate error as most devices do not support updates.
func (d *deviceCommon) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
	return ErrCannotUpdate
}

// Remove returns nil error as majority of devices don't need to do any host-side cleanup on delete.
func (d *deviceCommon) Remove() error {
	return nil
}

// generateHostName generates the name to use for the host side NIC interface based on the
// instances.nic.host_name setting.
// Accepts prefix argument to use with random interface generation.
// Accepts optional hwaddr MAC address to use for generating the interface name in mac mode.
// In mac mode the interface prefix is always "lxd".
func (d *deviceCommon) generateHostName(prefix string, hwaddr string) (string, error) {
	hostNameMode := d.state.GlobalConfig.InstancesNICHostname()

	// Handle instances.nic.host_name mac mode if a MAC address has been supplied.
	if hostNameMode == "mac" && hwaddr != "" {
		mac, err := net.ParseMAC(hwaddr)
		if err != nil {
			return "", fmt.Errorf("Failed parsing MAC address %q: %w", hwaddr, err)
		}

		return network.MACDevName(mac), nil
	}

	// Handle instances.nic.host_name random mode or where no MAC address supplied.
	return network.RandomDevName(prefix), nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018, EnMasse authors.
 * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
 */

package v1beta1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type MessagingUser struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   MessagingUserSpec   `json:"spec"`
	Status MessagingUserStatus `json:"status,omitempty"`
}

type MessagingUserSpec struct {
	Username       string              `json:"username"`
	Authentication AuthenticationSpec  `json:"authentication"`
	Authorization  []AuthorizationSpec `json:"authorization,omitempty"`
}

type UserPhase string

const (
	UserPending     UserPhase = "Pending"
	UserConfiguring UserPhase = "Configuring"
	UserActive      UserPhase = "Active"
	UserTerminating UserPhase = "Terminating"
)

type MessagingUserStatus struct {
	Phase      UserPhase `json:"phase,omitempty"`
	Message    string    `json:"message,omitempty"`
	Generation int64     `json:"generation,omitempty"`
}

type AuthenticationType string

const (
	Password       AuthenticationType = "password"
	Federated      AuthenticationType = "federated"
	ServiceAccount AuthenticationType = "serviceaccount"
)

type AuthenticationSpec struct {
	Type     AuthenticationType `json:"type"`
	Password []byte             `json:"password,omitempty"`
	Provider string             `json:"provider,omitempty"`
}

type AuthorizationOperation string

const (
	Send   AuthorizationOperation = "send"
	Recv   AuthorizationOperation = "recv"
	View   AuthorizationOperation = "view"
	Manage AuthorizationOperation = "manage"
)

var Operations = map[string]AuthorizationOperation{
	"send":   Send,
	"recv":   Recv,
	"view":   View,
	"manage": Manage,
}

type AuthorizationSpec struct {
	Addresses  []string                 `json:"addresses"`
	Operations []AuthorizationOperation `json:"operations"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type MessagingUserList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`

	Items []MessagingUser `json:"items"`
}
<commit_msg>Allow addresses field to be optional (#3999)<commit_after>/*
 * Copyright 2018, EnMasse authors.
 * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
 */

package v1beta1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type MessagingUser struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   MessagingUserSpec   `json:"spec"`
	Status MessagingUserStatus `json:"status,omitempty"`
}

type MessagingUserSpec struct {
	Username       string              `json:"username"`
	Authentication AuthenticationSpec  `json:"authentication"`
	Authorization  []AuthorizationSpec `json:"authorization,omitempty"`
}

type UserPhase string

const (
	UserPending     UserPhase = "Pending"
	UserConfiguring UserPhase = "Configuring"
	UserActive      UserPhase = "Active"
	UserTerminating UserPhase = "Terminating"
)

type MessagingUserStatus struct {
	Phase      UserPhase `json:"phase,omitempty"`
	Message    string    `json:"message,omitempty"`
	Generation int64     `json:"generation,omitempty"`
}

type AuthenticationType string

const (
	Password       AuthenticationType = "password"
	Federated      AuthenticationType = "federated"
	ServiceAccount AuthenticationType = "serviceaccount"
)

type AuthenticationSpec struct {
	Type     AuthenticationType `json:"type"`
	Password []byte             `json:"password,omitempty"`
	Provider string             `json:"provider,omitempty"`
}

type AuthorizationOperation string

const (
	Send   AuthorizationOperation = "send"
	Recv   AuthorizationOperation = "recv"
	View   AuthorizationOperation = "view"
	Manage AuthorizationOperation = "manage"
)

var Operations = map[string]AuthorizationOperation{
	"send":   Send,
	"recv":   Recv,
	"view":   View,
	"manage": Manage,
}

type AuthorizationSpec struct {
	Addresses  []string                 `json:"addresses,omitempty"`
	Operations []AuthorizationOperation `json:"operations"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type MessagingUserList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata,omitempty"`

	Items []MessagingUser `json:"items"`
}
<|endoftext|>


<commit_before>package api

import (
	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kruntime "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

// Authorization is calculated against
// 1. all deny RoleBinding PolicyRules in the master namespace - short circuit on match
// 2. all allow RoleBinding PolicyRules in the master namespace - short circuit on match
// 3. all deny RoleBinding PolicyRules in the namespace - short circuit on match
// 4. all allow RoleBinding PolicyRules in the namespace - short circuit on match
// 5. deny by default

const (
	// Policy is a singleton and this is its name
	PolicyName     = "default"
	ResourceAll    = "*"
	VerbAll        = "*"
	NonResourceAll = "*"
)

const (
	// ResourceGroupPrefix is the prefix for indicating that a resource entry is actually a group of resources.  The groups are defined in code and indicate resources that are commonly permissioned together
	ResourceGroupPrefix = "resourcegroup"
	BuildGroupName      = ResourceGroupPrefix + ":builds"
	DeploymentGroupName = ResourceGroupPrefix + ":deployments"
	ImageGroupName      = ResourceGroupPrefix + ":images"
	OAuthGroupName      = ResourceGroupPrefix + ":oauth"
	UserGroupName       = ResourceGroupPrefix + ":users"
	// PolicyOwnerGroupName includes the physical resources behind the PermissionGrantingGroupName.  Unless these physical objects are created first, users with privileges to PermissionGrantingGroupName will
	// only be able to bind to global roles
	PolicyOwnerGroupName = ResourceGroupPrefix + ":policy"
	// PermissionGrantingGroupName includes resources that are necessary to maintain authorization roles and bindings.  By itself, this group is insufficient to create anything except for bindings
	// to master roles.  If a local Policy already exists, then privileges to this group will allow for modification of local roles.
	PermissionGrantingGroupName = ResourceGroupPrefix + ":granter"
	// OpenshiftExposedGroupName includes resources that are commonly viewed and modified by end users of the system.  It does not include any sensitive resources that control authentication or authorization
	OpenshiftExposedGroupName = ResourceGroupPrefix + ":exposedopenshift"
	OpenshiftAllGroupName     = ResourceGroupPrefix + ":allopenshift"

	QuotaGroupName = ResourceGroupPrefix + ":quota"
	// KubeInternalsGroupName includes those resources that should reasonably be viewable to end users, but that most users should probably not modify.  Kubernetes herself will maintain these resources
	KubeInternalsGroupName = ResourceGroupPrefix + ":privatekube"
	// KubeExposedGroupName includes resources that are commonly viewed and modified by end users of the system.
	KubeExposedGroupName = ResourceGroupPrefix + ":exposedkube"
	KubeAllGroupName     = ResourceGroupPrefix + ":allkube"
)

var (
	GroupsToResources = map[string][]string{
		BuildGroupName:              {"builds", "buildconfigs", "buildlogs"},
		ImageGroupName:              {"images", "imagerepositories", "imagerepositorymappings", "imagerepositorytags"},
		DeploymentGroupName:         {"deployments", "deploymentconfigs", "generatedeploymentconfigs", "deploymentconfigrollbacks"},
		UserGroupName:               {"users", "useridentitymappings"},
		OAuthGroupName:              {"oauthauthorizetokens", "oauthaccesstokens", "oauthclients", "oauthclientauthorizations"},
		PolicyOwnerGroupName:        {"policies", "policybindings"},
		PermissionGrantingGroupName: {"roles", "rolebindings", "resourceaccessreviews", "subjectaccessreviews"},
		OpenshiftExposedGroupName:   {BuildGroupName, ImageGroupName, DeploymentGroupName, "templateconfigs", "routes", "projects"},
		OpenshiftAllGroupName:       {OpenshiftExposedGroupName, UserGroupName, OAuthGroupName, PolicyOwnerGroupName, PermissionGrantingGroupName},

		QuotaGroupName:         {"limitranges", "resourcequotas", "resourcequotausages"},
		KubeInternalsGroupName: {"endpoints", "minions", "nodes", "bindings", "events", "namespaces"},
		KubeExposedGroupName:   {"pods", "replicationcontrollers", "services"},
		KubeAllGroupName:       {KubeInternalsGroupName, KubeExposedGroupName, QuotaGroupName},
	}
)

// PolicyRule holds information that describes a policy rule, but does not contain information
// about who the rule applies to or which namespace the rule applies to.
type PolicyRule struct {
	// Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule.  VerbAll represents all kinds.
	Verbs kutil.StringSet
	// AttributeRestrictions will vary depending on what the Authorizer/AuthorizationAttributeBuilder pair supports.
	// If the Authorizer does not recognize how to handle the AttributeRestrictions, the Authorizer should report an error.
	AttributeRestrictions kruntime.EmbeddedObject
	// Resources is a list of resources this rule applies to.  ResourceAll represents all resources.
	Resources kutil.StringSet `json:"resources"`
	// ResourceNames is an optional white list of names that the rule applies to.  An empty set means that everything is allowed.
	ResourceNames kutil.StringSet
	// NonResourceURLs is a set of partial urls that a user should have access to.  *s are allowed, but only as the full, final step in the path
	// If an action is not a resource API request, then the URL is split on '/' and is checked against the NonResourceURLs to look for a match.
	NonResourceURLs kutil.StringSet
}

// Role is a logical grouping of PolicyRules that can be referenced as a unit by RoleBindings.
type Role struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// Rules holds all the PolicyRules for this Role
	Rules []PolicyRule
}

// RoleBinding references a Role, but not contain it.  It can reference any Role in the same namespace or in the global namespace.
// It adds who information via Users and Groups and namespace information by which namespace it exists in.  RoleBindings in a given
// namespace only have effect in that namespace (excepting the master namespace which has power in all namespaces).
type RoleBinding struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// UserNames holds all the usernames directly bound to the role
	Users kutil.StringSet
	// GroupNames holds all the groups directly bound to the role
	Groups kutil.StringSet

	// Since Policy is a singleton, this is sufficient knowledge to locate a role
	// RoleRefs can only reference the current namespace and the global namespace
	// If the RoleRef cannot be resolved, the Authorizer must return an error.
	RoleRef kapi.ObjectReference
}

// Policy is a object that holds all the Roles for a particular namespace.  There is at most
// one Policy document per namespace.
type Policy struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// LastModified is the last time that any part of the Policy was created, updated, or deleted
	LastModified kutil.Time

	// Roles holds all the Roles held by this Policy, mapped by Role.Name
	Roles map[string]Role
}

// PolicyBinding is a object that holds all the RoleBindings for a particular namespace.  There is
// one PolicyBinding document per referenced Policy namespace
type PolicyBinding struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// LastModified is the last time that any part of the PolicyBinding was created, updated, or deleted
	LastModified kutil.Time

	// PolicyRef is a reference to the Policy that contains all the Roles that this PolicyBinding's RoleBindings may reference
	PolicyRef kapi.ObjectReference
	// RoleBindings holds all the RoleBindings held by this PolicyBinding, mapped by RoleBinding.Name
	RoleBindings map[string]RoleBinding
}

// ResourceAccessReviewResponse describes who can perform the action
type ResourceAccessReviewResponse struct {
	kapi.TypeMeta

	// Namespace is the namespace used for the access review
	Namespace string
	// Users is the list of users who can perform the action
	Users []string
	// Groups is the list of groups who can perform the action
	Groups []string
}

// ResourceAccessReview is a means to request a list of which users and groups are authorized to perform the
// action specified by spec
type ResourceAccessReview struct {
	kapi.TypeMeta

	// Verb is one of: get, list, watch, create, update, delete
	Verb string
	// Resource is one of the existing resource types
	Resource string
	// Content is the actual content of the request for create and update
	Content kruntime.EmbeddedObject
	// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete"
	ResourceName string
}

// SubjectAccessReviewResponse describes whether or not a user or group can perform an action
type SubjectAccessReviewResponse struct {
	kapi.TypeMeta

	// Namespace is the namespace used for the access review
	Namespace string
	// Allowed is required.  True if the action would be allowed, false otherwise.
	Allowed bool
	// Reason is optional.  It indicates why a request was allowed or denied.
	Reason string
}

// SubjectAccessReview is an object for requesting information about whether a user or group can perform an action
type SubjectAccessReview struct {
	kapi.TypeMeta

	// Verb is one of: get, list, watch, create, update, delete
	Verb string
	// Resource is one of the existing resource types
	Resource string
	// User is optional.  If both User and Groups are empty, the current authenticated user is used.
	User string
	// Groups is optional.  Groups is the list of groups to which the User belongs.
	Groups []string
	// Content is the actual content of the request for create and update
	Content kruntime.EmbeddedObject
	// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete"
	ResourceName string
}

// PolicyList is a collection of Policies
type PolicyList struct {
	kapi.TypeMeta
	kapi.ListMeta
	Items []Policy
}

// PolicyBindingList is a collection of PolicyBindings
type PolicyBindingList struct {
	kapi.TypeMeta
	kapi.ListMeta
	Items []PolicyBinding
}

// RoleBindingList is a collection of PolicyBindings
type RoleBindingList struct {
	kapi.TypeMeta
	kapi.ListMeta
	Items []RoleBinding
}
<commit_msg>Add templates to list of authorized resources<commit_after>package api

import (
	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kruntime "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

// Authorization is calculated against
// 1. all deny RoleBinding PolicyRules in the master namespace - short circuit on match
// 2. all allow RoleBinding PolicyRules in the master namespace - short circuit on match
// 3. all deny RoleBinding PolicyRules in the namespace - short circuit on match
// 4. all allow RoleBinding PolicyRules in the namespace - short circuit on match
// 5. deny by default

const (
	// Policy is a singleton and this is its name
	PolicyName     = "default"
	ResourceAll    = "*"
	VerbAll        = "*"
	NonResourceAll = "*"
)

const (
	// ResourceGroupPrefix is the prefix for indicating that a resource entry is actually a group of resources.  The groups are defined in code and indicate resources that are commonly permissioned together
	ResourceGroupPrefix = "resourcegroup"
	BuildGroupName      = ResourceGroupPrefix + ":builds"
	DeploymentGroupName = ResourceGroupPrefix + ":deployments"
	ImageGroupName      = ResourceGroupPrefix + ":images"
	OAuthGroupName      = ResourceGroupPrefix + ":oauth"
	UserGroupName       = ResourceGroupPrefix + ":users"
	// PolicyOwnerGroupName includes the physical resources behind the PermissionGrantingGroupName.  Unless these physical objects are created first, users with privileges to PermissionGrantingGroupName will
	// only be able to bind to global roles
	PolicyOwnerGroupName = ResourceGroupPrefix + ":policy"
	// PermissionGrantingGroupName includes resources that are necessary to maintain authorization roles and bindings.  By itself, this group is insufficient to create anything except for bindings
	// to master roles.  If a local Policy already exists, then privileges to this group will allow for modification of local roles.
	PermissionGrantingGroupName = ResourceGroupPrefix + ":granter"
	// OpenshiftExposedGroupName includes resources that are commonly viewed and modified by end users of the system.  It does not include any sensitive resources that control authentication or authorization
	OpenshiftExposedGroupName = ResourceGroupPrefix + ":exposedopenshift"
	OpenshiftAllGroupName     = ResourceGroupPrefix + ":allopenshift"

	QuotaGroupName = ResourceGroupPrefix + ":quota"
	// KubeInternalsGroupName includes those resources that should reasonably be viewable to end users, but that most users should probably not modify.  Kubernetes herself will maintain these resources
	KubeInternalsGroupName = ResourceGroupPrefix + ":privatekube"
	// KubeExposedGroupName includes resources that are commonly viewed and modified by end users of the system.
	KubeExposedGroupName = ResourceGroupPrefix + ":exposedkube"
	KubeAllGroupName     = ResourceGroupPrefix + ":allkube"
)

var (
	GroupsToResources = map[string][]string{
		BuildGroupName:              {"builds", "buildconfigs", "buildlogs"},
		ImageGroupName:              {"images", "imagerepositories", "imagerepositorymappings", "imagerepositorytags"},
		DeploymentGroupName:         {"deployments", "deploymentconfigs", "generatedeploymentconfigs", "deploymentconfigrollbacks"},
		UserGroupName:               {"users", "useridentitymappings"},
		OAuthGroupName:              {"oauthauthorizetokens", "oauthaccesstokens", "oauthclients", "oauthclientauthorizations"},
		PolicyOwnerGroupName:        {"policies", "policybindings"},
		PermissionGrantingGroupName: {"roles", "rolebindings", "resourceaccessreviews", "subjectaccessreviews"},
		OpenshiftExposedGroupName:   {BuildGroupName, ImageGroupName, DeploymentGroupName, "templates", "templateconfigs", "routes", "projects"},
		OpenshiftAllGroupName:       {OpenshiftExposedGroupName, UserGroupName, OAuthGroupName, PolicyOwnerGroupName, PermissionGrantingGroupName},

		QuotaGroupName:         {"limitranges", "resourcequotas", "resourcequotausages"},
		KubeInternalsGroupName: {"endpoints", "minions", "nodes", "bindings", "events", "namespaces"},
		KubeExposedGroupName:   {"pods", "replicationcontrollers", "services"},
		KubeAllGroupName:       {KubeInternalsGroupName, KubeExposedGroupName, QuotaGroupName},
	}
)

// PolicyRule holds information that describes a policy rule, but does not contain information
// about who the rule applies to or which namespace the rule applies to.
type PolicyRule struct {
	// Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule.  VerbAll represents all kinds.
	Verbs kutil.StringSet
	// AttributeRestrictions will vary depending on what the Authorizer/AuthorizationAttributeBuilder pair supports.
	// If the Authorizer does not recognize how to handle the AttributeRestrictions, the Authorizer should report an error.
	AttributeRestrictions kruntime.EmbeddedObject
	// Resources is a list of resources this rule applies to.  ResourceAll represents all resources.
	Resources kutil.StringSet `json:"resources"`
	// ResourceNames is an optional white list of names that the rule applies to.  An empty set means that everything is allowed.
	ResourceNames kutil.StringSet
	// NonResourceURLs is a set of partial urls that a user should have access to.  *s are allowed, but only as the full, final step in the path
	// If an action is not a resource API request, then the URL is split on '/' and is checked against the NonResourceURLs to look for a match.
	NonResourceURLs kutil.StringSet
}

// Role is a logical grouping of PolicyRules that can be referenced as a unit by RoleBindings.
type Role struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// Rules holds all the PolicyRules for this Role
	Rules []PolicyRule
}

// RoleBinding references a Role, but not contain it.  It can reference any Role in the same namespace or in the global namespace.
// It adds who information via Users and Groups and namespace information by which namespace it exists in.  RoleBindings in a given
// namespace only have effect in that namespace (excepting the master namespace which has power in all namespaces).
type RoleBinding struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// UserNames holds all the usernames directly bound to the role
	Users kutil.StringSet
	// GroupNames holds all the groups directly bound to the role
	Groups kutil.StringSet

	// Since Policy is a singleton, this is sufficient knowledge to locate a role
	// RoleRefs can only reference the current namespace and the global namespace
	// If the RoleRef cannot be resolved, the Authorizer must return an error.
	RoleRef kapi.ObjectReference
}

// Policy is a object that holds all the Roles for a particular namespace.  There is at most
// one Policy document per namespace.
type Policy struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// LastModified is the last time that any part of the Policy was created, updated, or deleted
	LastModified kutil.Time

	// Roles holds all the Roles held by this Policy, mapped by Role.Name
	Roles map[string]Role
}

// PolicyBinding is a object that holds all the RoleBindings for a particular namespace.  There is
// one PolicyBinding document per referenced Policy namespace
type PolicyBinding struct {
	kapi.TypeMeta
	kapi.ObjectMeta

	// LastModified is the last time that any part of the PolicyBinding was created, updated, or deleted
	LastModified kutil.Time

	// PolicyRef is a reference to the Policy that contains all the Roles that this PolicyBinding's RoleBindings may reference
	PolicyRef kapi.ObjectReference
	// RoleBindings holds all the RoleBindings held by this PolicyBinding, mapped by RoleBinding.Name
	RoleBindings map[string]RoleBinding
}

// ResourceAccessReviewResponse describes who can perform the action
type ResourceAccessReviewResponse struct {
	kapi.TypeMeta

	// Namespace is the namespace used for the access review
	Namespace string
	// Users is the list of users who can perform the action
	Users []string
	// Groups is the list of groups who can perform the action
	Groups []string
}

// ResourceAccessReview is a means to request a list of which users and groups are authorized to perform the
// action specified by spec
type ResourceAccessReview struct {
	kapi.TypeMeta

	// Verb is one of: get, list, watch, create, update, delete
	Verb string
	// Resource is one of the existing resource types
	Resource string
	// Content is the actual content of the request for create and update
	Content kruntime.EmbeddedObject
	// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete"
	ResourceName string
}

// SubjectAccessReviewResponse describes whether or not a user or group can perform an action
type SubjectAccessReviewResponse struct {
	kapi.TypeMeta

	// Namespace is the namespace used for the access review
	Namespace string
	// Allowed is required.  True if the action would be allowed, false otherwise.
	Allowed bool
	// Reason is optional.  It indicates why a request was allowed or denied.
	Reason string
}

// SubjectAccessReview is an object for requesting information about whether a user or group can perform an action
type SubjectAccessReview struct {
	kapi.TypeMeta

	// Verb is one of: get, list, watch, create, update, delete
	Verb string
	// Resource is one of the existing resource types
	Resource string
	// User is optional.  If both User and Groups are empty, the current authenticated user is used.
	User string
	// Groups is optional.  Groups is the list of groups to which the User belongs.
	Groups []string
	// Content is the actual content of the request for create and update
	Content kruntime.EmbeddedObject
	// ResourceName is the name of the resource being requested for a "get" or deleted for a "delete"
	ResourceName string
}

// PolicyList is a collection of Policies
type PolicyList struct {
	kapi.TypeMeta
	kapi.ListMeta
	Items []Policy
}

// PolicyBindingList is a collection of PolicyBindings
type PolicyBindingList struct {
	kapi.TypeMeta
	kapi.ListMeta
	Items []PolicyBinding
}

// RoleBindingList is a collection of PolicyBindings
type RoleBindingList struct {
	kapi.TypeMeta
	kapi.ListMeta
	Items []RoleBinding
}
<|endoftext|>