<commit_before>/*
 * Minio Cloud Storage, (C) 2015 Minio, 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 main

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/minio-xl/pkg/minhttp"
	"github.com/minio/minio-xl/pkg/probe"
)

var serverCmd = cli.Command{
	Name:   "server",
	Usage:  "Start Minio cloud storage server.",
	Action: serverMain,
	CustomHelpTemplate: `NAME:
  minio {{.Name}} - {{.Usage}}

USAGE:
  minio {{.Name}} [OPTION VALUE] PATH

  OPTION = expiry        VALUE = NN[h|m|s] [DEFAULT=Unlimited]
  OPTION = min-free-disk VALUE = NN% [DEFAULT: 10%]

EXAMPLES:
  1. Start minio server on Linux.
      $ minio {{.Name}} /home/shared

  2. Start minio server on Windows.
      $ minio {{.Name}} C:\MyShare

  3. Start minio server bound to a specific IP:PORT, when you have multiple network interfaces.
      $ minio --address 192.168.1.101:9000 {{.Name}} /home/shared

  4. Start minio server with minimum free disk threshold to 5%
      $ minio {{.Name}} min-free-disk 5% /home/shared/Pictures

  5. Start minio server with minimum free disk threshold to 15% with auto expiration set to 1h
      $ minio {{.Name}} min-free-disk 15% expiry 1h /home/shared/Documents

`,
}

// cloudServerConfig - http server config
type cloudServerConfig struct {
	/// HTTP server options
	Address   string // Address:Port listening
	AccessLog bool   // Enable access log handler
	Anonymous bool   // No signature turn off

	/// FS options
	Path        string        // Path to export for cloud storage
	MinFreeDisk int64         // Minimum free disk space for filesystem
	Expiry      time.Duration // Set auto expiry for filesystem

	/// TLS service
	TLS      bool   // TLS on when certs are specified
	CertFile string // Domain certificate
	KeyFile  string // Domain key

	/// Advanced HTTP server options
	RateLimit int // Ratelimited server of incoming connections
}

// configureAPIServer configure a new server instance
func configureAPIServer(conf cloudServerConfig) (*http.Server, *probe.Error) {
	// Minio server config
	apiServer := &http.Server{
		Addr:           conf.Address,
		Handler:        getCloudStorageAPIHandler(getNewCloudStorageAPI(conf)),
		MaxHeaderBytes: 1 << 20,
	}

	if conf.TLS {
		var err error
		apiServer.TLSConfig = &tls.Config{}
		apiServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
		apiServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(conf.CertFile, conf.KeyFile)
		if err != nil {
			return nil, probe.NewError(err)
		}
	}

	host, port, err := net.SplitHostPort(conf.Address)
	if err != nil {
		return nil, probe.NewError(err)
	}

	var hosts []string
	switch {
	case host != "":
		hosts = append(hosts, host)
	default:
		addrs, err := net.InterfaceAddrs()
		if err != nil {
			return nil, probe.NewError(err)
		}
		for _, addr := range addrs {
			if addr.Network() == "ip+net" {
				host := strings.Split(addr.String(), "/")[0]
				if ip := net.ParseIP(host); ip.To4() != nil {
					hosts = append(hosts, host)
				}
			}
		}
	}

	Println("Starting minio server:")
	for _, host := range hosts {
		if conf.TLS {
			Printf("Listening on https://%s:%s\n", host, port)
		} else {
			Printf("Listening on http://%s:%s\n", host, port)
		}
	}
	return apiServer, nil
}

// startServer starts an s3 compatible cloud storage server
func startServer(conf cloudServerConfig) *probe.Error {
	apiServer, err := configureAPIServer(conf)
	if err != nil {
		return err.Trace()
	}
	rateLimit := conf.RateLimit
	if err := minhttp.ListenAndServeLimited(rateLimit, apiServer); err != nil {
		return err.Trace()
	}
	return nil
}

// parse input string with percent to int64
func parsePercentToInt(s string, bitSize int) (int64, *probe.Error) {
	i := strings.Index(s, "%")
	if i < 0 {
		// no percentage string found try to parse the whole string anyways
		p, err := strconv.ParseInt(s, 10, bitSize)
		if err != nil {
			return 0, probe.NewError(err)
		}
		return p, nil
	}
	p, err := strconv.ParseInt(s[:i], 10, bitSize)
	if err != nil {
		return 0, probe.NewError(err)
	}
	return p, nil
}
func setLogger(conf *configV2) *probe.Error {
	if conf.IsMongoLoggingEnabled() {
		err := log2Mongo(conf.MongoLogger.Addr, conf.MongoLogger.DB, conf.MongoLogger.Collection)
		if err != nil {
			return err.Trace()
		}
	}
	if conf.IsSysloggingEnabled() {
		err := log2Syslog(conf.SyslogLogger.Network, conf.SyslogLogger.Addr)
		if err != nil {
			return err.Trace()
		}
	}
	if conf.IsFileLoggingEnabled() {
		err := log2File(conf.FileLogger.Filename)
		if err != nil {
			return err.Trace()
		}
	}
	return nil
}

// Generates config if it doesn't exist, otherwise returns back the saved ones.
func getConfig() (*configV2, *probe.Error) {
	if err := createConfigPath(); err != nil {
		return nil, err.Trace()
	}
	config, err := loadConfigV2()
	if err != nil {
		if os.IsNotExist(err.ToGoError()) {
			// Initialize new config, since config file doesn't exist yet
			config := &configV2{}
			config.Version = "2"
			config.Credentials.AccessKeyID = string(mustGenerateAccessKeyID())
			config.Credentials.SecretAccessKey = string(mustGenerateSecretAccessKey())
			config.Credentials.Region = "us-east-1"
			if err := saveConfig(config); err != nil {
				return nil, err.Trace()
			}
			return config, nil
		}
		return nil, err.Trace()
	}
	return config, nil
}

type accessKeys struct {
	*configV2
}

func (a accessKeys) String() string {
	magenta := color.New(color.FgMagenta, color.Bold).SprintFunc()
	white := color.New(color.FgWhite, color.Bold).SprintfFunc()
	return fmt.Sprint(magenta("AccessKey: ") + white(a.Credentials.AccessKeyID) + "  " + magenta("SecretKey: ") + white(a.Credentials.SecretAccessKey))
}

// JSON - json formatted output
func (a accessKeys) JSON() string {
	b, err := json.Marshal(a)
	errorIf(probe.NewError(err), "Unable to marshal json", nil)
	return string(b)
}

// initServer initialize server
func initServer() *probe.Error {
	conf, err := getConfig()
	if err != nil {
		return err.Trace()
	}
	if err := setLogger(conf); err != nil {
		return err.Trace()
	}
	if conf != nil {
		Println()
		Println(accessKeys{conf})
	}
	Println("\nTo configure Minio Client.")
	if runtime.GOOS == "windows" {
		Println("\n\tDownload https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc.exe")
		Println("\t$ mc.exe config host add http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey)
		Println("\t$ mc.exe mb localhost:9000/photobucket")
		Println("\t$ mc.exe cp --recursive C:\\Photos localhost:9000/photobucket")
	} else {
		Println("\n\t$ wget https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc")
		Println("\t$ chmod 755 mc")
		Println("\t$ ./mc config host add http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey)
		Println("\t$ ./mc mb localhost:9000/photobucket")
		Println("\t$ ./mc cp --recursive ~/Photos localhost:9000/photobucket")
	}
	Println()
	return nil
}

func checkServerSyntax(c *cli.Context) {
	if !c.Args().Present() || c.Args().First() == "help" {
		cli.ShowCommandHelpAndExit(c, "server", 1)
	}
	if len(c.Args()) > 5 {
		fatalIf(probe.NewError(errInvalidArgument), "Unnecessary arguments passed. Please refer ‘mc server help’", nil)
	}
	path := strings.TrimSpace(c.Args().Last())
	if path == "" {
		fatalIf(probe.NewError(errInvalidArgument), "Path argument cannot be empty.", nil)
	}
}

func serverMain(c *cli.Context) {
	checkServerSyntax(c)

	perr := initServer()
	fatalIf(perr.Trace(), "Failed to read config for minio.", nil)

	certFile := c.GlobalString("cert")
	keyFile := c.GlobalString("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		fatalIf(probe.NewError(errInvalidArgument), "Both certificate and key are required to enable https.", nil)
	}

	var minFreeDisk int64
	minFreeDiskSet := false
	// Default
	minFreeDisk = 10

	var expiration time.Duration
	expirationSet := false

	args := c.Args()
	for len(args) >= 2 {
		switch args.First() {
		case "min-free-disk":
			if minFreeDiskSet {
				fatalIf(probe.NewError(errInvalidArgument), "Minimum free disk should be set only once.", nil)
			}
			args = args.Tail()
			var err *probe.Error
			minFreeDisk, err = parsePercentToInt(args.First(), 64)
			fatalIf(err.Trace(args.First()), "Invalid minium free disk size "+args.First()+" passed.", nil)
			args = args.Tail()
			minFreeDiskSet = true
		case "expiry":
			if expirationSet {
				fatalIf(probe.NewError(errInvalidArgument), "Expiration should be set only once.", nil)
			}
			args = args.Tail()
			var err error
			expiration, err = time.ParseDuration(args.First())
			fatalIf(probe.NewError(err), "Invalid expiration time "+args.First()+" passed.", nil)
			args = args.Tail()
			expirationSet = true
		default:
			cli.ShowCommandHelpAndExit(c, "server", 1) // last argument is exit code
		}
	}

	path := strings.TrimSpace(c.Args().Last())
	// Last argument is always path
	if _, err := os.Stat(path); err != nil {
		fatalIf(probe.NewError(err), "Unable to validate the path", nil)
	}
	tls := (certFile != "" && keyFile != "")
	apiServerConfig := cloudServerConfig{
		Address:     c.GlobalString("address"),
		AccessLog:   c.GlobalBool("enable-accesslog"),
		Anonymous:   c.GlobalBool("anonymous"),
		Path:        path,
		MinFreeDisk: minFreeDisk,
		Expiry:      expiration,
		TLS:         tls,
		CertFile:    certFile,
		KeyFile:     keyFile,
		RateLimit:   c.GlobalInt("ratelimit"),
	}
	perr = startServer(apiServerConfig)
	errorIf(perr.Trace(), "Failed to start the minio server.", nil)
}
<commit_msg>server: Fix shadowing bug reported by go vet on go1.6beta2<commit_after>/*
 * Minio Cloud Storage, (C) 2015 Minio, 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 main

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/minio/cli"
	"github.com/minio/minio-xl/pkg/minhttp"
	"github.com/minio/minio-xl/pkg/probe"
)

var serverCmd = cli.Command{
	Name:   "server",
	Usage:  "Start Minio cloud storage server.",
	Action: serverMain,
	CustomHelpTemplate: `NAME:
  minio {{.Name}} - {{.Usage}}

USAGE:
  minio {{.Name}} [OPTION VALUE] PATH

  OPTION = expiry        VALUE = NN[h|m|s] [DEFAULT=Unlimited]
  OPTION = min-free-disk VALUE = NN% [DEFAULT: 10%]

EXAMPLES:
  1. Start minio server on Linux.
      $ minio {{.Name}} /home/shared

  2. Start minio server on Windows.
      $ minio {{.Name}} C:\MyShare

  3. Start minio server bound to a specific IP:PORT, when you have multiple network interfaces.
      $ minio --address 192.168.1.101:9000 {{.Name}} /home/shared

  4. Start minio server with minimum free disk threshold to 5%
      $ minio {{.Name}} min-free-disk 5% /home/shared/Pictures

  5. Start minio server with minimum free disk threshold to 15% with auto expiration set to 1h
      $ minio {{.Name}} min-free-disk 15% expiry 1h /home/shared/Documents

`,
}

// cloudServerConfig - http server config
type cloudServerConfig struct {
	/// HTTP server options
	Address   string // Address:Port listening
	AccessLog bool   // Enable access log handler
	Anonymous bool   // No signature turn off

	/// FS options
	Path        string        // Path to export for cloud storage
	MinFreeDisk int64         // Minimum free disk space for filesystem
	Expiry      time.Duration // Set auto expiry for filesystem

	/// TLS service
	TLS      bool   // TLS on when certs are specified
	CertFile string // Domain certificate
	KeyFile  string // Domain key

	/// Advanced HTTP server options
	RateLimit int // Ratelimited server of incoming connections
}

// configureAPIServer configure a new server instance
func configureAPIServer(conf cloudServerConfig) (*http.Server, *probe.Error) {
	// Minio server config
	apiServer := &http.Server{
		Addr:           conf.Address,
		Handler:        getCloudStorageAPIHandler(getNewCloudStorageAPI(conf)),
		MaxHeaderBytes: 1 << 20,
	}

	if conf.TLS {
		var err error
		apiServer.TLSConfig = &tls.Config{}
		apiServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
		apiServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(conf.CertFile, conf.KeyFile)
		if err != nil {
			return nil, probe.NewError(err)
		}
	}

	host, port, err := net.SplitHostPort(conf.Address)
	if err != nil {
		return nil, probe.NewError(err)
	}

	var hosts []string
	switch {
	case host != "":
		hosts = append(hosts, host)
	default:
		addrs, err := net.InterfaceAddrs()
		if err != nil {
			return nil, probe.NewError(err)
		}
		for _, addr := range addrs {
			if addr.Network() == "ip+net" {
				host := strings.Split(addr.String(), "/")[0]
				if ip := net.ParseIP(host); ip.To4() != nil {
					hosts = append(hosts, host)
				}
			}
		}
	}

	Println("Starting minio server:")
	for _, host := range hosts {
		if conf.TLS {
			Printf("Listening on https://%s:%s\n", host, port)
		} else {
			Printf("Listening on http://%s:%s\n", host, port)
		}
	}
	return apiServer, nil
}

// startServer starts an s3 compatible cloud storage server
func startServer(conf cloudServerConfig) *probe.Error {
	apiServer, err := configureAPIServer(conf)
	if err != nil {
		return err.Trace()
	}
	rateLimit := conf.RateLimit
	if err := minhttp.ListenAndServeLimited(rateLimit, apiServer); err != nil {
		return err.Trace()
	}
	return nil
}

// parse input string with percent to int64
func parsePercentToInt(s string, bitSize int) (int64, *probe.Error) {
	i := strings.Index(s, "%")
	if i < 0 {
		// no percentage string found try to parse the whole string anyways
		p, err := strconv.ParseInt(s, 10, bitSize)
		if err != nil {
			return 0, probe.NewError(err)
		}
		return p, nil
	}
	p, err := strconv.ParseInt(s[:i], 10, bitSize)
	if err != nil {
		return 0, probe.NewError(err)
	}
	return p, nil
}
func setLogger(conf *configV2) *probe.Error {
	if conf.IsMongoLoggingEnabled() {
		err := log2Mongo(conf.MongoLogger.Addr, conf.MongoLogger.DB, conf.MongoLogger.Collection)
		if err != nil {
			return err.Trace()
		}
	}
	if conf.IsSysloggingEnabled() {
		err := log2Syslog(conf.SyslogLogger.Network, conf.SyslogLogger.Addr)
		if err != nil {
			return err.Trace()
		}
	}
	if conf.IsFileLoggingEnabled() {
		err := log2File(conf.FileLogger.Filename)
		if err != nil {
			return err.Trace()
		}
	}
	return nil
}

// Generates config if it doesn't exist, otherwise returns back the saved ones.
func getConfig() (*configV2, *probe.Error) {
	if err := createConfigPath(); err != nil {
		return nil, err.Trace()
	}
	config, err := loadConfigV2()
	if err != nil {
		if os.IsNotExist(err.ToGoError()) {
			// Initialize new config, since config file doesn't exist yet
			config = &configV2{}
			config.Version = "2"
			config.Credentials.AccessKeyID = string(mustGenerateAccessKeyID())
			config.Credentials.SecretAccessKey = string(mustGenerateSecretAccessKey())
			config.Credentials.Region = "us-east-1"
			if err = saveConfig(config); err != nil {
				return nil, err.Trace()
			}
			return config, nil
		}
		return nil, err.Trace()
	}
	return config, nil
}

type accessKeys struct {
	*configV2
}

func (a accessKeys) String() string {
	magenta := color.New(color.FgMagenta, color.Bold).SprintFunc()
	white := color.New(color.FgWhite, color.Bold).SprintfFunc()
	return fmt.Sprint(magenta("AccessKey: ") + white(a.Credentials.AccessKeyID) + "  " + magenta("SecretKey: ") + white(a.Credentials.SecretAccessKey))
}

// JSON - json formatted output
func (a accessKeys) JSON() string {
	b, err := json.Marshal(a)
	errorIf(probe.NewError(err), "Unable to marshal json", nil)
	return string(b)
}

// initServer initialize server
func initServer() *probe.Error {
	conf, err := getConfig()
	if err != nil {
		return err.Trace()
	}
	if err := setLogger(conf); err != nil {
		return err.Trace()
	}
	if conf != nil {
		Println()
		Println(accessKeys{conf})
	}
	Println("\nTo configure Minio Client.")
	if runtime.GOOS == "windows" {
		Println("\n\tDownload https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc.exe")
		Println("\t$ mc.exe config host add http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey)
		Println("\t$ mc.exe mb localhost:9000/photobucket")
		Println("\t$ mc.exe cp --recursive C:\\Photos localhost:9000/photobucket")
	} else {
		Println("\n\t$ wget https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc")
		Println("\t$ chmod 755 mc")
		Println("\t$ ./mc config host add http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey)
		Println("\t$ ./mc mb localhost:9000/photobucket")
		Println("\t$ ./mc cp --recursive ~/Photos localhost:9000/photobucket")
	}
	Println()
	return nil
}

func checkServerSyntax(c *cli.Context) {
	if !c.Args().Present() || c.Args().First() == "help" {
		cli.ShowCommandHelpAndExit(c, "server", 1)
	}
	if len(c.Args()) > 5 {
		fatalIf(probe.NewError(errInvalidArgument), "Unnecessary arguments passed. Please refer ‘mc server help’", nil)
	}
	path := strings.TrimSpace(c.Args().Last())
	if path == "" {
		fatalIf(probe.NewError(errInvalidArgument), "Path argument cannot be empty.", nil)
	}
}

func serverMain(c *cli.Context) {
	checkServerSyntax(c)

	perr := initServer()
	fatalIf(perr.Trace(), "Failed to read config for minio.", nil)

	certFile := c.GlobalString("cert")
	keyFile := c.GlobalString("key")
	if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") {
		fatalIf(probe.NewError(errInvalidArgument), "Both certificate and key are required to enable https.", nil)
	}

	var minFreeDisk int64
	minFreeDiskSet := false
	// Default
	minFreeDisk = 10

	var expiration time.Duration
	expirationSet := false

	args := c.Args()
	for len(args) >= 2 {
		switch args.First() {
		case "min-free-disk":
			if minFreeDiskSet {
				fatalIf(probe.NewError(errInvalidArgument), "Minimum free disk should be set only once.", nil)
			}
			args = args.Tail()
			var err *probe.Error
			minFreeDisk, err = parsePercentToInt(args.First(), 64)
			fatalIf(err.Trace(args.First()), "Invalid minium free disk size "+args.First()+" passed.", nil)
			args = args.Tail()
			minFreeDiskSet = true
		case "expiry":
			if expirationSet {
				fatalIf(probe.NewError(errInvalidArgument), "Expiration should be set only once.", nil)
			}
			args = args.Tail()
			var err error
			expiration, err = time.ParseDuration(args.First())
			fatalIf(probe.NewError(err), "Invalid expiration time "+args.First()+" passed.", nil)
			args = args.Tail()
			expirationSet = true
		default:
			cli.ShowCommandHelpAndExit(c, "server", 1) // last argument is exit code
		}
	}

	path := strings.TrimSpace(c.Args().Last())
	// Last argument is always path
	if _, err := os.Stat(path); err != nil {
		fatalIf(probe.NewError(err), "Unable to validate the path", nil)
	}
	tls := (certFile != "" && keyFile != "")
	apiServerConfig := cloudServerConfig{
		Address:     c.GlobalString("address"),
		AccessLog:   c.GlobalBool("enable-accesslog"),
		Anonymous:   c.GlobalBool("anonymous"),
		Path:        path,
		MinFreeDisk: minFreeDisk,
		Expiry:      expiration,
		TLS:         tls,
		CertFile:    certFile,
		KeyFile:     keyFile,
		RateLimit:   c.GlobalInt("ratelimit"),
	}
	perr = startServer(apiServerConfig)
	errorIf(perr.Trace(), "Failed to start the minio server.", nil)
}
<|endoftext|>


<commit_before>package permissions

// Middleware handler for handling requests and permissions

import (
	"fmt"
	"net/http"
	"strings"
)

type Permissions struct {
	state              *UserState
	adminPathPrefixes  []string
	userPathPrefixes   []string
	publicPathPrefixes []string
	rootIsPublic       bool
	denied             http.HandlerFunc
}

const (
	// Version number. Stable API within major version numbers.
	Version = 1.0
)

// Initialize a Permissions struct with all the default settings.
// This will also connect to the redis host at localhost:6379.
func New() *Permissions {
	return NewPermissions(NewUserStateSimple())
}

// Initialize a Permissions struct with Redis DB index and host:port
func NewWithRedisConf(dbindex int, hostPort string) *Permissions {
	return NewPermissions(NewUserState(dbindex, true, hostPort))
}

// Initialize a Permissions struct with the given UserState and
// a few default paths for admin/user/public path prefixes.
func NewPermissions(state *UserState) *Permissions {
	// default permissions
	return &Permissions{state,
		[]string{"/admin"},         // admin path prefixes
		[]string{"/repo", "/data"}, // user path prefixes
		[]string{"/", "/login", "/register", "/favicon.ico", "/style", "/img", "/js",
			"/favicon.ico", "/robots.txt", "/sitemap_index.xml"}, // public
		true,
		PermissionDenied}
}

// Specify the http.HandlerFunc for when the permissions are denied
func (perm *Permissions) SetDenyFunction(f http.HandlerFunc) {
	perm.denied = f
}

// Retrieve the UserState struct
func (perm *Permissions) UserState() *UserState {
	return perm.state
}

// Add an url path prefix that is a page for the logged in administrators
func (perm *Permissions) AddAdminPath(prefix string) {
	perm.adminPathPrefixes = append(perm.adminPathPrefixes, prefix)
}

// Add an url path prefix that is a page for the logged in users
func (perm *Permissions) AddUserPath(prefix string) {
	perm.userPathPrefixes = append(perm.userPathPrefixes, prefix)
}

// Add an url path prefix that is a public page
func (perm *Permissions) AddPublicPath(prefix string) {
	perm.publicPathPrefixes = append(perm.publicPathPrefixes, prefix)
}

// Set all url path prefixes that are for the logged in administrator pages
func (perm *Permissions) SetAdminPath(pathPrefixes []string) {
	perm.adminPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the logged in user pages
func (perm *Permissions) SetUserPath(pathPrefixes []string) {
	perm.userPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the public pages
func (perm *Permissions) SetPublicPath(pathPrefixes []string) {
	perm.publicPathPrefixes = pathPrefixes
}

// The default "permission denied" http handler
func PermissionDenied(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(rw, "Permission denied.")
}

// Check if a given request should be rejected.
// Also calls the permission denied function, if needed.
// If that is the case, the next middleware handler should not be called.
func (perm *Permissions) Rejected(rw http.ResponseWriter, req *http.Request) bool {
	path := req.URL.Path // the path of the url that the user wish to visit
	reject := false

	// If it's not "/" and set to be public regardless of permissions
	if !(perm.rootIsPublic && path == "/") {

		// Reject if it is an admin page and user does not have admin permissions
		for _, prefix := range perm.adminPathPrefixes {
			if strings.HasPrefix(path, prefix) {
				if !perm.state.AdminRights(req) {
					reject = true
					break
				}
			}
		}

		if !reject {
			// Reject if it's a user page and the user does not have user rights
			for _, prefix := range perm.userPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					if !perm.state.UserRights(req) {
						reject = true
						break
					}
				}
			}
		}

		if !reject {
			// Reject if it's not a public page
			found := false
			for _, prefix := range perm.publicPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					found = true
					break
				}
			}
			if !found {
				reject = true
			}
		}

	}

	if reject {
		// Permission denied function
		perm.denied(rw, req)
	}

	return reject

}

// Negroni middleware handler
func (perm *Permissions) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	// Check if the user has the right admin/user rights
	if perm.Rejected(rw, req) {
		// Reject the request by not calling the next handler below
		return
	}

	// Call the next middleware handler
	next(rw, req)
}
<commit_msg>Refactoring<commit_after>package permissions

// Middleware handler for handling requests and permissions

import (
	"fmt"
	"net/http"
	"strings"
)

type Permissions struct {
	state              *UserState
	adminPathPrefixes  []string
	userPathPrefixes   []string
	publicPathPrefixes []string
	rootIsPublic       bool
	denied             http.HandlerFunc
}

const (
	// Version number. Stable API within major version numbers.
	Version = 1.0
)

// Initialize a Permissions struct with all the default settings.
// This will also connect to the redis host at localhost:6379.
func New() *Permissions {
	return NewPermissions(NewUserStateSimple())
}

// Initialize a Permissions struct with Redis DB index and host:port
func NewWithRedisConf(dbindex int, hostPort string) *Permissions {
	return NewPermissions(NewUserState(dbindex, true, hostPort))
}

// Initialize a Permissions struct with the given UserState and
// a few default paths for admin/user/public path prefixes.
func NewPermissions(state *UserState) *Permissions {
	// default permissions
	return &Permissions{state,
		[]string{"/admin"},         // admin path prefixes
		[]string{"/repo", "/data"}, // user path prefixes
		[]string{"/", "/login", "/register", "/favicon.ico", "/style", "/img", "/js",
			"/favicon.ico", "/robots.txt", "/sitemap_index.xml"}, // public
		true,
		PermissionDenied}
}

// Specify the http.HandlerFunc for when the permissions are denied
func (perm *Permissions) SetDenyFunction(f http.HandlerFunc) {
	perm.denied = f
}

// Retrieve the UserState struct
func (perm *Permissions) UserState() *UserState {
	return perm.state
}

// Add an url path prefix that is a page for the logged in administrators
func (perm *Permissions) AddAdminPath(prefix string) {
	perm.adminPathPrefixes = append(perm.adminPathPrefixes, prefix)
}

// Add an url path prefix that is a page for the logged in users
func (perm *Permissions) AddUserPath(prefix string) {
	perm.userPathPrefixes = append(perm.userPathPrefixes, prefix)
}

// Add an url path prefix that is a public page
func (perm *Permissions) AddPublicPath(prefix string) {
	perm.publicPathPrefixes = append(perm.publicPathPrefixes, prefix)
}

// Set all url path prefixes that are for the logged in administrator pages
func (perm *Permissions) SetAdminPath(pathPrefixes []string) {
	perm.adminPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the logged in user pages
func (perm *Permissions) SetUserPath(pathPrefixes []string) {
	perm.userPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the public pages
func (perm *Permissions) SetPublicPath(pathPrefixes []string) {
	perm.publicPathPrefixes = pathPrefixes
}

// The default "permission denied" http handler
func PermissionDenied(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(rw, "Permission denied.")
}

// Check if a given request should be rejected.
func (perm *Permissions) Rejected(rw http.ResponseWriter, req *http.Request) bool {
	reject := false
	path := req.URL.Path // the path of the url that the user wish to visit

	// If it's not "/" and set to be public regardless of permissions
	if !(perm.rootIsPublic && path == "/") {

		// Reject if it is an admin page and user does not have admin permissions
		for _, prefix := range perm.adminPathPrefixes {
			if strings.HasPrefix(path, prefix) {
				if !perm.state.AdminRights(req) {
					reject = true
					break
				}
			}
		}

		if !reject {
			// Reject if it's a user page and the user does not have user rights
			for _, prefix := range perm.userPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					if !perm.state.UserRights(req) {
						reject = true
						break
					}
				}
			}
		}

		if !reject {
			// Reject if it's not a public page
			found := false
			for _, prefix := range perm.publicPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					found = true
					break
				}
			}
			if !found {
				reject = true
			}
		}

	}

	return reject
}

// Negroni middleware handler
func (perm *Permissions) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	// Check if the user has the right admin/user rights
	if perm.Rejected(rw, req) {
		// Permission denied function
		perm.denied(rw, req)
		// Reject the request by not calling the next handler below
		return
	}

	// Call the next middleware handler
	next(rw, req)
}
<|endoftext|>


<commit_before>package permutation

import (
	"errors"
	"reflect"
	"sort"
)

type Less func(i, j interface{}) bool

type sortable struct {
	value reflect.Value
	less  Less
}

func (s sortable) Len() int {
	return s.value.Len()
}
func (s sortable) Less(i, j int) bool {
	return s.less(s.value.Index(i).Interface(), s.value.Index(j).Interface())
}
func (s sortable) Swap(i, j int) {
	temp := reflect.ValueOf(s.value.Index(i).Interface())
	s.value.Index(i).Set(s.value.Index(j))
	s.value.Index(j).Set(temp)
}

type Permutator struct {
	idle   chan bool
	value  reflect.Value
	less   Less
	length int
	index  int
	amount int
}
//Reset the Permutator, next time invoke p.Next() will return the first permutation in lexicalorder
func (p *Permutator) Reset() {
	<-p.idle
	sort.Sort(sortable{p.value, p.less})
	p.index = 1
	p.idle <- true
}
//return the next n permuations, if n>p.Left(),return all the left permuations
//if all permutaions generated or n is illegal(n<=0),return a empty slice
func (p *Permutator) NextN(n int) interface{} {
	<-p.idle
	//if n<=0 or we generate all pemutations,just return a empty slice
	if n <= 0 || p.left() == 0 {
		p.idle <- true
		return reflect.MakeSlice(reflect.SliceOf(p.value.Type()), 0, 0).Interface()
	}

	var i,j int
	cap := p.left()
	if cap > n {
		cap = n
	}

	result := reflect.MakeSlice(reflect.SliceOf(p.value.Type()), cap, cap)

	if p.length == 1 {
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		p.idle <- true
		result.Index(0).Set(l)
		return result.Interface()
	}

	if p.index == 1 {
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		result.Index(0).Set(l)
	}

	for k := 1; k < cap; k++ {
		for i = p.length - 2; i >= 0; i-- {
			if p.less(p.value.Index(i).Interface(), p.value.Index(i+1).Interface()) {
				break
			}
		}
		for j = p.length - 1; j >= 0; j-- {
			if p.less(p.value.Index(i).Interface(), p.value.Index(j).Interface()) {
				break
			}
		}
		//swap
		temp := reflect.ValueOf(p.value.Index(i).Interface())
		p.value.Index(i).Set(p.value.Index(j))
		p.value.Index(j).Set(temp)
		//reverse
		reverse(p.value, i+1, p.length-1)

		//increase the counter
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		result.Index(k).Set(l)
	}
	p.idle<-true
	return result.Interface()
}

//Invoke Permutator.Index() to return the index of last permutation, which start from 1 to n! (n is the length of slice)
func (p Permutator) Index() int {
	<-p.idle

	j := p.index-1
	p.idle <- true
	return j
}

//Generate a New Permuatator, the argument k must be a non-nil slice,and the less argument must be a Less function that implements compare functionality of k's element type
//if k's element is ordered,less argument can be nil
//for ordered in Golang, visit http://golang.org/ref/spec#Comparison_operators
//After generating a Permutator, the argument k can be modified and deleted,Permutator store a copy of k internel.Rght now, a Permutator can  be used concurrently

func NewPerm(k interface{}, less Less) (*Permutator, error) {
	v := reflect.ValueOf(k)
	//check to see if i is a slice
	if v.Kind() != reflect.Slice {
		return nil, errors.New("argument must be a slice")
	}
	if v.IsValid() != true {
		return nil, errors.New("argument must not be nil")
	}
	if v.Len() == 0 {
		return nil, errors.New("argument must not be empty")
	}

	l := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
	reflect.Copy(l, v)
	v = l

	length := v.Len()
	if less == nil {
		switch v.Type().Elem().Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			less = lessInt
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			less = lessUint
		case reflect.Float32, reflect.Float64:
			less = lessFloat
		case reflect.String:
			less = lessString
		default:
			return nil, errors.New("the element type of slice is not ordered,you must provide a function\n")
		}
	}
	//check to see if v is in increasing order,if not sort it
	i := 0
	for i = 0; i < length-1; i++ {
		if !less(v.Index(i).Interface(), v.Index(i+1).Interface()) {
			break
		}
	}
	if i != length-1 {
		sort.Sort(sortable{v, less})
	}
	s := &Permutator{value: v, less: less, length: length, index: 1, amount: factorial(length)}
	s.idle = make(chan bool, 1)
	s.idle <- true
	return s, nil
}

//generate the next permuation in lexcial order,if all permutations generated,return an error
func (p *Permutator) Next() (interface{}, error) {
	<-p.idle
	//check to see if all permutations generated
	if p.left() <= 0 {
		p.idle <- true
		return nil, errors.New("all Permutations generated")
	}

	var i,j int
	//the first permuation is just p.value
	if p.index == 1 {
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		p.idle <- true
		return l.Interface(), nil
	}

	//when we arrive here, there must be some permutations to generate
	for i = p.length - 2; i >= 0; i-- {
		if p.less(p.value.Index(i).Interface(), p.value.Index(i+1).Interface()) {
			break
		}
	}
	for j = p.length - 1; j >= 0; j-- {
		if p.less(p.value.Index(i).Interface(), p.value.Index(j).Interface()) {
			break
		}
	}
	//swap
	temp := reflect.ValueOf(p.value.Index(i).Interface())
	p.value.Index(i).Set(p.value.Index(j))
	p.value.Index(j).Set(temp)
	//reverse
	reverse(p.value, i+1, p.length-1)

	//increase the counter
	p.index++
	l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
	reflect.Copy(l, p.value)
	p.idle <- true
	return l.Interface(), nil
}
//return the left permutation that can be generated
func (p Permutator) Left() int {
	<-p.idle
	j :=p.left()
	p.idle <- true
	return j
}
//because we use left inside some methods,so we need a non-block version
func (p Permutator) left() int {
	return p.amount - p.index + 1
}

//reverse the slice v[i:j]
func reverse(v reflect.Value, i, j int) {
	length := j - i + 1

	if length < 2 {
		return
	}

	for length >= 2 {
		temp := reflect.ValueOf(v.Index(j).Interface())
		v.Index(j).Set(v.Index(i))
		v.Index(i).Set(temp)

		length -= 2
		i++
		j--
	}
}
//caculate n!,because this function can only be invoked by NewPerm,so we do not need the check if i>=0
func factorial(i int) int {
	result := 1
	for i > 0 {
		result *= i
		i--
	}
	return result
}

func lessUint(i, j interface{}) bool {
	return reflect.ValueOf(i).Uint() < reflect.ValueOf(j).Uint()
}
func lessInt(i, j interface{}) bool {
	return reflect.ValueOf(i).Int() < reflect.ValueOf(j).Int()
}
func lessFloat(i, j interface{}) bool {
	return reflect.ValueOf(i).Float() < reflect.ValueOf(j).Float()
}
func lessString(i, j interface{}) bool {
	return reflect.ValueOf(i).Interface().(string) < reflect.ValueOf(j).Interface().(string)
}
<commit_msg>Fix to the panic: reflect: slice index out of range problem change loop indicies so they cannot go negative seems to work on all test cases<commit_after>package permutation

import (
	"errors"
	"reflect"
	"sort"
)

type Less func(i, j interface{}) bool

type sortable struct {
	value reflect.Value
	less  Less
}

func (s sortable) Len() int {
	return s.value.Len()
}
func (s sortable) Less(i, j int) bool {
	return s.less(s.value.Index(i).Interface(), s.value.Index(j).Interface())
}
func (s sortable) Swap(i, j int) {
	temp := reflect.ValueOf(s.value.Index(i).Interface())
	s.value.Index(i).Set(s.value.Index(j))
	s.value.Index(j).Set(temp)
}

type Permutator struct {
	idle   chan bool
	value  reflect.Value
	less   Less
	length int
	index  int
	amount int
}
//Reset the Permutator, next time invoke p.Next() will return the first permutation in lexicalorder
func (p *Permutator) Reset() {
	<-p.idle
	sort.Sort(sortable{p.value, p.less})
	p.index = 1
	p.idle <- true
}
//return the next n permuations, if n>p.Left(),return all the left permuations
//if all permutaions generated or n is illegal(n<=0),return a empty slice
func (p *Permutator) NextN(n int) interface{} {
	<-p.idle
	//if n<=0 or we generate all pemutations,just return a empty slice
	if n <= 0 || p.left() == 0 {
		p.idle <- true
		return reflect.MakeSlice(reflect.SliceOf(p.value.Type()), 0, 0).Interface()
	}

	var i,j int
	cap := p.left()
	if cap > n {
		cap = n
	}

	result := reflect.MakeSlice(reflect.SliceOf(p.value.Type()), cap, cap)

	if p.length == 1 {
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		p.idle <- true
		result.Index(0).Set(l)
		return result.Interface()
	}

	if p.index == 1 {
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		result.Index(0).Set(l)
	}

	for k := 1; k < cap; k++ {
		for i = p.length - 2; i > 0; i-- {
			if p.less(p.value.Index(i).Interface(), p.value.Index(i+1).Interface()) {
				break
			}
		}
		for j = p.length - 1; j > 0; j-- {
			if p.less(p.value.Index(i).Interface(), p.value.Index(j).Interface()) {
				break
			}
		}
		//swap
		temp := reflect.ValueOf(p.value.Index(i).Interface())
		p.value.Index(i).Set(p.value.Index(j))
		p.value.Index(j).Set(temp)
		//reverse
		reverse(p.value, i+1, p.length-1)

		//increase the counter
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		result.Index(k).Set(l)
	}
	p.idle<-true
	return result.Interface()
}

//Invoke Permutator.Index() to return the index of last permutation, which start from 1 to n! (n is the length of slice)
func (p Permutator) Index() int {
	<-p.idle

	j := p.index-1
	p.idle <- true
	return j
}

//Generate a New Permuatator, the argument k must be a non-nil slice,and the less argument must be a Less function that implements compare functionality of k's element type
//if k's element is ordered,less argument can be nil
//for ordered in Golang, visit http://golang.org/ref/spec#Comparison_operators
//After generating a Permutator, the argument k can be modified and deleted,Permutator store a copy of k internel.Rght now, a Permutator can  be used concurrently

func NewPerm(k interface{}, less Less) (*Permutator, error) {
	v := reflect.ValueOf(k)
	//check to see if i is a slice
	if v.Kind() != reflect.Slice {
		return nil, errors.New("argument must be a slice")
	}
	if v.IsValid() != true {
		return nil, errors.New("argument must not be nil")
	}
	if v.Len() == 0 {
		return nil, errors.New("argument must not be empty")
	}

	l := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
	reflect.Copy(l, v)
	v = l

	length := v.Len()
	if less == nil {
		switch v.Type().Elem().Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			less = lessInt
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			less = lessUint
		case reflect.Float32, reflect.Float64:
			less = lessFloat
		case reflect.String:
			less = lessString
		default:
			return nil, errors.New("the element type of slice is not ordered,you must provide a function\n")
		}
	}
	//check to see if v is in increasing order,if not sort it
	i := 0
	for i = 0; i < length-1; i++ {
		if !less(v.Index(i).Interface(), v.Index(i+1).Interface()) {
			break
		}
	}
	if i != length-1 {
		sort.Sort(sortable{v, less})
	}
	s := &Permutator{value: v, less: less, length: length, index: 1, amount: factorial(length)}
	s.idle = make(chan bool, 1)
	s.idle <- true
	return s, nil
}

//generate the next permuation in lexcial order,if all permutations generated,return an error
func (p *Permutator) Next() (interface{}, error) {
	<-p.idle
	//check to see if all permutations generated
	if p.left() <= 0 {
		p.idle <- true
		return nil, errors.New("all Permutations generated")
	}

	var i,j int
	//the first permuation is just p.value
	if p.index == 1 {
		p.index++
		l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
		reflect.Copy(l, p.value)
		p.idle <- true
		return l.Interface(), nil
	}

	//when we arrive here, there must be some permutations to generate

	for i = p.length - 2; i > 0; i-- {
		if p.less(p.value.Index(i).Interface(), p.value.Index(i+1).Interface()) {
			break
		}
	}
	for j = p.length - 1; j > 0; j-- {
		if p.less(p.value.Index(i).Interface(), p.value.Index(j).Interface()) {
			break
		}
	}
	//swap
	temp := reflect.ValueOf(p.value.Index(i).Interface())
	p.value.Index(i).Set(p.value.Index(j))
	p.value.Index(j).Set(temp)
	//reverse
	reverse(p.value, i+1, p.length-1)

	//increase the counter
	p.index++
	l := reflect.MakeSlice(p.value.Type(), p.length, p.length)
	reflect.Copy(l, p.value)
	p.idle <- true
	return l.Interface(), nil
}
//return the left permutation that can be generated
func (p Permutator) Left() int {
	<-p.idle
	j :=p.left()
	p.idle <- true
	return j
}
//because we use left inside some methods,so we need a non-block version
func (p Permutator) left() int {
	return p.amount - p.index + 1
}

//reverse the slice v[i:j]
func reverse(v reflect.Value, i, j int) {
	length := j - i + 1

	if length < 2 {
		return
	}

	for length >= 2 {
		temp := reflect.ValueOf(v.Index(j).Interface())
		v.Index(j).Set(v.Index(i))
		v.Index(i).Set(temp)

		length -= 2
		i++
		j--
	}
}
//caculate n!,because this function can only be invoked by NewPerm,so we do not need the check if i>=0
func factorial(i int) int {
	result := 1
	for i > 0 {
		result *= i
		i--
	}
	return result
}

func lessUint(i, j interface{}) bool {
	return reflect.ValueOf(i).Uint() < reflect.ValueOf(j).Uint()
}
func lessInt(i, j interface{}) bool {
	return reflect.ValueOf(i).Int() < reflect.ValueOf(j).Int()
}
func lessFloat(i, j interface{}) bool {
	return reflect.ValueOf(i).Float() < reflect.ValueOf(j).Float()
}
func lessString(i, j interface{}) bool {
	return reflect.ValueOf(i).Interface().(string) < reflect.ValueOf(j).Interface().(string)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

//go:build go1.18
// +build go1.18

package syntax_test

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

	"mvdan.cc/sh/v3/syntax"
)

func FuzzQuote(f *testing.F) {
	if _, err := exec.LookPath("bash"); err != nil {
		f.Skipf("requires bash to verify quoted strings")
	}

	// Keep in sync with ExampleQuote.
	f.Add("foo")
	f.Add("bar $baz")
	f.Add(`"won't"`)
	f.Add(`~/home`)
	f.Add("#1304")
	f.Add("name=value")
	f.Add(`glob-*`)
	f.Add("invalid-\xe2'")
	f.Add("nonprint-\x0b\x1b")
	f.Fuzz(func(t *testing.T, s string) {
		quoted, ok := syntax.Quote(s)
		if !ok {
			// Contains a null byte; not interesting.
			t.Skip()
		}
		// Beware that this might run arbitrary code
		// if Quote is too naive and allows ';' or '$'.
		//
		// Also note that this fuzzing would not catch '=',
		// as we don't use the quoted string as a first argument
		// to avoid running random commands.
		//
		// We could consider ways to fully sandbox the bash process,
		// but for now that feels overkill.
		out, err := exec.Command("bash", "-c", "printf %s "+quoted).CombinedOutput()
		if err != nil {
			t.Fatalf("bash error on %q quoted as %s: %v: %s", s, quoted, err, out)
		}
		want, got := s, string(out)
		if want != got {
			t.Fatalf("output mismatch on %q quoted as %s: got %q (len=%d)", want, quoted, got, len(got))
		}
	})
}

func FuzzParsePrint(f *testing.F) {
	// TODO: turn these into separate option parameters
	var zeroParserOpts uint8
	var zeroPrinterOpts uint16

	// TODO: probably use f.Add on table-driven test cases too?
	// In the past, any crashers found by go-fuzz got put there.

	f.Add("echo foo", zeroParserOpts, zeroPrinterOpts)
	f.Add("'foo' \"bar\" $'baz'", zeroParserOpts, zeroPrinterOpts)
	f.Add("if foo; then bar; baz; fi", zeroParserOpts, zeroPrinterOpts)
	f.Add("{ (foo; bar); baz; }", zeroParserOpts, zeroPrinterOpts)
	f.Add("$foo ${bar} ${baz[@]} $", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo >bar <<EOF\nbaz\nEOF", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo=bar baz=(x y z)", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo && bar || baz", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo | bar # baz", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo \\\n bar \\\\ba\\z", zeroParserOpts, zeroPrinterOpts)

	f.Fuzz(func(t *testing.T, src string, parserOpts uint8, printerOpts uint16) {
		// Below are the bit masks for parserOpts and printerOpts.
		// Most masks are a single bit, for boolean options.
		const (
			// parser
			// TODO: also fuzz StopAt
			maskLangVariant  = 0b0000_0011 // two bits; 0-3 matching the iota
			maskKeepComments = 0b0000_0100
			maskSimplify     = 0b0000_1000 // pretend it's a parser option

			// printer
			maskIndent           = 0b0000_0000_0000_1111 // three bits; 0-15
			maskBinaryNextLine   = 0b0000_0000_0001_0000
			maskSwitchCaseIndent = 0b0000_0000_0010_0000
			maskSpaceRedirects   = 0b0000_0000_0100_0000
			maskKeepPadding      = 0b0000_0000_1000_0000
			maskMinify           = 0b0000_0001_0000_0000
			maskSingleLine       = 0b0000_0010_0000_0000
			maskFunctionNextLine = 0b0000_0100_0000_0000
		)

		parser := syntax.NewParser()
		lang := syntax.LangVariant(parserOpts & maskLangVariant) // range 0-3
		syntax.Variant(lang)(parser)
		syntax.KeepComments(parserOpts&maskKeepComments != 0)(parser)

		prog, err := parser.Parse(strings.NewReader(src), "")
		if err != nil {
			t.Skip() // not valid shell syntax
		}

		if parserOpts&maskSimplify != 0 {
			syntax.Simplify(prog)
		}

		printer := syntax.NewPrinter()
		indent := uint(printerOpts & maskIndent) // range 0-15
		syntax.Indent(indent)(printer)
		syntax.BinaryNextLine(printerOpts&maskBinaryNextLine != 0)(printer)
		syntax.SwitchCaseIndent(printerOpts&maskSwitchCaseIndent != 0)(printer)
		syntax.SpaceRedirects(printerOpts&maskSpaceRedirects != 0)(printer)
		syntax.KeepPadding(printerOpts&maskKeepPadding != 0)(printer)
		syntax.Minify(printerOpts&maskMinify != 0)(printer)
		syntax.SingleLine(printerOpts&maskSingleLine != 0)(printer)
		syntax.FunctionNextLine(printerOpts&maskFunctionNextLine != 0)(printer)

		if err := printer.Print(io.Discard, prog); err != nil {
			t.Skip() // e.g. invalid option
		}
	})
}
<commit_msg>syntax: make FuzzParsePrint take structured parameters<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

//go:build go1.18
// +build go1.18

package syntax

import (
	"io"
	"os/exec"
	"strings"
	"testing"
)

func FuzzQuote(f *testing.F) {
	if _, err := exec.LookPath("bash"); err != nil {
		f.Skipf("requires bash to verify quoted strings")
	}

	// Keep in sync with ExampleQuote.
	f.Add("foo")
	f.Add("bar $baz")
	f.Add(`"won't"`)
	f.Add(`~/home`)
	f.Add("#1304")
	f.Add("name=value")
	f.Add(`glob-*`)
	f.Add("invalid-\xe2'")
	f.Add("nonprint-\x0b\x1b")
	f.Fuzz(func(t *testing.T, s string) {
		quoted, ok := Quote(s)
		if !ok {
			// Contains a null byte; not interesting.
			t.Skip()
		}
		// Beware that this might run arbitrary code
		// if Quote is too naive and allows ';' or '$'.
		//
		// Also note that this fuzzing would not catch '=',
		// as we don't use the quoted string as a first argument
		// to avoid running random commands.
		//
		// We could consider ways to fully sandbox the bash process,
		// but for now that feels overkill.
		out, err := exec.Command("bash", "-c", "printf %s "+quoted).CombinedOutput()
		if err != nil {
			t.Fatalf("bash error on %q quoted as %s: %v: %s", s, quoted, err, out)
		}
		want, got := s, string(out)
		if want != got {
			t.Fatalf("output mismatch on %q quoted as %s: got %q (len=%d)", want, quoted, got, len(got))
		}
	})
}

func FuzzParsePrint(f *testing.F) {
	// TODO: probably use f.Add on table-driven test cases too?
	// In the past, any crashers found by go-fuzz got put there.

	add := func(src string) {
		// For now, default to just LangBash with KeepComments.
		f.Add(src, uint8(LangBash), true, false,
			uint8(0), false, false, false, false, false, false, false)
	}

	add("echo foo")
	add("'foo' \"bar\" $'baz'")
	add("if foo; then bar; baz; fi")
	add("{ (foo; bar); baz; }")
	add("$foo ${bar} ${baz[@]} $")
	add("foo >bar <<EOF\nbaz\nEOF")
	add("foo=bar baz=(x y z)")
	add("foo && bar || baz")
	add("foo | bar # baz")
	add("foo \\\n bar \\\\ba\\z")

	f.Fuzz(func(t *testing.T,
		src string,

		// parser options
		// TODO: also fuzz StopAt
		langVariant uint8, // 0-3
		keepComments bool,

		simplify bool,

		// printer options
		indent uint8, // 0-255
		binaryNextLine bool,
		switchCaseIndent bool,
		spaceRedirects bool,
		keepPadding bool,
		minify bool,
		singleLine bool,
		functionNextLine bool,
	) {
		if langVariant > 3 {
			t.Skip() // lang variants are 0-3
		}
		if indent > 16 {
			t.Skip() // more indentation won't really be interesting
		}

		parser := NewParser()
		Variant(LangVariant(langVariant))(parser)
		KeepComments(keepComments)(parser)

		prog, err := parser.Parse(strings.NewReader(src), "")
		if err != nil {
			t.Skip() // not valid shell syntax
		}

		if simplify {
			Simplify(prog)
		}

		printer := NewPrinter()
		Indent(uint(indent))(printer)
		BinaryNextLine(binaryNextLine)(printer)
		SwitchCaseIndent(switchCaseIndent)(printer)
		SpaceRedirects(spaceRedirects)(printer)
		KeepPadding(keepPadding)(printer)
		Minify(minify)(printer)
		SingleLine(singleLine)(printer)
		FunctionNextLine(functionNextLine)(printer)

		if err := printer.Print(io.Discard, prog); err != nil {
			t.Skip() // e.g. invalid option
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"log"

	"github.com/envoyproxy/go-control-plane/pkg/cache/v3"
	serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3"
	logrus "github.com/sirupsen/logrus"

	pb "github.com/zachmandeville/tester-prototype/test-target/test-target"
	"github.com/zachmandeville/tester-prototype/test-target/internal/processor"
	"github.com/zachmandeville/tester-prototype/test-target/internal/server"
	"google.golang.org/grpc"
)

var (
	nodeID   string
	l        logrus.FieldLogger
	shimPort string
	port     uint
)

type shimServer struct {
	pb.UnimplementedShimServer
}

func (s *shimServer) GiveCompliment(ctx context.Context, req *pb.ComplimentRequest) (res *pb.ComplimentResponse, err error) {
	name := req.Name
	compliment := &pb.ComplimentResponse{
		Compliment: fmt.Sprintf("Hi, %v, you are GREAT!", name),
	}
	return compliment, nil
}

func init() {
	l = logrus.New()
	logrus.SetLevel(logrus.DebugLevel)
	flag.UintVar(&port, "port", 18000, "xDS management server port")
	flag.StringVar(&nodeID, "nodeID", "test-id", "NodeID")
	flag.StringVar(&shimPort, "shimPort", ":17000", "port of xds server shim")
}

func main() {
	flag.Parse()
	cache := cache.NewSnapshotCache(false, cache.IDHash{}, l)
	proc := processor.NewProcessor(
		cache, nodeID, logrus.WithField("context", "processor"))

	proc.UpdateSnapshot("foooo")
	go func() {
		// Run the xDS server
		ctx := context.Background()
		srv := serverv3.NewServer(ctx, cache, nil)
		server.RunServer(ctx, srv, port)
	}()

		lis, err := net.Listen("tcp", shimPort)
		if err != nil {
			log.Fatalf("failed to listen: %v", err)
		}
		s := grpc.NewServer()
		pb.RegisterShimServer(s, &shimServer{})
		log.Printf("shim server listening on port %v\n", shimPort)
		if err := s.Serve(lis); err != nil {
			log.Fatalf("Failed to serve: %v", err)
		}
}
<commit_msg>implement addService in shim server<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"log"

	"github.com/envoyproxy/go-control-plane/pkg/cache/v3"
	serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3"
	logrus "github.com/sirupsen/logrus"

	pb "github.com/zachmandeville/tester-prototype/test-target/test-target"
	"github.com/zachmandeville/tester-prototype/test-target/internal/processor"
	"github.com/zachmandeville/tester-prototype/test-target/internal/server"
	"google.golang.org/grpc"
)

var (
	nodeID   string
	l        logrus.FieldLogger
	shimPort string
	port     uint
	proc *processor.Processor
)

type shimServer struct {
	pb.UnimplementedShimServer
}

func (s *shimServer) GiveCompliment(ctx context.Context, req *pb.ComplimentRequest) (res *pb.ComplimentResponse, err error) {
	name := req.Name
	compliment := &pb.ComplimentResponse{
		Compliment: fmt.Sprintf("Hi, %v, you are GREAT!", name),
	}
	return compliment, nil
}

func (s *shimServer) AddCluster(ctx context.Context, req *pb.ClusterRequest) (res *pb.ClusterResponse, err error) {
	cluster := req.Cluster
	proc.UpdateSnapshot(cluster)
	response := &pb.ClusterResponse{
		Message: fmt.Sprintf("A cluster named %v was added\n",cluster),
	}
	return response, nil
}

func init() {
	l = logrus.New()
	logrus.SetLevel(logrus.DebugLevel)
	flag.UintVar(&port, "port", 18000, "xDS management server port")
	flag.StringVar(&nodeID, "nodeID", "test-id", "NodeID")
	flag.StringVar(&shimPort, "shimPort", ":17000", "port of xds server shim")
}

func main() {
	flag.Parse()
	cache := cache.NewSnapshotCache(false, cache.IDHash{}, l)
	proc = processor.NewProcessor(
		cache, nodeID, logrus.WithField("context", "processor"))
	go func() {
		// Run the xDS server
		ctx := context.Background()
		srv := serverv3.NewServer(ctx, cache, nil)
		server.RunServer(ctx, srv, port)
	}()

		lis, err := net.Listen("tcp", shimPort)
		if err != nil {
			log.Fatalf("failed to listen: %v", err)
		}
		s := grpc.NewServer()
		pb.RegisterShimServer(s, &shimServer{})
		log.Printf("shim server listening on port %v\n", shimPort)
		if err := s.Serve(lis); err != nil {
			log.Fatalf("Failed to serve: %v", err)
		}
}
<|endoftext|>


<commit_before>package splunk

import (
	"crypto/tls"
	"time"
	"bytes"
	"os"
	"net/http"
	"encoding/json"
	"errors"
)

// Event represents the log event object that is sent to Splunk when *HTTPCollector.Log is called.
type Event struct {
	Time 		int64		`json:"time" binding:"required"`	// epoch time in seconds
	Host		string  	`json:"host" binding:"required"`	// hostname
	Source		string  	`json:"source" binding:"required"`	// app name
	SourceType	string 		`json:"sourcetype" binding:"required"`	// Splunk bucket to group logs in
	Index		string		`json:"index" binding:"required"`	// idk what it does..
	Event		map[string]string `json:"event" binding:"required"`	// throw any useful key/val pairs here
}

type Client struct {
	HTTPClient *http.Client	 // HTTP client used to communicate with the API
	URL string
	Token string
	Source string
	SourceType string
	Index string
}

func NewClient(httpClient *http.Client, URL string, Token string, Source string, SourceType string, Index string) (*Client) {
	// Create a new client
	if httpClient == nil {
		tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} // turn off certificate checking
		httpClient = &http.Client{Timeout: time.Second * 20, Transport: tr}
	}

	c := &Client{HTTPClient: httpClient, URL: URL, Token: Token, Source: Source, SourceType: SourceType, Index: Index}

	return c
}

func NewEvent(event map[string]string, source string, sourcetype string, index string) (Event) {
	hostname, _ := os.Hostname()
	e := Event{Time: time.Now().Unix(), Host: hostname, Source: source, SourceType: sourcetype, Index: index, Event: event}

	return e
}

func (c *Client) Log(event map[string]string) (error) {
	// create Splunk log
	log := NewEvent(event, c.Source, c.SourceType, c.Index)

	// Convert requestBody struct to byte slice to prep for http.NewRequest
	b, err := json.Marshal(log)
	if err != nil {
		return err
	}

	//log.Print(string(b[:])) // print what the splunk post body will be for checking/debugging

	// make new request
	url := c.URL
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Splunk " + c.Token)

	// receive response
	res, err := c.HTTPClient.Do(req)
	if err != nil {
		return err
	}

	// If statusCode is not good, return error string
	switch res.StatusCode {
	case 200:
	default:
		// Turn response into string and return it
		buf := new(bytes.Buffer)
		buf.ReadFrom(res.Body)
		responseBody := buf.String()
		err = errors.New(responseBody)

	//log.Print(responseBody)	// print error to screen for checking/debugging
	}

	return err
}
<commit_msg>updated docs<commit_after>package splunk

import (
	"crypto/tls"
	"time"
	"bytes"
	"os"
	"net/http"
	"encoding/json"
	"errors"
)

// Event represents the log event object that is sent to Splunk when Client.Log is called.
type Event struct {
	Time 		int64		`json:"time" binding:"required"`	// epoch time in seconds
	Host		string  	`json:"host" binding:"required"`	// hostname
	Source		string  	`json:"source" binding:"required"`	// app name
	SourceType	string 		`json:"sourcetype" binding:"required"`	// Splunk bucket to group logs in
	Index		string		`json:"index" binding:"required"`	// idk what it does..
	Event		map[string]string `json:"event" binding:"required"`	// throw any useful key/val pairs here
}

// Client manages communication with Splunk's HTTP Event Collector.
// New client objects should be created using the NewClient function.
//
// The URL field must be defined and pointed at a Splunk servers Event Collector port (i.e. https://{your-splunk-URL}:8088/services/collector).
// The Token field must be defined with your access token to the Event Collector.
// The Source, SourceType, and Index fields must be defined.
type Client struct {
	HTTPClient *http.Client	 // HTTP client used to communicate with the API
	URL string
	Token string
	Source string
	SourceType string
	Index string
}

// NewClient creates a new client to Splunk.
// This should be the primary way a Splunk client object is constructed.
//
// If an httpClient object is specified it will be used instead of the
// default http.DefaultClient.
func NewClient(httpClient *http.Client, URL string, Token string, Source string, SourceType string, Index string) (*Client) {
	// Create a new client
	if httpClient == nil {
		tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} // turn off certificate checking
		httpClient = &http.Client{Timeout: time.Second * 20, Transport: tr}
	}

	c := &Client{HTTPClient: httpClient, URL: URL, Token: Token, Source: Source, SourceType: SourceType, Index: Index}

	return c
}

// NewEvent creates a new log event to send to Splunk.
// This should be the primary way a Splunk log object is constructed, and is automatically called by the Log function attached to the client.
func NewEvent(event map[string]string, source string, sourcetype string, index string) (Event) {
	hostname, _ := os.Hostname()
	e := Event{Time: time.Now().Unix(), Host: hostname, Source: source, SourceType: sourcetype, Index: index, Event: event}

	return e
}

// Client.Log is used to construct a new log event and POST it to the Splunk server.
//
// All that must be provided for a log event are the desired map[string]string key/val pairs. These can be anything
// that provide context or information for the situation you are trying to log (i.e. err messages, status codes, etc).
// The function auto-generates the event timestamp and hostname for you.
func (c *Client) Log(event map[string]string) (error) {
	// create Splunk log
	log := NewEvent(event, c.Source, c.SourceType, c.Index)

	// Convert requestBody struct to byte slice to prep for http.NewRequest
	b, err := json.Marshal(log)
	if err != nil {
		return err
	}

	//log.Print(string(b[:])) // print what the splunk post body will be for checking/debugging

	// make new request
	url := c.URL
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Splunk " + c.Token)

	// receive response
	res, err := c.HTTPClient.Do(req)
	if err != nil {
		return err
	}

	// If statusCode is not good, return error string
	switch res.StatusCode {
	case 200:
	default:
		// Turn response into string and return it
		buf := new(bytes.Buffer)
		buf.ReadFrom(res.Body)
		responseBody := buf.String()
		err = errors.New(responseBody)

	//log.Print(responseBody)	// print error to screen for checking/debugging
	}

	return err
}
<|endoftext|>


<commit_before>package splunk

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"os"
	"time"
)

// Event represents the log event object that is sent to Splunk when Client.Log is called.
type Event struct {
	Time       int64       `json:"time" binding:"required"`       // epoch time in seconds
	Host       string      `json:"host" binding:"required"`       // hostname
	Source     string      `json:"source" binding:"required"`     // app name
	SourceType string      `json:"sourcetype" binding:"required"` // Splunk bucket to group logs in
	Index      string      `json:"index" binding:"required"`      // idk what it does..
	Event      interface{} `json:"event" binding:"required"`      // throw any useful key/val pairs here
}

// Client manages communication with Splunk's HTTP Event Collector.
// New client objects should be created using the NewClient function.
//
// The URL field must be defined and pointed at a Splunk servers Event Collector port (i.e. https://{your-splunk-URL}:8088/services/collector).
// The Token field must be defined with your access token to the Event Collector.
// The Source, SourceType, and Index fields must be defined.
type Client struct {
	HTTPClient *http.Client // HTTP client used to communicate with the API
	URL        string
	Hostname   string
	Token      string
	Source     string //Default source
	SourceType string //Default source type
	Index      string //Default index
}

// NewClient creates a new client to Splunk.
// This should be the primary way a Splunk client object is constructed.
//
// If an httpClient object is specified it will be used instead of the
// default http.DefaultClient.
func NewClient(httpClient *http.Client, URL string, Token string, Source string, SourceType string, Index string) *Client {
	// Use default client
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	hostname, _ := os.Hostname()
	c := &Client{
		HTTPClient: httpClient,
		URL:        URL,
		Hostname:   hostname,
		Token:      Token,
		Source:     Source,
		SourceType: SourceType,
		Index:      Index,
	}
	return c
}

// NewEvent creates a new log event to send to Splunk.
// This should be the primary way a Splunk log object is constructed, and is automatically called by the Log function attached to the client.
// This method takes the current timestamp for the event, meaning that the event is generated at runtime.
func (c *Client) NewEvent(event interface{}, source string, sourcetype string, index string) *Event {
	e := &Event{
		Time:       time.Now().Unix(),
		Host:       c.Hostname,
		Source:     source,
		SourceType: sourcetype,
		Index:      index,
		Event:      event,
	}
	return e
}

// NewEventWithTime creates a new log event with a specified timetamp to send to Splunk.
// This is similar to NewEvent but if you want to log in a different time rather than time.Now this becomes handy. If that's
// the case, use this function to create the Event object and the the LogEvent function.
func (c *Client) NewEventWithTime(t int64, event interface{}, source string, sourcetype string, index string) *Event {
	e := &Event{
		Time:       t,
		Host:       c.Hostname,
		Source:     source,
		SourceType: sourcetype,
		Index:      index,
		Event:      event,
	}
	return e
}

// Client.Log is used to construct a new log event and POST it to the Splunk server.
//
// All that must be provided for a log event are the desired map[string]string key/val pairs. These can be anything
// that provide context or information for the situation you are trying to log (i.e. err messages, status codes, etc).
// The function auto-generates the event timestamp and hostname for you.
func (c *Client) Log(event interface{}) error {
	// create Splunk log
	log := c.NewEvent(event, c.Source, c.SourceType, c.Index)
	return c.LogEvent(log)
}

// Client.LogWithTime is used to construct a new log event with a scpecified timestamp and POST it to the Splunk server.
//
// This is similar to Client.Log, just with the t parameter.
func (c *Client) LogWithTime(t int64, event interface{}) error {
	// create Splunk log
	log := c.NewEventWithTime(t, event, c.Source, c.SourceType, c.Index)
	return c.LogEvent(log)
}

// Client.LogEvent is used to POST a single event to the Splunk server.
func (c *Client) LogEvent(e *Event) error {
	// Convert requestBody struct to byte slice to prep for http.NewRequest
	b, err := json.Marshal(e)
	if err != nil {
		return err
	}
	return c.doRequest(bytes.NewBuffer(b))
}

// Client.LogEvents is used to POST multiple events with a single request to the Splunk server.
func (c *Client) LogEvents(events []*Event) error {
	buf := new(bytes.Buffer)
	for _, e := range events {
		b, err := json.Marshal(e)
		if err != nil {
			return err
		}
		buf.Write(b)
		// Each json object should be separated by a blank line
		buf.WriteString("\r\n\r\n")
	}
	// Convert requestBody struct to byte slice to prep for http.NewRequest
	return c.doRequest(buf)
}

// Client.doRequest is used internally to POST the bytes of events to the Splunk server.
func (c *Client) doRequest(b *bytes.Buffer) error {
	// make new request
	url := c.URL
	req, err := http.NewRequest("POST", url, b)
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Splunk "+c.Token)

	// receive response
	res, err := c.HTTPClient.Do(req)
	if err != nil {
		return err
	}

	// If statusCode is not good, return error string
	switch res.StatusCode {
	case 200:
		return nil
	default:
		// Turn response into string and return it
		buf := new(bytes.Buffer)
		buf.ReadFrom(res.Body)
		responseBody := buf.String()
		err = errors.New(responseBody)

	}
	return err
}
<commit_msg>Keep timeouts, but don't InsecureSkipVerify<commit_after>package splunk

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"net/http"
	"os"
	"time"
)

// Event represents the log event object that is sent to Splunk when Client.Log is called.
type Event struct {
	Time       int64       `json:"time" binding:"required"`       // epoch time in seconds
	Host       string      `json:"host" binding:"required"`       // hostname
	Source     string      `json:"source" binding:"required"`     // app name
	SourceType string      `json:"sourcetype" binding:"required"` // Splunk bucket to group logs in
	Index      string      `json:"index" binding:"required"`      // idk what it does..
	Event      interface{} `json:"event" binding:"required"`      // throw any useful key/val pairs here
}

// Client manages communication with Splunk's HTTP Event Collector.
// New client objects should be created using the NewClient function.
//
// The URL field must be defined and pointed at a Splunk servers Event Collector port (i.e. https://{your-splunk-URL}:8088/services/collector).
// The Token field must be defined with your access token to the Event Collector.
// The Source, SourceType, and Index fields must be defined.
type Client struct {
	HTTPClient *http.Client // HTTP client used to communicate with the API
	URL        string
	Hostname   string
	Token      string
	Source     string //Default source
	SourceType string //Default source type
	Index      string //Default index
}

// NewClient creates a new client to Splunk.
// This should be the primary way a Splunk client object is constructed.
//
// If an httpClient object is specified it will be used instead of the
// default http.DefaultClient.
func NewClient(httpClient *http.Client, URL string, Token string, Source string, SourceType string, Index string) *Client {
	// Create a new client
	if httpClient == nil {
		tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: false}}
		httpClient = &http.Client{Timeout: time.Second * 20, Transport: tr}
	}
	hostname, _ := os.Hostname()
	c := &Client{
		HTTPClient: httpClient,
		URL:        URL,
		Hostname:   hostname,
		Token:      Token,
		Source:     Source,
		SourceType: SourceType,
		Index:      Index,
	}
	return c
}

// NewEvent creates a new log event to send to Splunk.
// This should be the primary way a Splunk log object is constructed, and is automatically called by the Log function attached to the client.
// This method takes the current timestamp for the event, meaning that the event is generated at runtime.
func (c *Client) NewEvent(event interface{}, source string, sourcetype string, index string) *Event {
	e := &Event{
		Time:       time.Now().Unix(),
		Host:       c.Hostname,
		Source:     source,
		SourceType: sourcetype,
		Index:      index,
		Event:      event,
	}
	return e
}

// NewEventWithTime creates a new log event with a specified timetamp to send to Splunk.
// This is similar to NewEvent but if you want to log in a different time rather than time.Now this becomes handy. If that's
// the case, use this function to create the Event object and the the LogEvent function.
func (c *Client) NewEventWithTime(t int64, event interface{}, source string, sourcetype string, index string) *Event {
	e := &Event{
		Time:       t,
		Host:       c.Hostname,
		Source:     source,
		SourceType: sourcetype,
		Index:      index,
		Event:      event,
	}
	return e
}

// Client.Log is used to construct a new log event and POST it to the Splunk server.
//
// All that must be provided for a log event are the desired map[string]string key/val pairs. These can be anything
// that provide context or information for the situation you are trying to log (i.e. err messages, status codes, etc).
// The function auto-generates the event timestamp and hostname for you.
func (c *Client) Log(event interface{}) error {
	// create Splunk log
	log := c.NewEvent(event, c.Source, c.SourceType, c.Index)
	return c.LogEvent(log)
}

// Client.LogWithTime is used to construct a new log event with a scpecified timestamp and POST it to the Splunk server.
//
// This is similar to Client.Log, just with the t parameter.
func (c *Client) LogWithTime(t int64, event interface{}) error {
	// create Splunk log
	log := c.NewEventWithTime(t, event, c.Source, c.SourceType, c.Index)
	return c.LogEvent(log)
}

// Client.LogEvent is used to POST a single event to the Splunk server.
func (c *Client) LogEvent(e *Event) error {
	// Convert requestBody struct to byte slice to prep for http.NewRequest
	b, err := json.Marshal(e)
	if err != nil {
		return err
	}
	return c.doRequest(bytes.NewBuffer(b))
}

// Client.LogEvents is used to POST multiple events with a single request to the Splunk server.
func (c *Client) LogEvents(events []*Event) error {
	buf := new(bytes.Buffer)
	for _, e := range events {
		b, err := json.Marshal(e)
		if err != nil {
			return err
		}
		buf.Write(b)
		// Each json object should be separated by a blank line
		buf.WriteString("\r\n\r\n")
	}
	// Convert requestBody struct to byte slice to prep for http.NewRequest
	return c.doRequest(buf)
}

// Client.doRequest is used internally to POST the bytes of events to the Splunk server.
func (c *Client) doRequest(b *bytes.Buffer) error {
	// make new request
	url := c.URL
	req, err := http.NewRequest("POST", url, b)
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", "Splunk "+c.Token)

	// receive response
	res, err := c.HTTPClient.Do(req)
	if err != nil {
		return err
	}

	// If statusCode is not good, return error string
	switch res.StatusCode {
	case 200:
		return nil
	default:
		// Turn response into string and return it
		buf := new(bytes.Buffer)
		buf.ReadFrom(res.Body)
		responseBody := buf.String()
		err = errors.New(responseBody)

	}
	return err
}
<|endoftext|>


<commit_before>// e2e provides an E2E test for TFJobs.
//
// The test creates TFJobs and runs various checks to ensure various operations work as intended.
// The test is intended to run as a helm test that ensures the TFJob operator is working correctly.
// Thus, the program returns non-zero exit status on error.
//
// TODO(jlewi): Do we need to make the test output conform to the TAP(https://testanything.org/)
// protocol so we can fit into the K8s dashboard
//
// TODO(https://github.com/tensorflow/k8s/issues/21) The E2E test should actually run distributed TensorFlow.
package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/gogo/protobuf/proto"
	log "github.com/golang/glog"
	tfv1alpha1 "github.com/tensorflow/k8s/pkg/apis/tensorflow/v1alpha1"
	tfjobclient "github.com/tensorflow/k8s/pkg/client/clientset/versioned"
	"github.com/tensorflow/k8s/pkg/util"
	"k8s.io/api/core/v1"
	k8s_errors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const (
	Namespace = "default"
)

var (
	image   = flag.String("image", "", "The Docker image containing the TF program to run.")
	numJobs = flag.Int("num_jobs", 1, "The number of jobs to run.")
	timeout = flag.Duration("timeout", 5*time.Minute, "The timeout for the test")
)

func run() (string, error) {
	// creates the in-cluster config
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	kubeCli, err := clientset.NewForConfig(config)
	if err != nil {
		return "", err
	}

	tfJobClient, err := tfjobclient.NewForConfig(config)
	if err != nil {
		return "", err
	}

	name := "e2e-test-job-" + util.RandString(4)

	original := &tfv1alpha1.TFJob{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
			Labels: map[string]string{
				"test.mlkube.io": "",
			},
		},
		Spec: tfv1alpha1.TFJobSpec{
			ReplicaSpecs: []*tfv1alpha1.TFReplicaSpec{
				{
					Replicas:      proto.Int32(1),
					TFPort:        proto.Int32(2222),
					TFReplicaType: tfv1alpha1.MASTER,
					Template: &v1.PodTemplateSpec{
						Spec: v1.PodSpec{
							Containers: []v1.Container{
								{
									Name:  "tensorflow",
									Image: *image,
								},
							},
							RestartPolicy: v1.RestartPolicyOnFailure,
						},
					},
				},
				{
					Replicas:      proto.Int32(1),
					TFPort:        proto.Int32(2222),
					TFReplicaType: tfv1alpha1.PS,
					Template: &v1.PodTemplateSpec{
						Spec: v1.PodSpec{
							Containers: []v1.Container{
								{
									Name:  "tensorflow",
									Image: *image,
								},
							},
							RestartPolicy: v1.RestartPolicyOnFailure,
						},
					},
				},
				{
					Replicas:      proto.Int32(1),
					TFPort:        proto.Int32(2222),
					TFReplicaType: tfv1alpha1.WORKER,
					Template: &v1.PodTemplateSpec{
						Spec: v1.PodSpec{
							Containers: []v1.Container{
								{
									Name:  "tensorflow",
									Image: *image,
								},
							},
							RestartPolicy: v1.RestartPolicyOnFailure,
						},
					},
				},
			},
			TensorBoard: &tfv1alpha1.TensorBoardSpec{
				LogDir: "/tmp/tensorflow",
			},
		},
	}

	_, err = tfJobClient.KubeflowV1alpha1().TFJobs(Namespace).Create(original)

	if err != nil {
		log.Errorf("Creating the job failed; %v", err)
		return name, err
	}

	// Wait for the job to complete for up to timeout.
	var tfJob *tfv1alpha1.TFJob
	for endTime := time.Now().Add(*timeout); time.Now().Before(endTime); {
		tfJob, err = tfJobClient.KubeflowV1alpha1().TFJobs(Namespace).Get(name, metav1.GetOptions{})
		if err != nil {
			log.Warningf("There was a problem getting TFJob: %v; error %v", name, err)
		}

		if tfJob.Status.State == tfv1alpha1.StateSucceeded || tfJob.Status.State == tfv1alpha1.StateFailed {
			log.Infof("job %v finished:\n%v", name, util.Pformat(tfJob))
			break
		}
		log.Infof("Waiting for job %v to finish:\n%v", name, util.Pformat(tfJob))
		time.Sleep(5 * time.Second)
	}

	if tfJob == nil {
		return name, fmt.Errorf("Failed to get TFJob %v", name)
	}

	if tfJob.Status.State != tfv1alpha1.StateSucceeded {
		// TODO(jlewi): Should we clean up the job.
		return name, fmt.Errorf("TFJob %v did not succeed;\n %v", name, util.Pformat(tfJob))
	}

	if tfJob.Spec.RuntimeId == "" {
		return name, fmt.Errorf("TFJob %v doesn't have a RuntimeId", name)
	}

	// Loop over each replica and make sure the expected resources were created.
	for _, r := range original.Spec.ReplicaSpecs {
		baseName := strings.ToLower(string(r.TFReplicaType))

		for i := 0; i < int(*r.Replicas); i += 1 {
			jobName := fmt.Sprintf("%v-%v-%v-%v", fmt.Sprintf("%.40s", original.ObjectMeta.Name), baseName, tfJob.Spec.RuntimeId, i)

			_, err := kubeCli.BatchV1().Jobs(Namespace).Get(jobName, metav1.GetOptions{})

			if err != nil {
				return name, fmt.Errorf("TFob %v did not create Job %v for ReplicaType %v Index %v", name, jobName, r.TFReplicaType, i)
			}
		}
	}

	// Check that the TensorBoard deployment is present
	tbDeployName := fmt.Sprintf("%v-tensorboard-%v", fmt.Sprintf("%.40s", original.ObjectMeta.Name), tfJob.Spec.RuntimeId)
	_, err = kubeCli.ExtensionsV1beta1().Deployments(Namespace).Get(tbDeployName, metav1.GetOptions{})

	if err != nil {
		return name, fmt.Errorf("TFJob %v did not create Deployment %v for TensorBoard", name, tbDeployName)
	}

	// Check that the TensorBoard service is present
	_, err = kubeCli.CoreV1().Services(Namespace).Get(tbDeployName, metav1.GetOptions{})

	if err != nil {
		return name, fmt.Errorf("TFJob %v did not create Service %v for TensorBoard", name, tbDeployName)
	}

	// Delete the job and make sure all subresources are properly garbage collected.
	if err := tfJobClient.KubeflowV1alpha1().TFJobs(Namespace).Delete(name, &metav1.DeleteOptions{}); err != nil {
		log.Fatalf("Failed to delete TFJob %v; error %v", name, err)
	}

	// Define sets to keep track of Job controllers corresponding to Replicas
	// that still exist.
	jobs := make(map[string]bool)
	isTBDeployDeleted := false

	// Loop over each replica and make sure the expected resources are being deleted.
	for _, r := range original.Spec.ReplicaSpecs {
		baseName := strings.ToLower(string(r.TFReplicaType))

		for i := 0; i < int(*r.Replicas); i += 1 {
			jobName := fmt.Sprintf("%v-%v-%v-%v", fmt.Sprintf("%.40s", original.ObjectMeta.Name), baseName, tfJob.Spec.RuntimeId, i)

			jobs[jobName] = true
		}
	}

	// Wait for all jobs and deployment to be deleted.
	for endTime := time.Now().Add(*timeout); time.Now().Before(endTime) && (len(jobs) > 0 || !isTBDeployDeleted); {
		for k := range jobs {
			_, err := kubeCli.BatchV1().Jobs(Namespace).Get(k, metav1.GetOptions{})
			if k8s_errors.IsNotFound(err) {
				// Deleting map entry during loop is safe.
				// See: https://stackoverflow.com/questions/23229975/is-it-safe-to-remove-selected-keys-from-golang-map-within-a-range-loop
				delete(jobs, k)
			} else {
				log.Infof("Job %v still exists", k)
			}
		}

		if !isTBDeployDeleted {
			// Check that TensorBoard deployment is being deleted
			_, err = kubeCli.ExtensionsV1beta1().Deployments(Namespace).Get(tbDeployName, metav1.GetOptions{})
			if k8s_errors.IsNotFound(err) {
				isTBDeployDeleted = true
			} else {
				log.Infof("TensorBoard deployment %v still exists for TFJob %v", tbDeployName, name)
			}
		}

		if len(jobs) > 0 || !isTBDeployDeleted {
			time.Sleep(5 * time.Second)
		}
	}

	if len(jobs) > 0 {
		return name, fmt.Errorf("Not all Job controllers were successfully deleted for TFJob %v.", name)
	}

	if !isTBDeployDeleted {
		return name, fmt.Errorf("TensorBoard deployment %v was not successfully deleted for TFJob %v.", tbDeployName, name)
	}

	return name, nil
}

func main() {
	flag.Parse()

	if *image == "" {
		log.Fatalf("--image must be provided.")
	}

	type Result struct {
		Error error
		Name  string
	}
	c := make(chan Result)

	for i := 0; i < *numJobs; i += 1 {
		go func() {
			name, err := run()
			if err != nil {
				log.Errorf("TFJob %v didn't run successfully; %v", name, err)
			} else {
				log.Infof("TFJob %v ran successfully", name)
			}
			c <- Result{
				Name:  name,
				Error: err,
			}
		}()
	}

	numSucceded := 0
	numFailed := 0

	for endTime := time.Now().Add(*timeout); numSucceded+numFailed < *numJobs && time.Now().Before(endTime); {
		select {
		case res := <-c:
			if res.Error == nil {
				numSucceded += 1
			} else {
				numFailed += 1
			}
		case <-time.After(endTime.Sub(time.Now())):
			log.Errorf("Timeout waiting for TFJob to finish.")
			fmt.Println("timeout 2")
		}
	}

	if numSucceded+numFailed < *numJobs {
		log.Errorf("Timeout waiting for jobs to finish; only %v of %v TFJobs completed.", numSucceded+numFailed, *numJobs)
	}

	// Generate TAP (https://testanything.org/) output
	fmt.Println("1..1")
	if numSucceded == *numJobs {
		fmt.Println("ok 1 - Successfully ran TFJob")
	} else {
		fmt.Printf("not ok 1 - Running TFJobs failed \n")
		// Exit with non zero exit code for Helm tests.
		os.Exit(1)
	}
}
<commit_msg>nit: try to simplify e2e main.go (#359)<commit_after>// e2e provides an E2E test for TFJobs.
//
// The test creates TFJobs and runs various checks to ensure various operations work as intended.
// The test is intended to run as a helm test that ensures the TFJob operator is working correctly.
// Thus, the program returns non-zero exit status on error.
//
// TODO(jlewi): Do we need to make the test output conform to the TAP(https://testanything.org/)
// protocol so we can fit into the K8s dashboard
//
// TODO(https://github.com/tensorflow/k8s/issues/21) The E2E test should actually run distributed TensorFlow.
package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/gogo/protobuf/proto"
	log "github.com/golang/glog"
	tfv1alpha1 "github.com/tensorflow/k8s/pkg/apis/tensorflow/v1alpha1"
	tfjobclient "github.com/tensorflow/k8s/pkg/client/clientset/versioned"
	"github.com/tensorflow/k8s/pkg/util"
	"k8s.io/api/core/v1"
	k8s_errors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const (
	Namespace = "default"
)

var (
	image   = flag.String("image", "", "The Docker image containing the TF program to run.")
	numJobs = flag.Int("num_jobs", 1, "The number of jobs to run.")
	timeout = flag.Duration("timeout", 5*time.Minute, "The timeout for the test")
)

type tfReplicaType tfv1alpha1.TFReplicaType

func (tfrt tfReplicaType) toSpec() *tfv1alpha1.TFReplicaSpec {
	return &tfv1alpha1.TFReplicaSpec{
		Replicas:      proto.Int32(1),
		TFPort:        proto.Int32(2222),
		TFReplicaType: tfv1alpha1.TFReplicaType(tfrt),
		Template: &v1.PodTemplateSpec{
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:  "tensorflow",
						Image: *image,
					},
				},
				RestartPolicy: v1.RestartPolicyOnFailure,
			},
		},
	}
}

func run() (string, error) {
	// creates the in-cluster config
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	kubeCli, err := clientset.NewForConfig(config)
	if err != nil {
		return "", err
	}

	tfJobClient, err := tfjobclient.NewForConfig(config)
	if err != nil {
		return "", err
	}

	name := "e2e-test-job-" + util.RandString(4)

	original := &tfv1alpha1.TFJob{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
			Labels: map[string]string{
				"test.mlkube.io": "",
			},
		},
		Spec: tfv1alpha1.TFJobSpec{
			ReplicaSpecs: []*tfv1alpha1.TFReplicaSpec{
				tfReplicaType(tfv1alpha1.MASTER).toSpec(),
				tfReplicaType(tfv1alpha1.PS).toSpec(),
				tfReplicaType(tfv1alpha1.WORKER).toSpec(),
			},
			TensorBoard: &tfv1alpha1.TensorBoardSpec{
				LogDir: "/tmp/tensorflow",
			},
		},
	}

	_, err = tfJobClient.KubeflowV1alpha1().TFJobs(Namespace).Create(original)

	if err != nil {
		log.Errorf("Creating the job failed; %v", err)
		return name, err
	}

	// Wait for the job to complete for up to timeout.
	var tfJob *tfv1alpha1.TFJob
	for endTime := time.Now().Add(*timeout); time.Now().Before(endTime); {
		tfJob, err = tfJobClient.KubeflowV1alpha1().TFJobs(Namespace).Get(name, metav1.GetOptions{})
		if err != nil {
			log.Warningf("There was a problem getting TFJob: %v; error %v", name, err)
		}

		if tfJob.Status.State == tfv1alpha1.StateSucceeded || tfJob.Status.State == tfv1alpha1.StateFailed {
			log.Infof("job %v finished:\n%v", name, util.Pformat(tfJob))
			break
		}
		log.Infof("Waiting for job %v to finish:\n%v", name, util.Pformat(tfJob))
		time.Sleep(5 * time.Second)
	}

	if tfJob == nil {
		return name, fmt.Errorf("Failed to get TFJob %v", name)
	}

	if tfJob.Status.State != tfv1alpha1.StateSucceeded {
		// TODO(jlewi): Should we clean up the job.
		return name, fmt.Errorf("TFJob %v did not succeed;\n %v", name, util.Pformat(tfJob))
	}

	if tfJob.Spec.RuntimeId == "" {
		return name, fmt.Errorf("TFJob %v doesn't have a RuntimeId", name)
	}

	// Loop over each replica and make sure the expected resources were created.
	for _, r := range original.Spec.ReplicaSpecs {
		baseName := strings.ToLower(string(r.TFReplicaType))

		for i := 0; i < int(*r.Replicas); i += 1 {
			jobName := fmt.Sprintf("%v-%v-%v-%v", fmt.Sprintf("%.40s", original.ObjectMeta.Name), baseName, tfJob.Spec.RuntimeId, i)

			_, err := kubeCli.BatchV1().Jobs(Namespace).Get(jobName, metav1.GetOptions{})

			if err != nil {
				return name, fmt.Errorf("TFob %v did not create Job %v for ReplicaType %v Index %v", name, jobName, r.TFReplicaType, i)
			}
		}
	}

	// Check that the TensorBoard deployment is present
	tbDeployName := fmt.Sprintf("%v-tensorboard-%v", fmt.Sprintf("%.40s", original.ObjectMeta.Name), tfJob.Spec.RuntimeId)
	_, err = kubeCli.ExtensionsV1beta1().Deployments(Namespace).Get(tbDeployName, metav1.GetOptions{})

	if err != nil {
		return name, fmt.Errorf("TFJob %v did not create Deployment %v for TensorBoard", name, tbDeployName)
	}

	// Check that the TensorBoard service is present
	_, err = kubeCli.CoreV1().Services(Namespace).Get(tbDeployName, metav1.GetOptions{})

	if err != nil {
		return name, fmt.Errorf("TFJob %v did not create Service %v for TensorBoard", name, tbDeployName)
	}

	// Delete the job and make sure all subresources are properly garbage collected.
	if err := tfJobClient.KubeflowV1alpha1().TFJobs(Namespace).Delete(name, &metav1.DeleteOptions{}); err != nil {
		log.Fatalf("Failed to delete TFJob %v; error %v", name, err)
	}

	// Define sets to keep track of Job controllers corresponding to Replicas
	// that still exist.
	jobs := make(map[string]bool)
	isTBDeployDeleted := false

	// Loop over each replica and make sure the expected resources are being deleted.
	for _, r := range original.Spec.ReplicaSpecs {
		baseName := strings.ToLower(string(r.TFReplicaType))

		for i := 0; i < int(*r.Replicas); i += 1 {
			jobName := fmt.Sprintf("%v-%v-%v-%v", fmt.Sprintf("%.40s", original.ObjectMeta.Name), baseName, tfJob.Spec.RuntimeId, i)

			jobs[jobName] = true
		}
	}

	// Wait for all jobs and deployment to be deleted.
	for endTime := time.Now().Add(*timeout); time.Now().Before(endTime) && (len(jobs) > 0 || !isTBDeployDeleted); {
		for k := range jobs {
			_, err := kubeCli.BatchV1().Jobs(Namespace).Get(k, metav1.GetOptions{})
			if k8s_errors.IsNotFound(err) {
				// Deleting map entry during loop is safe.
				// See: https://stackoverflow.com/questions/23229975/is-it-safe-to-remove-selected-keys-from-golang-map-within-a-range-loop
				delete(jobs, k)
			} else {
				log.Infof("Job %v still exists", k)
			}
		}

		if !isTBDeployDeleted {
			// Check that TensorBoard deployment is being deleted
			_, err = kubeCli.ExtensionsV1beta1().Deployments(Namespace).Get(tbDeployName, metav1.GetOptions{})
			if k8s_errors.IsNotFound(err) {
				isTBDeployDeleted = true
			} else {
				log.Infof("TensorBoard deployment %v still exists for TFJob %v", tbDeployName, name)
			}
		}

		if len(jobs) > 0 || !isTBDeployDeleted {
			time.Sleep(5 * time.Second)
		}
	}

	if len(jobs) > 0 {
		return name, fmt.Errorf("Not all Job controllers were successfully deleted for TFJob %v.", name)
	}

	if !isTBDeployDeleted {
		return name, fmt.Errorf("TensorBoard deployment %v was not successfully deleted for TFJob %v.", tbDeployName, name)
	}

	return name, nil
}

func main() {
	flag.Parse()

	if *image == "" {
		log.Fatalf("--image must be provided.")
	}

	type Result struct {
		Error error
		Name  string
	}
	c := make(chan Result)

	for i := 0; i < *numJobs; i += 1 {
		go func() {
			name, err := run()
			if err != nil {
				log.Errorf("TFJob %v didn't run successfully; %v", name, err)
			} else {
				log.Infof("TFJob %v ran successfully", name)
			}
			c <- Result{
				Name:  name,
				Error: err,
			}
		}()
	}

	numSucceded := 0
	numFailed := 0

	for endTime := time.Now().Add(*timeout); numSucceded+numFailed < *numJobs && time.Now().Before(endTime); {
		select {
		case res := <-c:
			if res.Error == nil {
				numSucceded += 1
			} else {
				numFailed += 1
			}
		case <-time.After(endTime.Sub(time.Now())):
			log.Errorf("Timeout waiting for TFJob to finish.")
			fmt.Println("timeout 2")
		}
	}

	if numSucceded+numFailed < *numJobs {
		log.Errorf("Timeout waiting for jobs to finish; only %v of %v TFJobs completed.", numSucceded+numFailed, *numJobs)
	}

	// Generate TAP (https://testanything.org/) output
	fmt.Println("1..1")
	if numSucceded == *numJobs {
		fmt.Println("ok 1 - Successfully ran TFJob")
	} else {
		fmt.Printf("not ok 1 - Running TFJobs failed \n")
		// Exit with non zero exit code for Helm tests.
		os.Exit(1)
	}
}
<|endoftext|>