<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/nightlyone/lockfile"
	"io"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

// Avoid thundering herd problem on remote services used by this command.
// Interval will be 0, if this is not an issue.
func SpreadWait(interval time.Duration) {
	if interval > 0 {
		// Seed random generator with current process ID
		rand.Seed(int64(os.Getpid()))
		// Sleep for random amount of time within interval
		time.Sleep(time.Duration(rand.Int63n(int64(interval))))
	}
}

// Ok states that execution went well. Logs debug output and reports ok to
// monitoring.
func Ok() {
	log.Println("Ok")
	monitor(monitorOk, "")
}

// NotAvailable states that the command could not be started successfully. It
// might not be installed or has other problems.
func NotAvailable(err error) {
	s := fmt.Sprintln("Cannot start command: ", err)
	log.Println("FATAL:", s)
	monitor(monitorUnknown, s)
}

// TimedOut states that the command took too long and reports failure to the
// monitoring.
func TimedOut() {
	s := "execution took too long"
	log.Println("FATAL:", s)
	monitor(monitorCritical, s)
}

// Busy states that the command hangs and reports failure to the monitoring.
// Those tasks should be automatically killed, if it happens often.
func Busy() {
	s := "previous invocation of command still running"
	log.Println("FATAL:", s)
	monitor(monitorCritical, s)
}

// Failed states that the command didn't execute successfully and reports
// failure to the monitoring. Also Logs error output.
func Failed(err error) {
	s := fmt.Sprintln("Failed to execute: ", err)
	log.Println("FATAL:", s)
	monitor(monitorCritical, s)
}

func main() {
	var cmd *exec.Cmd
	var interval, timeout time.Duration

	// FIXME(mlafeldt) add command-line options for kill or wait on busy
	// state
	log.SetFlags(0)

	flag.DurationVar(&interval, "i", -1,
		"set execution interval for command, e.g. 45s, 2m, 1h30m, default: 1/10 of timeout")
	flag.DurationVar(&timeout, "t", 1*time.Minute,
		"set execution timeout for command, e.g. 45s, 2m, 1h30m, default: 1m")
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatal("FATAL: no command to execute")
		return
	}

	command := flag.Arg(0)
	monitoringEvent = filepath.Base(command)

	if interval >= timeout {
		log.Fatal("FATAL: interval >= timeout, no time left for actual command execution")
		return
	}

	if interval == -1 {
		interval = timeout / 10
	}

	loadMonitoringCommands()

	// FIXME(nightlyone) try two intervals instead of one?
	timer := time.AfterFunc(timeout, func() {
		TimedOut()
		if cmd != nil && cmd.Process != nil {
			cmd.Process.Kill()
		}
		os.Exit(0)
	})

	SpreadWait(interval)

	// Ensures that only one of these command runs concurrently on this
	// machine.  Also cleans up stale locks of dead instances.
	lock_dir := os.TempDir()
	os.Mkdir(filepath.Join(lock_dir, monitoringEvent), 0700)
	lock, _ := lockfile.New(filepath.Join(lock_dir, monitoringEvent, monitoringEvent+".lock"))
	if err := lock.TryLock(); err != nil {
		if err != lockfile.ErrBusy {
			log.Printf("ERROR: locking %s: reason: %v\n", lock, err)
		}
		timer.Stop()
		Busy()
		return
	}
	defer lock.Unlock()

	cmd = exec.Command(command, flag.Args()[1:]...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		for {
			if _, err := io.Copy(os.Stdout, stdout); err != nil {
				break
			}
		}
		wg.Done()
	}()

	if err := cmd.Start(); err != nil {
		timer.Stop()
		NotAvailable(err)
		return
	}

	if err := cmd.Wait(); err != nil {
		timer.Stop()
		Failed(err)
	} else {
		timer.Stop()
		Ok()
	}

	wg.Wait()
}
<commit_msg>add syslog logging<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/nightlyone/lockfile"
	"io"
	"log"
	"log/syslog"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

// Avoid thundering herd problem on remote services used by this command.
// Interval will be 0, if this is not an issue.
func SpreadWait(interval time.Duration) {
	if interval > 0 {
		// Seed random generator with current process ID
		rand.Seed(int64(os.Getpid()))
		// Sleep for random amount of time within interval
		time.Sleep(time.Duration(rand.Int63n(int64(interval))))
	}
}

// Ok states that execution went well. Logs debug output and reports ok to
// monitoring.
func Ok() {
	log.Println("Ok")
	monitor(monitorOk, "")
}

// NotAvailable states that the command could not be started successfully. It
// might not be installed or has other problems.
func NotAvailable(err error) {
	s := fmt.Sprintln("Cannot start command: ", err)
	log.Println("FATAL:", s)
	monitor(monitorUnknown, s)
}

// TimedOut states that the command took too long and reports failure to the
// monitoring.
func TimedOut() {
	s := "execution took too long"
	log.Println("FATAL:", s)
	monitor(monitorCritical, s)
}

// Busy states that the command hangs and reports failure to the monitoring.
// Those tasks should be automatically killed, if it happens often.
func Busy() {
	s := "previous invocation of command still running"
	log.Println("FATAL:", s)
	monitor(monitorCritical, s)
}

// Failed states that the command didn't execute successfully and reports
// failure to the monitoring. Also Logs error output.
func Failed(err error) {
	s := fmt.Sprintln("Failed to execute: ", err)
	log.Println("FATAL:", s)
	monitor(monitorCritical, s)
}

var useSyslog bool

// derive logger
func getLogger() (logger io.Writer, err error) {
	if useSyslog {
		logger, err = syslog.New(syslog.LOG_NOTICE, monitoringEvent)
	} else {
		logger = os.Stderr
	}
	if err != nil {
		log.SetOutput(logger)
	}
	return logger, err
}

func main() {
	var cmd *exec.Cmd
	var interval, timeout time.Duration

	// FIXME(mlafeldt) add command-line options for kill or wait on busy
	// state
	log.SetFlags(0)

	flag.DurationVar(&interval, "i", -1,
		"set execution interval for command, e.g. 45s, 2m, 1h30m, default: 1/10 of timeout")
	flag.DurationVar(&timeout, "t", 1*time.Minute,
		"set execution timeout for command, e.g. 45s, 2m, 1h30m, default: 1m")
	flag.BoolVar(&useSyslog, "l", false, "log via syslog")
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatal("FATAL: no command to execute")
		return
	}

	command := flag.Arg(0)
	monitoringEvent = filepath.Base(command)
	_, err := getLogger()

	if interval >= timeout {
		log.Fatal("FATAL: interval >= timeout, no time left for actual command execution")
		return
	}

	if interval == -1 {
		interval = timeout / 10
	}

	loadMonitoringCommands()

	// FIXME(nightlyone) try two intervals instead of one?
	timer := time.AfterFunc(timeout, func() {
		TimedOut()
		if cmd != nil && cmd.Process != nil {
			cmd.Process.Kill()
			// FIXME(nightlyone) log the kill
		}
		os.Exit(0)
	})

	SpreadWait(interval)

	// Ensures that only one of these command runs concurrently on this
	// machine.  Also cleans up stale locks of dead instances.
	lock_dir := os.TempDir()
	os.Mkdir(filepath.Join(lock_dir, monitoringEvent), 0700)
	lock, _ := lockfile.New(filepath.Join(lock_dir, monitoringEvent, monitoringEvent+".lock"))
	if err := lock.TryLock(); err != nil {
		if err != lockfile.ErrBusy {
			log.Printf("ERROR: locking %s: reason: %v\n", lock, err)
		}
		timer.Stop()
		Busy()
		return
	}
	defer lock.Unlock()

	cmd = exec.Command(command, flag.Args()[1:]...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		for {
			if _, err := io.Copy(os.Stdout, stdout); err != nil {
				break
			}
		}
		wg.Done()
	}()

	if err := cmd.Start(); err != nil {
		timer.Stop()
		NotAvailable(err)
		return
	}

	if err := cmd.Wait(); err != nil {
		timer.Stop()
		Failed(err)
	} else {
		timer.Stop()
		Ok()
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>package cmd

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"k8s.io/helm/pkg/helm"

	"github.com/databus23/helm-diff/diff"
	"github.com/databus23/helm-diff/manifest"
)

type diffCmd struct {
	release                  string
	chart                    string
	chartVersion             string
	client                   helm.Interface
	detailedExitCode         bool
	devel                    bool
	disableOpenAPIValidation bool
	namespace                string // namespace to assume the release to be installed into. Defaults to the current kube config namespace.
	valueFiles               valueFiles
	values                   []string
	stringValues             []string
	fileValues               []string
	reuseValues              bool
	resetValues              bool
	allowUnreleased          bool
	noHooks                  bool
	includeTests             bool
	suppressedKinds          []string
	outputContext            int
	showSecrets              bool
	postRenderer             string
	install                  bool
}

func (d *diffCmd) isAllowUnreleased() bool {
	// helm update --install is effectively the same as helm-diff's --allow-unreleased option,
	// support both so that helm diff plugin can be applied on the same command
	// https://github.com/databus23/helm-diff/issues/108
	return d.allowUnreleased || d.install
}

const globalUsage = `Show a diff explaining what a helm upgrade would change.

This fetches the currently deployed version of a release
and compares it to a chart plus values.
This can be used visualize what changes a helm upgrade will
perform.
`

func newChartCommand() *cobra.Command {
	diff := diffCmd{
		namespace: os.Getenv("HELM_NAMESPACE"),
	}

	cmd := &cobra.Command{
		Use:     "upgrade [flags] [RELEASE] [CHART]",
		Short:   "Show a diff explaining what a helm upgrade would change.",
		Long:    globalUsage,
		Example: "  helm diff upgrade my-release stable/postgresql --values values.yaml",
		Args: func(cmd *cobra.Command, args []string) error {
			return checkArgsLength(len(args), "release name", "chart path")
		},
		PreRun: func(*cobra.Command, []string) {
			expandTLSPaths()
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			// Suppress the command usage on error. See #77 for more info
			cmd.SilenceUsage = true

			if q, _ := cmd.Flags().GetBool("suppress-secrets"); q {
				diff.suppressedKinds = append(diff.suppressedKinds, "Secret")
			}

			diff.release = args[0]
			diff.chart = args[1]
			if isHelm3() {
				return diff.runHelm3()
			}
			if diff.client == nil {
				diff.client = createHelmClient()
			}
			return diff.run()
		},
	}

	f := cmd.Flags()
	f.StringVar(&diff.chartVersion, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
	f.BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
	f.BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
	f.BoolVar(&diff.showSecrets, "show-secrets", false, "do not redact secret values in the output")
	f.VarP(&diff.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
	f.StringArrayVar(&diff.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
	f.StringArrayVar(&diff.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
	f.StringArrayVar(&diff.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
	f.BoolVar(&diff.reuseValues, "reuse-values", false, "reuse the last release's values and merge in any new values. If '--reset-values' is specified, this is ignored")
	f.BoolVar(&diff.resetValues, "reset-values", false, "reset the values to the ones built into the chart and merge in any new values")
	f.BoolVar(&diff.allowUnreleased, "allow-unreleased", false, "enables diffing of releases that are not yet deployed via Helm")
	f.BoolVar(&diff.install, "install", false, "enables diffing of releases that are not yet deployed via Helm (equivalent to --allow-unreleased, added to match \"helm upgrade --install\" command")
	f.BoolVar(&diff.noHooks, "no-hooks", false, "disable diffing of hooks")
	f.BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
	f.BoolVar(&diff.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
	f.StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
	f.IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
	f.BoolVar(&diff.disableOpenAPIValidation, "disable-openapi-validation", false, "disables rendered templates validation against the Kubernetes OpenAPI Schema")
	f.StringVar(&diff.postRenderer, "post-renderer", "", "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
	if !isHelm3() {
		f.StringVar(&diff.namespace, "namespace", "default", "namespace to assume the release to be installed into")
	}

	if !isHelm3() {
		addCommonCmdOptions(f)
	}

	return cmd

}

func (d *diffCmd) runHelm3() error {

	if err := compatibleHelm3Version(); err != nil {
		return err
	}
	releaseManifest, err := getRelease(d.release, d.namespace)

	var newInstall bool
	if err != nil && strings.Contains(err.Error(), "release: not found") {
		if d.isAllowUnreleased() {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Diff will show entire contents as new.\n\n********************\n")
			newInstall = true
			err = nil
		} else {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Include the `--allow-unreleased` to perform diff without exiting in error.\n\n********************\n")
			return err
		}
	}
	if err != nil {
		return fmt.Errorf("Failed to get release %s in namespace %s: %s", d.release, d.namespace, err)
	}

	installManifest, err := d.template(!newInstall)
	if err != nil {
		return fmt.Errorf("Failed to render chart: %s", err)
	}

	currentSpecs := make(map[string]*manifest.MappingResult)
	if !newInstall {
		if !d.noHooks {
			hooks, err := getHooks(d.release, d.namespace)
			if err != nil {
				return err
			}
			releaseManifest = append(releaseManifest, hooks...)
		}
		if d.includeTests {
			currentSpecs = manifest.Parse(string(releaseManifest), d.namespace)
		} else {
			currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
		}
	}
	var newSpecs map[string]*manifest.MappingResult
	if d.includeTests {
		newSpecs = manifest.Parse(string(installManifest), d.namespace)
	} else {
		newSpecs = manifest.Parse(string(installManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
	}

	seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, os.Stdout)

	if d.detailedExitCode && seenAnyChanges {
		return Error{
			error: errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)"),
			Code:  2,
		}
	}

	return nil
}

func (d *diffCmd) run() error {
	if d.chartVersion == "" && d.devel {
		d.chartVersion = ">0.0.0-0"
	}

	chartPath, err := locateChartPath(d.chart, d.chartVersion, false, "")
	if err != nil {
		return err
	}

	if err := d.valueFiles.Valid(); err != nil {
		return err
	}

	rawVals, err := d.vals()
	if err != nil {
		return err
	}

	releaseResponse, err := d.client.ReleaseContent(d.release)

	var newInstall bool
	if err != nil && strings.Contains(err.Error(), fmt.Sprintf("release: %q not found", d.release)) {
		if d.isAllowUnreleased() {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Diff will show entire contents as new.\n\n********************\n")
			newInstall = true
			err = nil
		} else {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Include the `--allow-unreleased` to perform diff without exiting in error.\n\n********************\n")
		}

	}

	if err != nil {
		return prettyError(err)
	}

	var currentSpecs, newSpecs map[string]*manifest.MappingResult
	if newInstall {
		installResponse, err := d.client.InstallRelease(
			chartPath,
			d.namespace,
			helm.ReleaseName(d.release),
			helm.ValueOverrides(rawVals),
			helm.InstallDryRun(true),
		)
		if err != nil {
			return prettyError(err)
		}

		currentSpecs = make(map[string]*manifest.MappingResult)
		newSpecs = manifest.Parse(installResponse.Release.Manifest, installResponse.Release.Namespace)
	} else {
		upgradeResponse, err := d.client.UpdateRelease(
			d.release,
			chartPath,
			helm.UpdateValueOverrides(rawVals),
			helm.ReuseValues(d.reuseValues),
			helm.ResetValues(d.resetValues),
			helm.UpgradeDryRun(true),
		)
		if err != nil {
			return prettyError(err)
		}

		if d.noHooks {
			currentSpecs = manifest.Parse(releaseResponse.Release.Manifest, releaseResponse.Release.Namespace)
			newSpecs = manifest.Parse(upgradeResponse.Release.Manifest, upgradeResponse.Release.Namespace)
		} else {
			currentSpecs = manifest.ParseRelease(releaseResponse.Release, d.includeTests)
			newSpecs = manifest.ParseRelease(upgradeResponse.Release, d.includeTests)
		}
	}

	seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, os.Stdout)

	if d.detailedExitCode && seenAnyChanges {
		return Error{
			error: errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)"),
			Code:  2,
		}
	}

	return nil
}
<commit_msg>Allow passing --kubeconfig flag to diff plugin, fixes #197 (#198)<commit_after>package cmd

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"k8s.io/helm/pkg/helm"

	"github.com/databus23/helm-diff/diff"
	"github.com/databus23/helm-diff/manifest"
)

type diffCmd struct {
	release                  string
	chart                    string
	chartVersion             string
	client                   helm.Interface
	detailedExitCode         bool
	devel                    bool
	disableOpenAPIValidation bool
	namespace                string // namespace to assume the release to be installed into. Defaults to the current kube config namespace.
	valueFiles               valueFiles
	values                   []string
	stringValues             []string
	fileValues               []string
	reuseValues              bool
	resetValues              bool
	allowUnreleased          bool
	noHooks                  bool
	includeTests             bool
	suppressedKinds          []string
	outputContext            int
	showSecrets              bool
	postRenderer             string
	install                  bool
}

func (d *diffCmd) isAllowUnreleased() bool {
	// helm update --install is effectively the same as helm-diff's --allow-unreleased option,
	// support both so that helm diff plugin can be applied on the same command
	// https://github.com/databus23/helm-diff/issues/108
	return d.allowUnreleased || d.install
}

const globalUsage = `Show a diff explaining what a helm upgrade would change.

This fetches the currently deployed version of a release
and compares it to a chart plus values.
This can be used visualize what changes a helm upgrade will
perform.
`

func newChartCommand() *cobra.Command {
	diff := diffCmd{
		namespace: os.Getenv("HELM_NAMESPACE"),
	}

	cmd := &cobra.Command{
		Use:     "upgrade [flags] [RELEASE] [CHART]",
		Short:   "Show a diff explaining what a helm upgrade would change.",
		Long:    globalUsage,
		Example: "  helm diff upgrade my-release stable/postgresql --values values.yaml",
		Args: func(cmd *cobra.Command, args []string) error {
			return checkArgsLength(len(args), "release name", "chart path")
		},
		PreRun: func(*cobra.Command, []string) {
			expandTLSPaths()
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			// Suppress the command usage on error. See #77 for more info
			cmd.SilenceUsage = true

			if q, _ := cmd.Flags().GetBool("suppress-secrets"); q {
				diff.suppressedKinds = append(diff.suppressedKinds, "Secret")
			}

			diff.release = args[0]
			diff.chart = args[1]
			if isHelm3() {
				return diff.runHelm3()
			}
			if diff.client == nil {
				diff.client = createHelmClient()
			}
			return diff.run()
		},
	}

	f := cmd.Flags()
	var kubeconfig string
	f.StringVar(&kubeconfig, "kubeconfig", "", "This flag is ignored, to allow passing of this top level flag to helm")
	f.StringVar(&diff.chartVersion, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
	f.BoolVar(&diff.detailedExitCode, "detailed-exitcode", false, "return a non-zero exit code when there are changes")
	f.BoolP("suppress-secrets", "q", false, "suppress secrets in the output")
	f.BoolVar(&diff.showSecrets, "show-secrets", false, "do not redact secret values in the output")
	f.VarP(&diff.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
	f.StringArrayVar(&diff.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
	f.StringArrayVar(&diff.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
	f.StringArrayVar(&diff.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
	f.BoolVar(&diff.reuseValues, "reuse-values", false, "reuse the last release's values and merge in any new values. If '--reset-values' is specified, this is ignored")
	f.BoolVar(&diff.resetValues, "reset-values", false, "reset the values to the ones built into the chart and merge in any new values")
	f.BoolVar(&diff.allowUnreleased, "allow-unreleased", false, "enables diffing of releases that are not yet deployed via Helm")
	f.BoolVar(&diff.install, "install", false, "enables diffing of releases that are not yet deployed via Helm (equivalent to --allow-unreleased, added to match \"helm upgrade --install\" command")
	f.BoolVar(&diff.noHooks, "no-hooks", false, "disable diffing of hooks")
	f.BoolVar(&diff.includeTests, "include-tests", false, "enable the diffing of the helm test hooks")
	f.BoolVar(&diff.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
	f.StringArrayVar(&diff.suppressedKinds, "suppress", []string{}, "allows suppression of the values listed in the diff output")
	f.IntVarP(&diff.outputContext, "context", "C", -1, "output NUM lines of context around changes")
	f.BoolVar(&diff.disableOpenAPIValidation, "disable-openapi-validation", false, "disables rendered templates validation against the Kubernetes OpenAPI Schema")
	f.StringVar(&diff.postRenderer, "post-renderer", "", "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
	if !isHelm3() {
		f.StringVar(&diff.namespace, "namespace", "default", "namespace to assume the release to be installed into")
	}

	if !isHelm3() {
		addCommonCmdOptions(f)
	}

	return cmd

}

func (d *diffCmd) runHelm3() error {

	if err := compatibleHelm3Version(); err != nil {
		return err
	}
	releaseManifest, err := getRelease(d.release, d.namespace)

	var newInstall bool
	if err != nil && strings.Contains(err.Error(), "release: not found") {
		if d.isAllowUnreleased() {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Diff will show entire contents as new.\n\n********************\n")
			newInstall = true
			err = nil
		} else {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Include the `--allow-unreleased` to perform diff without exiting in error.\n\n********************\n")
			return err
		}
	}
	if err != nil {
		return fmt.Errorf("Failed to get release %s in namespace %s: %s", d.release, d.namespace, err)
	}

	installManifest, err := d.template(!newInstall)
	if err != nil {
		return fmt.Errorf("Failed to render chart: %s", err)
	}

	currentSpecs := make(map[string]*manifest.MappingResult)
	if !newInstall {
		if !d.noHooks {
			hooks, err := getHooks(d.release, d.namespace)
			if err != nil {
				return err
			}
			releaseManifest = append(releaseManifest, hooks...)
		}
		if d.includeTests {
			currentSpecs = manifest.Parse(string(releaseManifest), d.namespace)
		} else {
			currentSpecs = manifest.Parse(string(releaseManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
		}
	}
	var newSpecs map[string]*manifest.MappingResult
	if d.includeTests {
		newSpecs = manifest.Parse(string(installManifest), d.namespace)
	} else {
		newSpecs = manifest.Parse(string(installManifest), d.namespace, helm3TestHook, helm2TestSuccessHook)
	}

	seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, os.Stdout)

	if d.detailedExitCode && seenAnyChanges {
		return Error{
			error: errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)"),
			Code:  2,
		}
	}

	return nil
}

func (d *diffCmd) run() error {
	if d.chartVersion == "" && d.devel {
		d.chartVersion = ">0.0.0-0"
	}

	chartPath, err := locateChartPath(d.chart, d.chartVersion, false, "")
	if err != nil {
		return err
	}

	if err := d.valueFiles.Valid(); err != nil {
		return err
	}

	rawVals, err := d.vals()
	if err != nil {
		return err
	}

	releaseResponse, err := d.client.ReleaseContent(d.release)

	var newInstall bool
	if err != nil && strings.Contains(err.Error(), fmt.Sprintf("release: %q not found", d.release)) {
		if d.isAllowUnreleased() {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Diff will show entire contents as new.\n\n********************\n")
			newInstall = true
			err = nil
		} else {
			fmt.Printf("********************\n\n\tRelease was not present in Helm.  Include the `--allow-unreleased` to perform diff without exiting in error.\n\n********************\n")
		}

	}

	if err != nil {
		return prettyError(err)
	}

	var currentSpecs, newSpecs map[string]*manifest.MappingResult
	if newInstall {
		installResponse, err := d.client.InstallRelease(
			chartPath,
			d.namespace,
			helm.ReleaseName(d.release),
			helm.ValueOverrides(rawVals),
			helm.InstallDryRun(true),
		)
		if err != nil {
			return prettyError(err)
		}

		currentSpecs = make(map[string]*manifest.MappingResult)
		newSpecs = manifest.Parse(installResponse.Release.Manifest, installResponse.Release.Namespace)
	} else {
		upgradeResponse, err := d.client.UpdateRelease(
			d.release,
			chartPath,
			helm.UpdateValueOverrides(rawVals),
			helm.ReuseValues(d.reuseValues),
			helm.ResetValues(d.resetValues),
			helm.UpgradeDryRun(true),
		)
		if err != nil {
			return prettyError(err)
		}

		if d.noHooks {
			currentSpecs = manifest.Parse(releaseResponse.Release.Manifest, releaseResponse.Release.Namespace)
			newSpecs = manifest.Parse(upgradeResponse.Release.Manifest, upgradeResponse.Release.Namespace)
		} else {
			currentSpecs = manifest.ParseRelease(releaseResponse.Release, d.includeTests)
			newSpecs = manifest.ParseRelease(upgradeResponse.Release, d.includeTests)
		}
	}

	seenAnyChanges := diff.Manifests(currentSpecs, newSpecs, d.suppressedKinds, d.showSecrets, d.outputContext, os.Stdout)

	if d.detailedExitCode && seenAnyChanges {
		return Error{
			error: errors.New("identified at least one change, exiting with non-zero exit code (detailed-exitcode parameter enabled)"),
			Code:  2,
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"runtime"

	"github.com/spf13/cobra"
)

var (
	// Version Contains the version number for a particular Resumic build
	version = "dev"
	// GitCommit contains the commit that this build is based on
	gitCommit = "none"
)

const packageName string = "Resumic"

func versionFunc(cmd *cobra.Command, args []string) error {
	fmt.Println(packageName + "-" + version)
	fmt.Println(runtime.GOOS + ", " + runtime.Version() + ", " + gitCommit)
	return nil
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Show version information for this build of Resumic",
	Args:  cobra.ExactArgs(0),
	RunE:  versionFunc,
}

func init() {
	rootCmd.AddCommand(versionCmd)
}
<commit_msg>typo, changed gitCommit var's default value<commit_after>package cmd

import (
	"fmt"
	"runtime"

	"github.com/spf13/cobra"
)

var (
	// Version contains the version number for a particular Resumic build
	version = "dev"
	// GitCommit contains the commit that this build is based on
	gitCommit = "unknown"
)

const packageName string = "Resumic"

func versionFunc(cmd *cobra.Command, args []string) error {
	fmt.Println(packageName + "-" + version)
	fmt.Println(runtime.GOOS + ", " + runtime.Version() + ", " + gitCommit)
	return nil
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Show version information for this build of Resumic",
	Args:  cobra.ExactArgs(0),
	RunE:  versionFunc,
}

func init() {
	rootCmd.AddCommand(versionCmd)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 MBT 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 cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Display the version of mbt",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("0.17.2")
	},
}
<commit_msg>Bump version - v0.18.0<commit_after>/*
Copyright 2018 MBT 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 cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Display the version of mbt",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("0.18.0")
	},
}
<|endoftext|>


<commit_before>package workshop

func builtins() {

}<commit_msg>Some examples of builtins.<commit_after>package workshop

import "fmt"

func builtins() {
	var slice1 = []string{
		"I am a string",
		"I am the other string",
	}
	slice := append(slice1, "Just another string")
	sliceFinal := make([]string, len(slice))
	copy(sliceFinal, slice)
	comparator := func(slice, slicefinal []string) bool {
		fmt.Println(slice, sliceFinal)
		for i := range slice {
			if slice[i] != slicefinal[i] {
				return false
			}
		}
		return true
	}
	assert(comparator(slice, sliceFinal) == true)
}
<|endoftext|>


<commit_before>package api

import "fmt"

func (p Postgres) OffshootName() string {
	return fmt.Sprintf("%v-%v", p.Name, ResourceCodePostgres)
}

func (p Postgres) ServiceName() string {
	return p.Name
}

func (p Postgres) OffshootLabels() map[string]string {
	return map[string]string{
		"origin":          "kubedb",
		"kubedb.com/name": p.Name,
	}
}

func (e Elastic) OffshootName() string {
	return fmt.Sprintf("%v-%v", e.Name, ResourceCodeElastic)
}

func (e Elastic) ServiceName() string {
	return e.Name
}

func (e Elastic) OffshootLabels() map[string]string {
	return map[string]string{
		"origin":          "kubedb",
		"kubedb.com/name": e.Name,
	}
}
<commit_msg>Add more helper labels & annotations.<commit_after>package api

import "fmt"

const (
	DatabaseNamePrefix = "kubedb"
	LabelDatabaseKind  = "kubedb.com/kind"
	LabelDatabaseName  = "kubedb.com/name"

	PostgresDatabaseVersion = "postgres.kubedb.com/version"
	ElasticDatabaseVersion  = "elastic.kubedb.com/version"
)

func (p Postgres) OffshootName() string {
	return fmt.Sprintf("%v-%v", p.Name, ResourceCodePostgres)
}

func (p Postgres) ServiceName() string {
	return p.Name
}

func (p Postgres) OffshootLabels() map[string]string {
	return map[string]string{
		"origin":          "kubedb",
		LabelDatabaseKind: p.Name,
	}
}

func (p Postgres) StatefulSetLabels() map[string]string {
	labels := make(map[string]string)
	for key, val := range p.Labels {
		labels[key] = val
	}
	labels[LabelDatabaseKind] = ResourceKindPostgres
	return labels
}

func (p Postgres) StatefulSetAnnotations() map[string]string {
	annotations := make(map[string]string)
	for key, val := range p.Annotations {
		annotations[key] = val
	}
	annotations[PostgresDatabaseVersion] = string(p.Spec.Version)
	return annotations
}

func (e Elastic) OffshootName() string {
	return fmt.Sprintf("%v-%v", e.Name, ResourceCodeElastic)
}

func (e Elastic) ServiceName() string {
	return e.Name
}

func (e Elastic) OffshootLabels() map[string]string {
	return map[string]string{
		"origin":          "kubedb",
		LabelDatabaseKind: e.Name,
	}
}

func (p Elastic) StatefulSetLabels() map[string]string {
	labels := make(map[string]string)
	for key, val := range p.Labels {
		labels[key] = val
	}
	labels[LabelDatabaseKind] = ResourceKindElastic
	return labels
}

func (p Elastic) StatefulSetAnnotations() map[string]string {
	annotations := make(map[string]string)
	for key, val := range p.Annotations {
		annotations[key] = val
	}
	annotations[ElasticDatabaseVersion] = string(p.Spec.Version)
	return annotations
}
<|endoftext|>


<commit_before>package stripe

import (
	"encoding/json"
)

// CustomerTaxExempt is the type of tax exemption associated with a customer.
type CustomerTaxExempt string

// List of values that CustomerTaxInfoType can take.
const (
	CustomerTaxExemptExempt  CustomerTaxExempt = "exempt"
	CustomerTaxExemptNone    CustomerTaxExempt = "none"
	CustomerTaxExemptReverse CustomerTaxExempt = "reverse"
)

// CustomerTaxInfoType is the type of tax info associated with a customer.
type CustomerTaxInfoType string

// List of values that CustomerTaxInfoType can take.
const (
	CustomerTaxInfoTypeVAT CustomerTaxInfoType = "vat"
)

// CustomerTaxInfoVerificationStatus is the type of tax info associated with a customer.
type CustomerTaxInfoVerificationStatus string

// List of values that CustomerTaxInfoType can take.
const (
	CustomerTaxInfoVerificationStatusPending    CustomerTaxInfoVerificationStatus = "pending"
	CustomerTaxInfoVerificationStatusUnverified CustomerTaxInfoVerificationStatus = "unverified"
	CustomerTaxInfoVerificationStatusVerified   CustomerTaxInfoVerificationStatus = "verified"
)

// CustomerParams is the set of parameters that can be used when creating or updating a customer.
// For more details see https://stripe.com/docs/api#create_customer and https://stripe.com/docs/api#update_customer.
type CustomerParams struct {
	Params           `form:"*"`
	AccountBalance   *int64                         `form:"account_balance"`
	Address          *AddressParams                 `form:"address"`
	Coupon           *string                        `form:"coupon"`
	DefaultSource    *string                        `form:"default_source"`
	Description      *string                        `form:"description"`
	Email            *string                        `form:"email"`
	InvoicePrefix    *string                        `form:"invoice_prefix"`
	InvoiceSettings  *CustomerInvoiceSettingsParams `form:"invoice_settings"`
	Name             *string                        `form:"name"`
	PaymentMethod    *string                        `form:"payment_method"`
	Phone            *string                        `form:"phone"`
	PreferredLocales []*string                      `form:"preferred_locales"`
	Shipping         *CustomerShippingDetailsParams `form:"shipping"`
	Source           *SourceParams                  `form:"*"` // SourceParams has custom encoding so brought to top level with "*"
	TaxExempt        *string                        `form:"tax_exempt"`
	TaxIDData        []*CustomerTaxIDDataParams     `form:"tax_id_data"`
	Token            *string                        `form:"-"` // This doesn't seem to be used?

	// The following parameter is deprecated. Use TaxIDData instead.
	TaxInfo *CustomerTaxInfoParams `form:"tax_info"`

	// The parameters below are considered deprecated. Consider creating a Subscription separately instead.
	Plan       *string  `form:"plan"`
	Quantity   *int64   `form:"quantity"`
	TaxPercent *float64 `form:"tax_percent"`
	TrialEnd   *int64   `form:"trial_end"`
}

// CustomerInvoiceCustomFieldParams represents the parameters associated with one custom field on
// the customer's invoices.
type CustomerInvoiceCustomFieldParams struct {
	Name  *string `form:"name"`
	Value *string `form:"value"`
}

// CustomerInvoiceSettingsParams is the structure containing the default settings for invoices
// associated with this customer.
type CustomerInvoiceSettingsParams struct {
	CustomFields         []*CustomerInvoiceCustomFieldParams `form:"custom_fields"`
	DefaultPaymentMethod *string                             `form:"default_payment_method"`
	Footer               *string                             `form:"footer"`
}

// CustomerShippingDetailsParams is the structure containing shipping information.
type CustomerShippingDetailsParams struct {
	Address *AddressParams `form:"address"`
	Name    *string        `form:"name"`
	Phone   *string        `form:"phone"`
}

// CustomerTaxIDDataParams lets you pass the tax id details associated with a Customer.
type CustomerTaxIDDataParams struct {
	Type  *string `form:"type"`
	Value *string `form:"value"`
}

// CustomerTaxInfoParams is the structure containing tax information for the customer.
type CustomerTaxInfoParams struct {
	TaxID *string `form:"tax_id"`
	Type  *string `form:"type"`
}

// SetSource adds valid sources to a CustomerParams object,
// returning an error for unsupported sources.
func (cp *CustomerParams) SetSource(sp interface{}) error {
	source, err := SourceParamsFor(sp)
	cp.Source = source
	return err
}

// CustomerListParams is the set of parameters that can be used when listing customers.
// For more details see https://stripe.com/docs/api#list_customers.
type CustomerListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	Email        *string           `form:"email"`
}

// Customer is the resource representing a Stripe customer.
// For more details see https://stripe.com/docs/api#customers.
type Customer struct {
	AccountBalance   int64                    `json:"account_balance"`
	Address          Address                  `json:"address"`
	Created          int64                    `json:"created"`
	Currency         Currency                 `json:"currency"`
	DefaultSource    *PaymentSource           `json:"default_source"`
	Deleted          bool                     `json:"deleted"`
	Delinquent       bool                     `json:"delinquent"`
	Description      string                   `json:"description"`
	Discount         *Discount                `json:"discount"`
	Email            string                   `json:"email"`
	ID               string                   `json:"id"`
	InvoicePrefix    string                   `json:"invoice_prefix"`
	InvoiceSettings  *CustomerInvoiceSettings `json:"invoice_settings"`
	Livemode         bool                     `json:"livemode"`
	Metadata         map[string]string        `json:"metadata"`
	Name             string                   `json:"name"`
	Phone            string                   `json:"phone"`
	PreferredLocales []string                 `json:"preferred_locales"`
	Shipping         *CustomerShippingDetails `json:"shipping"`
	Sources          *SourceList              `json:"sources"`
	Subscriptions    *SubscriptionList        `json:"subscriptions"`
	TaxExempt        CustomerTaxExempt        `json:"tax_exempt"`
	TaxIDs           *TaxIDList               `json:"tax_ids"`

	// The following properties are deprecated. Use TaxIds instead.
	TaxInfo             *CustomerTaxInfo             `json:"tax_info"`
	TaxInfoVerification *CustomerTaxInfoVerification `json:"tax_info_verification"`
}

// CustomerInvoiceCustomField represents a custom field associated with the customer's invoices.
type CustomerInvoiceCustomField struct {
	Name  *string `form:"name"`
	Value *string `form:"value"`
}

// CustomerInvoiceSettings is the structure containing the default settings for invoices associated
// with this customer.
type CustomerInvoiceSettings struct {
	CustomFields         []*CustomerInvoiceCustomField `json:"custom_fields"`
	DefaultPaymentMethod *PaymentMethod                `json:"default_payment_method"`
	Footer               string                        `json:"footer"`
}

// CustomerList is a list of customers as retrieved from a list endpoint.
type CustomerList struct {
	ListMeta
	Data []*Customer `json:"data"`
}

// CustomerShippingDetails is the structure containing shipping information.
type CustomerShippingDetails struct {
	Address Address `json:"address"`
	Name    string  `json:"name"`
	Phone   string  `json:"phone"`
}

// CustomerTaxInfo is the structure containing tax information for the customer.
type CustomerTaxInfo struct {
	TaxID string              `json:"tax_id"`
	Type  CustomerTaxInfoType `json:"type"`
}

// CustomerTaxInfoVerification is the structure containing tax verification for the customer.
type CustomerTaxInfoVerification struct {
	Status       CustomerTaxInfoVerificationStatus `json:"status"`
	VerifiedName string                            `json:"verified_name"`
}

// UnmarshalJSON handles deserialization of a Customer.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *Customer) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type customer Customer
	var v customer
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = Customer(v)
	return nil
}
<commit_msg>Add support for Balance and deprecate AccountBalance on Customer<commit_after>package stripe

import (
	"encoding/json"
)

// CustomerTaxExempt is the type of tax exemption associated with a customer.
type CustomerTaxExempt string

// List of values that CustomerTaxInfoType can take.
const (
	CustomerTaxExemptExempt  CustomerTaxExempt = "exempt"
	CustomerTaxExemptNone    CustomerTaxExempt = "none"
	CustomerTaxExemptReverse CustomerTaxExempt = "reverse"
)

// CustomerTaxInfoType is the type of tax info associated with a customer.
type CustomerTaxInfoType string

// List of values that CustomerTaxInfoType can take.
const (
	CustomerTaxInfoTypeVAT CustomerTaxInfoType = "vat"
)

// CustomerTaxInfoVerificationStatus is the type of tax info associated with a customer.
type CustomerTaxInfoVerificationStatus string

// List of values that CustomerTaxInfoType can take.
const (
	CustomerTaxInfoVerificationStatusPending    CustomerTaxInfoVerificationStatus = "pending"
	CustomerTaxInfoVerificationStatusUnverified CustomerTaxInfoVerificationStatus = "unverified"
	CustomerTaxInfoVerificationStatusVerified   CustomerTaxInfoVerificationStatus = "verified"
)

// CustomerParams is the set of parameters that can be used when creating or updating a customer.
// For more details see https://stripe.com/docs/api#create_customer and https://stripe.com/docs/api#update_customer.
type CustomerParams struct {
	Params           `form:"*"`
	Address          *AddressParams                 `form:"address"`
	Balance          *int64                         `form:"balance"`
	Coupon           *string                        `form:"coupon"`
	DefaultSource    *string                        `form:"default_source"`
	Description      *string                        `form:"description"`
	Email            *string                        `form:"email"`
	InvoicePrefix    *string                        `form:"invoice_prefix"`
	InvoiceSettings  *CustomerInvoiceSettingsParams `form:"invoice_settings"`
	Name             *string                        `form:"name"`
	PaymentMethod    *string                        `form:"payment_method"`
	Phone            *string                        `form:"phone"`
	PreferredLocales []*string                      `form:"preferred_locales"`
	Shipping         *CustomerShippingDetailsParams `form:"shipping"`
	Source           *SourceParams                  `form:"*"` // SourceParams has custom encoding so brought to top level with "*"
	TaxExempt        *string                        `form:"tax_exempt"`
	TaxIDData        []*CustomerTaxIDDataParams     `form:"tax_id_data"`
	Token            *string                        `form:"-"` // This doesn't seem to be used?

	// The following parameter is deprecated. Use Balance instead.
	AccountBalance *int64 `form:"account_balance"`

	// The following parameter is deprecated. Use TaxIDData instead.
	TaxInfo *CustomerTaxInfoParams `form:"tax_info"`

	// The parameters below are considered deprecated. Consider creating a Subscription separately instead.
	Plan       *string  `form:"plan"`
	Quantity   *int64   `form:"quantity"`
	TaxPercent *float64 `form:"tax_percent"`
	TrialEnd   *int64   `form:"trial_end"`
}

// CustomerInvoiceCustomFieldParams represents the parameters associated with one custom field on
// the customer's invoices.
type CustomerInvoiceCustomFieldParams struct {
	Name  *string `form:"name"`
	Value *string `form:"value"`
}

// CustomerInvoiceSettingsParams is the structure containing the default settings for invoices
// associated with this customer.
type CustomerInvoiceSettingsParams struct {
	CustomFields         []*CustomerInvoiceCustomFieldParams `form:"custom_fields"`
	DefaultPaymentMethod *string                             `form:"default_payment_method"`
	Footer               *string                             `form:"footer"`
}

// CustomerShippingDetailsParams is the structure containing shipping information.
type CustomerShippingDetailsParams struct {
	Address *AddressParams `form:"address"`
	Name    *string        `form:"name"`
	Phone   *string        `form:"phone"`
}

// CustomerTaxIDDataParams lets you pass the tax id details associated with a Customer.
type CustomerTaxIDDataParams struct {
	Type  *string `form:"type"`
	Value *string `form:"value"`
}

// CustomerTaxInfoParams is the structure containing tax information for the customer.
type CustomerTaxInfoParams struct {
	TaxID *string `form:"tax_id"`
	Type  *string `form:"type"`
}

// SetSource adds valid sources to a CustomerParams object,
// returning an error for unsupported sources.
func (cp *CustomerParams) SetSource(sp interface{}) error {
	source, err := SourceParamsFor(sp)
	cp.Source = source
	return err
}

// CustomerListParams is the set of parameters that can be used when listing customers.
// For more details see https://stripe.com/docs/api#list_customers.
type CustomerListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	Email        *string           `form:"email"`
}

// Customer is the resource representing a Stripe customer.
// For more details see https://stripe.com/docs/api#customers.
type Customer struct {
	Address          Address                  `json:"address"`
	Balance          int64                    `json:"balance"`
	Created          int64                    `json:"created"`
	Currency         Currency                 `json:"currency"`
	DefaultSource    *PaymentSource           `json:"default_source"`
	Deleted          bool                     `json:"deleted"`
	Delinquent       bool                     `json:"delinquent"`
	Description      string                   `json:"description"`
	Discount         *Discount                `json:"discount"`
	Email            string                   `json:"email"`
	ID               string                   `json:"id"`
	InvoicePrefix    string                   `json:"invoice_prefix"`
	InvoiceSettings  *CustomerInvoiceSettings `json:"invoice_settings"`
	Livemode         bool                     `json:"livemode"`
	Metadata         map[string]string        `json:"metadata"`
	Name             string                   `json:"name"`
	Phone            string                   `json:"phone"`
	PreferredLocales []string                 `json:"preferred_locales"`
	Shipping         *CustomerShippingDetails `json:"shipping"`
	Sources          *SourceList              `json:"sources"`
	Subscriptions    *SubscriptionList        `json:"subscriptions"`
	TaxExempt        CustomerTaxExempt        `json:"tax_exempt"`
	TaxIDs           *TaxIDList               `json:"tax_ids"`

	// The following property is deprecated. Use Balance instead.
	AccountBalance int64 `json:"account_balance"`

	// The following properties are deprecated. Use TaxIds instead.
	TaxInfo             *CustomerTaxInfo             `json:"tax_info"`
	TaxInfoVerification *CustomerTaxInfoVerification `json:"tax_info_verification"`
}

// CustomerInvoiceCustomField represents a custom field associated with the customer's invoices.
type CustomerInvoiceCustomField struct {
	Name  *string `form:"name"`
	Value *string `form:"value"`
}

// CustomerInvoiceSettings is the structure containing the default settings for invoices associated
// with this customer.
type CustomerInvoiceSettings struct {
	CustomFields         []*CustomerInvoiceCustomField `json:"custom_fields"`
	DefaultPaymentMethod *PaymentMethod                `json:"default_payment_method"`
	Footer               string                        `json:"footer"`
}

// CustomerList is a list of customers as retrieved from a list endpoint.
type CustomerList struct {
	ListMeta
	Data []*Customer `json:"data"`
}

// CustomerShippingDetails is the structure containing shipping information.
type CustomerShippingDetails struct {
	Address Address `json:"address"`
	Name    string  `json:"name"`
	Phone   string  `json:"phone"`
}

// CustomerTaxInfo is the structure containing tax information for the customer.
type CustomerTaxInfo struct {
	TaxID string              `json:"tax_id"`
	Type  CustomerTaxInfoType `json:"type"`
}

// CustomerTaxInfoVerification is the structure containing tax verification for the customer.
type CustomerTaxInfoVerification struct {
	Status       CustomerTaxInfoVerificationStatus `json:"status"`
	VerifiedName string                            `json:"verified_name"`
}

// UnmarshalJSON handles deserialization of a Customer.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *Customer) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type customer Customer
	var v customer
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = Customer(v)
	return nil
}
<|endoftext|>


<commit_before>package imageprocessing

import (
	"math"
)

func ratio(x int, y int) float64 {
	return float64(x) / float64(y)
}

func roundFloat(f float64) int {
	if f < 0 {
		return int(math.Ceil(f - 0.5))
	}
	return int(math.Floor(f + 0.5))
}
<commit_msg>Use math.Round<commit_after>package imageprocessing

import (
	"math"
)

func ratio(x int, y int) float64 {
	return float64(x) / float64(y)
}

func roundFloat(f float64) int {
	return int(math.Round(f))
}
<|endoftext|>


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

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

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

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

package qos

import (
	"k8s.io/kubernetes/pkg/api"
)

const (
	PodInfraOOMAdj       int = -999
	KubeletOOMScoreAdj   int = -999
	KubeProxyOOMScoreAdj int = -999
)

// isMemoryBestEffort returns true if the container's memory requirements are best-effort.
func isMemoryBestEffort(container *api.Container) bool {
	// A container is memory best-effort if its memory request is unspecified or 0.
	// If a request is specified, then the user expects some kind of resource guarantee.
	return container.Resources.Requests.Memory().Value() == 0
}

// isMemoryGuaranteed returns true if the container's memory requirements are Guaranteed.
func isMemoryGuaranteed(container *api.Container) bool {
	// A container is memory guaranteed if its memory request == memory limit.
	// If memory request == memory limit, the user is very confident of resource consumption.
	memoryRequest := container.Resources.Requests.Memory()
	memoryLimit := container.Resources.Limits.Memory()
	return (*memoryRequest).Cmp(*memoryLimit) == 0 && memoryRequest.Value() != 0
}

// GetContainerOOMAdjust returns the amount by which the OOM score of all processes in the
// container should be adjusted. The OOM score of a process is the percentage of memory it consumes
// multiplied by 100 (barring exceptional cases) + a configurable quantity which is between -1000
// and 1000. Containers with higher OOM scores are killed if the system runs out of memory.
// See https://lwn.net/Articles/391222/ for more information.
func GetContainerOOMScoreAdjust(container *api.Container, memoryCapacity int64) int {
	if isMemoryGuaranteed(container) {
		// Memory guaranteed containers should be the last to get killed.
		return -999
	} else if isMemoryBestEffort(container) {
		// Memory best-effort containers should be the first to be killed.
		return 1000
	} else {
		// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
		// we want to protect Burstable containers that consume less memory than requested.
		// The formula below is a heuristic. A container requesting for 10% of a system's
		// memory will have an oom score adjust of 900. If a process in container Y
		// uses over 10% of memory, its OOM score will be 1000. The idea is that containers
		// which use more than their request will have an OOM score of 1000 and will be prime
		// targets for OOM kills.
		// Note that this is a heuristic, it won't work if a container has many small processes.
		memoryRequest := container.Resources.Requests.Memory().Value()
		oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
		// A memory guaranteed container using 100% of memory can have an OOM score of 1. Ensure
		// that memory burstable containers have a higher OOM score.
		if oomScoreAdjust < 2 {
			return 2
		}
		return int(oomScoreAdjust)
	}
}
<commit_msg>Fix incorrect OOM score description in kubelet/qos<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package qos

import (
	"k8s.io/kubernetes/pkg/api"
)

const (
	PodInfraOOMAdj       int = -999
	KubeletOOMScoreAdj   int = -999
	KubeProxyOOMScoreAdj int = -999
)

// isMemoryBestEffort returns true if the container's memory requirements are best-effort.
func isMemoryBestEffort(container *api.Container) bool {
	// A container is memory best-effort if its memory request is unspecified or 0.
	// If a request is specified, then the user expects some kind of resource guarantee.
	return container.Resources.Requests.Memory().Value() == 0
}

// isMemoryGuaranteed returns true if the container's memory requirements are Guaranteed.
func isMemoryGuaranteed(container *api.Container) bool {
	// A container is memory guaranteed if its memory request == memory limit.
	// If memory request == memory limit, the user is very confident of resource consumption.
	memoryRequest := container.Resources.Requests.Memory()
	memoryLimit := container.Resources.Limits.Memory()
	return (*memoryRequest).Cmp(*memoryLimit) == 0 && memoryRequest.Value() != 0
}

// GetContainerOOMAdjust returns the amount by which the OOM score of all processes in the
// container should be adjusted. The OOM score of a process is the percentage of memory it consumes
// multiplied by 10 (barring exceptional cases) + a configurable quantity which is between -1000
// and 1000. Containers with higher OOM scores are killed if the system runs out of memory.
// See https://lwn.net/Articles/391222/ for more information.
func GetContainerOOMScoreAdjust(container *api.Container, memoryCapacity int64) int {
	if isMemoryGuaranteed(container) {
		// Memory guaranteed containers should be the last to get killed.
		return -999
	} else if isMemoryBestEffort(container) {
		// Memory best-effort containers should be the first to be killed.
		return 1000
	} else {
		// Burstable containers are a middle tier, between Guaranteed and Best-Effort. Ideally,
		// we want to protect Burstable containers that consume less memory than requested.
		// The formula below is a heuristic. A container requesting for 10% of a system's
		// memory will have an oom score adjust of 900. If a process in container Y
		// uses over 10% of memory, its OOM score will be 1000. The idea is that containers
		// which use more than their request will have an OOM score of 1000 and will be prime
		// targets for OOM kills.
		// Note that this is a heuristic, it won't work if a container has many small processes.
		memoryRequest := container.Resources.Requests.Memory().Value()
		oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
		// A memory guaranteed container using 100% of memory can have an OOM score of 1. Ensure
		// that memory burstable containers have a higher OOM score.
		if oomScoreAdjust < 2 {
			return 2
		}
		return int(oomScoreAdjust)
	}
}
<|endoftext|>


<commit_before>// +build linux

package nsinit

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/system"
	"io/ioutil"
	"os"
	"path/filepath"
	"syscall"
)

// default mount point flags
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV

// setupNewMountNamespace is used to initialize a new mount namespace for an new
// container in the rootfs that is specified.
//
// There is no need to unmount the new mounts because as soon as the mount namespace
// is no longer in use, the mounts will be removed automatically
func setupNewMountNamespace(rootfs, console string, readonly bool) error {
	// mount as slave so that the new mounts do not propagate to the host
	if err := system.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mounting / as slave %s", err)
	}
	if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mouting %s as bind %s", rootfs, err)
	}
	if readonly {
		if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
			return fmt.Errorf("mounting %s as readonly %s", rootfs, err)
		}
	}
	if err := mountSystem(rootfs); err != nil {
		return fmt.Errorf("mount system %s", err)
	}
	if err := copyDevNodes(rootfs); err != nil {
		return fmt.Errorf("copy dev nodes %s", err)
	}
	// In non-privileged mode, this fails. Discard the error.
	setupLoopbackDevices(rootfs)
	if err := setupDev(rootfs); err != nil {
		return err
	}
	if console != "" {
		if err := setupPtmx(rootfs, console); err != nil {
			return err
		}
	}
	if err := system.Chdir(rootfs); err != nil {
		return fmt.Errorf("chdir into %s %s", rootfs, err)
	}

	pivotDir, err := ioutil.TempDir(rootfs, ".pivot_root")
	if err != nil {
		return fmt.Errorf("can't create pivot_root dir %s", pivotDir, err)
	}
	if err := system.Pivotroot(rootfs, pivotDir); err != nil {
		return fmt.Errorf("pivot_root %s", err)
	}
	if err := system.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}

	// path to pivot dir now changed, update
	pivotDir = filepath.Join("/", filepath.Base(pivotDir))

	if err := system.Unmount(pivotDir, syscall.MNT_DETACH); err != nil {
		return fmt.Errorf("unmount pivot_root dir %s", err)
	}

	if err := os.Remove(pivotDir); err != nil {
		return fmt.Errorf("remove pivot_root dir %s", err)
	}

	system.Umask(0022)

	return nil
}

// copyDevNodes mknods the hosts devices so the new container has access to them
func copyDevNodes(rootfs string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	for _, node := range []string{
		"null",
		"zero",
		"full",
		"random",
		"urandom",
		"tty",
	} {
		if err := copyDevNode(rootfs, node); err != nil {
			return err
		}
	}
	return nil
}

func setupLoopbackDevices(rootfs string) error {
	for i := 0; ; i++ {
		if err := copyDevNode(rootfs, fmt.Sprintf("loop%d", i)); err != nil {
			if !os.IsNotExist(err) {
				return err
			}
			break
		}

	}
	return nil
}

func copyDevNode(rootfs, node string) error {
	stat, err := os.Stat(filepath.Join("/dev", node))
	if err != nil {
		return err
	}
	var (
		dest = filepath.Join(rootfs, "dev", node)
		st   = stat.Sys().(*syscall.Stat_t)
	)
	if err := system.Mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) {
		return fmt.Errorf("copy %s %s", node, err)
	}
	return nil
}

// setupDev symlinks the current processes pipes into the
// appropriate destination on the containers rootfs
func setupDev(rootfs string) error {
	for _, link := range []struct {
		from string
		to   string
	}{
		{"/proc/kcore", "/dev/core"},
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1", "/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},
	} {
		dest := filepath.Join(rootfs, link.to)
		if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("remove %s %s", dest, err)
		}
		if err := os.Symlink(link.from, dest); err != nil {
			return fmt.Errorf("symlink %s %s", dest, err)
		}
	}
	return nil
}

// setupConsole ensures that the container has a proper /dev/console setup
func setupConsole(rootfs, console string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	stat, err := os.Stat(console)
	if err != nil {
		return fmt.Errorf("stat console %s %s", console, err)
	}
	var (
		st   = stat.Sys().(*syscall.Stat_t)
		dest = filepath.Join(rootfs, "dev/console")
	)
	if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("remove %s %s", dest, err)
	}
	if err := os.Chmod(console, 0600); err != nil {
		return err
	}
	if err := os.Chown(console, 0, 0); err != nil {
		return err
	}
	if err := system.Mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
		return fmt.Errorf("mknod %s %s", dest, err)
	}
	if err := system.Mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
		return fmt.Errorf("bind %s to %s %s", console, dest, err)
	}
	return nil
}

// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
// inside the mount namespace
func mountSystem(rootfs string) error {
	for _, m := range []struct {
		source string
		path   string
		device string
		flags  int
		data   string
	}{
		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
		{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags},
		{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: "mode=1777,size=65536k"},
		{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"},
	} {
		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
			return fmt.Errorf("mkdirall %s %s", m.path, err)
		}
		if err := system.Mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil {
			return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err)
		}
	}
	return nil
}

// setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and
// finishes setting up /dev/console
func setupPtmx(rootfs, console string) error {
	ptmx := filepath.Join(rootfs, "dev/ptmx")
	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
		return err
	}
	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
		return fmt.Errorf("symlink dev ptmx %s", err)
	}
	if err := setupConsole(rootfs, console); err != nil {
		return err
	}
	return nil
}

// remountProc is used to detach and remount the proc filesystem
// commonly needed with running a new process inside an existing container
func remountProc() error {
	if err := system.Unmount("/proc", syscall.MNT_DETACH); err != nil {
		return err
	}
	if err := system.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil {
		return err
	}
	return nil
}

func remountSys() error {
	if err := system.Unmount("/sys", syscall.MNT_DETACH); err != nil {
		if err != syscall.EINVAL {
			return err
		}
	} else {
		if err := system.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Revert "libcontainer: Use pivot_root instead of chroot"<commit_after>// +build linux

package nsinit

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/system"
	"os"
	"path/filepath"
	"syscall"
)

// default mount point flags
const defaultMountFlags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV

// setupNewMountNamespace is used to initialize a new mount namespace for an new
// container in the rootfs that is specified.
//
// There is no need to unmount the new mounts because as soon as the mount namespace
// is no longer in use, the mounts will be removed automatically
func setupNewMountNamespace(rootfs, console string, readonly bool) error {
	// mount as slave so that the new mounts do not propagate to the host
	if err := system.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mounting / as slave %s", err)
	}
	if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
		return fmt.Errorf("mouting %s as bind %s", rootfs, err)
	}
	if readonly {
		if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_REC, ""); err != nil {
			return fmt.Errorf("mounting %s as readonly %s", rootfs, err)
		}
	}
	if err := mountSystem(rootfs); err != nil {
		return fmt.Errorf("mount system %s", err)
	}
	if err := copyDevNodes(rootfs); err != nil {
		return fmt.Errorf("copy dev nodes %s", err)
	}
	// In non-privileged mode, this fails. Discard the error.
	setupLoopbackDevices(rootfs)
	if err := setupDev(rootfs); err != nil {
		return err
	}
	if console != "" {
		if err := setupPtmx(rootfs, console); err != nil {
			return err
		}
	}
	if err := system.Chdir(rootfs); err != nil {
		return fmt.Errorf("chdir into %s %s", rootfs, err)
	}
	if err := system.Mount(rootfs, "/", "", syscall.MS_MOVE, ""); err != nil {
		return fmt.Errorf("mount move %s into / %s", rootfs, err)
	}
	if err := system.Chroot("."); err != nil {
		return fmt.Errorf("chroot . %s", err)
	}
	if err := system.Chdir("/"); err != nil {
		return fmt.Errorf("chdir / %s", err)
	}

	system.Umask(0022)

	return nil
}

// copyDevNodes mknods the hosts devices so the new container has access to them
func copyDevNodes(rootfs string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	for _, node := range []string{
		"null",
		"zero",
		"full",
		"random",
		"urandom",
		"tty",
	} {
		if err := copyDevNode(rootfs, node); err != nil {
			return err
		}
	}
	return nil
}

func setupLoopbackDevices(rootfs string) error {
	for i := 0; ; i++ {
		if err := copyDevNode(rootfs, fmt.Sprintf("loop%d", i)); err != nil {
			if !os.IsNotExist(err) {
				return err
			}
			break
		}

	}
	return nil
}

func copyDevNode(rootfs, node string) error {
	stat, err := os.Stat(filepath.Join("/dev", node))
	if err != nil {
		return err
	}
	var (
		dest = filepath.Join(rootfs, "dev", node)
		st   = stat.Sys().(*syscall.Stat_t)
	)
	if err := system.Mknod(dest, st.Mode, int(st.Rdev)); err != nil && !os.IsExist(err) {
		return fmt.Errorf("copy %s %s", node, err)
	}
	return nil
}

// setupDev symlinks the current processes pipes into the
// appropriate destination on the containers rootfs
func setupDev(rootfs string) error {
	for _, link := range []struct {
		from string
		to   string
	}{
		{"/proc/kcore", "/dev/core"},
		{"/proc/self/fd", "/dev/fd"},
		{"/proc/self/fd/0", "/dev/stdin"},
		{"/proc/self/fd/1", "/dev/stdout"},
		{"/proc/self/fd/2", "/dev/stderr"},
	} {
		dest := filepath.Join(rootfs, link.to)
		if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("remove %s %s", dest, err)
		}
		if err := os.Symlink(link.from, dest); err != nil {
			return fmt.Errorf("symlink %s %s", dest, err)
		}
	}
	return nil
}

// setupConsole ensures that the container has a proper /dev/console setup
func setupConsole(rootfs, console string) error {
	oldMask := system.Umask(0000)
	defer system.Umask(oldMask)

	stat, err := os.Stat(console)
	if err != nil {
		return fmt.Errorf("stat console %s %s", console, err)
	}
	var (
		st   = stat.Sys().(*syscall.Stat_t)
		dest = filepath.Join(rootfs, "dev/console")
	)
	if err := os.Remove(dest); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("remove %s %s", dest, err)
	}
	if err := os.Chmod(console, 0600); err != nil {
		return err
	}
	if err := os.Chown(console, 0, 0); err != nil {
		return err
	}
	if err := system.Mknod(dest, (st.Mode&^07777)|0600, int(st.Rdev)); err != nil {
		return fmt.Errorf("mknod %s %s", dest, err)
	}
	if err := system.Mount(console, dest, "bind", syscall.MS_BIND, ""); err != nil {
		return fmt.Errorf("bind %s to %s %s", console, dest, err)
	}
	return nil
}

// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
// inside the mount namespace
func mountSystem(rootfs string) error {
	for _, m := range []struct {
		source string
		path   string
		device string
		flags  int
		data   string
	}{
		{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
		{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags},
		{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: "mode=1777,size=65536k"},
		{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: "newinstance,ptmxmode=0666,mode=620,gid=5"},
	} {
		if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
			return fmt.Errorf("mkdirall %s %s", m.path, err)
		}
		if err := system.Mount(m.source, m.path, m.device, uintptr(m.flags), m.data); err != nil {
			return fmt.Errorf("mounting %s into %s %s", m.source, m.path, err)
		}
	}
	return nil
}

// setupPtmx adds a symlink to pts/ptmx for /dev/ptmx and
// finishes setting up /dev/console
func setupPtmx(rootfs, console string) error {
	ptmx := filepath.Join(rootfs, "dev/ptmx")
	if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
		return err
	}
	if err := os.Symlink("pts/ptmx", ptmx); err != nil {
		return fmt.Errorf("symlink dev ptmx %s", err)
	}
	if err := setupConsole(rootfs, console); err != nil {
		return err
	}
	return nil
}

// remountProc is used to detach and remount the proc filesystem
// commonly needed with running a new process inside an existing container
func remountProc() error {
	if err := system.Unmount("/proc", syscall.MNT_DETACH); err != nil {
		return err
	}
	if err := system.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), ""); err != nil {
		return err
	}
	return nil
}

func remountSys() error {
	if err := system.Unmount("/sys", syscall.MNT_DETACH); err != nil {
		if err != syscall.EINVAL {
			return err
		}
	} else {
		if err := system.Mount("sysfs", "/sys", "sysfs", uintptr(defaultMountFlags), ""); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package futex provides an implementation of the futex interface as found in
// the Linux kernel. It allows one to easily transform Wait() calls into waits
// on a channel, which is useful in a Go-based kernel, for example.
package futex

import (
	"sync"
	"sync/atomic"

	"gvisor.googlesource.com/gvisor/pkg/syserror"
)

// Checker abstracts memory accesses. This is useful because the "addresses"
// used in this package may not be real addresses (they could be indices of an
// array, for example), or they could be mapped via some special mechanism.
//
// TODO: Replace this with usermem.IO.
type Checker interface {
	// Check should validate that given address contains the given value.
	// If it does not contain the value, syserror.EAGAIN must be returned.
	// Any other error may be returned, which will be propagated.
	Check(addr uintptr, val uint32) error

	// Op should atomically perform the operation encoded in op on the data
	// pointed to by addr, then apply the comparison encoded in op to the
	// original value at addr, returning the result.
	// Note that op is an opaque operation whose behaviour is defined
	// outside of the futex manager.
	Op(addr uintptr, op uint32) (bool, error)
}

// Waiter is the struct which gets enqueued into buckets for wake up routines
// and requeue routines to scan and notify. Once a Waiter has been enqueued by
// WaitPrepare(), callers may listen on C for wake up events.
type Waiter struct {
	// Synchronization:
	//
	// - A Waiter that is not enqueued in a bucket is exclusively owned (no
	// synchronization applies).
	//
	// - A Waiter is enqueued in a bucket by calling WaitPrepare(). After this,
	// waiterEntry, complete, and addr are protected by the bucket.mu ("bucket
	// lock") of the containing bucket, and bitmask is immutable. complete and
	// addr are additionally mutated using atomic memory operations, ensuring
	// that they can be read using atomic memory operations without holding the
	// bucket lock.
	//
	// - A Waiter is only guaranteed to be no longer queued after calling
	// WaitComplete().

	// waiterEntry links Waiter into bucket.waiters.
	waiterEntry

	// complete is 1 if the Waiter was removed from its bucket by a wakeup and
	// 0 otherwise.
	complete int32

	// C is sent to when the Waiter is woken.
	C chan struct{}

	// addr is the address being waited on.
	addr uintptr

	// The bitmask we're waiting on.
	// This is used the case of a FUTEX_WAKE_BITSET.
	bitmask uint32
}

// NewWaiter returns a new unqueued Waiter.
func NewWaiter() *Waiter {
	return &Waiter{
		C: make(chan struct{}, 1),
	}
}

// bucket holds a list of waiters for a given address hash.
type bucket struct {
	// mu protects waiters and contained Waiter state. See comment in Waiter.
	mu sync.Mutex `state:"nosave"`

	waiters waiterList `state:"zerovalue"`
}

// wakeLocked wakes up to n waiters matching the bitmask at the addr for this
// bucket and returns the number of waiters woken.
//
// Preconditions: b.mu must be locked.
func (b *bucket) wakeLocked(addr uintptr, bitmask uint32, n int) int {
	done := 0
	for w := b.waiters.Front(); done < n && w != nil; {
		if w.addr != addr || w.bitmask&bitmask == 0 {
			// Not matching.
			w = w.Next()
			continue
		}

		// Remove from the bucket and wake the waiter.
		woke := w
		w = w.Next() // Next iteration.
		b.waiters.Remove(woke)
		woke.C <- struct{}{}

		// NOTE: The above channel write establishes a write barrier
		// according to the memory model, so nothing may be ordered
		// around it. Since we've dequeued w and will never touch it
		// again, we can safely store 1 to w.complete here and allow
		// the WaitComplete() to short-circuit grabbing the bucket
		// lock. If they somehow miss the w.complete, we are still
		// holding the lock, so we can know that they won't dequeue w,
		// assume it's free and have the below operation afterwards.
		atomic.StoreInt32(&woke.complete, 1)
		done++
	}
	return done
}

// requeueLocked takes n waiters from the bucket and moves them to naddr on the
// bucket "to".
//
// Preconditions: b and to must be locked.
func (b *bucket) requeueLocked(to *bucket, addr, naddr uintptr, n int) int {
	done := 0
	for w := b.waiters.Front(); done < n && w != nil; {
		if w.addr != addr {
			// Not matching.
			w = w.Next()
			continue
		}

		requeued := w
		w = w.Next() // Next iteration.
		b.waiters.Remove(requeued)
		atomic.StoreUintptr(&requeued.addr, naddr)
		to.waiters.PushBack(requeued)
		done++
	}
	return done
}

const (
	// bucketCount is the number of buckets per Manager. By having many of
	// these we reduce contention when concurrent yet unrelated calls are made.
	bucketCount     = 1 << bucketCountBits
	bucketCountBits = 10
)

func checkAddr(addr uintptr) error {
	// Ensure the address is aligned.
	// It must be a DWORD boundary.
	if addr&0x3 != 0 {
		return syserror.EINVAL
	}

	return nil
}

// bucketIndexForAddr returns the index into Manager.buckets for addr.
func bucketIndexForAddr(addr uintptr) uintptr {
	// - The bottom 2 bits of addr must be 0, per checkAddr.
	//
	// - On amd64, the top 16 bits of addr (bits 48-63) must be equal to bit 47
	// for a canonical address, and (on all existing platforms) bit 47 must be
	// 0 for an application address.
	//
	// Thus 19 bits of addr are "useless" for hashing, leaving only 45 "useful"
	// bits. We choose one of the simplest possible hash functions that at
	// least uses all 45 useful bits in the output, given that bucketCountBits
	// == 10. This hash function also has the property that it will usually map
	// adjacent addresses to adjacent buckets, slightly improving memory
	// locality when an application synchronization structure uses multiple
	// nearby futexes.
	//
	// Note that despite the large number of arithmetic operations in the
	// function, many components can be computed in parallel, such that the
	// critical path is 1 bit shift + 3 additions (2 in h1, then h1 + h2). This
	// is also why h1 and h2 are grouped separately; for "(addr >> 2) + ... +
	// (addr >> 42)" without any additional grouping, the compiler puts all 4
	// additions in the critical path.
	h1 := (addr >> 2) + (addr >> 12) + (addr >> 22)
	h2 := (addr >> 32) + (addr >> 42)
	return (h1 + h2) % bucketCount
}

// Manager holds futex state for a single virtual address space.
type Manager struct {
	buckets [bucketCount]bucket
}

// NewManager returns an initialized futex manager.
// N.B. we use virtual address to tag futexes, so it only works for private
// (within a single process) futex.
func NewManager() *Manager {
	return &Manager{}
}

// lockBucket returns a locked bucket for the given addr.
//
// Preconditions: checkAddr(addr) == nil.
func (m *Manager) lockBucket(addr uintptr) *bucket {
	b := &m.buckets[bucketIndexForAddr(addr)]
	b.mu.Lock()
	return b
}

// lockBuckets returns locked buckets for the given addrs.
//
// Preconditions: checkAddr(addr1) == checkAddr(addr2) == nil.
func (m *Manager) lockBuckets(addr1 uintptr, addr2 uintptr) (*bucket, *bucket) {
	i1 := bucketIndexForAddr(addr1)
	i2 := bucketIndexForAddr(addr2)
	b1 := &m.buckets[i1]
	b2 := &m.buckets[i2]

	// Ensure that buckets are locked in a consistent order (lowest index
	// first) to avoid circular locking.
	switch {
	case i1 < i2:
		b1.mu.Lock()
		b2.mu.Lock()
	case i2 < i1:
		b2.mu.Lock()
		b1.mu.Lock()
	default:
		b1.mu.Lock()
	}

	return b1, b2
}

// Wake wakes up to n waiters matching the bitmask on the given addr.
// The number of waiters woken is returned.
func (m *Manager) Wake(addr uintptr, bitmask uint32, n int) (int, error) {
	if err := checkAddr(addr); err != nil {
		return 0, err
	}

	b := m.lockBucket(addr)
	// This function is very hot; avoid defer.
	r := b.wakeLocked(addr, bitmask, n)
	b.mu.Unlock()
	return r, nil
}

func (m *Manager) doRequeue(c Checker, addr uintptr, val uint32, naddr uintptr, nwake int, nreq int) (int, error) {
	if err := checkAddr(addr); err != nil {
		return 0, err
	}
	if err := checkAddr(naddr); err != nil {
		return 0, err
	}

	b1, b2 := m.lockBuckets(addr, naddr)
	defer b1.mu.Unlock()
	if b2 != b1 {
		defer b2.mu.Unlock()
	}

	// Check our value.
	// This only applied for RequeueCmp().
	if c != nil {
		if err := c.Check(addr, val); err != nil {
			return 0, err
		}
	}

	// Wake the number required.
	done := b1.wakeLocked(addr, ^uint32(0), nwake)

	// Requeue the number required.
	b1.requeueLocked(b2, addr, naddr, nreq)

	return done, nil
}

// Requeue wakes up to nwake waiters on the given addr, and unconditionally
// requeues up to nreq waiters on naddr.
func (m *Manager) Requeue(addr uintptr, naddr uintptr, nwake int, nreq int) (int, error) {
	return m.doRequeue(nil, addr, 0, naddr, nwake, nreq)
}

// RequeueCmp atomically checks that the addr contains val (via the Checker),
// wakes up to nwake waiters on addr and then unconditionally requeues nreq
// waiters on naddr.
func (m *Manager) RequeueCmp(c Checker, addr uintptr, val uint32, naddr uintptr, nwake int, nreq int) (int, error) {
	return m.doRequeue(c, addr, val, naddr, nwake, nreq)
}

// WakeOp atomically applies op to the memory address addr2, wakes up to nwake1
// waiters unconditionally from addr1, and, based on the original value at addr2
// and a comparison encoded in op, wakes up to nwake2 waiters from addr2.
// It returns the total number of waiters woken.
func (m *Manager) WakeOp(c Checker, addr1 uintptr, addr2 uintptr, nwake1 int, nwake2 int, op uint32) (int, error) {
	if err := checkAddr(addr1); err != nil {
		return 0, err
	}
	if err := checkAddr(addr2); err != nil {
		return 0, err
	}

	b1, b2 := m.lockBuckets(addr1, addr2)

	done := 0
	cond, err := c.Op(addr2, op)
	if err == nil {
		// Wake up up to nwake1 entries from the first bucket.
		done = b1.wakeLocked(addr1, ^uint32(0), nwake1)

		// Wake up up to nwake2 entries from the second bucket if the
		// operation yielded true.
		if cond {
			done += b2.wakeLocked(addr2, ^uint32(0), nwake2)
		}
	}

	b1.mu.Unlock()
	if b2 != b1 {
		b2.mu.Unlock()
	}
	return done, err
}

// WaitPrepare atomically checks that addr contains val (via the Checker), then
// enqueues w to be woken by a send to w.C. If WaitPrepare returns nil, the
// Waiter must be subsequently removed by calling WaitComplete, whether or not
// a wakeup is received on w.C.
func (m *Manager) WaitPrepare(w *Waiter, c Checker, addr uintptr, val uint32, bitmask uint32) error {
	if err := checkAddr(addr); err != nil {
		return err
	}

	// Prepare the Waiter before taking the bucket lock.
	w.complete = 0
	select {
	case <-w.C:
	default:
	}
	w.addr = addr
	w.bitmask = bitmask

	b := m.lockBucket(addr)
	// This function is very hot; avoid defer.

	// Perform our atomic check.
	if err := c.Check(addr, val); err != nil {
		b.mu.Unlock()
		return err
	}

	// Add the waiter to the bucket.
	b.waiters.PushBack(w)

	b.mu.Unlock()
	return nil
}

// WaitComplete must be called when a Waiter previously added by WaitPrepare is
// no longer eligible to be woken.
func (m *Manager) WaitComplete(w *Waiter) {
	// Can we short-circuit acquiring the lock?
	// This is the happy path where a notification
	// was received and we don't need to dequeue this
	// waiter from any list (or take any locks).
	if atomic.LoadInt32(&w.complete) != 0 {
		return
	}

	// Take the bucket lock. Note that without holding the bucket lock, the
	// waiter is not guaranteed to stay in that bucket, so after we take the
	// bucket lock, we must ensure that the bucket hasn't changed: if it
	// happens to have changed, we release the old bucket lock and try again
	// with the new bucket; if it hasn't changed, we know it won't change now
	// because we hold the lock.
	var b *bucket
	for {
		addr := atomic.LoadUintptr(&w.addr)
		b = m.lockBucket(addr)
		// We still have to use an atomic load here, because if w was racily
		// requeued then w.addr is not protected by b.mu.
		if addr == atomic.LoadUintptr(&w.addr) {
			break
		}
		b.mu.Unlock()
	}

	// Remove waiter from the bucket. w.complete can only be stored with b.mu
	// locked, so this load doesn't need to use sync/atomic.
	if w.complete == 0 {
		b.waiters.Remove(w)
	}
	b.mu.Unlock()
}
<commit_msg>sentry: futex S/R optimization.<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package futex provides an implementation of the futex interface as found in
// the Linux kernel. It allows one to easily transform Wait() calls into waits
// on a channel, which is useful in a Go-based kernel, for example.
package futex

import (
	"sync"
	"sync/atomic"

	"gvisor.googlesource.com/gvisor/pkg/syserror"
)

// Checker abstracts memory accesses. This is useful because the "addresses"
// used in this package may not be real addresses (they could be indices of an
// array, for example), or they could be mapped via some special mechanism.
//
// TODO: Replace this with usermem.IO.
type Checker interface {
	// Check should validate that given address contains the given value.
	// If it does not contain the value, syserror.EAGAIN must be returned.
	// Any other error may be returned, which will be propagated.
	Check(addr uintptr, val uint32) error

	// Op should atomically perform the operation encoded in op on the data
	// pointed to by addr, then apply the comparison encoded in op to the
	// original value at addr, returning the result.
	// Note that op is an opaque operation whose behaviour is defined
	// outside of the futex manager.
	Op(addr uintptr, op uint32) (bool, error)
}

// Waiter is the struct which gets enqueued into buckets for wake up routines
// and requeue routines to scan and notify. Once a Waiter has been enqueued by
// WaitPrepare(), callers may listen on C for wake up events.
type Waiter struct {
	// Synchronization:
	//
	// - A Waiter that is not enqueued in a bucket is exclusively owned (no
	// synchronization applies).
	//
	// - A Waiter is enqueued in a bucket by calling WaitPrepare(). After this,
	// waiterEntry, complete, and addr are protected by the bucket.mu ("bucket
	// lock") of the containing bucket, and bitmask is immutable. complete and
	// addr are additionally mutated using atomic memory operations, ensuring
	// that they can be read using atomic memory operations without holding the
	// bucket lock.
	//
	// - A Waiter is only guaranteed to be no longer queued after calling
	// WaitComplete().

	// waiterEntry links Waiter into bucket.waiters.
	waiterEntry

	// complete is 1 if the Waiter was removed from its bucket by a wakeup and
	// 0 otherwise.
	complete int32

	// C is sent to when the Waiter is woken.
	C chan struct{}

	// addr is the address being waited on.
	addr uintptr

	// The bitmask we're waiting on.
	// This is used the case of a FUTEX_WAKE_BITSET.
	bitmask uint32
}

// NewWaiter returns a new unqueued Waiter.
func NewWaiter() *Waiter {
	return &Waiter{
		C: make(chan struct{}, 1),
	}
}

// bucket holds a list of waiters for a given address hash.
type bucket struct {
	// mu protects waiters and contained Waiter state. See comment in Waiter.
	mu sync.Mutex `state:"nosave"`

	waiters waiterList `state:"zerovalue"`
}

// wakeLocked wakes up to n waiters matching the bitmask at the addr for this
// bucket and returns the number of waiters woken.
//
// Preconditions: b.mu must be locked.
func (b *bucket) wakeLocked(addr uintptr, bitmask uint32, n int) int {
	done := 0
	for w := b.waiters.Front(); done < n && w != nil; {
		if w.addr != addr || w.bitmask&bitmask == 0 {
			// Not matching.
			w = w.Next()
			continue
		}

		// Remove from the bucket and wake the waiter.
		woke := w
		w = w.Next() // Next iteration.
		b.waiters.Remove(woke)
		woke.C <- struct{}{}

		// NOTE: The above channel write establishes a write barrier
		// according to the memory model, so nothing may be ordered
		// around it. Since we've dequeued w and will never touch it
		// again, we can safely store 1 to w.complete here and allow
		// the WaitComplete() to short-circuit grabbing the bucket
		// lock. If they somehow miss the w.complete, we are still
		// holding the lock, so we can know that they won't dequeue w,
		// assume it's free and have the below operation afterwards.
		atomic.StoreInt32(&woke.complete, 1)
		done++
	}
	return done
}

// requeueLocked takes n waiters from the bucket and moves them to naddr on the
// bucket "to".
//
// Preconditions: b and to must be locked.
func (b *bucket) requeueLocked(to *bucket, addr, naddr uintptr, n int) int {
	done := 0
	for w := b.waiters.Front(); done < n && w != nil; {
		if w.addr != addr {
			// Not matching.
			w = w.Next()
			continue
		}

		requeued := w
		w = w.Next() // Next iteration.
		b.waiters.Remove(requeued)
		atomic.StoreUintptr(&requeued.addr, naddr)
		to.waiters.PushBack(requeued)
		done++
	}
	return done
}

const (
	// bucketCount is the number of buckets per Manager. By having many of
	// these we reduce contention when concurrent yet unrelated calls are made.
	bucketCount     = 1 << bucketCountBits
	bucketCountBits = 10
)

func checkAddr(addr uintptr) error {
	// Ensure the address is aligned.
	// It must be a DWORD boundary.
	if addr&0x3 != 0 {
		return syserror.EINVAL
	}

	return nil
}

// bucketIndexForAddr returns the index into Manager.buckets for addr.
func bucketIndexForAddr(addr uintptr) uintptr {
	// - The bottom 2 bits of addr must be 0, per checkAddr.
	//
	// - On amd64, the top 16 bits of addr (bits 48-63) must be equal to bit 47
	// for a canonical address, and (on all existing platforms) bit 47 must be
	// 0 for an application address.
	//
	// Thus 19 bits of addr are "useless" for hashing, leaving only 45 "useful"
	// bits. We choose one of the simplest possible hash functions that at
	// least uses all 45 useful bits in the output, given that bucketCountBits
	// == 10. This hash function also has the property that it will usually map
	// adjacent addresses to adjacent buckets, slightly improving memory
	// locality when an application synchronization structure uses multiple
	// nearby futexes.
	//
	// Note that despite the large number of arithmetic operations in the
	// function, many components can be computed in parallel, such that the
	// critical path is 1 bit shift + 3 additions (2 in h1, then h1 + h2). This
	// is also why h1 and h2 are grouped separately; for "(addr >> 2) + ... +
	// (addr >> 42)" without any additional grouping, the compiler puts all 4
	// additions in the critical path.
	h1 := (addr >> 2) + (addr >> 12) + (addr >> 22)
	h2 := (addr >> 32) + (addr >> 42)
	return (h1 + h2) % bucketCount
}

// Manager holds futex state for a single virtual address space.
type Manager struct {
	buckets [bucketCount]bucket `state:"zerovalue"`
}

// NewManager returns an initialized futex manager.
// N.B. we use virtual address to tag futexes, so it only works for private
// (within a single process) futex.
func NewManager() *Manager {
	return &Manager{}
}

// lockBucket returns a locked bucket for the given addr.
//
// Preconditions: checkAddr(addr) == nil.
func (m *Manager) lockBucket(addr uintptr) *bucket {
	b := &m.buckets[bucketIndexForAddr(addr)]
	b.mu.Lock()
	return b
}

// lockBuckets returns locked buckets for the given addrs.
//
// Preconditions: checkAddr(addr1) == checkAddr(addr2) == nil.
func (m *Manager) lockBuckets(addr1 uintptr, addr2 uintptr) (*bucket, *bucket) {
	i1 := bucketIndexForAddr(addr1)
	i2 := bucketIndexForAddr(addr2)
	b1 := &m.buckets[i1]
	b2 := &m.buckets[i2]

	// Ensure that buckets are locked in a consistent order (lowest index
	// first) to avoid circular locking.
	switch {
	case i1 < i2:
		b1.mu.Lock()
		b2.mu.Lock()
	case i2 < i1:
		b2.mu.Lock()
		b1.mu.Lock()
	default:
		b1.mu.Lock()
	}

	return b1, b2
}

// Wake wakes up to n waiters matching the bitmask on the given addr.
// The number of waiters woken is returned.
func (m *Manager) Wake(addr uintptr, bitmask uint32, n int) (int, error) {
	if err := checkAddr(addr); err != nil {
		return 0, err
	}

	b := m.lockBucket(addr)
	// This function is very hot; avoid defer.
	r := b.wakeLocked(addr, bitmask, n)
	b.mu.Unlock()
	return r, nil
}

func (m *Manager) doRequeue(c Checker, addr uintptr, val uint32, naddr uintptr, nwake int, nreq int) (int, error) {
	if err := checkAddr(addr); err != nil {
		return 0, err
	}
	if err := checkAddr(naddr); err != nil {
		return 0, err
	}

	b1, b2 := m.lockBuckets(addr, naddr)
	defer b1.mu.Unlock()
	if b2 != b1 {
		defer b2.mu.Unlock()
	}

	// Check our value.
	// This only applied for RequeueCmp().
	if c != nil {
		if err := c.Check(addr, val); err != nil {
			return 0, err
		}
	}

	// Wake the number required.
	done := b1.wakeLocked(addr, ^uint32(0), nwake)

	// Requeue the number required.
	b1.requeueLocked(b2, addr, naddr, nreq)

	return done, nil
}

// Requeue wakes up to nwake waiters on the given addr, and unconditionally
// requeues up to nreq waiters on naddr.
func (m *Manager) Requeue(addr uintptr, naddr uintptr, nwake int, nreq int) (int, error) {
	return m.doRequeue(nil, addr, 0, naddr, nwake, nreq)
}

// RequeueCmp atomically checks that the addr contains val (via the Checker),
// wakes up to nwake waiters on addr and then unconditionally requeues nreq
// waiters on naddr.
func (m *Manager) RequeueCmp(c Checker, addr uintptr, val uint32, naddr uintptr, nwake int, nreq int) (int, error) {
	return m.doRequeue(c, addr, val, naddr, nwake, nreq)
}

// WakeOp atomically applies op to the memory address addr2, wakes up to nwake1
// waiters unconditionally from addr1, and, based on the original value at addr2
// and a comparison encoded in op, wakes up to nwake2 waiters from addr2.
// It returns the total number of waiters woken.
func (m *Manager) WakeOp(c Checker, addr1 uintptr, addr2 uintptr, nwake1 int, nwake2 int, op uint32) (int, error) {
	if err := checkAddr(addr1); err != nil {
		return 0, err
	}
	if err := checkAddr(addr2); err != nil {
		return 0, err
	}

	b1, b2 := m.lockBuckets(addr1, addr2)

	done := 0
	cond, err := c.Op(addr2, op)
	if err == nil {
		// Wake up up to nwake1 entries from the first bucket.
		done = b1.wakeLocked(addr1, ^uint32(0), nwake1)

		// Wake up up to nwake2 entries from the second bucket if the
		// operation yielded true.
		if cond {
			done += b2.wakeLocked(addr2, ^uint32(0), nwake2)
		}
	}

	b1.mu.Unlock()
	if b2 != b1 {
		b2.mu.Unlock()
	}
	return done, err
}

// WaitPrepare atomically checks that addr contains val (via the Checker), then
// enqueues w to be woken by a send to w.C. If WaitPrepare returns nil, the
// Waiter must be subsequently removed by calling WaitComplete, whether or not
// a wakeup is received on w.C.
func (m *Manager) WaitPrepare(w *Waiter, c Checker, addr uintptr, val uint32, bitmask uint32) error {
	if err := checkAddr(addr); err != nil {
		return err
	}

	// Prepare the Waiter before taking the bucket lock.
	w.complete = 0
	select {
	case <-w.C:
	default:
	}
	w.addr = addr
	w.bitmask = bitmask

	b := m.lockBucket(addr)
	// This function is very hot; avoid defer.

	// Perform our atomic check.
	if err := c.Check(addr, val); err != nil {
		b.mu.Unlock()
		return err
	}

	// Add the waiter to the bucket.
	b.waiters.PushBack(w)

	b.mu.Unlock()
	return nil
}

// WaitComplete must be called when a Waiter previously added by WaitPrepare is
// no longer eligible to be woken.
func (m *Manager) WaitComplete(w *Waiter) {
	// Can we short-circuit acquiring the lock?
	// This is the happy path where a notification
	// was received and we don't need to dequeue this
	// waiter from any list (or take any locks).
	if atomic.LoadInt32(&w.complete) != 0 {
		return
	}

	// Take the bucket lock. Note that without holding the bucket lock, the
	// waiter is not guaranteed to stay in that bucket, so after we take the
	// bucket lock, we must ensure that the bucket hasn't changed: if it
	// happens to have changed, we release the old bucket lock and try again
	// with the new bucket; if it hasn't changed, we know it won't change now
	// because we hold the lock.
	var b *bucket
	for {
		addr := atomic.LoadUintptr(&w.addr)
		b = m.lockBucket(addr)
		// We still have to use an atomic load here, because if w was racily
		// requeued then w.addr is not protected by b.mu.
		if addr == atomic.LoadUintptr(&w.addr) {
			break
		}
		b.mu.Unlock()
	}

	// Remove waiter from the bucket. w.complete can only be stored with b.mu
	// locked, so this load doesn't need to use sync/atomic.
	if w.complete == 0 {
		b.waiters.Remove(w)
	}
	b.mu.Unlock()
}
<|endoftext|>