<commit_before>package config

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/BurntSushi/toml"
)

var (
	xdgRe = regexp.MustCompile("^XDG_*")
)

type ConfigFile struct {
	Options map[string]string `toml:"options"`
	Toggles map[string]bool   `toml:"toggles"`
}

func exportConfig() ConfigFile {
	c := ConfigFile{
		Options: make(map[string]string),
		Toggles: make(map[string]bool),
	}
	for _, p := range GlobalParams {
		c.Options[p.Key] = p.Val
	}
	for _, sw := range GlobalSwitches {
		c.Toggles[sw.Key] = sw.Val
	}
	return c
}

func Read() error {
	var config ConfigFile

	path, err := getConfigPath()
	if err != nil {
		return err
	}

	if _, err := toml.DecodeFile(path, &config); err != nil {
		return err
	}

	for k, v := range config.Options {
		Update(k, v)
	}
	for k, v := range config.Toggles {
		UpdateSwitch(k, v)
	}
	return nil
}

func Write() (path string, err error) {
	path, err = getConfigPath()
	if err != nil {
		return path, err
	}

	cfgdir := basedir(path)
	// create config dir if not exist
	if _, err := os.Stat(cfgdir); err != nil {
		err = os.MkdirAll(cfgdir, 0755)
		if err != nil {
			return path, fmt.Errorf("failed to create config dir [%s]: %s", cfgdir, err)
		}
	}

	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return path, fmt.Errorf("failed to open config for writing: %s", err)
	}

	writer := toml.NewEncoder(file)
	err = writer.Encode(exportConfig())
	if err != nil {
		return path, fmt.Errorf("failed to write config: %s", err)
	}

	return path, nil
}

// determine config path from environment
func getConfigPath() (path string, err error) {
	homeDir, ok := os.LookupEnv("HOME")
	if !ok {
		return path, fmt.Errorf("$HOME not set")
	}

	// use xdg config home if possible
	if xdgSupport() {
		xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME")
		if !ok {
			xdgHome = fmt.Sprintf("%s/.config", homeDir)
		}
		path = fmt.Sprintf("%s/ctop/config", xdgHome)
	} else {
		path = fmt.Sprintf("%s/.ctop", homeDir)
	}

	return path, nil
}

// test for environemnt supporting XDG spec
func xdgSupport() bool {
	for _, e := range os.Environ() {
		if xdgRe.FindAllString(e, 1) != nil {
			return true
		}
	}
	return false
}

func basedir(path string) string {
	parts := strings.Split(path, "/")
	return strings.Join((parts[0 : len(parts)-1]), "/")
}
<commit_msg>rename ConfigFile to File because `config.ConfigFile` stutters. Instead it's `config.File`<commit_after>package config

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/BurntSushi/toml"
)

var (
	xdgRe = regexp.MustCompile("^XDG_*")
)

type File struct {
	Options map[string]string `toml:"options"`
	Toggles map[string]bool   `toml:"toggles"`
}

func exportConfig() File {
	c := File{
		Options: make(map[string]string),
		Toggles: make(map[string]bool),
	}
	for _, p := range GlobalParams {
		c.Options[p.Key] = p.Val
	}
	for _, sw := range GlobalSwitches {
		c.Toggles[sw.Key] = sw.Val
	}
	return c
}

func Read() error {
	var config File

	path, err := getConfigPath()
	if err != nil {
		return err
	}

	if _, err := toml.DecodeFile(path, &config); err != nil {
		return err
	}

	for k, v := range config.Options {
		Update(k, v)
	}
	for k, v := range config.Toggles {
		UpdateSwitch(k, v)
	}
	return nil
}

func Write() (path string, err error) {
	path, err = getConfigPath()
	if err != nil {
		return path, err
	}

	cfgdir := basedir(path)
	// create config dir if not exist
	if _, err := os.Stat(cfgdir); err != nil {
		err = os.MkdirAll(cfgdir, 0755)
		if err != nil {
			return path, fmt.Errorf("failed to create config dir [%s]: %s", cfgdir, err)
		}
	}

	file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return path, fmt.Errorf("failed to open config for writing: %s", err)
	}

	writer := toml.NewEncoder(file)
	err = writer.Encode(exportConfig())
	if err != nil {
		return path, fmt.Errorf("failed to write config: %s", err)
	}

	return path, nil
}

// determine config path from environment
func getConfigPath() (path string, err error) {
	homeDir, ok := os.LookupEnv("HOME")
	if !ok {
		return path, fmt.Errorf("$HOME not set")
	}

	// use xdg config home if possible
	if xdgSupport() {
		xdgHome, ok := os.LookupEnv("XDG_CONFIG_HOME")
		if !ok {
			xdgHome = fmt.Sprintf("%s/.config", homeDir)
		}
		path = fmt.Sprintf("%s/ctop/config", xdgHome)
	} else {
		path = fmt.Sprintf("%s/.ctop", homeDir)
	}

	return path, nil
}

// test for environemnt supporting XDG spec
func xdgSupport() bool {
	for _, e := range os.Environ() {
		if xdgRe.FindAllString(e, 1) != nil {
			return true
		}
	}
	return false
}

func basedir(path string) string {
	parts := strings.Split(path, "/")
	return strings.Join((parts[0 : len(parts)-1]), "/")
}
<|endoftext|>


<commit_before>/*
 * Minimalist Object Storage, (C) 2014, 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 (
	"strings"

	"net/url"

	"github.com/minio-io/cli"
	"github.com/minio-io/iodine"
)

// URLType defines supported storage protocols
type urlType int

const (
	urlUnknown urlType = iota // Unknown type
	urlS3                     // Minio and S3 compatible object storage
	urlDonut                  // Donut object storage
	urlFile                   // POSIX compatible file systems
)

// urlType detects the type of URL
func getURLType(urlStr string) (uType urlType, err error) {
	u, err := url.Parse(urlStr)
	if err != nil {
		return urlUnknown, err
	}
	switch u.Scheme {
	case "http":
		fallthrough
	case "https":
		return urlS3, nil
	case "donut":
		return urlDonut, nil
	case "file":
		fallthrough
	case "":
		return urlFile, nil
	default:
		return urlUnknown, nil
	}
}

// isValidURL checks the validity of supported URL types
func isValidURL(urlStr string) bool {
	u, e := getURLType(urlStr)
	if e != nil || u == urlUnknown {
		return false
	}
	return true
}

// isValidURL checks the validity of supported URL types
func isValidFileURL(urlStr string) bool {
	utype, e := getURLType(urlStr)
	if e != nil || utype != urlFile {
		return false
	}
	return true
}

// fixFileURL rewrites file URL to proper file:///path/to/ form.
func fixFileURL(urlStr string) (fixedURL string, err error) {
	if urlStr == "" {
		return "", errEmptyURL
	}

	utype, e := getURLType(urlStr)
	if e != nil || utype != urlFile {
		return "", e
	}

	u, e := url.Parse(urlStr)
	if e != nil {
		return "", e
	}

	// file:///path should always have empty host
	if u.Host != "" {
		// Not really a file URL. Host is not empty.
		return "", errInvalidURL
	}

	// fill missing scheme
	if u.Scheme == "" {
		// Set it to file
		u.Scheme = "file"
	}

	fixedURL = u.String()
	return fixedURL, nil

}

// url2Host extracts hostname from the URL.
func url2Host(urlStr string) (host string, err error) {
	if urlStr == "" {
		return "", errEmptyURL
	}

	utype, e := getURLType(urlStr)
	if e != nil || utype != urlFile {
		return "", e
	}

	u, e := url.Parse(urlStr)
	if e != nil {
		return "", e
	}

	return u.Host, nil
}

// url2Object converts URL to bucket and objectname
func url2Object(urlStr string) (bucketName, objectName string, err error) {
	u, err := url.Parse(urlStr)
	if u.Path == "" {
		// No bucket name passed. It is a valid case
		return "", "", nil
	}
	splits := strings.SplitN(u.Path, "/", 3)
	switch len(splits) {
	case 0, 1:
		bucketName = ""
		objectName = ""
	case 2:
		bucketName = splits[1]
		objectName = ""
	case 3:
		bucketName = splits[1]
		objectName = splits[2]
	}
	return bucketName, objectName, nil
}

// url2Bucket converts URL to bucket name
func url2Bucket(urlStr string) (bucketName string, err error) {
	bucketName, _, err = url2Object(urlStr)
	return bucketName, iodine.New(err, nil)
}

// parseURL extracts URL string from a single cmd-line argument
func parseURL(arg string) (urlStr string, err error) {
	urlStr = arg
	// Use default host if no argument is passed
	if urlStr == "" {
		// Load config file
		config, err := getMcConfig()
		if err != nil {
			return "", iodine.New(err, nil)
		}
		urlStr = config.DefaultHost
	}

	// Check and expand Alias
	urlStr, err = aliasExpand(urlStr)
	if err != nil {
		return "", iodine.New(err, nil)
	}

	if !isValidURL(urlStr) {
		return "", iodine.New(errUnsupportedScheme, nil)
	}

	// If it is a file URL, rewrite to file:///path/to form
	if isValidFileURL(urlStr) {
		return fixFileURL(urlStr)
	}
	return urlStr, nil
}

// parseURL extracts multiple URL strings from a single cmd-line argument
func parseURLs(c *cli.Context) (urlStr []string, err error) {
	for _, arg := range c.Args() {
		u, err := parseURL(arg)
		if err != nil {
			return nil, iodine.New(err, nil)
		}
		urlStr = append(urlStr, u)
	}
	return urlStr, iodine.New(err, nil)
}
<commit_msg>Adding more iodine to url parser<commit_after>/*
 * Minimalist Object Storage, (C) 2014, 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 (
	"strings"

	"net/url"

	"github.com/minio-io/cli"
	"github.com/minio-io/iodine"
)

// URLType defines supported storage protocols
type urlType int

const (
	urlUnknown urlType = iota // Unknown type
	urlS3                     // Minio and S3 compatible object storage
	urlDonut                  // Donut object storage
	urlFile                   // POSIX compatible file systems
)

// urlType detects the type of URL
func getURLType(urlStr string) (uType urlType, err error) {
	u, err := url.Parse(urlStr)
	if err != nil {
		return urlUnknown, iodine.New(err, nil)
	}
	switch u.Scheme {
	case "http":
		fallthrough
	case "https":
		return urlS3, nil
	case "donut":
		return urlDonut, nil
	case "file":
		fallthrough
	case "":
		return urlFile, nil
	default:
		return urlUnknown, nil
	}
}

// isValidURL checks the validity of supported URL types
func isValidURL(urlStr string) bool {
	u, e := getURLType(urlStr)
	if e != nil || u == urlUnknown {
		return false
	}
	return true
}

// isValidURL checks the validity of supported URL types
func isValidFileURL(urlStr string) bool {
	utype, e := getURLType(urlStr)
	if e != nil || utype != urlFile {
		return false
	}
	return true
}

// fixFileURL rewrites file URL to proper file:///path/to/ form.
func fixFileURL(urlStr string) (fixedURL string, err error) {
	if urlStr == "" {
		return "", iodine.New(errEmptyURL, nil)
	}

	utype, e := getURLType(urlStr)
	if e != nil || utype != urlFile {
		return "", iodine.New(e, nil)
	}

	u, e := url.Parse(urlStr)
	if e != nil {
		return "", iodine.New(e, nil)
	}

	// file:///path should always have empty host
	if u.Host != "" {
		// Not really a file URL. Host is not empty.
		return "", iodine.New(errInvalidURL, nil)
	}

	// fill missing scheme
	if u.Scheme == "" {
		// Set it to file
		u.Scheme = "file"
	}

	fixedURL = u.String()
	return fixedURL, nil

}

// url2Host extracts hostname from the URL.
func url2Host(urlStr string) (host string, err error) {
	if urlStr == "" {
		return "", iodine.New(errEmptyURL, nil)
	}

	utype, e := getURLType(urlStr)
	if e != nil || utype != urlFile {
		return "", iodine.New(e, nil)
	}

	u, e := url.Parse(urlStr)
	if e != nil {
		return "", iodine.New(e, nil)
	}

	return u.Host, nil
}

// url2Object converts URL to bucket and objectname
func url2Object(urlStr string) (bucketName, objectName string, err error) {
	u, err := url.Parse(urlStr)
	if u.Path == "" {
		// No bucket name passed. It is a valid case
		return "", "", iodine.New(errInvalidbucket, nil)
	}
	splits := strings.SplitN(u.Path, "/", 3)
	switch len(splits) {
	case 0, 1:
		bucketName = ""
		objectName = ""
	case 2:
		bucketName = splits[1]
		objectName = ""
	case 3:
		bucketName = splits[1]
		objectName = splits[2]
	}
	return bucketName, objectName, nil
}

// url2Bucket converts URL to bucket name
func url2Bucket(urlStr string) (bucketName string, err error) {
	bucketName, _, err = url2Object(urlStr)
	return bucketName, iodine.New(err, nil)
}

// parseURL extracts URL string from a single cmd-line argument
func parseURL(arg string) (urlStr string, err error) {
	urlStr = arg
	// Use default host if no argument is passed
	if urlStr == "" {
		// Load config file
		config, err := getMcConfig()
		if err != nil {
			return "", iodine.New(err, nil)
		}
		urlStr = config.DefaultHost
	}

	// Check and expand Alias
	urlStr, err = aliasExpand(urlStr)
	if err != nil {
		return "", iodine.New(err, nil)
	}

	if !isValidURL(urlStr) {
		return "", iodine.New(errUnsupportedScheme, nil)
	}

	// If it is a file URL, rewrite to file:///path/to form
	if isValidFileURL(urlStr) {
		return fixFileURL(urlStr)
	}
	return urlStr, nil
}

// parseURL extracts multiple URL strings from a single cmd-line argument
func parseURLs(c *cli.Context) (urlStr []string, err error) {
	for _, arg := range c.Args() {
		u, err := parseURL(arg)
		if err != nil {
			return nil, iodine.New(err, nil)
		}
		urlStr = append(urlStr, u)
	}
	return urlStr, iodine.New(err, nil)
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/sellweek/TOGY/util"
	"time"
)

//Tests if submitted time is during an overriden day.
func (c Config) IsOverridenDay(t time.Time) bool {
	_, ok := c.OverrideDays[util.NormalizeDate(t).Unix()]
	return ok
}

//Tests if it should broadcast on time with timeconfig.
func (tc TimeConfig) IsBroadcastingTime(t time.Time) bool {
	afterOn := util.NormalizeTime(t).After(tc.TurnOn)
	beforeOff := tc.TurnOff.After(util.NormalizeTime(t))
	return afterOn && beforeOff
}

//Tests if according to the config there should be a broadcast on specified time.
func (c Config) BroadcastingTime(t time.Time) bool {
	if c.OverrideOn {
		return true
	}
	if c.OverrideOff {
		return false
	}
	if c.IsOverridenDay(t) {
		return c.OverrideDays[util.NormalizeDate(t).Unix()].IsBroadcastingTime(t)
	}
	if c.Weekends {
		return c.StandardTimeSettings.IsBroadcastingTime(t)
	}

	return !isWeekend(t) && c.StandardTimeSettings.IsBroadcastingTime(t)
}

//Returns whether there should be broadcast at the current time.
func (c Config) Broadcast() bool {
	now := util.NormalizeTime(time.Now())
	return c.BroadcastingTime(now)
}

func isWeekend(t time.Time) bool {
	return 0 == t.Weekday() || 6 == t.Weekday()
}
<commit_msg>That's what I get for fixing bugs in a rush<commit_after>package config

import (
	"github.com/sellweek/TOGY/util"
	"time"
)

//Tests if submitted time is during an overriden day.
func (c Config) IsOverridenDay(t time.Time) bool {
	_, ok := c.OverrideDays[util.NormalizeDate(t).Unix()]
	return ok
}

//Tests if it should broadcast on time with timeconfig.
func (tc TimeConfig) IsBroadcastingTime(t time.Time) bool {
	afterOn := util.NormalizeTime(t).After(tc.TurnOn)
	beforeOff := tc.TurnOff.After(util.NormalizeTime(t))
	return afterOn && beforeOff
}

//Tests if according to the config there should be a broadcast on specified time.
func (c Config) BroadcastingTime(t time.Time) bool {
	if c.OverrideOn {
		return true
	}
	if c.OverrideOff {
		return false
	}
	if c.IsOverridenDay(t) {
		return c.OverrideDays[util.NormalizeDate(t).Unix()].IsBroadcastingTime(t)
	}
	if c.Weekends {
		return c.StandardTimeSettings.IsBroadcastingTime(t)
	}

	return !isWeekend(t) && c.StandardTimeSettings.IsBroadcastingTime(t)
}

//Returns whether there should be broadcast at the current time.
func (c Config) Broadcast() bool {
	now := time.Now()
	val := c.BroadcastingTime(now)
}

func isWeekend(t time.Time) bool {
	return 0 == t.Weekday() || 6 == t.Weekday()
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

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

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

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

package e2e

import (
	"context"
	"fmt"
	"math"
	"net/url"
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/spoof"
	"knative.dev/serving/pkg/apis/serving/v1alpha1"
	"knative.dev/serving/pkg/pool"
	serviceresourcenames "knative.dev/serving/pkg/reconciler/service/resources/names"
	v1alpha1testing "knative.dev/serving/pkg/testing/v1alpha1"
	"knative.dev/serving/test"
	v1a1test "knative.dev/serving/test/v1alpha1"
)

// Latencies is an interface for providing mechanisms for recording timings
// for the parts of the scale test.
type Latencies interface {
	// Add takes the name of this measurement and the time at which it began.
	// This should be called at the moment of completion, so that duration may
	// be computed with `time.Since(start)`.  We use this signature to that this
	// function is suitable for use in a `defer`.
	Add(name string, start time.Time)
}

func abortOnTimeout(ctx context.Context) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		return true, ctx.Err()
	}
}

func ScaleToWithin(t *testing.T, scale int, duration time.Duration, latencies Latencies) {
	clients := Setup(t)

	cleanupCh := make(chan test.ResourceNames, scale)
	defer close(cleanupCh)

	// These are the local (per-probe) and global (all probes) targets for the scale test.
	// 90 = 18/20, so allow two failures with the minimum number of probes, but expect
	// us to have 2.5 9s overall.
	//
	// TODO(#2850): After moving to Istio 1.1 we need to revisit these SLOs.
	const (
		localSLO  = 0.90
		globalSLO = 0.995
		minProbes = 20
	)
	pm := test.NewProberManager(t.Logf, clients, minProbes)

	ctx, cancel := context.WithTimeout(context.Background(), duration)
	defer cancel()
	width := int(math.Ceil(math.Log10(float64(scale))))

	t.Log("Creating new Services")
	wg := pool.NewWithCapacity(50 /* maximum in-flight creates */, scale /* capacity */)
	for i := 0; i < scale; i++ {
		// https://golang.org/doc/faq#closures_and_goroutines
		i := i
		wg.Go(func() error {
			names := test.ResourceNames{
				Service: test.SubServiceNameForTest(t, fmt.Sprintf("%0[1]*[2]d", width, i)),
				Image:   "helloworld",
			}

			// Start the clock for various waypoints towards Service readiness.
			start := time.Now()
			// Record the overall completion time regardless of success/failure.
			defer latencies.Add("time-to-done", start)

			svc, err := v1a1test.CreateLatestService(t, clients, names,
				v1alpha1testing.WithResourceRequirements(corev1.ResourceRequirements{
					Limits: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("50Mi"),
					},
					Requests: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("20Mi"),
					},
				}),
				v1alpha1testing.WithConfigAnnotations(map[string]string{
					"autoscaling.knative.dev/maxScale": "1",
				}),
				v1alpha1testing.WithReadinessProbe(&corev1.Probe{
					Handler: corev1.Handler{
						HTTPGet: &corev1.HTTPGetAction{
							Path: "/",
						},
					},
				}),
				v1alpha1testing.WithRevisionTimeoutSeconds(10))

			if err != nil {
				t.Errorf("CreateLatestService() = %v", err)
				return fmt.Errorf("CreateLatestService() failed: %w", err)
			}
			// Record the time it took to create the service.
			latencies.Add("time-to-create", start)
			names.Route = serviceresourcenames.Route(svc)
			names.Config = serviceresourcenames.Configuration(svc)

			// Send it to our cleanup logic (below)
			cleanupCh <- names

			t.Logf("Wait for %s to become ready.", names.Service)
			var url *url.URL
			err = v1a1test.WaitForServiceState(clients.ServingAlphaClient, names.Service, func(s *v1alpha1.Service) (bool, error) {
				if ctx.Err() != nil {
					return false, ctx.Err()
				}
				if s.Status.URL == nil {
					return false, nil
				}
				url = s.Status.URL.URL()
				return v1a1test.IsServiceReady(s)
			}, "ServiceUpdatedWithURL")
			if err != nil {
				t.Errorf("WaitForServiceState(w/ Domain) = %v", err)
				return fmt.Errorf("WaitForServiceState(w/ Domain) failed: %w", err)
			}

			// Record the time it took to become ready.
			latencies.Add("time-to-ready", start)

			_, err = pkgTest.WaitForEndpointState(
				clients.KubeClient,
				t.Logf,
				url,
				v1a1test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.MatchesBody(test.HelloWorldText), abortOnTimeout(ctx))),
				"WaitForEndpointToServeText",
				test.ServingFlags.ResolvableDomain)
			if err != nil {
				t.Errorf("WaitForEndpointState(expected text) = %v", err)
				return fmt.Errorf("WaitForEndpointState(expected text) failed: %w", err)
			}

			// Record the time it took to get back a 200 with the expected text.
			latencies.Add("time-to-200", start)

			// Start probing the domain until the test is complete.
			pm.Spawn(url)

			t.Logf("%s is ready.", names.Service)
			return nil
		})
	}

	// Wait for all of the service creations to complete (possibly in failure),
	// and signal the done channel.
	doneCh := make(chan error)
	go func() {
		defer close(doneCh)
		if err := wg.Wait(); err != nil {
			doneCh <- err
		}
	}()

	for {
		// As services get created, add logic to clean them up.
		// When all of the creations have finished, then stop all of the active probers
		// and check our SLIs against our SLOs.
		// All of this has to finish within the configured timeout.
		select {
		case names := <-cleanupCh:
			t.Logf("Added %v to cleanup routine.", names)
			test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
			defer test.TearDown(clients, names)

		case err := <-doneCh:
			if err != nil {
				// If we don't do this first, then we'll see tons of 503s from the ongoing probes
				// as we tear down the things they are probing.
				defer pm.Stop()
				t.Fatalf("Unexpected error: %v", err)
			}

			// This ProberManager implementation waits for minProbes before actually stopping.
			if err := pm.Stop(); err != nil {
				t.Fatalf("Stop() = %v", err)
			}
			// Check each of the local SLOs
			pm.Foreach(func(u *url.URL, p test.Prober) {
				if err := test.CheckSLO(localSLO, u.String(), p); err != nil {
					t.Errorf("CheckSLO() = %v", err)
				}
			})
			// Check the global SLO
			if err := test.CheckSLO(globalSLO, "aggregate", pm); err != nil {
				t.Errorf("CheckSLO() = %v", err)
			}
			return
		}
	}
}
<commit_msg>Fix the outdated comment (#6679)<commit_after>/*
Copyright 2019 The Knative Authors

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

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

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

package e2e

import (
	"context"
	"fmt"
	"math"
	"net/url"
	"testing"
	"time"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/spoof"
	"knative.dev/serving/pkg/apis/serving/v1alpha1"
	"knative.dev/serving/pkg/pool"
	serviceresourcenames "knative.dev/serving/pkg/reconciler/service/resources/names"
	v1alpha1testing "knative.dev/serving/pkg/testing/v1alpha1"
	"knative.dev/serving/test"
	v1a1test "knative.dev/serving/test/v1alpha1"
)

// Latencies is an interface for providing mechanisms for recording timings
// for the parts of the scale test.
type Latencies interface {
	// Add takes the name of this measurement and the time at which it began.
	// This should be called at the moment of completion, so that duration may
	// be computed with `time.Since(start)`.  We use this signature to that this
	// function is suitable for use in a `defer`.
	Add(name string, start time.Time)
}

func abortOnTimeout(ctx context.Context) spoof.ResponseChecker {
	return func(resp *spoof.Response) (bool, error) {
		return true, ctx.Err()
	}
}

func ScaleToWithin(t *testing.T, scale int, duration time.Duration, latencies Latencies) {
	clients := Setup(t)

	cleanupCh := make(chan test.ResourceNames, scale)
	defer close(cleanupCh)

	// These are the local (per-probe) and global (all probes) targets for the scale test.
	// 90 = 18/20, so allow two failures with the minimum number of probes, but expect
	// us to have 2.5 9s overall.
	//
	// TODO(#3210): After moving to Istio 1.1 we need to revisit these SLOs.
	const (
		localSLO  = 0.90
		globalSLO = 0.995
		minProbes = 20
	)
	pm := test.NewProberManager(t.Logf, clients, minProbes)

	ctx, cancel := context.WithTimeout(context.Background(), duration)
	defer cancel()
	width := int(math.Ceil(math.Log10(float64(scale))))

	t.Log("Creating new Services")
	wg := pool.NewWithCapacity(50 /* maximum in-flight creates */, scale /* capacity */)
	for i := 0; i < scale; i++ {
		// https://golang.org/doc/faq#closures_and_goroutines
		i := i
		wg.Go(func() error {
			names := test.ResourceNames{
				Service: test.SubServiceNameForTest(t, fmt.Sprintf("%0[1]*[2]d", width, i)),
				Image:   "helloworld",
			}

			// Start the clock for various waypoints towards Service readiness.
			start := time.Now()
			// Record the overall completion time regardless of success/failure.
			defer latencies.Add("time-to-done", start)

			svc, err := v1a1test.CreateLatestService(t, clients, names,
				v1alpha1testing.WithResourceRequirements(corev1.ResourceRequirements{
					Limits: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("50Mi"),
					},
					Requests: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("20Mi"),
					},
				}),
				v1alpha1testing.WithConfigAnnotations(map[string]string{
					"autoscaling.knative.dev/maxScale": "1",
				}),
				v1alpha1testing.WithReadinessProbe(&corev1.Probe{
					Handler: corev1.Handler{
						HTTPGet: &corev1.HTTPGetAction{
							Path: "/",
						},
					},
				}),
				v1alpha1testing.WithRevisionTimeoutSeconds(10))

			if err != nil {
				t.Errorf("CreateLatestService() = %v", err)
				return fmt.Errorf("CreateLatestService() failed: %w", err)
			}
			// Record the time it took to create the service.
			latencies.Add("time-to-create", start)
			names.Route = serviceresourcenames.Route(svc)
			names.Config = serviceresourcenames.Configuration(svc)

			// Send it to our cleanup logic (below)
			cleanupCh <- names

			t.Logf("Wait for %s to become ready.", names.Service)
			var url *url.URL
			err = v1a1test.WaitForServiceState(clients.ServingAlphaClient, names.Service, func(s *v1alpha1.Service) (bool, error) {
				if ctx.Err() != nil {
					return false, ctx.Err()
				}
				if s.Status.URL == nil {
					return false, nil
				}
				url = s.Status.URL.URL()
				return v1a1test.IsServiceReady(s)
			}, "ServiceUpdatedWithURL")
			if err != nil {
				t.Errorf("WaitForServiceState(w/ Domain) = %v", err)
				return fmt.Errorf("WaitForServiceState(w/ Domain) failed: %w", err)
			}

			// Record the time it took to become ready.
			latencies.Add("time-to-ready", start)

			_, err = pkgTest.WaitForEndpointState(
				clients.KubeClient,
				t.Logf,
				url,
				v1a1test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.MatchesBody(test.HelloWorldText), abortOnTimeout(ctx))),
				"WaitForEndpointToServeText",
				test.ServingFlags.ResolvableDomain)
			if err != nil {
				t.Errorf("WaitForEndpointState(expected text) = %v", err)
				return fmt.Errorf("WaitForEndpointState(expected text) failed: %w", err)
			}

			// Record the time it took to get back a 200 with the expected text.
			latencies.Add("time-to-200", start)

			// Start probing the domain until the test is complete.
			pm.Spawn(url)

			t.Logf("%s is ready.", names.Service)
			return nil
		})
	}

	// Wait for all of the service creations to complete (possibly in failure),
	// and signal the done channel.
	doneCh := make(chan error)
	go func() {
		defer close(doneCh)
		if err := wg.Wait(); err != nil {
			doneCh <- err
		}
	}()

	for {
		// As services get created, add logic to clean them up.
		// When all of the creations have finished, then stop all of the active probers
		// and check our SLIs against our SLOs.
		// All of this has to finish within the configured timeout.
		select {
		case names := <-cleanupCh:
			t.Logf("Added %v to cleanup routine.", names)
			test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
			defer test.TearDown(clients, names)

		case err := <-doneCh:
			if err != nil {
				// If we don't do this first, then we'll see tons of 503s from the ongoing probes
				// as we tear down the things they are probing.
				defer pm.Stop()
				t.Fatalf("Unexpected error: %v", err)
			}

			// This ProberManager implementation waits for minProbes before actually stopping.
			if err := pm.Stop(); err != nil {
				t.Fatalf("Stop() = %v", err)
			}
			// Check each of the local SLOs
			pm.Foreach(func(u *url.URL, p test.Prober) {
				if err := test.CheckSLO(localSLO, u.String(), p); err != nil {
					t.Errorf("CheckSLO() = %v", err)
				}
			})
			// Check the global SLO
			if err := test.CheckSLO(globalSLO, "aggregate", pm); err != nil {
				t.Errorf("CheckSLO() = %v", err)
			}
			return
		}
	}
}
<|endoftext|>


<commit_before>package siteengines

// TODO: Check for the visual likeness of two usernames when checking for availability! Generate images and compare pixels.
// TODO: Logging in should work case sensitively, or at least without concern for the case of the first letter
// TODO: Consider using "0" and "1" instead of "true" or "false" when setting values, while still understanding "true" or "false"

import (
	"crypto/sha256"
	"io"
	"math/rand"
	"strings"
	"time"

	. "github.com/xyproto/browserspeak"
	. "github.com/xyproto/genericsite"
	"github.com/xyproto/instapage"
	"github.com/xyproto/web"
)

// An Engine is a specific piece of a website
// This part handles the login/logout/registration/confirmation pages

const (
	MINIMUM_CONFIRMATION_CODE_LENGTH = 20
	USERNAME_ALLOWED_LETTERS         = "abcdefghijklmnopqrstuvwxyzæøåABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ_0123456789"
)

type UserEngine struct {
	state *UserState
}

func NewUserEngine(userState *UserState) *UserEngine {
	// For the secure cookies
	// This must happen before the random seeding, or
	// else people will have to log in again after every server restart
	web.Config.CookieSecret = RandomCookieFriendlyString(30)

	rand.Seed(time.Now().UnixNano())

	return &UserEngine{userState}
}

func (ue *UserEngine) GetState() *UserState {
	return ue.state
}

func CorrectPassword(state *UserState, username, password string) bool {
	passwordHash, err := state.GetPasswordHash(username)
	if err != nil {
		return false
	}
	if passwordHash == HashPasswordVersion2(password) {
		return true
	}
	return false
}

// Goes through all the confirmationCodes of all the unconfirmed users
// and checks if this confirmationCode already is in use
func AlreadyHasConfirmationCode(state *UserState, confirmationCode string) bool {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return false
	}
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, that's okay too
			return false
		}
		if confirmationCode == aConfirmationCode {
			// Found it
			return true
		}
	}
	return false
}

// Create a user by adding the username to the list of usernames
func GenerateConfirmUser(state *UserState) WebHandle {
	return func(ctx *web.Context, val string) string {
		confirmationCode := val

		unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
		if err != nil {
			return instapage.MessageOKurl("Confirmation", "All users are confirmed already.", "/register")
		}

		// Find the username by looking up the confirmationCode on unconfirmed users
		username := ""
		for _, aUsername := range unconfirmedUsernames {
			aConfirmationCode, err := state.GetConfirmationCode(username)
			if err != nil {
				panic("ERROR: Inconsistent user!")
			}
			if confirmationCode == aConfirmationCode {
				// Found the right user
				username = aUsername
				break
			}
		}

		// Check that the user is there
		if username == "" {
			// Say "no longer" because we don't care about people that just try random confirmation links
			return instapage.MessageOKurl("Confirmation", "The confirmation link is no longer valid.", "/register")
		}
		hasUser := state.HasUser(username)
		if !hasUser {
			return instapage.MessageOKurl("Confirmation", "The user you wish to confirm does not exist anymore.", "/register")
		}

		// Remove from the list of unconfirmed usernames
		state.RemoveUnconfirmed(username)

		// Mark user as confirmed
		state.MarkConfirmed(username)

		return instapage.MessageOKurl("Confirmation", "Thank you "+username+", you can now log in.", "/login")
	}
}

// Log in a user by changing the loggedin value
func GenerateLoginUser(state *UserState) WebHandle {
	return func(ctx *web.Context, val string) string {
		// Fetch password from ctx
		password, found := ctx.Params["password"]
		if !found {
			return instapage.MessageOKback("Login", "Can't log in without a password.")
		}
		username := val
		if username == "" {
			return instapage.MessageOKback("Login", "Can't log in with a blank username.")
		}
		if !state.HasUser(username) {
			return instapage.MessageOKback("Login", "User "+username+" does not exist, could not log in.")
		}
		if !state.IsConfirmed(username) {
			return instapage.MessageOKback("Login", "The email for "+username+" has not been confirmed, check your email and follow the link.")
		}
		if !CorrectPassword(state, username, password) {
			return instapage.MessageOKback("Login", "Wrong password.")
		}

		// Log in the user by changing the database and setting a secure cookie
		state.SetLoggedIn(username)

		state.SetBrowserUsername(ctx, username)

		// TODO: Use a welcoming messageOK where the user can see when he/she last logged in and from which host

		if username == "admin" {
			ctx.SetHeader("Refresh", "0; url=/admin", true)
		} else {
			// TODO: Redirect to the page the user was at before logging in
			ctx.SetHeader("Refresh", "0; url=/", true)
		}

		return ""
	}
}

func HashPasswordVersion2(password string) string {
	hasher := sha256.New()
	// TODO: Read up on password hashing
	io.WriteString(hasher, password+"some salt is better than none")
	return string(hasher.Sum(nil))
}

// TODO: Forgot username? Enter email, send username.
// TODO: Lost confirmation link? Enter mail, Receive confirmation link.
// TODO: Forgot password? Enter mail, receive reset-password link.
// TODO: Make sure not two usernames can register at once before confirming
// TODO: Only one username per email address? (meh? can use more than one address?=
// TODO: Maximum 1 confirmation email per email adress
// TODO: Maximum 1 forgot username per email adress per day
// TODO: Maximum 1 forgot password per email adress per day
// TODO: Maximum 1 lost confirmation link per email adress per day
// TODO: Link for "Did you not request this email? Click here" i alle eposter som sendes.
// TODO: Rate limiting, maximum rate per minute or day

// Register a new user
func GenerateRegisterUser(state *UserState) WebHandle {
	return func(ctx *web.Context, val string) string {

		// Password checks
		password1, found := ctx.Params["password1"]
		if password1 == "" || !found {
			return instapage.MessageOKback("Register", "Can't register without a password.")
		}
		password2, found := ctx.Params["password2"]
		if password2 == "" || !found {
			return instapage.MessageOKback("Register", "Please confirm the password by typing it in twice.")
		}
		if password1 != password2 {
			return instapage.MessageOKback("Register", "The password and confirmation password must be equal.")
		}

		// Email checks
		email, found := ctx.Params["email"]
		if !found {
			return instapage.MessageOKback("Register", "Can't register without an email address.")
		}
		// must have @ and ., but no " "
		if !strings.Contains(email, "@") || !strings.Contains(email, ".") || strings.Contains(email, " ") {
			return instapage.MessageOKback("Register", "Please use a valid email address.")
		}

		// Username checks
		username := val
		if username == "" {
			return instapage.MessageOKback("Register", "Can't register without a username.")
		}
		if state.HasUser(username) {
			return instapage.MessageOKback("Register", "That user already exists, try another username.")
		}

		// Only some letters are allowed in the username
	NEXT:
		for _, letter := range username {
			for _, allowedLetter := range USERNAME_ALLOWED_LETTERS {
				if letter == allowedLetter {
					continue NEXT
				}
			}
			return instapage.MessageOKback("Register", "Only a-å, A-Å, 0-9 and _ are allowed in usernames.")
		}
		if username == password1 {
			return instapage.MessageOKback("Register", "Username and password must be different, try another password.")
		}

		adminuser := false
		// A special case
		if username == "admin" {
			// The first user to register with the username "admin" becomes the administrator
			adminuser = true
		}

		// Register the user
		passwordHash := HashPasswordVersion2(password1)
		state.AddUserUnchecked(username, passwordHash, email)

		// Mark user as administrator if that is the case
		if adminuser {
			// Set admin status
			state.SetAdminStatus(username)
		}

		// The confirmation code must be a minimum of 8 letters long
		length := MINIMUM_CONFIRMATION_CODE_LENGTH
		confirmationCode := RandomHumanFriendlyString(length)
		for AlreadyHasConfirmationCode(state, confirmationCode) {
			// Increase the length of the confirmationCode random string every time there is a collision
			length++
			confirmationCode = RandomHumanFriendlyString(length)
			if length > 100 {
				// This should never happen
				panic("ERROR: Too many generated confirmation codes are not unique, something is wrong")
			}
		}

		// Send confirmation email
		ConfirmationEmail("archlinux.no", "https://archlinux.no/confirm/"+confirmationCode, username, email)

		// Register the need to be confirmed
		state.AddUnconfirmed(username, confirmationCode)

		// Redirect
		return instapage.MessageOKurl("Registration complete", "Thanks for registering, the confirmation e-mail has been sent.", "/login")
	}
}

// Log out a user by changing the loggedin value
func GenerateLogoutCurrentUser(state *UserState) SimpleContextHandle {
	return func(ctx *web.Context) string {
		username := GetBrowserUsername(ctx)
		if username == "" {
			return instapage.MessageOKback("Logout", "No user to log out")
		}
		if !state.HasUser(username) {
			return instapage.MessageOKback("Logout", "user "+username+" does not exist, could not log out")
		}

		// Log out the user by changing the database, the cookie can stay
		state.SetLoggedOut(username)

		// Redirect
		//ctx.SetHeader("Refresh", "0; url=/login", true)
		return instapage.MessageOKurl("Logout", username+" is now logged out. Hope to see you soon!", "/login")
	}
}

func GenerateNoJavascriptMessage() SimpleContextHandle {
	return func(ctx *web.Context) string {
		return instapage.MessageOKback("JavaScript error", "Cookies and Javascriåt must be enabled.<br />Older browsers might be supported in the future.")
	}
}

func LoginCP(basecp BaseCP, state *UserState, url string) *ContentPage {
	cp := basecp(state)
	cp.ContentTitle = "Login"
	cp.ContentHTML = instapage.LoginForm()
	cp.ContentJS += OnClick("#loginButton", "$('#loginForm').get(0).setAttribute('action', '/login/' + $('#username').val());")
	//cp.ExtraCSSurls = append(cp.ExtraCSSurls, "/css/login.css")
	cp.Url = url

	// Hide the Login menu if we're on the Login page
	//cp.HeaderJS = strings.Replace(cp.HeaderJS, "menuLogin", "menuNop", 1)
	//cp.ContentJS += Hide("#menuLogin")

	return cp
}

func RegisterCP(basecp BaseCP, state *UserState, url string) *ContentPage {
	cp := basecp(state)
	cp.ContentTitle = "Register"
	cp.ContentHTML = instapage.RegisterForm()
	cp.ContentJS += OnClick("#registerButton", "$('#registerForm').get(0).setAttribute('action', '/register/' + $('#username').val());")
	//cp.ExtraCSSurls = append(cp.ExtraCSSurls, "/css/register.css")
	cp.Url = url

	// Hide the Register menu if we're on the Register page
	//cp.HeaderJS = strings.Replace(cp.HeaderJS, "menuRegister", "menuNop", 1)
	//cp.ContentJS += Hide("#menuRegister")

	return cp
}

func (ue *UserEngine) ServePages() {
	state := ue.state
	web.Post("/register/(.*)", GenerateRegisterUser(state))
	web.Post("/login/(.*)", GenerateLoginUser(state))
	web.Post("/login", GenerateNoJavascriptMessage())
	web.Get("/logout", GenerateLogoutCurrentUser(state))
	web.Get("/confirm/(.*)", GenerateConfirmUser(state))
}
<commit_msg>Fixed a typo<commit_after>package siteengines

// TODO: Check for the visual likeness of two usernames when checking for availability! Generate images and compare pixels.
// TODO: Logging in should work case sensitively, or at least without concern for the case of the first letter
// TODO: Consider using "0" and "1" instead of "true" or "false" when setting values, while still understanding "true" or "false"

import (
	"crypto/sha256"
	"io"
	"math/rand"
	"strings"
	"time"

	. "github.com/xyproto/browserspeak"
	. "github.com/xyproto/genericsite"
	"github.com/xyproto/instapage"
	"github.com/xyproto/web"
)

// An Engine is a specific piece of a website
// This part handles the login/logout/registration/confirmation pages

const (
	MINIMUM_CONFIRMATION_CODE_LENGTH = 20
	USERNAME_ALLOWED_LETTERS         = "abcdefghijklmnopqrstuvwxyzæøåABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ_0123456789"
)

type UserEngine struct {
	state *UserState
}

func NewUserEngine(userState *UserState) *UserEngine {
	// For the secure cookies
	// This must happen before the random seeding, or
	// else people will have to log in again after every server restart
	web.Config.CookieSecret = RandomCookieFriendlyString(30)

	rand.Seed(time.Now().UnixNano())

	return &UserEngine{userState}
}

func (ue *UserEngine) GetState() *UserState {
	return ue.state
}

func CorrectPassword(state *UserState, username, password string) bool {
	passwordHash, err := state.GetPasswordHash(username)
	if err != nil {
		return false
	}
	if passwordHash == HashPasswordVersion2(password) {
		return true
	}
	return false
}

// Goes through all the confirmationCodes of all the unconfirmed users
// and checks if this confirmationCode already is in use
func AlreadyHasConfirmationCode(state *UserState, confirmationCode string) bool {
	unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
	if err != nil {
		return false
	}
	for _, aUsername := range unconfirmedUsernames {
		aConfirmationCode, err := state.GetConfirmationCode(aUsername)
		if err != nil {
			// If the confirmation code can not be found, that's okay too
			return false
		}
		if confirmationCode == aConfirmationCode {
			// Found it
			return true
		}
	}
	return false
}

// Create a user by adding the username to the list of usernames
func GenerateConfirmUser(state *UserState) WebHandle {
	return func(ctx *web.Context, val string) string {
		confirmationCode := val

		unconfirmedUsernames, err := state.GetAllUnconfirmedUsernames()
		if err != nil {
			return instapage.MessageOKurl("Confirmation", "All users are confirmed already.", "/register")
		}

		// Find the username by looking up the confirmationCode on unconfirmed users
		username := ""
		for _, aUsername := range unconfirmedUsernames {
			aConfirmationCode, err := state.GetConfirmationCode(aUsername)
			if err != nil {
				// If the confirmation code can not be found, just skip this one
				continue
			}
			if confirmationCode == aConfirmationCode {
				// Found the right user
				username = aUsername
				break
			}
		}

		// Check that the user is there
		if username == "" {
			// Say "no longer" because we don't care about people that just try random confirmation links
			return instapage.MessageOKurl("Confirmation", "The confirmation link is no longer valid.", "/register")
		}
		hasUser := state.HasUser(username)
		if !hasUser {
			return instapage.MessageOKurl("Confirmation", "The user you wish to confirm does not exist anymore.", "/register")
		}

		// Remove from the list of unconfirmed usernames
		state.RemoveUnconfirmed(username)

		// Mark user as confirmed
		state.MarkConfirmed(username)

		return instapage.MessageOKurl("Confirmation", "Thank you "+username+", you can now log in.", "/login")
	}
}

// Log in a user by changing the loggedin value
func GenerateLoginUser(state *UserState) WebHandle {
	return func(ctx *web.Context, val string) string {
		// Fetch password from ctx
		password, found := ctx.Params["password"]
		if !found {
			return instapage.MessageOKback("Login", "Can't log in without a password.")
		}
		username := val
		if username == "" {
			return instapage.MessageOKback("Login", "Can't log in with a blank username.")
		}
		if !state.HasUser(username) {
			return instapage.MessageOKback("Login", "User "+username+" does not exist, could not log in.")
		}
		if !state.IsConfirmed(username) {
			return instapage.MessageOKback("Login", "The email for "+username+" has not been confirmed, check your email and follow the link.")
		}
		if !CorrectPassword(state, username, password) {
			return instapage.MessageOKback("Login", "Wrong password.")
		}

		// Log in the user by changing the database and setting a secure cookie
		state.SetLoggedIn(username)

		state.SetBrowserUsername(ctx, username)

		// TODO: Use a welcoming messageOK where the user can see when he/she last logged in and from which host

		if username == "admin" {
			ctx.SetHeader("Refresh", "0; url=/admin", true)
		} else {
			// TODO: Redirect to the page the user was at before logging in
			ctx.SetHeader("Refresh", "0; url=/", true)
		}

		return ""
	}
}

func HashPasswordVersion2(password string) string {
	hasher := sha256.New()
	// TODO: Read up on password hashing
	io.WriteString(hasher, password+"some salt is better than none")
	return string(hasher.Sum(nil))
}

// TODO: Forgot username? Enter email, send username.
// TODO: Lost confirmation link? Enter mail, Receive confirmation link.
// TODO: Forgot password? Enter mail, receive reset-password link.
// TODO: Make sure not two usernames can register at once before confirming
// TODO: Only one username per email address? (meh? can use more than one address?=
// TODO: Maximum 1 confirmation email per email adress
// TODO: Maximum 1 forgot username per email adress per day
// TODO: Maximum 1 forgot password per email adress per day
// TODO: Maximum 1 lost confirmation link per email adress per day
// TODO: Link for "Did you not request this email? Click here" i alle eposter som sendes.
// TODO: Rate limiting, maximum rate per minute or day

// Register a new user
func GenerateRegisterUser(state *UserState) WebHandle {
	return func(ctx *web.Context, val string) string {

		// Password checks
		password1, found := ctx.Params["password1"]
		if password1 == "" || !found {
			return instapage.MessageOKback("Register", "Can't register without a password.")
		}
		password2, found := ctx.Params["password2"]
		if password2 == "" || !found {
			return instapage.MessageOKback("Register", "Please confirm the password by typing it in twice.")
		}
		if password1 != password2 {
			return instapage.MessageOKback("Register", "The password and confirmation password must be equal.")
		}

		// Email checks
		email, found := ctx.Params["email"]
		if !found {
			return instapage.MessageOKback("Register", "Can't register without an email address.")
		}
		// must have @ and ., but no " "
		if !strings.Contains(email, "@") || !strings.Contains(email, ".") || strings.Contains(email, " ") {
			return instapage.MessageOKback("Register", "Please use a valid email address.")
		}

		// Username checks
		username := val
		if username == "" {
			return instapage.MessageOKback("Register", "Can't register without a username.")
		}
		if state.HasUser(username) {
			return instapage.MessageOKback("Register", "That user already exists, try another username.")
		}

		// Only some letters are allowed in the username
	NEXT:
		for _, letter := range username {
			for _, allowedLetter := range USERNAME_ALLOWED_LETTERS {
				if letter == allowedLetter {
					continue NEXT
				}
			}
			return instapage.MessageOKback("Register", "Only a-å, A-Å, 0-9 and _ are allowed in usernames.")
		}
		if username == password1 {
			return instapage.MessageOKback("Register", "Username and password must be different, try another password.")
		}

		adminuser := false
		// A special case
		if username == "admin" {
			// The first user to register with the username "admin" becomes the administrator
			adminuser = true
		}

		// Register the user
		passwordHash := HashPasswordVersion2(password1)
		state.AddUserUnchecked(username, passwordHash, email)

		// Mark user as administrator if that is the case
		if adminuser {
			// Set admin status
			state.SetAdminStatus(username)
		}

		// The confirmation code must be a minimum of 8 letters long
		length := MINIMUM_CONFIRMATION_CODE_LENGTH
		confirmationCode := RandomHumanFriendlyString(length)
		for AlreadyHasConfirmationCode(state, confirmationCode) {
			// Increase the length of the confirmationCode random string every time there is a collision
			length++
			confirmationCode = RandomHumanFriendlyString(length)
			if length > 100 {
				// This should never happen
				panic("ERROR: Too many generated confirmation codes are not unique, something is wrong")
			}
		}

		// Send confirmation email
		ConfirmationEmail("archlinux.no", "https://archlinux.no/confirm/"+confirmationCode, username, email)

		// Register the need to be confirmed
		state.AddUnconfirmed(username, confirmationCode)

		// Redirect
		return instapage.MessageOKurl("Registration complete", "Thanks for registering, the confirmation e-mail has been sent.", "/login")
	}
}

// Log out a user by changing the loggedin value
func GenerateLogoutCurrentUser(state *UserState) SimpleContextHandle {
	return func(ctx *web.Context) string {
		username := GetBrowserUsername(ctx)
		if username == "" {
			return instapage.MessageOKback("Logout", "No user to log out")
		}
		if !state.HasUser(username) {
			return instapage.MessageOKback("Logout", "user "+username+" does not exist, could not log out")
		}

		// Log out the user by changing the database, the cookie can stay
		state.SetLoggedOut(username)

		// Redirect
		//ctx.SetHeader("Refresh", "0; url=/login", true)
		return instapage.MessageOKurl("Logout", username+" is now logged out. Hope to see you soon!", "/login")
	}
}

func GenerateNoJavascriptMessage() SimpleContextHandle {
	return func(ctx *web.Context) string {
		return instapage.MessageOKback("JavaScript error", "Cookies and Javascriåt must be enabled.<br />Older browsers might be supported in the future.")
	}
}

func LoginCP(basecp BaseCP, state *UserState, url string) *ContentPage {
	cp := basecp(state)
	cp.ContentTitle = "Login"
	cp.ContentHTML = instapage.LoginForm()
	cp.ContentJS += OnClick("#loginButton", "$('#loginForm').get(0).setAttribute('action', '/login/' + $('#username').val());")
	//cp.ExtraCSSurls = append(cp.ExtraCSSurls, "/css/login.css")
	cp.Url = url

	// Hide the Login menu if we're on the Login page
	//cp.HeaderJS = strings.Replace(cp.HeaderJS, "menuLogin", "menuNop", 1)
	//cp.ContentJS += Hide("#menuLogin")

	return cp
}

func RegisterCP(basecp BaseCP, state *UserState, url string) *ContentPage {
	cp := basecp(state)
	cp.ContentTitle = "Register"
	cp.ContentHTML = instapage.RegisterForm()
	cp.ContentJS += OnClick("#registerButton", "$('#registerForm').get(0).setAttribute('action', '/register/' + $('#username').val());")
	//cp.ExtraCSSurls = append(cp.ExtraCSSurls, "/css/register.css")
	cp.Url = url

	// Hide the Register menu if we're on the Register page
	//cp.HeaderJS = strings.Replace(cp.HeaderJS, "menuRegister", "menuNop", 1)
	//cp.ContentJS += Hide("#menuRegister")

	return cp
}

func (ue *UserEngine) ServePages() {
	state := ue.state
	web.Post("/register/(.*)", GenerateRegisterUser(state))
	web.Post("/login/(.*)", GenerateLoginUser(state))
	web.Post("/login", GenerateNoJavascriptMessage())
	web.Get("/logout", GenerateLogoutCurrentUser(state))
	web.Get("/confirm/(.*)", GenerateConfirmUser(state))
}
<|endoftext|>


<commit_before>package getopt

import (
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

func mapifyConfig(environment []string) (envArray map[string]string) {
	envArray = make(map[string]string)

	for _, cur := range environment {
		envVar := strings.Split(cur, "=")
		if len(envVar) > 1 {
			envArray[strings.TrimSpace(envVar[0])] = strings.TrimSpace(envVar[1])
		}
	}

	return
}

func readConfigFile(path string) (configEntries []string, err *GetOptError) {
	validConfigEntry := regexp.MustCompile("^[A-z0-9_.,]+=.*$")

	content, ioErr := ioutil.ReadFile(path)
	contentStringified := fmt.Sprintf("%s", content)

	if ioErr != nil {
		err = &GetOptError{ConfigFileNotFound, ioErr.Error()}
	} else {
		for _, line := range strings.Split(contentStringified, "\n") {
			if validConfigEntry.MatchString(line) {
				configEntries = append(configEntries, line)
			}
		}
	}

	return
}

func processConfigFile(path string) (err *GetOptError) {
	configEntries, err := readConfigFile(path)
	if err == nil {
		for key, value := range mapifyConfig(configEntries) {
			if os.Getenv(key) == "" {
				os.Setenv(key, value)
			}
		}
	}

	return
}
<commit_msg>use conversion instead of Sprintf<commit_after>package getopt

import (
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

func mapifyConfig(environment []string) (envArray map[string]string) {
	envArray = make(map[string]string)

	for _, cur := range environment {
		envVar := strings.Split(cur, "=")
		if len(envVar) > 1 {
			envArray[strings.TrimSpace(envVar[0])] = strings.TrimSpace(envVar[1])
		}
	}

	return
}

func readConfigFile(path string) (configEntries []string, err *GetOptError) {
	validConfigEntry := regexp.MustCompile("^[A-z0-9_.,]+=.*$")

	content, ioErr := ioutil.ReadFile(path)
	contentStringified := string(content)

	if ioErr != nil {
		err = &GetOptError{ConfigFileNotFound, ioErr.Error()}
	} else {
		for _, line := range strings.Split(contentStringified, "\n") {
			if validConfigEntry.MatchString(line) {
				configEntries = append(configEntries, line)
			}
		}
	}

	return
}

func processConfigFile(path string) (err *GetOptError) {
	configEntries, err := readConfigFile(path)
	if err == nil {
		for key, value := range mapifyConfig(configEntries) {
			if os.Getenv(key) == "" {
				os.Setenv(key, value)
			}
		}
	}

	return
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestParseConfig(t *testing.T) {
	cfg, err := ParseConfig("config.json")
	if err != nil {
		t.Error("parse config.json errer:", err.Error())
	}
	if cfg.MysqlPassword == "" {
		t.Error("parse struct error")
	}
	_, err = ParseConfig("non-existen.json")
	if err == nil {
		t.Error("should throw err when parse from non-existen json file")
	}
}
<commit_msg>config<commit_after>package main

import "testing"

func TestParseConfig(t *testing.T) {
	cfg, err := ParseConfig("config.json.example")
	if err != nil {
		t.Error("parse config.json errer:", err.Error())
	}
	if cfg.MysqlPassword == "" {
		t.Error("parse struct error")
	}
	_, err = ParseConfig("non-existen.json")
	if err == nil {
		t.Error("should throw err when parse from non-existen json file")
	}
}
<|endoftext|>


<commit_before>package config

import (
	"testing"

	"os"
)

func BenchmarkGet(b *testing.B) {
	c := New("test.json")

	var v string
	for i := 0; i < b.N; i++ {
		c.Get("test", &v)
	}
}

func TestGet(t *testing.T) {
	c := New("test.json")

	t.Run("Get", func(t *testing.T) {
		t.Run("should successfully retrieve string", func(t *testing.T) {
			var s string

			err := c.Get("string", &s)
			if err != nil {
				t.Error(err)
			}

			if s != "asdf" {
				t.Errorf("Expected 'asdf', got '%v'", s)
			}
		})

		t.Run("should successfully cast bool to string", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully cast float to string", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully retrieve bool", func(t *testing.T) {
			var b bool

			err := c.Get("bool", &b)
			if err != nil {
				t.Error(err)
			}

			if b != true {
				t.Errorf("Expected 'true', got '%v'", b)
			}
		})

		t.Run("should successfully cast string to bool", func(t *testing.T) {
			var b bool

			err := c.Get("string", &b)
			if err != nil {
				t.Error(err)
			}

			if b != true {
				t.Errorf("Expected 'true', got '%v'", b)
			}
		})

		t.Run("should successfully cast float to bool", func(t *testing.T) {
			var b bool

			// truthy
			err := c.Get("float64", &b)
			if err != nil {
				t.Error(err)
			}

			if b != true {
				t.Errorf("Expected 'true', got '%v'", b)
			}

			// falsey
			err = c.Get("falsey", &b)
			if err != nil {
				t.Error(err)
			}

			if b != false {
				t.Errorf("Expected 'false', got '%v'", b)
			}
		})

		t.Run("should successfully retrieve float64", func(t *testing.T) {
			var f float64

			err := c.Get("float64", &f)
			if err != nil {
				t.Error(err)
			}

			if f != 64.4 {
				t.Errorf("Expected '64.4', got '%v'", f)
			}
		})

		t.Run("should successfully cast string to float", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully cast bool to float", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully override with Env", func(t *testing.T) {
			var s string

			err := os.Setenv("string", "abcd")
			if err != nil {
				t.Error(err)
			}

			err = c.Get("string", &s)
			if err != nil {
				t.Error(err)
			}

			if s != "abcd" {
				t.Errorf("Expected 'abcd', got '%v'", s)
			}
		})

		t.Run("should correctly cast env to float64", func(t *testing.T) {
			var f float64

			err := os.Setenv("float64-2", "50")
			if err != nil {
				t.Error(err)
			}

			err = c.Get("float64-2", &f)
			if err != nil {
				t.Error(err)
			}

			if f != 50 {
				t.Errorf("Expected '50', got '%v'", f)
			}
		})

		t.Run("should correctly cast env to int", func(t *testing.T) {
			var i int

			err := os.Setenv("int", "50")
			if err != nil {
				t.Error(err)
			}

			err = c.Get("int", &i)
			if err != nil {
				t.Error(err)
			}

			if i != 50 {
				t.Errorf("Expected '50', got '%v'", i)
			}
		})

		t.Run("should successfully cast float to int", func(t *testing.T) {
			var i int

			err := c.Get("float64", &i)
			if err != nil {
				t.Error(err)
			}

			if i != 64 {
				t.Errorf("Expected '64', got '%v'", i)
			}
		})

		t.Run("should correctly error for invalid types", func(t *testing.T) {
			var r rune

			err := c.Get("float64", &r)
			if err == nil {
				t.Error("Expected an error, got nil")
			}
		})
	})
}
<commit_msg>Add tests for invalid files<commit_after>package config

import (
	"testing"

	"os"
)

func BenchmarkGet(b *testing.B) {
	c := New("test.json")

	var v string
	for i := 0; i < b.N; i++ {
		c.Get("test", &v)
	}
}

func TestGet(t *testing.T) {
	c := New("test.json")

	t.Run("Get", func(t *testing.T) {
		t.Run("should successfully retrieve string", func(t *testing.T) {
			var s string

			err := c.Get("string", &s)
			if err != nil {
				t.Error(err)
			}

			if s != "asdf" {
				t.Errorf("Expected 'asdf', got '%v'", s)
			}
		})

		t.Run("should successfully cast bool to string", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully cast float to string", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully retrieve bool", func(t *testing.T) {
			var b bool

			err := c.Get("bool", &b)
			if err != nil {
				t.Error(err)
			}

			if b != true {
				t.Errorf("Expected 'true', got '%v'", b)
			}
		})

		t.Run("should successfully cast string to bool", func(t *testing.T) {
			var b bool

			err := c.Get("string", &b)
			if err != nil {
				t.Error(err)
			}

			if b != true {
				t.Errorf("Expected 'true', got '%v'", b)
			}
		})

		t.Run("should successfully cast float to bool", func(t *testing.T) {
			var b bool

			// truthy
			err := c.Get("float64", &b)
			if err != nil {
				t.Error(err)
			}

			if b != true {
				t.Errorf("Expected 'true', got '%v'", b)
			}

			// falsey
			err = c.Get("falsey", &b)
			if err != nil {
				t.Error(err)
			}

			if b != false {
				t.Errorf("Expected 'false', got '%v'", b)
			}
		})

		t.Run("should successfully retrieve float64", func(t *testing.T) {
			var f float64

			err := c.Get("float64", &f)
			if err != nil {
				t.Error(err)
			}

			if f != 64.4 {
				t.Errorf("Expected '64.4', got '%v'", f)
			}
		})

		t.Run("should successfully cast string to float", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully cast bool to float", func(t *testing.T) {
			t.Skip()
		})

		t.Run("should successfully override with Env", func(t *testing.T) {
			var s string

			err := os.Setenv("string", "abcd")
			if err != nil {
				t.Error(err)
			}

			err = c.Get("string", &s)
			if err != nil {
				t.Error(err)
			}

			if s != "abcd" {
				t.Errorf("Expected 'abcd', got '%v'", s)
			}
		})

		t.Run("should correctly cast env to float64", func(t *testing.T) {
			var f float64

			err := os.Setenv("float64-2", "50")
			if err != nil {
				t.Error(err)
			}

			err = c.Get("float64-2", &f)
			if err != nil {
				t.Error(err)
			}

			if f != 50 {
				t.Errorf("Expected '50', got '%v'", f)
			}
		})

		t.Run("should correctly cast env to int", func(t *testing.T) {
			var i int

			err := os.Setenv("int", "50")
			if err != nil {
				t.Error(err)
			}

			err = c.Get("int", &i)
			if err != nil {
				t.Error(err)
			}

			if i != 50 {
				t.Errorf("Expected '50', got '%v'", i)
			}
		})

		t.Run("should successfully cast float to int", func(t *testing.T) {
			var i int

			err := c.Get("float64", &i)
			if err != nil {
				t.Error(err)
			}

			if i != 64 {
				t.Errorf("Expected '64', got '%v'", i)
			}
		})

		t.Run("should correctly error for invalid types", func(t *testing.T) {
			var r rune

			err := c.Get("float64", &r)
			if err == nil {
				t.Error("Expected an error, got nil")
			}
		})

		t.Run("should correctly error for nonexistant files", func(t *testing.T) {
			c2 := New("nonexistant.json")
			err := c2.Reload()
			if err == nil {
				t.Error("Expected an error, got nil")
			}
		})

		t.Run("should correctly error for invalid files", func(t *testing.T) {
			c2 := New("invalid.json")
			err := c2.Reload()
			if err == nil {
				t.Error("Expected an error, got nil")
			}
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func writeFiles(name, config, compose string, t *testing.T) (string, string) {
	tmpDir, err := ioutil.TempDir("", name)
	if err != nil {
		t.Fatal("Couldn't create temp dir", err)
	}

	configFile := fmt.Sprintf("%s/%s", tmpDir, "delmo.yml")
	err = ioutil.WriteFile(configFile, []byte(config), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write config", err)
	}
	t.Logf("Written config file %s", configFile)

	composeFile := fmt.Sprintf("%s/%s", tmpDir, "docker-compose.yml")
	err = ioutil.WriteFile(composeFile, []byte(compose), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write compose", err)
	}
	t.Logf("Written compose files %s", composeFile)

	return tmpDir, configFile
}

func TestConfig_Load(t *testing.T) {
	t.Parallel()

	rawConfig := `
suite:
  name: test
  system: docker-compose.yml
  task_service: redis

tasks:
- name: echo
  command: echo hello, world

tests:
- name: simple
  spec:
  - {wait: [echo]}
  - {stop: [redis]}
  - {start: [redis]}`

	compose := `
version: '2'
services:
  redis:
    image: redis
    build: redis`

	tmpDir, configFile := writeFiles("TestSuite_Load", rawConfig, compose, t)
	defer os.Remove(tmpDir)

	config, err := LoadConfig(configFile)
	if err != nil {
		t.Fatal("Load Suite Failed!", err)
	}

	if want, got := "test", config.Suite.Name; want != got {
		t.Errorf("Name not correct. Want: %s, got: %s", want, got)
	}

	if want, got := tmpDir+"/docker-compose.yml", config.Suite.System; want != got {
		t.Errorf("Path to docker-compose.yml not correct. Want: %s, got: %s", want, got)
	}

	if want, got := "redis", config.Tasks["echo"].Service; want != got {
		t.Errorf("Service not set correctly. Want: %d, got: %d", want, got)
	}

	if want, got := "echo hello, world", config.Tasks["echo"].Cmd; want != got {
		t.Errorf("Command not set correctly. Want: %d, got: %d", want, got)
	}

	if want, got := 2, len(config.Tests[0].Spec); want != got {
		t.Errorf("Spec not parsed correctly. Want: %d, got: %d", want, got)
	}
}
<commit_msg>Fix spec<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func writeFiles(name, config, compose string, t *testing.T) (string, string) {
	tmpDir, err := ioutil.TempDir("", name)
	if err != nil {
		t.Fatal("Couldn't create temp dir", err)
	}

	configFile := fmt.Sprintf("%s/%s", tmpDir, "delmo.yml")
	err = ioutil.WriteFile(configFile, []byte(config), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write config", err)
	}
	t.Logf("Written config file %s", configFile)

	composeFile := fmt.Sprintf("%s/%s", tmpDir, "docker-compose.yml")
	err = ioutil.WriteFile(composeFile, []byte(compose), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write compose", err)
	}
	t.Logf("Written compose files %s", composeFile)

	return tmpDir, configFile
}

func TestConfig_Load(t *testing.T) {
	t.Parallel()

	rawConfig := `
suite:
  name: test
  system: docker-compose.yml
  task_service: redis

tasks:
- name: echo
  command: echo hello, world

tests:
- name: simple
  spec:
  - {wait: [echo]}
  - {stop: [redis]}
  - {start: [redis]}`

	compose := `
version: '2'
services:
  redis:
    image: redis
    build: redis`

	tmpDir, configFile := writeFiles("TestSuite_Load", rawConfig, compose, t)
	defer os.Remove(tmpDir)

	config, err := LoadConfig(configFile)
	if err != nil {
		t.Fatal("Load Suite Failed!", err)
	}

	if want, got := "test", config.Suite.Name; want != got {
		t.Errorf("Name not correct. Want: %s, got: %s", want, got)
	}

	if want, got := tmpDir+"/docker-compose.yml", config.Suite.System; want != got {
		t.Errorf("Path to docker-compose.yml not correct. Want: %s, got: %s", want, got)
	}

	if want, got := "redis", config.Tasks["echo"].Service; want != got {
		t.Errorf("Service not set correctly. Want: %d, got: %d", want, got)
	}

	if want, got := "echo hello, world", config.Tasks["echo"].Cmd; want != got {
		t.Errorf("Command not set correctly. Want: %d, got: %d", want, got)
	}

	if want, got := 3, len(config.Tests[0].Spec); want != got {
		t.Errorf("Spec not parsed correctly. Want: %d, got: %d", want, got)
	}
}
<|endoftext|>


<commit_before>package gerrittest

import (
	"io/ioutil"
	"os"

	"github.com/go-ini/ini"
	. "gopkg.in/check.v1"
)

type ConfigTest struct {
	value string
	set   bool
}

var _ = Suite(&ConfigTest{})

func (s *ConfigTest) SetUpTest(c *C) {
	value, set := os.LookupEnv(DefaultImageEnvironmentVar)
	s.value = value
	s.set = set
	c.Assert(os.Unsetenv(DefaultImageEnvironmentVar), IsNil)
}

func (s *ConfigTest) TearDownTest(c *C) {
	if s.set {
		c.Assert(os.Setenv(DefaultImageEnvironmentVar, s.value), IsNil)
		return
	}
	c.Assert(os.Unsetenv(DefaultImageEnvironmentVar), IsNil)
}

func (s *ConfigTest) TestNewConfigOverride(c *C) {
	c.Assert(os.Setenv(DefaultImageEnvironmentVar, "override"), IsNil)
	cfg := NewConfig()
	c.Assert(cfg.Image, Equals, "override")
}

func (s *ConfigTest) testWrittenConfig(c *C, path string) {
	cfg, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, path)
	c.Assert(err, IsNil)
	heads := cfg.Section(accessHeads)
	c.Assert(
		heads.Key("label-Verified").ValueWithShadows(), DeepEquals,
		[]string{"-1..+1 group Administrators", "-1..+1 group Project Owners"})
	verifiedLabel := cfg.Section(labelVerified)
	c.Assert(verifiedLabel.Key("function").Value(), Equals, "MaxWithBlock")
	c.Assert(verifiedLabel.Key("defaultValue").Value(), Equals, "0")
	c.Assert(
		verifiedLabel.Key("value").ValueWithShadows(), DeepEquals,
		[]string{"-1 Fails", "0 No Score", "+1 Verified"})
}

func (s *ConfigTest) Test_projectConfig_newProjectConfig_missingFile(c *C) {
	file, err := ioutil.TempFile("", "")
	defer os.Remove(file.Name()) // nolint: errcheck
	c.Assert(err, IsNil)
	c.Assert(file.Close(), IsNil)
	c.Assert(os.Remove(file.Name()), IsNil)
	cfg, err := newProjectConfig(file.Name())
	c.Assert(err, IsNil)
	c.Assert(cfg.write(file.Name()), IsNil)
	s.testWrittenConfig(c, file.Name())
}

func (s *ConfigTest) Test_projectConfig_newProjectConfig_existingConifg(c *C) {
	file, err := ioutil.TempFile("", "")
	c.Assert(err, IsNil)
	_, err = file.WriteString(`
[label "Verified"]
default = 1
	`)
	c.Assert(err, IsNil)
	defer os.Remove(file.Name()) // nolint: errcheck
	c.Assert(err, IsNil)
	c.Assert(file.Close(), IsNil)
	cfg, err := newProjectConfig(file.Name())
	c.Assert(err, IsNil)
	c.Assert(cfg.write(file.Name()), IsNil)
	s.testWrittenConfig(c, file.Name())
}
<commit_msg>added test for Copy()<commit_after>package gerrittest

import (
	"io/ioutil"
	"os"

	"github.com/go-ini/ini"
	. "gopkg.in/check.v1"
)

type ConfigTest struct {
	value string
	set   bool
}

var _ = Suite(&ConfigTest{})

func (s *ConfigTest) SetUpTest(c *C) {
	value, set := os.LookupEnv(DefaultImageEnvironmentVar)
	s.value = value
	s.set = set
	c.Assert(os.Unsetenv(DefaultImageEnvironmentVar), IsNil)
}

func (s *ConfigTest) TearDownTest(c *C) {
	if s.set {
		c.Assert(os.Setenv(DefaultImageEnvironmentVar, s.value), IsNil)
		return
	}
	c.Assert(os.Unsetenv(DefaultImageEnvironmentVar), IsNil)
}

func (s *ConfigTest) TestNewConfigOverride(c *C) {
	c.Assert(os.Setenv(DefaultImageEnvironmentVar, "override"), IsNil)
	cfg := NewConfig()
	c.Assert(cfg.Image, Equals, "override")
}

func (s *ConfigTest) TestConfigCopy(c *C) {
	cfg := NewConfig()
	cfg.RepoRoot = "foo"
	cfg2 := cfg.Copy()
	c.Assert(cfg2.RepoRoot, Equals, "foo")
	cfg2.RepoRoot = "bar"
	c.Assert(cfg2.RepoRoot, Equals, "bar")
	c.Assert(cfg.RepoRoot, Equals, "foo")
}

func (s *ConfigTest) testWrittenConfig(c *C, path string) {
	cfg, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, path)
	c.Assert(err, IsNil)
	heads := cfg.Section(accessHeads)
	c.Assert(
		heads.Key("label-Verified").ValueWithShadows(), DeepEquals,
		[]string{"-1..+1 group Administrators", "-1..+1 group Project Owners"})
	verifiedLabel := cfg.Section(labelVerified)
	c.Assert(verifiedLabel.Key("function").Value(), Equals, "MaxWithBlock")
	c.Assert(verifiedLabel.Key("defaultValue").Value(), Equals, "0")
	c.Assert(
		verifiedLabel.Key("value").ValueWithShadows(), DeepEquals,
		[]string{"-1 Fails", "0 No Score", "+1 Verified"})
}

func (s *ConfigTest) Test_projectConfig_newProjectConfig_missingFile(c *C) {
	file, err := ioutil.TempFile("", "")
	defer os.Remove(file.Name()) // nolint: errcheck
	c.Assert(err, IsNil)
	c.Assert(file.Close(), IsNil)
	c.Assert(os.Remove(file.Name()), IsNil)
	cfg, err := newProjectConfig(file.Name())
	c.Assert(err, IsNil)
	c.Assert(cfg.write(file.Name()), IsNil)
	s.testWrittenConfig(c, file.Name())
}

func (s *ConfigTest) Test_projectConfig_newProjectConfig_existingConifg(c *C) {
	file, err := ioutil.TempFile("", "")
	c.Assert(err, IsNil)
	_, err = file.WriteString(`
[label "Verified"]
default = 1
	`)
	c.Assert(err, IsNil)
	defer os.Remove(file.Name()) // nolint: errcheck
	c.Assert(err, IsNil)
	c.Assert(file.Close(), IsNil)
	cfg, err := newProjectConfig(file.Name())
	c.Assert(err, IsNil)
	c.Assert(cfg.write(file.Name()), IsNil)
	s.testWrittenConfig(c, file.Name())
}
<|endoftext|>


<commit_before>package ddtxn

import (
	"container/heap"
	"ddtxn/dlog"
	"flag"
	"log"
	"sync/atomic"
	"time"
)

const (
	BUMP_EPOCH_MS = 80
	EPOCH_INCR    = 1 << 32
	TXID_MASK     = 0x00000000ffffffff
	CLEAR_TID     = 0xffffffff00000000
)

var PhaseLength = flag.Int("phase", 80, "Phase length in milliseconds, default 80")

type Coordinator struct {
	n        int
	Workers  []*Worker
	epochTID uint64 // Global TID, atomically incremented and read

	// Notify workers
	wepoch []chan TID
	wsafe  []chan TID
	wgo    []chan TID
	wdone  []chan TID

	Coordinate            bool
	PotentialPhaseChanges int64
	Done                  chan chan bool
	Accelerate            chan bool
	trigger               int32
	to_remove             map[Key]bool
}

func NewCoordinator(n int, s *Store) *Coordinator {
	c := &Coordinator{
		n:                     n,
		Workers:               make([]*Worker, n),
		epochTID:              EPOCH_INCR,
		wepoch:                make([]chan TID, n),
		wsafe:                 make([]chan TID, n),
		wgo:                   make([]chan TID, n),
		wdone:                 make([]chan TID, n),
		Done:                  make(chan chan bool),
		Accelerate:            make(chan bool),
		Coordinate:            false,
		PotentialPhaseChanges: 0,
		to_remove:             make(map[Key]bool),
	}
	for i := 0; i < n; i++ {
		c.wepoch[i] = make(chan TID)
		c.wsafe[i] = make(chan TID)
		c.wgo[i] = make(chan TID)
		c.wdone[i] = make(chan TID)
		c.Workers[i] = NewWorker(i, s, c)
	}
	dlog.Printf("[coordinator] %v workers\n", n)
	go c.Process()
	return c
}

var NextEpoch int64

func (c *Coordinator) NextGlobalTID() TID {
	atomic.AddInt64(&NextEpoch, 1)
	x := atomic.AddUint64(&c.epochTID, EPOCH_INCR)
	return TID(x)
}

func (c *Coordinator) GetEpoch() TID {
	x := atomic.LoadUint64(&c.epochTID)
	return TID(x)
}

var RMoved int64
var WMoved int64
var Time_in_IE time.Duration
var Time_in_IE1 time.Duration

func (c *Coordinator) Stats() (map[Key]bool, map[Key]bool) {
	if c.PotentialPhaseChanges%(10) != 0 {
		return nil, nil
	}
	start2 := time.Now()
	s := c.Workers[0].store
	for i := 0; i < c.n; i++ {
		w := c.Workers[i]
		c.Workers[i].Lock()
		s.cand.Merge(w.local_store.candidates)
	}
	potential_dd_keys := make(map[Key]bool)
	to_remove := make(map[Key]bool)
	xx := len(*s.cand.h)
	for i := 0; i < xx; i++ {
		o := heap.Pop(s.cand.h).(*OneStat)
		br, _ := s.getKey(o.k)
		if !br.dd {
			if o.ratio() > *WRRatio && (o.writes > 1 || o.conflicts > 1) {
				if len(s.dd) == 0 { // Higher threshold for the first one, since it kicks off phases
					if o.ratio() > 1.33*(*WRRatio) && (o.writes > 1 || o.conflicts > 2) {
						potential_dd_keys[o.k] = true
						dlog.Printf("Moving %v to split r:%v w:%v c:%v s:%v ratio:%v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
					} else {
						dlog.Printf("Key %v didn't pass higher ratio threshold; len(s.dd)==0  r:%v w:%v c:%v s:%v ratio:%v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
					}
				} else {
					potential_dd_keys[o.k] = true
					dlog.Printf("Moving %v to split r:%v w:%v c:%v s:%v ratio:%v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
				}
			} else {
				dlog.Printf("Not enough writes or conflicts or high enough ratio yet for key : %v; r:%v w:%v c:%v s:%v ratio:%v; wr: %v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio(), *WRRatio)
			}
		} else if br.dd {
			// Key is split; might potentially move back
		}
	}
	// Check to see if we need to remove anything from dd
	for k, v := range s.dd {
		if !v {
			continue
		}
		o, ok := s.cand.m[k]
		if !ok {
			dlog.Printf("Key %v was split but now is not in store candidates\n", k)
			continue
		}
		if o.ratio() < (*WRRatio)/2 {
			if x, ok := c.to_remove[k]; x && ok {
				c.to_remove[k] = false
				to_remove[k] = true
			} else {
				c.to_remove[k] = true
			}
			dlog.Printf("Moved %v from split r:%v w:%v c:%v s:%v ratio:%v\n", k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
		}
	}
	if len(s.dd) == 0 && len(potential_dd_keys) == 0 {
		s.any_dd = false
		c.Coordinate = false
	} else {
		c.Coordinate = true
		s.any_dd = true
	}
	// Reset global store
	x := make([]*OneStat, 0)
	sh := StatsHeap(x)
	s.cand = &Candidates{make(map[Key]*OneStat), &sh}

	for i := 0; i < c.n; i++ {
		// Reset local stores and unlock
		w := c.Workers[i]
		x := make([]*OneStat, 0)
		sh := StatsHeap(x)
		w.local_store.candidates = &Candidates{make(map[Key]*OneStat), &sh}
		w.Unlock()
	}
	end := time.Since(start2)
	Time_in_IE1 += end
	return potential_dd_keys, to_remove
}

func (c *Coordinator) IncrementEpoch(force bool) {
	c.PotentialPhaseChanges++
	move_dd, remove_dd := c.Stats()
	if !c.Coordinate && !force {
		return
	}
	start := time.Now()
	next_epoch := c.NextGlobalTID()

	// Wait for everyone to merge the previous epoch
	for i := 0; i < c.n; i++ {
		e := <-c.wepoch[i]
		if e != next_epoch {
			log.Fatalf("Out of alignment in epoch ack; I expected %v, got %v\n", next_epoch, e)
		}
	}

	// All merged.  The previous epoch is now safe; tell everyone to
	// do their reads.
	atomic.StoreInt32(&c.trigger, 0)
	for i := 0; i < c.n; i++ {
		c.wsafe[i] <- next_epoch
	}
	for i := 0; i < c.n; i++ {
		e := <-c.wdone[i]
		if e != next_epoch {
			log.Fatalf("Out of alignment in done; I expected %v, got %v\n", next_epoch, e)
		}

	}

	s := c.Workers[0].store
	// Merge dd
	if move_dd != nil {
		for k, _ := range move_dd {
			br, _ := s.getKey(k)
			br.dd = true
			s.dd[k] = true
			WMoved += 1
		}
	}
	if remove_dd != nil {
		for k, _ := range remove_dd {
			br, _ := s.getKey(k)
			br.dd = false
			s.dd[k] = false
			RMoved += 1
		}
	}

	for i := 0; i < c.n; i++ {
		c.wgo[i] <- next_epoch
	}
	end := time.Since(start)
	Time_in_IE += end
}

func (c *Coordinator) Finish() {
	dlog.Printf("Coordinator finishing\n")
	x := make(chan bool)
	c.Done <- x
	<-x
}

var Nfast int64

func (c *Coordinator) Process() {
	tm := time.NewTicker(time.Duration(*PhaseLength) * time.Millisecond).C

	// More frequently, check if the workers are demanding a phase
	// change due to long stashed queue lengths.
	check_trigger := time.NewTicker(time.Duration(*PhaseLength) * time.Microsecond * 10).C

	for {
		select {
		case x := <-c.Done:
			if *SysType == DOPPEL {
				c.IncrementEpoch(true)
			}
			for i := 0; i < c.n; i++ {
				c.Workers[i].done <- true
			}
			x <- true
			return
		case <-tm:
			if *SysType == DOPPEL {
				c.IncrementEpoch(false)
			}
		case <-check_trigger:
			if *SysType == DOPPEL {
				x := atomic.LoadInt32(&c.trigger)
				if x == int32(c.n) {
					Nfast++
					atomic.StoreInt32(&c.trigger, 0)
					c.IncrementEpoch(true)
				}
			}
		case <-c.Accelerate:
			if *SysType == DOPPEL {
				dlog.Printf("Accelerating\n")
				c.IncrementEpoch(true)
			}
		}
	}
}
<commit_msg>Don't transition phases if there is only one core<commit_after>package ddtxn

import (
	"container/heap"
	"ddtxn/dlog"
	"flag"
	"log"
	"sync/atomic"
	"time"
)

const (
	BUMP_EPOCH_MS = 80
	EPOCH_INCR    = 1 << 32
	TXID_MASK     = 0x00000000ffffffff
	CLEAR_TID     = 0xffffffff00000000
)

var PhaseLength = flag.Int("phase", 80, "Phase length in milliseconds, default 80")

type Coordinator struct {
	n        int
	Workers  []*Worker
	epochTID uint64 // Global TID, atomically incremented and read

	// Notify workers
	wepoch []chan TID
	wsafe  []chan TID
	wgo    []chan TID
	wdone  []chan TID

	Coordinate            bool
	PotentialPhaseChanges int64
	Done                  chan chan bool
	Accelerate            chan bool
	trigger               int32
	to_remove             map[Key]bool
}

func NewCoordinator(n int, s *Store) *Coordinator {
	c := &Coordinator{
		n:                     n,
		Workers:               make([]*Worker, n),
		epochTID:              EPOCH_INCR,
		wepoch:                make([]chan TID, n),
		wsafe:                 make([]chan TID, n),
		wgo:                   make([]chan TID, n),
		wdone:                 make([]chan TID, n),
		Done:                  make(chan chan bool),
		Accelerate:            make(chan bool),
		Coordinate:            false,
		PotentialPhaseChanges: 0,
		to_remove:             make(map[Key]bool),
	}
	for i := 0; i < n; i++ {
		c.wepoch[i] = make(chan TID)
		c.wsafe[i] = make(chan TID)
		c.wgo[i] = make(chan TID)
		c.wdone[i] = make(chan TID)
		c.Workers[i] = NewWorker(i, s, c)
	}
	dlog.Printf("[coordinator] %v workers\n", n)
	go c.Process()
	return c
}

var NextEpoch int64

func (c *Coordinator) NextGlobalTID() TID {
	atomic.AddInt64(&NextEpoch, 1)
	x := atomic.AddUint64(&c.epochTID, EPOCH_INCR)
	return TID(x)
}

func (c *Coordinator) GetEpoch() TID {
	x := atomic.LoadUint64(&c.epochTID)
	return TID(x)
}

var RMoved int64
var WMoved int64
var Time_in_IE time.Duration
var Time_in_IE1 time.Duration

func (c *Coordinator) Stats() (map[Key]bool, map[Key]bool) {
	if c.PotentialPhaseChanges%(10) != 0 {
		return nil, nil
	}
	start2 := time.Now()
	s := c.Workers[0].store
	for i := 0; i < c.n; i++ {
		w := c.Workers[i]
		c.Workers[i].Lock()
		s.cand.Merge(w.local_store.candidates)
	}
	potential_dd_keys := make(map[Key]bool)
	to_remove := make(map[Key]bool)
	xx := len(*s.cand.h)
	for i := 0; i < xx; i++ {
		o := heap.Pop(s.cand.h).(*OneStat)
		br, _ := s.getKey(o.k)
		if !br.dd {
			if o.ratio() > *WRRatio && (o.writes > 1 || o.conflicts > 1) {
				if len(s.dd) == 0 { // Higher threshold for the first one, since it kicks off phases
					if o.ratio() > 1.33*(*WRRatio) && (o.writes > 1 || o.conflicts > 2) {
						potential_dd_keys[o.k] = true
						dlog.Printf("Moving %v to split r:%v w:%v c:%v s:%v ratio:%v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
					} else {
						dlog.Printf("Key %v didn't pass higher ratio threshold; len(s.dd)==0  r:%v w:%v c:%v s:%v ratio:%v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
					}
				} else {
					potential_dd_keys[o.k] = true
					dlog.Printf("Moving %v to split r:%v w:%v c:%v s:%v ratio:%v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
				}
			} else {
				dlog.Printf("Not enough writes or conflicts or high enough ratio yet for key : %v; r:%v w:%v c:%v s:%v ratio:%v; wr: %v\n", o.k, o.reads, o.writes, o.conflicts, o.stash, o.ratio(), *WRRatio)
			}
		} else if br.dd {
			// Key is split; might potentially move back
		}
	}
	// Check to see if we need to remove anything from dd
	for k, v := range s.dd {
		if !v {
			continue
		}
		o, ok := s.cand.m[k]
		if !ok {
			dlog.Printf("Key %v was split but now is not in store candidates\n", k)
			continue
		}
		if o.ratio() < (*WRRatio)/2 {
			if x, ok := c.to_remove[k]; x && ok {
				c.to_remove[k] = false
				to_remove[k] = true
			} else {
				c.to_remove[k] = true
			}
			dlog.Printf("Moved %v from split r:%v w:%v c:%v s:%v ratio:%v\n", k, o.reads, o.writes, o.conflicts, o.stash, o.ratio())
		}
	}
	if len(s.dd) == 0 && len(potential_dd_keys) == 0 {
		s.any_dd = false
		c.Coordinate = false
	} else {
		c.Coordinate = true
		s.any_dd = true
	}
	// Reset global store
	x := make([]*OneStat, 0)
	sh := StatsHeap(x)
	s.cand = &Candidates{make(map[Key]*OneStat), &sh}

	for i := 0; i < c.n; i++ {
		// Reset local stores and unlock
		w := c.Workers[i]
		x := make([]*OneStat, 0)
		sh := StatsHeap(x)
		w.local_store.candidates = &Candidates{make(map[Key]*OneStat), &sh}
		w.Unlock()
	}
	end := time.Since(start2)
	Time_in_IE1 += end
	return potential_dd_keys, to_remove
}

func (c *Coordinator) IncrementEpoch(force bool) {
	c.PotentialPhaseChanges++
	move_dd, remove_dd := c.Stats()
	if !c.Coordinate && !force {
		return
	}
	start := time.Now()
	next_epoch := c.NextGlobalTID()

	// Wait for everyone to merge the previous epoch
	for i := 0; i < c.n; i++ {
		e := <-c.wepoch[i]
		if e != next_epoch {
			log.Fatalf("Out of alignment in epoch ack; I expected %v, got %v\n", next_epoch, e)
		}
	}

	// All merged.  The previous epoch is now safe; tell everyone to
	// do their reads.
	atomic.StoreInt32(&c.trigger, 0)
	for i := 0; i < c.n; i++ {
		c.wsafe[i] <- next_epoch
	}
	for i := 0; i < c.n; i++ {
		e := <-c.wdone[i]
		if e != next_epoch {
			log.Fatalf("Out of alignment in done; I expected %v, got %v\n", next_epoch, e)
		}

	}

	s := c.Workers[0].store
	// Merge dd
	if move_dd != nil {
		for k, _ := range move_dd {
			br, _ := s.getKey(k)
			br.dd = true
			s.dd[k] = true
			WMoved += 1
		}
	}
	if remove_dd != nil {
		for k, _ := range remove_dd {
			br, _ := s.getKey(k)
			br.dd = false
			s.dd[k] = false
			RMoved += 1
		}
	}

	for i := 0; i < c.n; i++ {
		c.wgo[i] <- next_epoch
	}
	end := time.Since(start)
	Time_in_IE += end
}

func (c *Coordinator) Finish() {
	dlog.Printf("Coordinator finishing\n")
	x := make(chan bool)
	c.Done <- x
	<-x
}

var Nfast int64

func (c *Coordinator) Process() {
	tm := time.NewTicker(time.Duration(*PhaseLength) * time.Millisecond).C

	// More frequently, check if the workers are demanding a phase
	// change due to long stashed queue lengths.
	check_trigger := time.NewTicker(time.Duration(*PhaseLength) * time.Microsecond * 10).C

	for {
		select {
		case x := <-c.Done:
			if *SysType == DOPPEL && c.n > 1 {
				c.IncrementEpoch(true)
			}
			for i := 0; i < c.n; i++ {
				c.Workers[i].done <- true
			}
			x <- true
			return
		case <-tm:
			if *SysType == DOPPEL && c.n > 1 {
				c.IncrementEpoch(false)
			}
		case <-check_trigger:
			if *SysType == DOPPEL && c.n > 1 {
				x := atomic.LoadInt32(&c.trigger)
				if x == int32(c.n) {
					Nfast++
					atomic.StoreInt32(&c.trigger, 0)
					c.IncrementEpoch(true)
				}
			}
		case <-c.Accelerate:
			if *SysType == DOPPEL && c.n > 1 {
				dlog.Printf("Accelerating\n")
				c.IncrementEpoch(true)
			}
		}
	}
}
<|endoftext|>


<commit_before>package util

import (
	"regexp"
)

var validTopicNameRegex = regexp.MustCompile(`^[\.a-zA-Z0-9_-]+$`)
var validChannelNameRegex = regexp.MustCompile(`^[\.a-zA-Z0-9_-]+(#ephemeral)?$`)

// IsValidTopicName checks a topic name for correctness
func IsValidTopicName(name string) bool {
	if len(name) > 32 || len(name) < 1 {
		return false
	}
	return validTopicNameRegex.MatchString(name)
}

// IsValidChannelName checks a channel name for correctness
func IsValidChannelName(name string) bool {
	if len(name) > 32 || len(name) < 1 {
		return false
	}
	return validChannelNameRegex.MatchString(name)
}
<commit_msg>increased allowable channel and topic name length<commit_after>package util

import (
	"regexp"
)

var validTopicNameRegex = regexp.MustCompile(`^[\.a-zA-Z0-9_-]+$`)
var validChannelNameRegex = regexp.MustCompile(`^[\.a-zA-Z0-9_-]+(#ephemeral)?$`)

// IsValidTopicName checks a topic name for correctness
func IsValidTopicName(name string) bool {
	if len(name) > 64 || len(name) < 1 {
		return false
	}
	return validTopicNameRegex.MatchString(name)
}

// IsValidChannelName checks a channel name for correctness
func IsValidChannelName(name string) bool {
	if len(name) > 64 || len(name) < 1 {
		return false
	}
	return validChannelNameRegex.MatchString(name)
}
<|endoftext|>