<commit_before>/*
Copyright 2014 The Kubernetes 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"
	"io"

	"github.com/spf13/cobra"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/util/i18n"
)

var (
	explainLong = templates.LongDesc(`
		Documentation of resources.

		` + validResources)

	explainExamples = templates.Examples(i18n.T(`
		# Get the documentation of the resource and its fields
		kubectl explain pods

		# Get the documentation of a specific field of a resource
		kubectl explain pods.spec.containers`))
)

// NewCmdExplain returns a cobra command for swagger docs
func NewCmdExplain(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
	cmd := &cobra.Command{
		Use:     "explain RESOURCE",
		Short:   i18n.T("Documentation of resources"),
		Long:    explainLong,
		Example: explainExamples,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunExplain(f, out, cmdErr, cmd, args)
			cmdutil.CheckErr(err)
		},
	}
	cmd.Flags().Bool("recursive", false, "Print the fields of fields (Currently only 1 level deep)")
	cmd.Flags().String("api-version", "", "Get different explanations for particular API version")
	cmdutil.AddInclude3rdPartyFlags(cmd)
	return cmd
}

// RunExplain executes the appropriate steps to print a model's documentation
func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		fmt.Fprint(cmdErr, "You must specify the type of resource to explain. ", validResources)
		return cmdutil.UsageErrorf(cmd, "Required resource not specified.")
	}
	if len(args) > 1 {
		return cmdutil.UsageErrorf(cmd, "We accept only this format: explain RESOURCE")
	}

	recursive := cmdutil.GetFlagBool(cmd, "recursive")
	apiVersionString := cmdutil.GetFlagString(cmd, "api-version")
	apiVersion := schema.GroupVersion{}

	mapper, _ := f.Object()
	// TODO: After we figured out the new syntax to separate group and resource, allow
	// the users to use it in explain (kubectl explain <group><syntax><resource>).
	// Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax.
	inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper)
	if err != nil {
		return err
	}

	// TODO: We should deduce the group for a resource by discovering the supported resources at server.
	fullySpecifiedGVR, groupResource := schema.ParseResourceArg(inModel)
	gvk := schema.GroupVersionKind{}
	if fullySpecifiedGVR != nil {
		gvk, _ = mapper.KindFor(*fullySpecifiedGVR)
	}
	if gvk.Empty() {
		gvk, err = mapper.KindFor(groupResource.WithVersion(""))
		if err != nil {
			return err
		}
	}

	if len(apiVersionString) == 0 {
		groupMeta, err := api.Registry.Group(gvk.Group)
		if err != nil {
			return err
		}
		apiVersion = groupMeta.GroupVersion

	} else {
		apiVersion, err = schema.ParseGroupVersion(apiVersionString)
		if err != nil {
			return nil
		}
	}

	schema, err := f.SwaggerSchema(apiVersion.WithKind(gvk.Kind))
	if err != nil {
		return err
	}

	return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive)
}
<commit_msg>Add whitespace to improve error msg clarity<commit_after>/*
Copyright 2014 The Kubernetes 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"
	"io"

	"github.com/spf13/cobra"

	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/util/i18n"
)

var (
	explainLong = templates.LongDesc(`
		Documentation of resources.

		` + validResources)

	explainExamples = templates.Examples(i18n.T(`
		# Get the documentation of the resource and its fields
		kubectl explain pods

		# Get the documentation of a specific field of a resource
		kubectl explain pods.spec.containers`))
)

// NewCmdExplain returns a cobra command for swagger docs
func NewCmdExplain(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
	cmd := &cobra.Command{
		Use:     "explain RESOURCE",
		Short:   i18n.T("Documentation of resources"),
		Long:    explainLong,
		Example: explainExamples,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunExplain(f, out, cmdErr, cmd, args)
			cmdutil.CheckErr(err)
		},
	}
	cmd.Flags().Bool("recursive", false, "Print the fields of fields (Currently only 1 level deep)")
	cmd.Flags().String("api-version", "", "Get different explanations for particular API version")
	cmdutil.AddInclude3rdPartyFlags(cmd)
	return cmd
}

// RunExplain executes the appropriate steps to print a model's documentation
func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		fmt.Fprintf(cmdErr, "You must specify the type of resource to explain. %s\n", validResources)
		return cmdutil.UsageErrorf(cmd, "Required resource not specified.")
	}
	if len(args) > 1 {
		return cmdutil.UsageErrorf(cmd, "We accept only this format: explain RESOURCE")
	}

	recursive := cmdutil.GetFlagBool(cmd, "recursive")
	apiVersionString := cmdutil.GetFlagString(cmd, "api-version")
	apiVersion := schema.GroupVersion{}

	mapper, _ := f.Object()
	// TODO: After we figured out the new syntax to separate group and resource, allow
	// the users to use it in explain (kubectl explain <group><syntax><resource>).
	// Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax.
	inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper)
	if err != nil {
		return err
	}

	// TODO: We should deduce the group for a resource by discovering the supported resources at server.
	fullySpecifiedGVR, groupResource := schema.ParseResourceArg(inModel)
	gvk := schema.GroupVersionKind{}
	if fullySpecifiedGVR != nil {
		gvk, _ = mapper.KindFor(*fullySpecifiedGVR)
	}
	if gvk.Empty() {
		gvk, err = mapper.KindFor(groupResource.WithVersion(""))
		if err != nil {
			return err
		}
	}

	if len(apiVersionString) == 0 {
		groupMeta, err := api.Registry.Group(gvk.Group)
		if err != nil {
			return err
		}
		apiVersion = groupMeta.GroupVersion

	} else {
		apiVersion, err = schema.ParseGroupVersion(apiVersionString)
		if err != nil {
			return nil
		}
	}

	schema, err := f.SwaggerSchema(apiVersion.WithKind(gvk.Kind))
	if err != nil {
		return err
	}

	return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"

	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

var (
	replaceLong = templates.LongDesc(i18n.T(`
		Replace a resource by filename or stdin.

		JSON and YAML formats are accepted. If replacing an existing resource, the
		complete resource spec must be provided. This can be obtained by

		    $ kubectl get TYPE NAME -o yaml

		Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.`))

	replaceExample = templates.Examples(i18n.T(`
		# Replace a pod using the data in pod.json.
		kubectl replace -f ./pod.json

		# Replace a pod based on the JSON passed into stdin.
		cat pod.json | kubectl replace -f -

		# Update a single-container pod's image version (tag) to v4
		kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

		# Force replace, delete and then re-create the resource
		kubectl replace --force -f ./pod.json`))
)

func NewCmdReplace(f cmdutil.Factory, out io.Writer) *cobra.Command {
	options := &resource.FilenameOptions{}

	cmd := &cobra.Command{
		Use:     "replace -f FILENAME",
		Short:   i18n.T("Replace a resource by filename or stdin"),
		Long:    replaceLong,
		Example: replaceExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
			err := RunReplace(f, out, cmd, args, options)
			cmdutil.CheckErr(err)
		},
	}
	usage := "to use to replace the resource."
	cmdutil.AddFilenameOptionFlags(cmd, options, usage)
	cmd.MarkFlagRequired("filename")
	cmd.Flags().Bool("force", false, "Delete and re-create the specified resource")
	cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).")
	cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.")
	cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddOutputFlagsForMutation(cmd)
	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddRecordFlag(cmd)
	cmdutil.AddInclude3rdPartyFlags(cmd)

	return cmd
}

func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
	if err != nil {
		return err
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	force := cmdutil.GetFlagBool(cmd, "force")
	if cmdutil.IsFilenameSliceEmpty(options.Filenames) {
		return cmdutil.UsageErrorf(cmd, "Must specify --filename to replace")
	}

	shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
	if force {
		return forceReplace(f, out, cmd, args, shortOutput, options)
	}

	if cmdutil.GetFlagInt(cmd, "grace-period") >= 0 {
		return fmt.Errorf("--grace-period must have --force specified")
	}

	if cmdutil.GetFlagDuration(cmd, "timeout") != 0 {
		return fmt.Errorf("--timeout must have --force specified")
	}

	mapper, typer, err := f.UnstructuredObject()
	if err != nil {
		return err
	}

	r := f.NewBuilder().
		Unstructured(f.UnstructuredClientForMapping, mapper, typer).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	return r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
			return cmdutil.AddSourceToErr("replacing", info.Source, err)
		}

		if cmdutil.ShouldRecord(cmd, info) {
			if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
				return cmdutil.AddSourceToErr("replacing", info.Source, err)
			}
		}

		// Serialize the object with the annotation applied.
		obj, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, true, info.Object)
		if err != nil {
			return cmdutil.AddSourceToErr("replacing", info.Source, err)
		}

		info.Refresh(obj, true)
		f.PrintObjectSpecificMessage(obj, out)
		cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "replaced")
		return nil
	})
}

func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *resource.FilenameOptions) error {
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
	if err != nil {
		return err
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	for i, filename := range options.Filenames {
		if filename == "-" {
			tempDir, err := ioutil.TempDir("", "kubectl_replace_")
			if err != nil {
				return err
			}
			defer os.RemoveAll(tempDir)
			tempFilename := filepath.Join(tempDir, "resource.stdin")
			err = cmdutil.DumpReaderToFile(os.Stdin, tempFilename)
			if err != nil {
				return err
			}
			options.Filenames[i] = tempFilename
		}
	}

	mapper, typer, err := f.UnstructuredObject()
	if err != nil {
		return err
	}
	r := resource.NewBuilder(mapper, f.CategoryExpander(), typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), unstructured.UnstructuredJSONScheme).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options).
		ResourceTypeOrNameArgs(false, args...).RequireObject(false).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}
	//Replace will create a resource if it doesn't exist already, so ignore not found error
	ignoreNotFound := true
	timeout := cmdutil.GetFlagDuration(cmd, "timeout")
	gracePeriod := cmdutil.GetFlagInt(cmd, "grace-period")
	waitForDeletion := false
	if gracePeriod == 0 {
		// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
		// into --grace-period=1 and wait until the object is successfully deleted.
		gracePeriod = 1
		waitForDeletion = true
	}
	// By default use a reaper to delete all related resources.
	if cmdutil.GetFlagBool(cmd, "cascade") {
		glog.Warningf("\"cascade\" is set, kubectl will delete and re-create all resources managed by this resource (e.g. Pods created by a ReplicationController). Consider using \"kubectl rolling-update\" if you want to update a ReplicationController together with its Pods.")
		err = ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, timeout, gracePeriod, waitForDeletion, shortOutput, mapper, false)
	} else {
		err = DeleteResult(r, out, ignoreNotFound, gracePeriod, shortOutput, mapper)
	}
	if err != nil {
		return err
	}

	if timeout == 0 {
		timeout = kubectl.Timeout
	}
	r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		return wait.PollImmediate(kubectl.Interval, timeout, func() (bool, error) {
			if err := info.Get(); !errors.IsNotFound(err) {
				return false, err
			}
			return true, nil
		})
	})

	r = f.NewBuilder().
		Unstructured(f.UnstructuredClientForMapping, mapper, typer).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	count := 0
	err = r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
			return err
		}

		if cmdutil.ShouldRecord(cmd, info) {
			if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
				return cmdutil.AddSourceToErr("replacing", info.Source, err)
			}
		}

		obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
		if err != nil {
			return err
		}

		count++
		info.Refresh(obj, true)
		f.PrintObjectSpecificMessage(obj, out)
		cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "replaced")
		return nil
	})
	if err != nil {
		return err
	}
	if count == 0 {
		return fmt.Errorf("no objects passed to replace")
	}
	return nil
}
<commit_msg>should check and return err when visit failure<commit_after>/*
Copyright 2014 The Kubernetes 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"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"

	"github.com/golang/glog"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

var (
	replaceLong = templates.LongDesc(i18n.T(`
		Replace a resource by filename or stdin.

		JSON and YAML formats are accepted. If replacing an existing resource, the
		complete resource spec must be provided. This can be obtained by

		    $ kubectl get TYPE NAME -o yaml

		Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.`))

	replaceExample = templates.Examples(i18n.T(`
		# Replace a pod using the data in pod.json.
		kubectl replace -f ./pod.json

		# Replace a pod based on the JSON passed into stdin.
		cat pod.json | kubectl replace -f -

		# Update a single-container pod's image version (tag) to v4
		kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

		# Force replace, delete and then re-create the resource
		kubectl replace --force -f ./pod.json`))
)

func NewCmdReplace(f cmdutil.Factory, out io.Writer) *cobra.Command {
	options := &resource.FilenameOptions{}

	cmd := &cobra.Command{
		Use:     "replace -f FILENAME",
		Short:   i18n.T("Replace a resource by filename or stdin"),
		Long:    replaceLong,
		Example: replaceExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
			err := RunReplace(f, out, cmd, args, options)
			cmdutil.CheckErr(err)
		},
	}
	usage := "to use to replace the resource."
	cmdutil.AddFilenameOptionFlags(cmd, options, usage)
	cmd.MarkFlagRequired("filename")
	cmd.Flags().Bool("force", false, "Delete and re-create the specified resource")
	cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).")
	cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.")
	cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddOutputFlagsForMutation(cmd)
	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddRecordFlag(cmd)
	cmdutil.AddInclude3rdPartyFlags(cmd)

	return cmd
}

func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
	if err != nil {
		return err
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	force := cmdutil.GetFlagBool(cmd, "force")
	if cmdutil.IsFilenameSliceEmpty(options.Filenames) {
		return cmdutil.UsageErrorf(cmd, "Must specify --filename to replace")
	}

	shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
	if force {
		return forceReplace(f, out, cmd, args, shortOutput, options)
	}

	if cmdutil.GetFlagInt(cmd, "grace-period") >= 0 {
		return fmt.Errorf("--grace-period must have --force specified")
	}

	if cmdutil.GetFlagDuration(cmd, "timeout") != 0 {
		return fmt.Errorf("--timeout must have --force specified")
	}

	mapper, typer, err := f.UnstructuredObject()
	if err != nil {
		return err
	}

	r := f.NewBuilder().
		Unstructured(f.UnstructuredClientForMapping, mapper, typer).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	return r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
			return cmdutil.AddSourceToErr("replacing", info.Source, err)
		}

		if cmdutil.ShouldRecord(cmd, info) {
			if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
				return cmdutil.AddSourceToErr("replacing", info.Source, err)
			}
		}

		// Serialize the object with the annotation applied.
		obj, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, true, info.Object)
		if err != nil {
			return cmdutil.AddSourceToErr("replacing", info.Source, err)
		}

		info.Refresh(obj, true)
		f.PrintObjectSpecificMessage(obj, out)
		cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "replaced")
		return nil
	})
}

func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *resource.FilenameOptions) error {
	schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
	if err != nil {
		return err
	}

	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	for i, filename := range options.Filenames {
		if filename == "-" {
			tempDir, err := ioutil.TempDir("", "kubectl_replace_")
			if err != nil {
				return err
			}
			defer os.RemoveAll(tempDir)
			tempFilename := filepath.Join(tempDir, "resource.stdin")
			err = cmdutil.DumpReaderToFile(os.Stdin, tempFilename)
			if err != nil {
				return err
			}
			options.Filenames[i] = tempFilename
		}
	}

	mapper, typer, err := f.UnstructuredObject()
	if err != nil {
		return err
	}
	r := resource.NewBuilder(mapper, f.CategoryExpander(), typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), unstructured.UnstructuredJSONScheme).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options).
		ResourceTypeOrNameArgs(false, args...).RequireObject(false).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}
	//Replace will create a resource if it doesn't exist already, so ignore not found error
	ignoreNotFound := true
	timeout := cmdutil.GetFlagDuration(cmd, "timeout")
	gracePeriod := cmdutil.GetFlagInt(cmd, "grace-period")
	waitForDeletion := false
	if gracePeriod == 0 {
		// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
		// into --grace-period=1 and wait until the object is successfully deleted.
		gracePeriod = 1
		waitForDeletion = true
	}
	// By default use a reaper to delete all related resources.
	if cmdutil.GetFlagBool(cmd, "cascade") {
		glog.Warningf("\"cascade\" is set, kubectl will delete and re-create all resources managed by this resource (e.g. Pods created by a ReplicationController). Consider using \"kubectl rolling-update\" if you want to update a ReplicationController together with its Pods.")
		err = ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, timeout, gracePeriod, waitForDeletion, shortOutput, mapper, false)
	} else {
		err = DeleteResult(r, out, ignoreNotFound, gracePeriod, shortOutput, mapper)
	}
	if err != nil {
		return err
	}

	if timeout == 0 {
		timeout = kubectl.Timeout
	}
	err = r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		return wait.PollImmediate(kubectl.Interval, timeout, func() (bool, error) {
			if err := info.Get(); !errors.IsNotFound(err) {
				return false, err
			}
			return true, nil
		})
	})
	if err != nil {
		return err
	}

	r = f.NewBuilder().
		Unstructured(f.UnstructuredClientForMapping, mapper, typer).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).DefaultNamespace().
		FilenameParam(enforceNamespace, options).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	count := 0
	err = r.Visit(func(info *resource.Info, err error) error {
		if err != nil {
			return err
		}

		if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
			return err
		}

		if cmdutil.ShouldRecord(cmd, info) {
			if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
				return cmdutil.AddSourceToErr("replacing", info.Source, err)
			}
		}

		obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
		if err != nil {
			return err
		}

		count++
		info.Refresh(obj, true)
		f.PrintObjectSpecificMessage(obj, out)
		cmdutil.PrintSuccess(mapper, shortOutput, out, info.Mapping.Resource, info.Name, false, "replaced")
		return nil
	})
	if err != nil {
		return err
	}
	if count == 0 {
		return fmt.Errorf("no objects passed to replace")
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 localkube

import (
	"strings"

	apiserver "k8s.io/kubernetes/cmd/kube-apiserver/app"
	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"

	kuberest "k8s.io/kubernetes/pkg/client/restclient"
	kubeclient "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/storage/storagebackend"
)

func (lk LocalkubeServer) NewAPIServer() Server {
	return NewSimpleServer("apiserver", serverInterval, StartAPIServer(lk))
}

func StartAPIServer(lk LocalkubeServer) func() error {
	config := options.NewAPIServer()

	config.BindAddress = lk.APIServerAddress
	config.SecurePort = lk.APIServerPort
	config.InsecureBindAddress = lk.APIServerInsecureAddress
	config.InsecurePort = lk.APIServerInsecurePort

	config.ClientCAFile = lk.GetCAPublicKeyCertPath()
	config.TLSCertFile = lk.GetPublicKeyCertPath()
	config.TLSPrivateKeyFile = lk.GetPrivateKeyCertPath()
	config.AdmissionControl = "NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota"

	// use localkube etcd
	config.StorageConfig = storagebackend.Config{ServerList: KubeEtcdClientURLs}

	// set Service IP range
	config.ServiceClusterIPRange = lk.ServiceClusterIPRange

	// defaults from apiserver command
	config.EnableProfiling = true
	config.EnableWatchCache = true
	config.MinRequestTimeout = 1800

	config.AllowPrivileged = true

	config.RuntimeConfig = lk.RuntimeConfig

	lk.SetExtraConfigForComponent("apiserver", &config)

	return func() error {
		return apiserver.Run(config)
	}
}

// notFoundErr returns true if the passed error is an API server object not found error
func notFoundErr(err error) bool {
	if err == nil {
		return false
	}
	return strings.HasSuffix(err.Error(), "not found")
}

func kubeClient() *kubeclient.Client {
	config := &kuberest.Config{
		Host: "http://localhost:8080", // TODO: Make configurable
	}
	client, err := kubeclient.New(config)
	if err != nil {
		panic(err)
	}
	return client
}
<commit_msg>Add DefaultStorageClass to admission controllers<commit_after>/*
Copyright 2016 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 localkube

import (
	"strings"

	apiserver "k8s.io/kubernetes/cmd/kube-apiserver/app"
	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"

	kuberest "k8s.io/kubernetes/pkg/client/restclient"
	kubeclient "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/storage/storagebackend"
)

func (lk LocalkubeServer) NewAPIServer() Server {
	return NewSimpleServer("apiserver", serverInterval, StartAPIServer(lk))
}

func StartAPIServer(lk LocalkubeServer) func() error {
	config := options.NewAPIServer()

	config.BindAddress = lk.APIServerAddress
	config.SecurePort = lk.APIServerPort
	config.InsecureBindAddress = lk.APIServerInsecureAddress
	config.InsecurePort = lk.APIServerInsecurePort

	config.ClientCAFile = lk.GetCAPublicKeyCertPath()
	config.TLSCertFile = lk.GetPublicKeyCertPath()
	config.TLSPrivateKeyFile = lk.GetPrivateKeyCertPath()
	config.AdmissionControl = "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota"

	// use localkube etcd
	config.StorageConfig = storagebackend.Config{ServerList: KubeEtcdClientURLs}

	// set Service IP range
	config.ServiceClusterIPRange = lk.ServiceClusterIPRange

	// defaults from apiserver command
	config.EnableProfiling = true
	config.EnableWatchCache = true
	config.MinRequestTimeout = 1800

	config.AllowPrivileged = true

	config.RuntimeConfig = lk.RuntimeConfig

	lk.SetExtraConfigForComponent("apiserver", &config)

	return func() error {
		return apiserver.Run(config)
	}
}

// notFoundErr returns true if the passed error is an API server object not found error
func notFoundErr(err error) bool {
	if err == nil {
		return false
	}
	return strings.HasSuffix(err.Error(), "not found")
}

func kubeClient() *kubeclient.Client {
	config := &kuberest.Config{
		Host: "http://localhost:8080", // TODO: Make configurable
	}
	client, err := kubeclient.New(config)
	if err != nil {
		panic(err)
	}
	return client
}
<|endoftext|>


<commit_before>// Copyright 2017-2019 Authors of Cilium
//
// 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 agent

import (
	"bytes"
	"context"
	"encoding/gob"
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/cilium/cilium/api/v1/models"
	oldBPF "github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/monitor/agent/listener"
	"github.com/cilium/cilium/pkg/monitor/payload"
	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/perf"

	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

const eventsMapName = "cilium_events"

// isCtxDone is a utility function that returns true when the context's Done()
// channel is closed. It is intended to simplify goroutines that need to check
// this multiple times in their loop.
func isCtxDone(ctx context.Context) bool {
	select {
	case <-ctx.Done():
		return true
	default:
		return false
	}
}

// Agent structure for centralizing the responsibilities of the main events
// reader.
// There is some racey-ness around perfReaderCancel since it replaces on every
// perf reader start. In the event that a MonitorListener from a previous
// generation calls its cleanup after the start of the new perf reader, we
// might call the new, and incorrect, cancel function. We guard for this by
// checking the number of listeners during the cleanup call. The perf reader
// must have at least one MonitorListener (since it started) so no cancel is called.
// If it doesn't, the cancel is the correct behavior (the older generation
// cancel must have been called for us to get this far anyway).
type Agent struct {
	lock.Mutex
	models.MonitorStatus

	ctx              context.Context
	perfReaderCancel context.CancelFunc
	listeners        map[listener.MonitorListener]struct{}

	events        *ebpf.Map
	monitorEvents *perf.Reader
}

// NewAgent starts a new monitor agent instance which distributes monitor events
// to registered listeners. It spawns a singleton goroutine reading events from
// the BPF perf ring buffer and provides an interface to pass in non-BPF events.
// The instance can be stopped by cancelling ctx, which will stop the perf reader
// go routine and close all registered listeners.
// Note that the perf buffer reader is started only when listeners are
// connected.
func NewAgent(ctx context.Context, nPages int) (a *Agent, err error) {
	// assert that we can actually connect the monitor
	path := oldBPF.MapPath(eventsMapName)
	eventsMap, err := ebpf.LoadPinnedMap(path)
	if err != nil {
		return nil, err
	}

	a = &Agent{
		ctx:              ctx,
		listeners:        make(map[listener.MonitorListener]struct{}),
		perfReaderCancel: func() {}, // no-op to avoid doing null checks everywhere
		events:           eventsMap,
		MonitorStatus: models.MonitorStatus{
			Cpus:     int64(eventsMap.ABI().MaxEntries),
			Npages:   int64(nPages),
			Pagesize: int64(os.Getpagesize()),
		},
	}

	return a, nil
}

// SendEvent distributes an event to all monitor listeners
func (a *Agent) SendEvent(typ int, event interface{}) error {
	if a == nil {
		return fmt.Errorf("monitor agent is not set up")
	}

	var buf bytes.Buffer
	if err := buf.WriteByte(byte(typ)); err != nil {
		return fmt.Errorf("unable to initialize buffer: %w", err)
	}

	if err := gob.NewEncoder(&buf).Encode(event); err != nil {
		return fmt.Errorf("unable to gob encode: %w", err)
	}

	p := payload.Payload{Data: buf.Bytes(), CPU: 0, Lost: 0, Type: payload.EventSample}
	a.send(&p)

	return nil
}

// Context returns the underlying context of this monitor instance. It can be
// used to derive other contexts which should be stopped when the monitor is
// stopped.
func (a *Agent) Context() context.Context {
	return a.ctx
}

// RegisterNewListener adds the new MonitorListener to the global list. It also spawns
// a singleton goroutine to read and distribute the events. The goroutine is spawned
// with a context derived from m.Context() and the cancelFunc is assigned to
// perfReaderCancel. Note that cancelling m.Context() (e.g. on program shutdown)
// will also cancel the derived context.
func (a *Agent) RegisterNewListener(newListener listener.MonitorListener) {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	if isCtxDone(a.ctx) {
		log.Debug("RegisterNewListener called on stopped monitor")
		newListener.Close()
		return
	}

	// If this is the first listener, start the perf reader
	if len(a.listeners) == 0 {
		a.perfReaderCancel() // don't leak any old readers, just in case.
		perfEventReaderCtx, cancel := context.WithCancel(a.ctx)
		a.perfReaderCancel = cancel
		go a.handleEvents(perfEventReaderCtx)
	}
	version := newListener.Version()
	switch newListener.Version() {
	case listener.Version1_2:
		a.listeners[newListener] = struct{}{}

	default:
		newListener.Close()
		log.WithField("version", version).Error("Closing listener from unsupported monitor client version")
	}

	log.WithFields(logrus.Fields{
		"count.listener": len(a.listeners),
		"version":        version,
	}).Debug("New listener connected")
}

// RemoveListener deletes the MonitorListener from the list, closes its queue, and
// stops perfReader if this is the last MonitorListener
func (a *Agent) RemoveListener(ml listener.MonitorListener) {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	// Remove the listener and close it.
	delete(a.listeners, ml)
	log.WithFields(logrus.Fields{
		"count.listener": len(a.listeners),
		"version":        ml.Version(),
	}).Debug("Removed listener")
	ml.Close()

	// If this was the final listener, shutdown the perf reader and unmap our
	// ring buffer readers. This tells the kernel to not emit this data.
	// Note: it is critical to hold the lock and check the number of listeners.
	// This guards against an older generation listener calling the
	// current generation perfReaderCancel
	if len(a.listeners) == 0 {
		a.perfReaderCancel()
	}
}

// handleEvents reads events from the perf buffer and processes them. It
// will exit when stopCtx is done. Note, however, that it will block in the
// Poll call but assumes enough events are generated that these blocks are
// short.
func (a *Agent) handleEvents(stopCtx context.Context) {
	scopedLog := log.WithField(logfields.StartTime, time.Now())
	scopedLog.Info("Beginning to read perf buffer")
	defer scopedLog.Info("Stopped reading perf buffer")

	bufferSize := int(a.Pagesize * a.Npages)
	monitorEvents, err := perf.NewReader(a.events, bufferSize)
	if err != nil {
		scopedLog.WithError(err).Fatal("Cannot initialise BPF perf ring buffer sockets")
	}
	defer func() {
		monitorEvents.Close()
		a.Lock()
		a.monitorEvents = nil
		a.Unlock()
	}()

	a.Lock()
	a.monitorEvents = monitorEvents
	a.Unlock()

	for !isCtxDone(stopCtx) {
		record, err := monitorEvents.Read()
		switch {
		case isCtxDone(stopCtx):
			return
		case err != nil:
			if perf.IsUnknownEvent(err) {
				a.Lock()
				a.MonitorStatus.Unknown++
				a.Unlock()
			} else {
				scopedLog.WithError(err).Warn("Error received while reading from perf buffer")
				if errors.Is(err, unix.EBADFD) {
					return
				}
			}
			continue
		}

		a.processPerfRecord(record)
	}
}

// processPerfRecord processes a record from the datapath and sends it to any
// registered subscribers
func (a *Agent) processPerfRecord(record perf.Record) {
	a.Lock()
	plType := payload.EventSample
	if record.LostSamples > 0 {
		plType = payload.RecordLost
		a.MonitorStatus.Lost += int64(record.LostSamples)
	}
	pl := payload.Payload{
		Data: record.RawSample,
		CPU:  record.CPU,
		Lost: record.LostSamples,
		Type: plType,
	}
	a.sendLocked(&pl)
	a.Unlock()
}

// State returns the current status of the monitor
func (a *Agent) State() *models.MonitorStatus {
	if a == nil {
		return nil
	}

	a.Lock()
	defer a.Unlock()

	if a.monitorEvents == nil {
		return nil
	}

	// Shallow-copy the structure, then return the newly allocated copy.
	status := a.MonitorStatus
	return &status
}

// send enqueues the payload to all listeners.
func (a *Agent) send(pl *payload.Payload) {
	a.Lock()
	defer a.Unlock()
	a.sendLocked(pl)
}

// sendLocked enqueues the payload to all listeners while holding the monitor lock.
func (a *Agent) sendLocked(pl *payload.Payload) {
	for ml := range a.listeners {
		ml.Enqueue(pl)
	}
}
<commit_msg>monitor: Rename send to sendToListeners<commit_after>// Copyright 2017-2019 Authors of Cilium
//
// 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 agent

import (
	"bytes"
	"context"
	"encoding/gob"
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/cilium/cilium/api/v1/models"
	oldBPF "github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/monitor/agent/listener"
	"github.com/cilium/cilium/pkg/monitor/payload"
	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/perf"

	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

const eventsMapName = "cilium_events"

// isCtxDone is a utility function that returns true when the context's Done()
// channel is closed. It is intended to simplify goroutines that need to check
// this multiple times in their loop.
func isCtxDone(ctx context.Context) bool {
	select {
	case <-ctx.Done():
		return true
	default:
		return false
	}
}

// Agent structure for centralizing the responsibilities of the main events
// reader.
// There is some racey-ness around perfReaderCancel since it replaces on every
// perf reader start. In the event that a MonitorListener from a previous
// generation calls its cleanup after the start of the new perf reader, we
// might call the new, and incorrect, cancel function. We guard for this by
// checking the number of listeners during the cleanup call. The perf reader
// must have at least one MonitorListener (since it started) so no cancel is called.
// If it doesn't, the cancel is the correct behavior (the older generation
// cancel must have been called for us to get this far anyway).
type Agent struct {
	lock.Mutex
	models.MonitorStatus

	ctx              context.Context
	perfReaderCancel context.CancelFunc
	listeners        map[listener.MonitorListener]struct{}

	events        *ebpf.Map
	monitorEvents *perf.Reader
}

// NewAgent starts a new monitor agent instance which distributes monitor events
// to registered listeners. It spawns a singleton goroutine reading events from
// the BPF perf ring buffer and provides an interface to pass in non-BPF events.
// The instance can be stopped by cancelling ctx, which will stop the perf reader
// go routine and close all registered listeners.
// Note that the perf buffer reader is started only when listeners are
// connected.
func NewAgent(ctx context.Context, nPages int) (a *Agent, err error) {
	// assert that we can actually connect the monitor
	path := oldBPF.MapPath(eventsMapName)
	eventsMap, err := ebpf.LoadPinnedMap(path)
	if err != nil {
		return nil, err
	}

	a = &Agent{
		ctx:              ctx,
		listeners:        make(map[listener.MonitorListener]struct{}),
		perfReaderCancel: func() {}, // no-op to avoid doing null checks everywhere
		events:           eventsMap,
		MonitorStatus: models.MonitorStatus{
			Cpus:     int64(eventsMap.ABI().MaxEntries),
			Npages:   int64(nPages),
			Pagesize: int64(os.Getpagesize()),
		},
	}

	return a, nil
}

// SendEvent distributes an event to all monitor listeners
func (a *Agent) SendEvent(typ int, event interface{}) error {
	if a == nil {
		return fmt.Errorf("monitor agent is not set up")
	}

	var buf bytes.Buffer
	if err := buf.WriteByte(byte(typ)); err != nil {
		return fmt.Errorf("unable to initialize buffer: %w", err)
	}

	if err := gob.NewEncoder(&buf).Encode(event); err != nil {
		return fmt.Errorf("unable to gob encode: %w", err)
	}

	p := payload.Payload{Data: buf.Bytes(), CPU: 0, Lost: 0, Type: payload.EventSample}
	a.sendToListeners(&p)

	return nil
}

// Context returns the underlying context of this monitor instance. It can be
// used to derive other contexts which should be stopped when the monitor is
// stopped.
func (a *Agent) Context() context.Context {
	return a.ctx
}

// RegisterNewListener adds the new MonitorListener to the global list. It also spawns
// a singleton goroutine to read and distribute the events. The goroutine is spawned
// with a context derived from m.Context() and the cancelFunc is assigned to
// perfReaderCancel. Note that cancelling m.Context() (e.g. on program shutdown)
// will also cancel the derived context.
func (a *Agent) RegisterNewListener(newListener listener.MonitorListener) {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	if isCtxDone(a.ctx) {
		log.Debug("RegisterNewListener called on stopped monitor")
		newListener.Close()
		return
	}

	// If this is the first listener, start the perf reader
	if len(a.listeners) == 0 {
		a.perfReaderCancel() // don't leak any old readers, just in case.
		perfEventReaderCtx, cancel := context.WithCancel(a.ctx)
		a.perfReaderCancel = cancel
		go a.handleEvents(perfEventReaderCtx)
	}
	version := newListener.Version()
	switch newListener.Version() {
	case listener.Version1_2:
		a.listeners[newListener] = struct{}{}

	default:
		newListener.Close()
		log.WithField("version", version).Error("Closing listener from unsupported monitor client version")
	}

	log.WithFields(logrus.Fields{
		"count.listener": len(a.listeners),
		"version":        version,
	}).Debug("New listener connected")
}

// RemoveListener deletes the MonitorListener from the list, closes its queue, and
// stops perfReader if this is the last MonitorListener
func (a *Agent) RemoveListener(ml listener.MonitorListener) {
	if a == nil {
		return
	}

	a.Lock()
	defer a.Unlock()

	// Remove the listener and close it.
	delete(a.listeners, ml)
	log.WithFields(logrus.Fields{
		"count.listener": len(a.listeners),
		"version":        ml.Version(),
	}).Debug("Removed listener")
	ml.Close()

	// If this was the final listener, shutdown the perf reader and unmap our
	// ring buffer readers. This tells the kernel to not emit this data.
	// Note: it is critical to hold the lock and check the number of listeners.
	// This guards against an older generation listener calling the
	// current generation perfReaderCancel
	if len(a.listeners) == 0 {
		a.perfReaderCancel()
	}
}

// handleEvents reads events from the perf buffer and processes them. It
// will exit when stopCtx is done. Note, however, that it will block in the
// Poll call but assumes enough events are generated that these blocks are
// short.
func (a *Agent) handleEvents(stopCtx context.Context) {
	scopedLog := log.WithField(logfields.StartTime, time.Now())
	scopedLog.Info("Beginning to read perf buffer")
	defer scopedLog.Info("Stopped reading perf buffer")

	bufferSize := int(a.Pagesize * a.Npages)
	monitorEvents, err := perf.NewReader(a.events, bufferSize)
	if err != nil {
		scopedLog.WithError(err).Fatal("Cannot initialise BPF perf ring buffer sockets")
	}
	defer func() {
		monitorEvents.Close()
		a.Lock()
		a.monitorEvents = nil
		a.Unlock()
	}()

	a.Lock()
	a.monitorEvents = monitorEvents
	a.Unlock()

	for !isCtxDone(stopCtx) {
		record, err := monitorEvents.Read()
		switch {
		case isCtxDone(stopCtx):
			return
		case err != nil:
			if perf.IsUnknownEvent(err) {
				a.Lock()
				a.MonitorStatus.Unknown++
				a.Unlock()
			} else {
				scopedLog.WithError(err).Warn("Error received while reading from perf buffer")
				if errors.Is(err, unix.EBADFD) {
					return
				}
			}
			continue
		}

		a.processPerfRecord(record)
	}
}

// processPerfRecord processes a record from the datapath and sends it to any
// registered subscribers
func (a *Agent) processPerfRecord(record perf.Record) {
	a.Lock()
	plType := payload.EventSample
	if record.LostSamples > 0 {
		plType = payload.RecordLost
		a.MonitorStatus.Lost += int64(record.LostSamples)
	}
	pl := payload.Payload{
		Data: record.RawSample,
		CPU:  record.CPU,
		Lost: record.LostSamples,
		Type: plType,
	}
	a.sendToListenersLocked(&pl)
	a.Unlock()
}

// State returns the current status of the monitor
func (a *Agent) State() *models.MonitorStatus {
	if a == nil {
		return nil
	}

	a.Lock()
	defer a.Unlock()

	if a.monitorEvents == nil {
		return nil
	}

	// Shallow-copy the structure, then return the newly allocated copy.
	status := a.MonitorStatus
	return &status
}

// sendToListeners enqueues the payload to all listeners.
func (a *Agent) sendToListeners(pl *payload.Payload) {
	a.Lock()
	defer a.Unlock()
	a.sendToListenersLocked(pl)
}

// sendToListenersLocked enqueues the payload to all listeners while holding the monitor lock.
func (a *Agent) sendToListenersLocked(pl *payload.Payload) {
	for ml := range a.listeners {
		ml.Enqueue(pl)
	}
}
<|endoftext|>


<commit_before>package notify

import (
	"encoding/json"
	"fmt"
	"net/url"
)

const (
	slackEndpoint       = "https://%s.slack.com/services/hooks/incoming-webhook?token=%s"
	slackStartedMessage = "*Building* %s, commit <%s|%s>, author %s"
	slackSuccessMessage = "*Success* %s, commit <%s|%s>, author %s"
	slackFailureMessage = "*Failed* %s, commit <%s|%s>, author %s"
)

type Slack struct {
	Team     string `yaml:"team,omitempty"`
	Channel  string `yaml:"channel,omitempty"`
	Username string `yaml:"username,omitempty"`
	Token    string `yaml:"token,omitempty"`
	Started  bool   `yaml:"on_started,omitempty"`
	Success  bool   `yaml:"on_success,omitempty"`
	Failure  bool   `yaml:"on_failure,omitempty"`
}

func (s *Slack) Send(context *Context) error {
	switch {
	case context.Commit.Status == "Started" && s.Started:
		return s.sendStarted(context)
	case context.Commit.Status == "Success" && s.Success:
		return s.sendSuccess(context)
	case context.Commit.Status == "Failure" && s.Failure:
		return s.sendFailure(context)
	}

	return nil
}

func getBuildUrl(context *Context) string {
	branchQuery := url.Values{}
	if context.Commit.Branch != "" {
		branchQuery.Set("branch", context.Commit.Branch)
	}

	return fmt.Sprintf("%s/%s/commit/%s?%s", context.Host, context.Repo.Slug, context.Commit.Hash, branchQuery.Encode())
}

func getMessage(context *Context, message string) string {
	url := getBuildUrl(context)
	return fmt.Sprintf(message, context.Repo.Name, url, context.Commit.HashShort(), context.Commit.Author)
}

func (s *Slack) sendStarted(context *Context) error {
	return s.send(getMessage(context, slackStartedMessage))
}

func (s *Slack) sendSuccess(context *Context) error {
	return s.send(getMessage(context, slackSuccessMessage))
}

func (s *Slack) sendFailure(context *Context) error {
	return s.send(getMessage(context, slackFailureMessage))
}

// helper function to send HTTP requests
func (s *Slack) send(msg string) error {
	// data will get posted in this format
	data := struct {
		Channel  string `json:"channel"`
		Username string `json:"username"`
		Text     string `json:"text"`
	}{s.Channel, s.Username, msg}

	// data json encoded
	payload, err := json.Marshal(data)
	if err != nil {
		return err
	}

	// send payload
	url := fmt.Sprintf(slackEndpoint, s.Team, s.Token)
	go sendJson(url, payload)

	return nil
}
<commit_msg>Tweaking slack to include commit message<commit_after>package notify

import (
	"encoding/json"
	"fmt"
	"net/url"
)

const (
	slackEndpoint       = "https://%s.slack.com/services/hooks/incoming-webhook?token=%s"
	slackStartedMessage = "*Building* %s <%s|%s>, by %s:\n> %s"
	slackSuccessMessage = "*Success* %s <%s|%s>, by %s:\n> %s"
	slackFailureMessage = "*Failed* %s <%s|%s>, by %s:\n> %s"
)

type Slack struct {
	Team     string `yaml:"team,omitempty"`
	Channel  string `yaml:"channel,omitempty"`
	Username string `yaml:"username,omitempty"`
	Token    string `yaml:"token,omitempty"`
	Started  bool   `yaml:"on_started,omitempty"`
	Success  bool   `yaml:"on_success,omitempty"`
	Failure  bool   `yaml:"on_failure,omitempty"`
}

func (s *Slack) Send(context *Context) error {
	switch {
	case context.Commit.Status == "Started" && s.Started:
		return s.sendStarted(context)
	case context.Commit.Status == "Success" && s.Success:
		return s.sendSuccess(context)
	case context.Commit.Status == "Failure" && s.Failure:
		return s.sendFailure(context)
	}

	return nil
}

func getBuildUrl(context *Context) string {
	branchQuery := url.Values{}
	if context.Commit.Branch != "" {
		branchQuery.Set("branch", context.Commit.Branch)
	}

	return fmt.Sprintf("%s/%s/commit/%s?%s", context.Host, context.Repo.Slug, context.Commit.Hash, branchQuery.Encode())
}

func getMessage(context *Context, message string) string {
	url := getBuildUrl(context)
	return fmt.Sprintf(
		message,
		context.Repo.Name,
		url,
		context.Commit.HashShort(),
		context.Commit.Author,
		context.Commit.Message)
}

func (s *Slack) sendStarted(context *Context) error {
	return s.send(getMessage(context, slackStartedMessage))
}

func (s *Slack) sendSuccess(context *Context) error {
	return s.send(getMessage(context, slackSuccessMessage))
}

func (s *Slack) sendFailure(context *Context) error {
	return s.send(getMessage(context, slackFailureMessage))
}

// helper function to send HTTP requests
func (s *Slack) send(msg string) error {
	// data will get posted in this format
	data := struct {
		Channel  string `json:"channel"`
		Username string `json:"username"`
		Text     string `json:"text"`
	}{s.Channel, s.Username, msg}

	// data json encoded
	payload, err := json.Marshal(data)
	if err != nil {
		return err
	}

	// send payload
	url := fmt.Sprintf(slackEndpoint, s.Team, s.Token)
	go sendJson(url, payload)

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes 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 server

import (
	"fmt"

	"golang.org/x/net/context"
	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
)

// ImageStatus returns the status of the image, returns nil if the image isn't present.
// TODO(random-liu): We should change CRI to distinguish image id and image spec. (See
// kubernetes/kubernetes#46255)
func (c *criContainerdService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) {
	image, err := c.localResolve(ctx, r.GetImage().GetImage())
	if err != nil {
		return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err)
	}
	if image == nil {
		// return empty without error when image not found.
		return &runtime.ImageStatusResponse{}, nil
	}
	// TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot
	// doesn't exist?
	runtimeImage := &runtime.Image{
		Id:          image.ID,
		RepoTags:    image.RepoTags,
		RepoDigests: image.RepoDigests,
		Size_:       uint64(image.Size),
	}
	uid, username := getUserFromImage(image.Config.User)
	if uid != nil {
		runtimeImage.Uid = &runtime.Int64Value{Value: *uid}
	}
	runtimeImage.Username = username

	// TODO(mikebrow): write a ImageMetadata to runtime.Image converter
	return &runtime.ImageStatusResponse{Image: runtimeImage}, nil
}
<commit_msg>adding info map for verbose image status<commit_after>/*
Copyright 2017 The Kubernetes 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 server

import (
	"encoding/json"
	"fmt"

	"github.com/golang/glog"
	"golang.org/x/net/context"
	"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"

	content "github.com/containerd/containerd/content"
	imagestore "github.com/kubernetes-incubator/cri-containerd/pkg/store/image"
	imagespec "github.com/opencontainers/image-spec/specs-go/v1"
)

// ImageStatus returns the status of the image, returns nil if the image isn't present.
// TODO(random-liu): We should change CRI to distinguish image id and image spec. (See
// kubernetes/kubernetes#46255)
func (c *criContainerdService) ImageStatus(ctx context.Context, r *runtime.ImageStatusRequest) (*runtime.ImageStatusResponse, error) {
	image, err := c.localResolve(ctx, r.GetImage().GetImage())
	if err != nil {
		return nil, fmt.Errorf("can not resolve %q locally: %v", r.GetImage().GetImage(), err)
	}
	if image == nil {
		// return empty without error when image not found.
		return &runtime.ImageStatusResponse{}, nil
	}
	// TODO(random-liu): [P0] Make sure corresponding snapshot exists. What if snapshot
	// doesn't exist?

	runtimeImage := toCRIRuntimeImage(image)
	info, err := c.toCRIImageInfo(ctx, image, r.GetVerbose())
	if err != nil {
		return nil, fmt.Errorf("failed to generate image info: %v", err)
	}

	return &runtime.ImageStatusResponse{
		Image: runtimeImage,
		Info:  info,
	}, nil
}

// toCRIRuntimeImage converts internal image object to CRI runtime.Image.
func toCRIRuntimeImage(image *imagestore.Image) *runtime.Image {
	runtimeImage := &runtime.Image{
		Id:          image.ID,
		RepoTags:    image.RepoTags,
		RepoDigests: image.RepoDigests,
		Size_:       uint64(image.Size),
	}
	uid, username := getUserFromImage(image.Config.User)
	if uid != nil {
		runtimeImage.Uid = &runtime.Int64Value{Value: *uid}
	}
	runtimeImage.Username = username

	return runtimeImage
}

// TODO (mikebrow): discuss moving this struct and / or constants for info map for some or all of these fields to CRI
type verboseImageInfo struct {
	Config             *imagespec.ImageConfig `json:"config"`
	ConfigDescriptor   imagespec.Descriptor   `json:"configDescriptor"`
	ManifestDescriptor imagespec.Descriptor   `json:"manifestDescriptor"`
	LayerInfo          []content.Info         `json:"layerInfo"`
}

// toCRIImageInfo converts internal image object information to CRI image status response info map.
func (c *criContainerdService) toCRIImageInfo(ctx context.Context, image *imagestore.Image, verbose bool) (map[string]string, error) {
	if !verbose {
		return nil, nil
	}

	info := make(map[string]string)
	i := image.Image
	descriptor, err := i.Config(ctx)
	if err != nil {
		glog.Errorf("Failed to get image config %q: %v", image.ID, err)
	} // fallthrough

	targetDescriptor := i.Target()
	var dia []content.Info
	digests, err := i.RootFS(ctx)
	if err != nil {
		glog.Errorf("Failed to get target digests %q: %v", i.Name(), err)
	} else {
		dia = make([]content.Info, len(digests))
		for i, d := range digests {
			di, err := c.client.ContentStore().Info(ctx, d)
			if err == nil {
				dia[i] = di
			}
		}
	}

	imi := &verboseImageInfo{
		Config:             image.Config,
		ConfigDescriptor:   descriptor,
		ManifestDescriptor: targetDescriptor,
		LayerInfo:          dia,
	}

	m, err := json.Marshal(imi)
	if err == nil {
		info["info"] = string(m)
	} else {
		glog.Errorf("failed to marshal info %v: %v", imi, err)
		info["info"] = err.Error()
	}

	return info, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 wait

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func TestUntil(t *testing.T) {
	ch := make(chan struct{})
	close(ch)
	Until(func() {
		t.Fatal("should not have been invoked")
	}, 0, ch)

	ch = make(chan struct{})
	called := make(chan struct{})
	go func() {
		Until(func() {
			called <- struct{}{}
		}, 0, ch)
		close(called)
	}()
	<-called
	close(ch)
	<-called
}

func TestUntilReturnsImmediately(t *testing.T) {
	now := time.Now()
	ch := make(chan struct{})
	Until(func() {
		close(ch)
	}, 30*time.Second, ch)
	if now.Add(25 * time.Second).Before(time.Now()) {
		t.Errorf("Until did not return immediately when the stop chan was closed inside the func")
	}
}

func TestExponentialBackoff(t *testing.T) {
	opts := Backoff{Factor: 1.0, Steps: 3}

	// waits up to steps
	i := 0
	err := ExponentialBackoff(opts, func() (bool, error) {
		i++
		return false, nil
	})
	if err != ErrWaitTimeout || i != opts.Steps {
		t.Errorf("unexpected error: %v", err)
	}

	// returns immediately
	i = 0
	err = ExponentialBackoff(opts, func() (bool, error) {
		i++
		return true, nil
	})
	if err != nil || i != 1 {
		t.Errorf("unexpected error: %v", err)
	}

	// returns immediately on error
	testErr := fmt.Errorf("some other error")
	err = ExponentialBackoff(opts, func() (bool, error) {
		return false, testErr
	})
	if err != testErr {
		t.Errorf("unexpected error: %v", err)
	}

	// invoked multiple times
	i = 1
	err = ExponentialBackoff(opts, func() (bool, error) {
		if i < opts.Steps {
			i++
			return false, nil
		}
		return true, nil
	})
	if err != nil || i != opts.Steps {
		t.Errorf("unexpected error: %v", err)
	}
}

func TestPoller(t *testing.T) {
	done := make(chan struct{})
	defer close(done)
	w := poller(time.Millisecond, 2*time.Millisecond)
	ch := w(done)
	count := 0
DRAIN:
	for {
		select {
		case _, open := <-ch:
			if !open {
				break DRAIN
			}
			count++
		case <-time.After(ForeverTestTimeout):
			t.Errorf("unexpected timeout after poll")
		}
	}
	if count > 3 {
		t.Errorf("expected up to three values, got %d", count)
	}
}

type fakePoller struct {
	max  int
	used int32 // accessed with atomics
	wg   sync.WaitGroup
}

func fakeTicker(max int, used *int32, doneFunc func()) WaitFunc {
	return func(done <-chan struct{}) <-chan struct{} {
		ch := make(chan struct{})
		go func() {
			defer doneFunc()
			defer close(ch)
			for i := 0; i < max; i++ {
				select {
				case ch <- struct{}{}:
				case <-done:
					return
				}
				if used != nil {
					atomic.AddInt32(used, 1)
				}
			}
		}()
		return ch
	}
}

func (fp *fakePoller) GetWaitFunc() WaitFunc {
	fp.wg.Add(1)
	return fakeTicker(fp.max, &fp.used, fp.wg.Done)
}

func TestPoll(t *testing.T) {
	invocations := 0
	f := ConditionFunc(func() (bool, error) {
		invocations++
		return true, nil
	})
	fp := fakePoller{max: 1}
	if err := pollInternal(fp.GetWaitFunc(), f); err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	fp.wg.Wait()
	if invocations != 1 {
		t.Errorf("Expected exactly one invocation, got %d", invocations)
	}
	used := atomic.LoadInt32(&fp.used)
	if used != 1 {
		t.Errorf("Expected exactly one tick, got %d", used)
	}
}

func TestPollError(t *testing.T) {
	expectedError := errors.New("Expected error")
	f := ConditionFunc(func() (bool, error) {
		return false, expectedError
	})
	fp := fakePoller{max: 1}
	if err := pollInternal(fp.GetWaitFunc(), f); err == nil || err != expectedError {
		t.Fatalf("Expected error %v, got none %v", expectedError, err)
	}
	fp.wg.Wait()
	used := atomic.LoadInt32(&fp.used)
	if used != 1 {
		t.Errorf("Expected exactly one tick, got %d", used)
	}
}

func TestPollImmediate(t *testing.T) {
	invocations := 0
	f := ConditionFunc(func() (bool, error) {
		invocations++
		return true, nil
	})
	fp := fakePoller{max: 0}
	if err := pollImmediateInternal(fp.GetWaitFunc(), f); err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	// We don't need to wait for fp.wg, as pollImmediate shouldn't call WaitFunc at all.
	if invocations != 1 {
		t.Errorf("Expected exactly one invocation, got %d", invocations)
	}
	used := atomic.LoadInt32(&fp.used)
	if used != 0 {
		t.Errorf("Expected exactly zero ticks, got %d", used)
	}
}

func TestPollImmediateError(t *testing.T) {
	expectedError := errors.New("Expected error")
	f := ConditionFunc(func() (bool, error) {
		return false, expectedError
	})
	fp := fakePoller{max: 0}
	if err := pollImmediateInternal(fp.GetWaitFunc(), f); err == nil || err != expectedError {
		t.Fatalf("Expected error %v, got none %v", expectedError, err)
	}
	// We don't need to wait for fp.wg, as pollImmediate shouldn't call WaitFunc at all.
	used := atomic.LoadInt32(&fp.used)
	if used != 0 {
		t.Errorf("Expected exactly zero ticks, got %d", used)
	}
}

func TestPollForever(t *testing.T) {
	ch := make(chan struct{})
	done := make(chan struct{}, 1)
	complete := make(chan struct{})
	go func() {
		f := ConditionFunc(func() (bool, error) {
			ch <- struct{}{}
			select {
			case <-done:
				return true, nil
			default:
			}
			return false, nil
		})

		if err := PollInfinite(time.Microsecond, f); err != nil {
			t.Fatalf("unexpected error %v", err)
		}

		close(ch)
		complete <- struct{}{}
	}()

	// ensure the condition is opened
	<-ch

	// ensure channel sends events
	for i := 0; i < 10; i++ {
		select {
		case _, open := <-ch:
			if !open {
				t.Fatalf("did not expect channel to be closed")
			}
		case <-time.After(ForeverTestTimeout):
			t.Fatalf("channel did not return at least once within the poll interval")
		}
	}

	// at most one poll notification should be sent once we return from the condition
	done <- struct{}{}
	go func() {
		for i := 0; i < 2; i++ {
			_, open := <-ch
			if !open {
				return
			}
		}
		t.Fatalf("expected closed channel after two iterations")
	}()
	<-complete
}

func TestWaitFor(t *testing.T) {
	var invocations int
	testCases := map[string]struct {
		F       ConditionFunc
		Ticks   int
		Invoked int
		Err     bool
	}{
		"invoked once": {
			ConditionFunc(func() (bool, error) {
				invocations++
				return true, nil
			}),
			2,
			1,
			false,
		},
		"invoked and returns a timeout": {
			ConditionFunc(func() (bool, error) {
				invocations++
				return false, nil
			}),
			2,
			3, // the contract of WaitFor() says the func is called once more at the end of the wait
			true,
		},
		"returns immediately on error": {
			ConditionFunc(func() (bool, error) {
				invocations++
				return false, errors.New("test")
			}),
			2,
			1,
			true,
		},
	}
	for k, c := range testCases {
		invocations = 0
		ticker := fakeTicker(c.Ticks, nil, func() {})
		err := func() error {
			done := make(chan struct{})
			defer close(done)
			return WaitFor(ticker, c.F, done)
		}()
		switch {
		case c.Err && err == nil:
			t.Errorf("%s: Expected error, got nil", k)
			continue
		case !c.Err && err != nil:
			t.Errorf("%s: Expected no error, got: %#v", k, err)
			continue
		}
		if invocations != c.Invoked {
			t.Errorf("%s: Expected %d invocations, got %d", k, c.Invoked, invocations)
		}
	}
}

func TestWaitForWithDelay(t *testing.T) {
	done := make(chan struct{})
	defer close(done)
	WaitFor(poller(time.Millisecond, ForeverTestTimeout), func() (bool, error) {
		time.Sleep(10 * time.Millisecond)
		return true, nil
	}, done)
	// If polling goroutine doesn't see the done signal it will leak timers.
	select {
	case done <- struct{}{}:
	case <-time.After(ForeverTestTimeout):
		t.Errorf("expected an ack of the done signal.")
	}
}
<commit_msg>Add unit test for JitterUntil<commit_after>/*
Copyright 2014 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 wait

import (
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

func TestUntil(t *testing.T) {
	ch := make(chan struct{})
	close(ch)
	Until(func() {
		t.Fatal("should not have been invoked")
	}, 0, ch)

	ch = make(chan struct{})
	called := make(chan struct{})
	go func() {
		Until(func() {
			called <- struct{}{}
		}, 0, ch)
		close(called)
	}()
	<-called
	close(ch)
	<-called
}

func TestUntilReturnsImmediately(t *testing.T) {
	now := time.Now()
	ch := make(chan struct{})
	Until(func() {
		close(ch)
	}, 30*time.Second, ch)
	if now.Add(25 * time.Second).Before(time.Now()) {
		t.Errorf("Until did not return immediately when the stop chan was closed inside the func")
	}
}

func TestJitterUntil(t *testing.T) {
	ch := make(chan struct{})
	// if a channel is closed JitterUntil never calls function f
	// and returns imidiatelly
	close(ch)
	JitterUntil(func() {
		t.Fatal("should not have been invoked")
	}, 0, 1.0, ch)

	ch = make(chan struct{})
	called := make(chan struct{})
	go func() {
		JitterUntil(func() {
			called <- struct{}{}
		}, 0, 1.0, ch)
		close(called)
	}()
	<-called
	close(ch)
	<-called
}

func TestJitterUntilReturnsImmediately(t *testing.T) {
	now := time.Now()
	ch := make(chan struct{})
	JitterUntil(func() {
		close(ch)
	}, 30*time.Second, 1.0, ch)
	if now.Add(25 * time.Second).Before(time.Now()) {
		t.Errorf("JitterUntil did not return immediately when the stop chan was closed inside the func")
	}
}

func TestJitterUntilNegativeFactor(t *testing.T) {
	now := time.Now()
	ch := make(chan struct{})
	called := make(chan struct{})
	received := make(chan struct{})
	go func() {
		JitterUntil(func() {
			called <- struct{}{}
			<-received
		}, time.Second, -30.0, ch)
	}()
	// first loop
	<-called
	received <- struct{}{}
	// second loop
	<-called
	close(ch)
	received <- struct{}{}

	// it should take at most 2 seconds + some overhead, not 3
	if now.Add(3 * time.Second).Before(time.Now()) {
		t.Errorf("JitterUntil did not returned after predefined period with negative jitter factor when the stop chan was closed inside the func")
	}

}

func TestExponentialBackoff(t *testing.T) {
	opts := Backoff{Factor: 1.0, Steps: 3}

	// waits up to steps
	i := 0
	err := ExponentialBackoff(opts, func() (bool, error) {
		i++
		return false, nil
	})
	if err != ErrWaitTimeout || i != opts.Steps {
		t.Errorf("unexpected error: %v", err)
	}

	// returns immediately
	i = 0
	err = ExponentialBackoff(opts, func() (bool, error) {
		i++
		return true, nil
	})
	if err != nil || i != 1 {
		t.Errorf("unexpected error: %v", err)
	}

	// returns immediately on error
	testErr := fmt.Errorf("some other error")
	err = ExponentialBackoff(opts, func() (bool, error) {
		return false, testErr
	})
	if err != testErr {
		t.Errorf("unexpected error: %v", err)
	}

	// invoked multiple times
	i = 1
	err = ExponentialBackoff(opts, func() (bool, error) {
		if i < opts.Steps {
			i++
			return false, nil
		}
		return true, nil
	})
	if err != nil || i != opts.Steps {
		t.Errorf("unexpected error: %v", err)
	}
}

func TestPoller(t *testing.T) {
	done := make(chan struct{})
	defer close(done)
	w := poller(time.Millisecond, 2*time.Millisecond)
	ch := w(done)
	count := 0
DRAIN:
	for {
		select {
		case _, open := <-ch:
			if !open {
				break DRAIN
			}
			count++
		case <-time.After(ForeverTestTimeout):
			t.Errorf("unexpected timeout after poll")
		}
	}
	if count > 3 {
		t.Errorf("expected up to three values, got %d", count)
	}
}

type fakePoller struct {
	max  int
	used int32 // accessed with atomics
	wg   sync.WaitGroup
}

func fakeTicker(max int, used *int32, doneFunc func()) WaitFunc {
	return func(done <-chan struct{}) <-chan struct{} {
		ch := make(chan struct{})
		go func() {
			defer doneFunc()
			defer close(ch)
			for i := 0; i < max; i++ {
				select {
				case ch <- struct{}{}:
				case <-done:
					return
				}
				if used != nil {
					atomic.AddInt32(used, 1)
				}
			}
		}()
		return ch
	}
}

func (fp *fakePoller) GetWaitFunc() WaitFunc {
	fp.wg.Add(1)
	return fakeTicker(fp.max, &fp.used, fp.wg.Done)
}

func TestPoll(t *testing.T) {
	invocations := 0
	f := ConditionFunc(func() (bool, error) {
		invocations++
		return true, nil
	})
	fp := fakePoller{max: 1}
	if err := pollInternal(fp.GetWaitFunc(), f); err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	fp.wg.Wait()
	if invocations != 1 {
		t.Errorf("Expected exactly one invocation, got %d", invocations)
	}
	used := atomic.LoadInt32(&fp.used)
	if used != 1 {
		t.Errorf("Expected exactly one tick, got %d", used)
	}
}

func TestPollError(t *testing.T) {
	expectedError := errors.New("Expected error")
	f := ConditionFunc(func() (bool, error) {
		return false, expectedError
	})
	fp := fakePoller{max: 1}
	if err := pollInternal(fp.GetWaitFunc(), f); err == nil || err != expectedError {
		t.Fatalf("Expected error %v, got none %v", expectedError, err)
	}
	fp.wg.Wait()
	used := atomic.LoadInt32(&fp.used)
	if used != 1 {
		t.Errorf("Expected exactly one tick, got %d", used)
	}
}

func TestPollImmediate(t *testing.T) {
	invocations := 0
	f := ConditionFunc(func() (bool, error) {
		invocations++
		return true, nil
	})
	fp := fakePoller{max: 0}
	if err := pollImmediateInternal(fp.GetWaitFunc(), f); err != nil {
		t.Fatalf("unexpected error %v", err)
	}
	// We don't need to wait for fp.wg, as pollImmediate shouldn't call WaitFunc at all.
	if invocations != 1 {
		t.Errorf("Expected exactly one invocation, got %d", invocations)
	}
	used := atomic.LoadInt32(&fp.used)
	if used != 0 {
		t.Errorf("Expected exactly zero ticks, got %d", used)
	}
}

func TestPollImmediateError(t *testing.T) {
	expectedError := errors.New("Expected error")
	f := ConditionFunc(func() (bool, error) {
		return false, expectedError
	})
	fp := fakePoller{max: 0}
	if err := pollImmediateInternal(fp.GetWaitFunc(), f); err == nil || err != expectedError {
		t.Fatalf("Expected error %v, got none %v", expectedError, err)
	}
	// We don't need to wait for fp.wg, as pollImmediate shouldn't call WaitFunc at all.
	used := atomic.LoadInt32(&fp.used)
	if used != 0 {
		t.Errorf("Expected exactly zero ticks, got %d", used)
	}
}

func TestPollForever(t *testing.T) {
	ch := make(chan struct{})
	done := make(chan struct{}, 1)
	complete := make(chan struct{})
	go func() {
		f := ConditionFunc(func() (bool, error) {
			ch <- struct{}{}
			select {
			case <-done:
				return true, nil
			default:
			}
			return false, nil
		})

		if err := PollInfinite(time.Microsecond, f); err != nil {
			t.Fatalf("unexpected error %v", err)
		}

		close(ch)
		complete <- struct{}{}
	}()

	// ensure the condition is opened
	<-ch

	// ensure channel sends events
	for i := 0; i < 10; i++ {
		select {
		case _, open := <-ch:
			if !open {
				t.Fatalf("did not expect channel to be closed")
			}
		case <-time.After(ForeverTestTimeout):
			t.Fatalf("channel did not return at least once within the poll interval")
		}
	}

	// at most one poll notification should be sent once we return from the condition
	done <- struct{}{}
	go func() {
		for i := 0; i < 2; i++ {
			_, open := <-ch
			if !open {
				return
			}
		}
		t.Fatalf("expected closed channel after two iterations")
	}()
	<-complete
}

func TestWaitFor(t *testing.T) {
	var invocations int
	testCases := map[string]struct {
		F       ConditionFunc
		Ticks   int
		Invoked int
		Err     bool
	}{
		"invoked once": {
			ConditionFunc(func() (bool, error) {
				invocations++
				return true, nil
			}),
			2,
			1,
			false,
		},
		"invoked and returns a timeout": {
			ConditionFunc(func() (bool, error) {
				invocations++
				return false, nil
			}),
			2,
			3, // the contract of WaitFor() says the func is called once more at the end of the wait
			true,
		},
		"returns immediately on error": {
			ConditionFunc(func() (bool, error) {
				invocations++
				return false, errors.New("test")
			}),
			2,
			1,
			true,
		},
	}
	for k, c := range testCases {
		invocations = 0
		ticker := fakeTicker(c.Ticks, nil, func() {})
		err := func() error {
			done := make(chan struct{})
			defer close(done)
			return WaitFor(ticker, c.F, done)
		}()
		switch {
		case c.Err && err == nil:
			t.Errorf("%s: Expected error, got nil", k)
			continue
		case !c.Err && err != nil:
			t.Errorf("%s: Expected no error, got: %#v", k, err)
			continue
		}
		if invocations != c.Invoked {
			t.Errorf("%s: Expected %d invocations, got %d", k, c.Invoked, invocations)
		}
	}
}

func TestWaitForWithDelay(t *testing.T) {
	done := make(chan struct{})
	defer close(done)
	WaitFor(poller(time.Millisecond, ForeverTestTimeout), func() (bool, error) {
		time.Sleep(10 * time.Millisecond)
		return true, nil
	}, done)
	// If polling goroutine doesn't see the done signal it will leak timers.
	select {
	case done <- struct{}{}:
	case <-time.After(ForeverTestTimeout):
		t.Errorf("expected an ack of the done signal.")
	}
}
<|endoftext|>


<commit_before>package profiling

import (
	"io/ioutil"
	"os"

	"cloud.google.com/go/profiler"
	"github.com/pkg/errors"
	"github.com/urfave/cli" // formerly known as github.com/codegangsta/cli
)

type GoogleProfilerFlag struct{}

// Apply starts the Google Cloud profiling agent.
//
// This allows us to use Google's nifty profiling UI to view live production
// profiling. https://cloud.google.com/profiler
func (f GoogleProfilerFlag) Set(projectID string) error {
	creds := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS_CONTENT")
	credsPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")

	if creds != "" {
		// kludge alert! Our 12-factor way of deploying applications can't pass
		// configuration to programs except through environment variables, but
		// the GCP SDK expects the credentials to be in a file at the path of
		// $GOOGLE_APPLICATION_CREDENTIALS. So, we put the actual credentials
		// in a different environment variable, and write them to a file.
		//
		// If $GOOGLE_APPLICATION_CREDENTIALS_CONTENT isn't set then we do
		// nothing, since we might be running in an environment where the
		// credentials might be discovered through other means.

		if err := ioutil.WriteFile(credsPath, []byte(creds), 0600); err != nil {
			return errors.Wrap(err, "could not write $GOOGLE_APPLICATION_CREDENTIALS_CONTENT to "+credsPath)
		}
	}

	cfg := profiler.Config{
		Service:        os.Getenv("EMPIRE_APPNAME") + "." + os.Getenv("EMPIRE_PROCESS"),
		ServiceVersion: os.Getenv("EMPIRE_RELEASE"),
		ProjectID:      projectID,
	}

	return profiler.Start(cfg)
}

func (f GoogleProfilerFlag) String() string {
	return ""
}

// NewCliFlag returns a flag that will enable Cloud Profiler
func NewCliFlag() cli.Flag {
	return cli.GenericFlag{
		Name:   "google-profiler-project",
		Value:  GoogleProfilerFlag{},
		Usage:  "The Google Project ID for submitting Cloud Profiler data",
		EnvVar: "GOOGLE_PROFILER_PROJECT",
	}
}
<commit_msg>Google Cloud Profiler: Never error on startup<commit_after>package profiling

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"cloud.google.com/go/profiler"
	"github.com/pkg/errors"
	"github.com/urfave/cli" // formerly known as github.com/codegangsta/cli
)

type GoogleProfilerFlag struct{}

// Apply starts the Google Cloud profiling agent.
//
// This allows us to use Google's nifty profiling UI to view live production
// profiling. https://cloud.google.com/profiler
func (f GoogleProfilerFlag) Set(projectID string) error {
	// Get all of the empire configs from the environment, and make sure they're
	// set.
	empire_appname := os.Getenv("EMPIRE_APPNAME")
	if empire_appname == "" {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: missing/blank required env var EMPIRE_APPNAME",
				),
			),
		)

		return nil
	}

	empire_process := os.Getenv("EMPIRE_PROCESS")
	if empire_process == "" {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: missing/blank required env var EMPIRE_PROCESS",
				),
			),
		)

		return nil
	}

	empire_release := os.Getenv("EMPIRE_RELEASE")
	if empire_release == "" {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: missing/blank required env var EMPIRE_RELEASE",
				),
			),
		)

		return nil
	}

	creds := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS_CONTENT")
	if creds == "" {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: missing/blank required env var GOOGLE_APPLICATION_CREDENTIALS_CONTENT",
				),
			),
		)

		return nil
	}

	credsPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
	if credsPath == "" {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: missing/blank required env var GOOGLE_APPLICATION_CREDENTIALS",
				),
			),
		)

		return nil
	}

	// kludge alert! Our 12-factor way of deploying applications can't pass
	// configuration to programs except through environment variables, but
	// the GCP SDK expects the credentials to be in a file at the path of
	// $GOOGLE_APPLICATION_CREDENTIALS. So, we put the actual credentials
	// in a different environment variable, and write them to a file.
	//
	// If $GOOGLE_APPLICATION_CREDENTIALS_CONTENT isn't set then we do
	// nothing, since we might be running in an environment where the
	// credentials might be discovered through other means.
	if err := ioutil.WriteFile(credsPath, []byte(creds), 0600); err != nil {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: could not write $GOOGLE_APPLICATION_CREDENTIALS_CONTENT to %s: %s",
					credsPath,
					err,
				),
			),
		)

		return nil
	}

	cfg := profiler.Config{
		Service:        fmt.Sprintf("%s.%s", empire_appname, empire_process),
		ServiceVersion: empire_release,
		ProjectID:      projectID,
	}

	err := profiler.Start(cfg)
	if err != nil {
		log.Printf(
			"%+v",
			errors.WithStack(
				fmt.Errorf(
					"pkg/profiling: GoogleProfilerFlag.Set: error starting profiler: %s",
					err,
				),
			),
		)
	}

	return nil
}

func (f GoogleProfilerFlag) String() string {
	return ""
}

// NewCliFlag returns a flag that will enable Cloud Profiler
func NewCliFlag() cli.Flag {
	return cli.GenericFlag{
		Name:   "google-profiler-project",
		Value:  GoogleProfilerFlag{},
		Usage:  "The Google Project ID for submitting Cloud Profiler data",
		EnvVar: "GOOGLE_PROFILER_PROJECT",
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2021 Google LLC

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

    https://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 deploybot

import (
	"context"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/deploybot/bot"
	"github.com/GoogleCloudPlatform/deploybot/gcpclouddeploy"
)

var (
	chatToken string
	channel   string
	chatApp   string
	theBot    bot.Bot
)

// init is used to make it easier to access secrets and to adapt the code
// to use other chat apps.
func init() {

	var found, found2, found3 bool

	chatToken, found = os.LookupEnv("TOKEN")
	channel, found2 = os.LookupEnv("CHANNEL")
	if !found || !found2 {
		log.Fatalf("please define the TOKEN and CHANNEL env vars")
	}

	// Customise this with your own chat app implementation if not using Slack or Google Chat.
	chatApp, found3 = os.LookupEnv("CHATAPP")

	if !found3 || chatApp == "slack" { // Slack by default
		theBot = &bot.SlackAdapter{BotToken: chatToken}
	}
	if chatApp == "google" {
		theBot = &bot.GChatAdapter{BotToken: chatToken}
	}
}

// CloudFuncPubSubCDOps is an entry point function for Google Cloud Functions
// which is triggered by a PubSub notification using Cloud Deploy's "clouddeploy-operations" topic
func CloudFuncPubSubCDOps(ctx context.Context, m gcpclouddeploy.OpsMessage) error {

	fmt.Printf("{\"message\": \"received: %s | status: %s\", \"severity\":\"info\"}\n", m.Attributes["ResourceType"], m.Attributes["Action"])

	resp, err := theBot.SendMessage(channel, m.Attributes)
	resp = strings.ReplaceAll(resp, "\"", "'")
	if err != nil {
		fmt.Printf("{\"message\":\"error posting to Chat App: %s\", \"severity\":\"error\"}\n", err)
	} else {
		fmt.Printf("{\"message\": \"success posting to Chat App: %s\", \"severity\": \"info\"}\n", resp)
	}

	// no need to ack as per comment box at
	// https://cloud.google.com/functions/docs/calling/pubsub#sample_code
	return nil
}
<commit_msg>corrected module path<commit_after>/*
Copyright 2021 Google LLC

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

    https://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 deploybot

import (
	"context"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/GoogleCloudPlatform/cloud-deploy-chatbot/bot"
	"github.com/GoogleCloudPlatform/cloud-deploy-chatbot/gcpclouddeploy"
)

var (
	chatToken string
	channel   string
	chatApp   string
	theBot    bot.Bot
)

// init is used to make it easier to access secrets and to adapt the code
// to use other chat apps.
func init() {

	var found, found2, found3 bool

	chatToken, found = os.LookupEnv("TOKEN")
	channel, found2 = os.LookupEnv("CHANNEL")
	if !found || !found2 {
		log.Fatalf("please define the TOKEN and CHANNEL env vars")
	}

	// Customise this with your own chat app implementation if not using Slack or Google Chat.
	chatApp, found3 = os.LookupEnv("CHATAPP")

	if !found3 || chatApp == "slack" { // Slack by default
		theBot = &bot.SlackAdapter{BotToken: chatToken}
	}
	if chatApp == "google" {
		theBot = &bot.GChatAdapter{BotToken: chatToken}
	}
}

// CloudFuncPubSubCDOps is an entry point function for Google Cloud Functions
// which is triggered by a PubSub notification using Cloud Deploy's "clouddeploy-operations" topic
func CloudFuncPubSubCDOps(ctx context.Context, m gcpclouddeploy.OpsMessage) error {

	fmt.Printf("{\"message\": \"received: %s | status: %s\", \"severity\":\"info\"}\n", m.Attributes["ResourceType"], m.Attributes["Action"])

	resp, err := theBot.SendMessage(channel, m.Attributes)
	resp = strings.ReplaceAll(resp, "\"", "'")
	if err != nil {
		fmt.Printf("{\"message\":\"error posting to Chat App: %s\", \"severity\":\"error\"}\n", err)
	} else {
		fmt.Printf("{\"message\": \"success posting to Chat App: %s\", \"severity\": \"info\"}\n", resp)
	}

	// no need to ack as per comment box at
	// https://cloud.google.com/functions/docs/calling/pubsub#sample_code
	return nil
}
<|endoftext|>


<commit_before>package sqlx

import (
	"flag"
	"log"
	"net/url"
	"os"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

var db *sqlx.DB
var Settings *Vertigo

var sqlite3 = `
CREATE TABLE users (
    id integer NOT NULL PRIMARY KEY,
    name varchar(255) NOT NULL,
    recovery char(36) NOT NULL DEFAULT "",
    digest blob NOT NULL,
    email varchar(255) NOT NULL UNIQUE,
    location varchar(255) NOT NULL DEFAULT "UTC"
);

CREATE TABLE posts (
    id integer NOT NULL PRIMARY KEY,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    markdown text NOT NULL,
    slug varchar(255) NOT NULL UNIQUE,
    author integer NOT NULL,
    excerpt varchar(255) NOT NULL,
    viewcount integer unsigned NOT NULL DEFAULT 0,
    published bool NOT NULL DEFAULT false,
    created integer unsigned NOT NULL,
    updated integer unsigned NOT NULL,
    timeoffset integer NOT NULL DEFAULT 0
);

CREATE TABLE settings (
    id integer NOT NULL PRIMARY KEY DEFAULT 1,
    name varchar(255) NOT NULL,
    hostname varchar(255) NOT NULL,
    firstrun bool NOT NULL DEFAULT true,
    cookiehash string NOT NULL,
    allowregistrations bool NOT NULL DEFAULT true,
    description varchar(255) NOT NULL,
    mailerlogin varchar(255),
    mailerport integer unsigned NOT NULL DEFAULT 587,
    mailerpassword varchar(255),
    mailerhostname varchar(255)
);`

var postgres = `
CREATE TABLE "users" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(255) NOT NULL,
    "recovery" char(36) NOT NULL DEFAULT '',
    "digest" bytea NOT NULL,
    "email" varchar(255) NOT NULL UNIQUE,
    "location" varchar(255) NOT NULL DEFAULT 'UTC'
);

CREATE TABLE "posts" (
    "id" serial NOT NULL PRIMARY KEY,
    "title" varchar(255) NOT NULL,
    "content" text NOT NULL,
    "markdown" text NOT NULL,
    "slug" varchar(255) NOT NULL UNIQUE,
    "author" integer NOT NULL,
    "excerpt" varchar(255) NOT NULL,
    "viewcount" integer NOT NULL DEFAULT '0',
    "published" bool NOT NULL DEFAULT false,
    "created" integer NOT NULL,
    "updated" integer NOT NULL,
    "timeoffset" integer NOT NULL DEFAULT '0'
);

CREATE TABLE "settings" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(255) NOT NULL,
    "hostname" varchar(255) NOT NULL,
    "firstrun" bool NOT NULL DEFAULT true,
    "cookiehash" bytea NOT NULL,
    "allowregistrations" bool NOT NULL DEFAULT true,
    "description" varchar(255) NOT NULL,
    "mailerlogin" varchar(255),
    "mailerport" integer NOT NULL DEFAULT 587,
    "mailerpassword" varchar(255),
    "mailerhostname" varchar(255)
);`

var mysql = `
CREATE DATABASE vertigo;
USE vertigo;

CREATE TABLE IF NOT EXISTS users (
    id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name varchar(255) NOT NULL,
    recovery char(36) NOT NULL DEFAULT "",
    digest blob NOT NULL,
    email varchar(255) NOT NULL UNIQUE,
    location varchar(255) NOT NULL DEFAULT "UTC"
);

CREATE TABLE IF NOT EXISTS posts (
    id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    markdown text NOT NULL,
    slug varchar(255) NOT NULL,
    author integer NOT NULL,
    excerpt varchar(255) NOT NULL,
    viewcount integer unsigned NOT NULL DEFAULT 0,
    published bool NOT NULL DEFAULT false,
    created integer unsigned NOT NULL,
    updated integer unsigned NOT NULL,
    timeoffset integer NOT NULL DEFAULT 0
);`

func Drop() {
	db.MustExec("DROP TABLE users")
	db.MustExec("DROP TABLE posts")
	db.MustExec("DROP TABLE settings")
	os.Remove("vertigo.db")
}

var Driver = flag.String("driver", "sqlite3", "Database driver to use (sqlite3, mysql, postgres)")
var Source = flag.String("source", "vertigo.db", "Database data source")

func connect(driver, source string) {
	conn, err := sqlx.Connect(driver, source)
	if err != nil {
		log.Fatal("sqlx connect:", err)
	}

	var schema string
	switch driver {
	case "sqlite3":
		schema = sqlite3
	case "mysql":
		schema = mysql
	case "postgres":
		schema = postgres
	}

	conn.MustExec(schema)

	log.Println("sqlx: using", driver)

	db = conn

	Settings = VertigoSettings()
}

func init() {

	flag.Parse()

	if os.Getenv("DATABASE_URL") != "" {
		u, err := url.Parse(os.Getenv("DATABASE_URL"))
		if err != nil {
			log.Fatal("database url parameter could not be parsed")
		}
		if u.Scheme != "postgres" && u.Scheme != "mysql" {
			log.Fatal("unsupported database type")
		}
		connect(u.Scheme, os.Getenv("DATABASE_URL"))
		return
	}

	connect(*Driver, *Source)
}
<commit_msg>disable mysql support<commit_after>package sqlx

import (
	"flag"
	"log"
	"net/url"
	"os"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

var db *sqlx.DB
var Settings *Vertigo

var sqlite3 = `
CREATE TABLE users (
    id integer NOT NULL PRIMARY KEY,
    name varchar(255) NOT NULL,
    recovery char(36) NOT NULL DEFAULT "",
    digest blob NOT NULL,
    email varchar(255) NOT NULL UNIQUE,
    location varchar(255) NOT NULL DEFAULT "UTC"
);

CREATE TABLE posts (
    id integer NOT NULL PRIMARY KEY,
    title varchar(255) NOT NULL,
    content text NOT NULL,
    markdown text NOT NULL,
    slug varchar(255) NOT NULL UNIQUE,
    author integer NOT NULL,
    excerpt varchar(255) NOT NULL,
    viewcount integer unsigned NOT NULL DEFAULT 0,
    published bool NOT NULL DEFAULT false,
    created integer unsigned NOT NULL,
    updated integer unsigned NOT NULL,
    timeoffset integer NOT NULL DEFAULT 0
);

CREATE TABLE settings (
    id integer NOT NULL PRIMARY KEY DEFAULT 1,
    name varchar(255) NOT NULL,
    hostname varchar(255) NOT NULL,
    firstrun bool NOT NULL DEFAULT true,
    cookiehash string NOT NULL,
    allowregistrations bool NOT NULL DEFAULT true,
    description varchar(255) NOT NULL,
    mailerlogin varchar(255),
    mailerport integer unsigned NOT NULL DEFAULT 587,
    mailerpassword varchar(255),
    mailerhostname varchar(255)
);`

var postgres = `
CREATE TABLE "users" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(255) NOT NULL,
    "recovery" char(36) NOT NULL DEFAULT '',
    "digest" bytea NOT NULL,
    "email" varchar(255) NOT NULL UNIQUE,
    "location" varchar(255) NOT NULL DEFAULT 'UTC'
);

CREATE TABLE "posts" (
    "id" serial NOT NULL PRIMARY KEY,
    "title" varchar(255) NOT NULL,
    "content" text NOT NULL,
    "markdown" text NOT NULL,
    "slug" varchar(255) NOT NULL UNIQUE,
    "author" integer NOT NULL,
    "excerpt" varchar(255) NOT NULL,
    "viewcount" integer NOT NULL DEFAULT '0',
    "published" bool NOT NULL DEFAULT false,
    "created" integer NOT NULL,
    "updated" integer NOT NULL,
    "timeoffset" integer NOT NULL DEFAULT '0'
);

CREATE TABLE "settings" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(255) NOT NULL,
    "hostname" varchar(255) NOT NULL,
    "firstrun" bool NOT NULL DEFAULT true,
    "cookiehash" bytea NOT NULL,
    "allowregistrations" bool NOT NULL DEFAULT true,
    "description" varchar(255) NOT NULL,
    "mailerlogin" varchar(255),
    "mailerport" integer NOT NULL DEFAULT 587,
    "mailerpassword" varchar(255),
    "mailerhostname" varchar(255)
);`

// var mysql = `
// CREATE DATABASE vertigo;
// USE vertigo;

// CREATE TABLE IF NOT EXISTS users (
//     id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
//     name varchar(255) NOT NULL,
//     recovery char(36) NOT NULL DEFAULT "",
//     digest blob NOT NULL,
//     email varchar(255) NOT NULL UNIQUE,
//     location varchar(255) NOT NULL DEFAULT "UTC"
// );

// CREATE TABLE IF NOT EXISTS posts (
//     id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
//     title varchar(255) NOT NULL,
//     content text NOT NULL,
//     markdown text NOT NULL,
//     slug varchar(255) NOT NULL,
//     author integer NOT NULL,
//     excerpt varchar(255) NOT NULL,
//     viewcount integer unsigned NOT NULL DEFAULT 0,
//     published bool NOT NULL DEFAULT false,
//     created integer unsigned NOT NULL,
//     updated integer unsigned NOT NULL,
//     timeoffset integer NOT NULL DEFAULT 0
// );`

func Drop() {
	db.MustExec("DROP TABLE users")
	db.MustExec("DROP TABLE posts")
	db.MustExec("DROP TABLE settings")
	os.Remove("vertigo.db")
}

var Driver = flag.String("driver", "sqlite3", "Database driver to use (sqlite3, mysql, postgres)")
var Source = flag.String("source", "vertigo.db", "Database data source")

func connect(driver, source string) {
	conn, err := sqlx.Connect(driver, source)
	if err != nil {
		log.Fatal("sqlx connect:", err)
	}

	var schema string
	switch driver {
	case "sqlite3":
		schema = sqlite3
	/*case "mysql":
	schema = mysql*/
	case "postgres":
		schema = postgres
	}

	conn.MustExec(schema)

	log.Println("sqlx: using", driver)

	db = conn

	Settings = VertigoSettings()
}

func init() {

	flag.Parse()

	if os.Getenv("DATABASE_URL") != "" {
		u, err := url.Parse(os.Getenv("DATABASE_URL"))
		if err != nil {
			log.Fatal("database url parameter could not be parsed")
		}
		if u.Scheme != "postgres" /* && u.Scheme != "mysql" */ {
			log.Fatal("unsupported database type")
		}
		connect(u.Scheme, os.Getenv("DATABASE_URL"))
		return
	}

	connect(*Driver, *Source)
}
<|endoftext|>