<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"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	scheme "k8s.io/kubernetes/pkg/api/legacyscheme"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

var (
	convert_long = templates.LongDesc(i18n.T(`
		Convert config files between different API versions. Both YAML
		and JSON formats are accepted.

		The command takes filename, directory, or URL as input, and convert it into format
		of version specified by --output-version flag. If target version is not specified or
		not supported, convert to latest version.

		The default output will be printed to stdout in YAML format. One can use -o option
		to change to output destination.`))

	convert_example = templates.Examples(i18n.T(`
		# Convert 'pod.yaml' to latest version and print to stdout.
		kubectl convert -f pod.yaml

		# Convert the live state of the resource specified by 'pod.yaml' to the latest version
		# and print to stdout in JSON format.
		kubectl convert -f pod.yaml --local -o json

		# Convert all files under current directory to latest version and create them all.
		kubectl convert -f . | kubectl create -f -`))
)

// NewCmdConvert creates a command object for the generic "convert" action, which
// translates the config file into a given version.
func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	options := NewConvertOptions(ioStreams)

	cmd := &cobra.Command{
		Use: "convert -f FILENAME",
		DisableFlagsInUseLine: true,
		Short:   i18n.T("Convert config files between different API versions"),
		Long:    convert_long,
		Example: convert_example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd))
			cmdutil.CheckErr(options.RunConvert())
		},
	}

	options.PrintFlags.AddFlags(cmd)

	usage := "to need to get converted."
	cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
	cmd.MarkFlagRequired("filename")
	cmdutil.AddValidateFlags(cmd)
	cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, convert will NOT try to contact api-server but run locally.")
	cmd.Flags().String("output-version", "", i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1').)"))
	return cmd
}

// ConvertOptions have the data required to perform the convert operation
type ConvertOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   printers.ResourcePrinterFunc

	resource.FilenameOptions

	builder *resource.Builder
	local   bool

	genericclioptions.IOStreams
	specifiedOutputVersion schema.GroupVersion
}

func NewConvertOptions(ioStreams genericclioptions.IOStreams) *ConvertOptions {
	return &ConvertOptions{
		PrintFlags: genericclioptions.NewPrintFlags("converted").WithTypeSetter(scheme.Scheme).WithDefaultOutput("yaml"),
		local:      true,
		IOStreams:  ioStreams,
	}
}

// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
// defaultVersion is never mutated.  Nil simply allows clean passing in common usage from client.Config
func outputVersion(cmd *cobra.Command) (schema.GroupVersion, error) {
	outputVersionString := cmdutil.GetFlagString(cmd, "output-version")
	if len(outputVersionString) == 0 {
		return schema.GroupVersion{}, nil
	}

	return schema.ParseGroupVersion(outputVersionString)
}

// Complete collects information required to run Convert command from command line.
func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err error) {
	o.specifiedOutputVersion, err = outputVersion(cmd)
	if err != nil {
		return err
	}

	// build the builder
	o.builder = f.NewBuilder().
		WithScheme(scheme.Scheme).
		LocalParam(o.local)
	if !o.local {
		schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
		if err != nil {
			return err
		}
		o.builder.Schema(schema)
	}

	cmdNamespace, _, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	o.builder.NamespaceParam(cmdNamespace).
		ContinueOnError().
		FilenameParam(false, &o.FilenameOptions).
		Flatten()

	// build the printer
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}
	o.PrintObj = printer.PrintObj
	return nil
}

// RunConvert implements the generic Convert command
func (o *ConvertOptions) RunConvert() error {
	r := o.builder.Do()
	err := r.Err()
	if err != nil {
		return err
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return err
	}

	if len(infos) == 0 {
		return fmt.Errorf("no objects passed to convert")
	}

	objects, err := asVersionedObject(infos, !singleItemImplied, o.specifiedOutputVersion, cmdutil.InternalVersionJSONEncoder())
	if err != nil {
		return err
	}

	if meta.IsListType(objects) {
		obj, err := objectListToVersionedObject([]runtime.Object{objects}, o.specifiedOutputVersion)
		if err != nil {
			return err
		}
		return o.PrintObj(obj, o.Out)
	}

	return o.PrintObj(objects, o.Out)
}

// objectListToVersionedObject receives a list of api objects and a group version
// and squashes the list's items into a single versioned runtime.Object.
func objectListToVersionedObject(objects []runtime.Object, specifiedOutputVersion schema.GroupVersion) (runtime.Object, error) {
	objectList := &api.List{Items: objects}
	targetVersions := []schema.GroupVersion{}
	if !specifiedOutputVersion.Empty() {
		targetVersions = append(targetVersions, specifiedOutputVersion)
	}
	targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})
	converted, err := tryConvert(scheme.Scheme, objectList, targetVersions...)
	if err != nil {
		return nil, err
	}
	return converted, nil
}

// asVersionedObject converts a list of infos into a single object - either a List containing
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObject(infos []*resource.Info, forceList bool, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
	objects, err := asVersionedObjects(infos, specifiedOutputVersion, encoder)
	if err != nil {
		return nil, err
	}

	var object runtime.Object
	if len(objects) == 1 && !forceList {
		object = objects[0]
	} else {
		object = &api.List{Items: objects}
		targetVersions := []schema.GroupVersion{}
		if !specifiedOutputVersion.Empty() {
			targetVersions = append(targetVersions, specifiedOutputVersion)
		}
		targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})

		converted, err := tryConvert(scheme.Scheme, object, targetVersions...)
		if err != nil {
			return nil, err
		}
		object = converted
	}

	actualVersion := object.GetObjectKind().GroupVersionKind()
	if actualVersion.Version != specifiedOutputVersion.Version {
		defaultVersionInfo := ""
		if len(actualVersion.Version) > 0 {
			defaultVersionInfo = fmt.Sprintf("Defaulting to %q", actualVersion.Version)
		}
		glog.V(1).Infof("info: the output version specified is invalid. %s\n", defaultVersionInfo)
	}
	return object, nil
}

// asVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
	objects := []runtime.Object{}
	for _, info := range infos {
		if info.Object == nil {
			continue
		}

		targetVersions := []schema.GroupVersion{}
		// objects that are not part of api.Scheme must be converted to JSON
		// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
		if !specifiedOutputVersion.Empty() {
			if _, _, err := scheme.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {
				// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
				data, err := runtime.Encode(encoder, info.Object)
				if err != nil {
					return nil, err
				}
				// TODO: Set ContentEncoding and ContentType.
				objects = append(objects, &runtime.Unknown{Raw: data})
				continue
			}
			targetVersions = append(targetVersions, specifiedOutputVersion)
		} else {
			gvks, _, err := scheme.Scheme.ObjectKinds(info.Object)
			if err == nil {
				for _, gvk := range gvks {
					for _, version := range scheme.Scheme.PrioritizedVersionsForGroup(gvk.Group) {
						targetVersions = append(targetVersions, version)
					}
				}
			}
		}

		converted, err := tryConvert(scheme.Scheme, info.Object, targetVersions...)
		if err != nil {
			return nil, err
		}
		objects = append(objects, converted)
	}
	return objects, nil
}

// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...schema.GroupVersion) (runtime.Object, error) {
	var last error
	for _, version := range versions {
		if version.Empty() {
			return object, nil
		}
		obj, err := converter.ConvertToVersion(object, version)
		if err != nil {
			last = err
			continue
		}
		return obj, nil
	}
	return nil, last
}
<commit_msg>kubectl convert should not double wrap output in nested lists<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"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	scheme "k8s.io/kubernetes/pkg/api/legacyscheme"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

var (
	convert_long = templates.LongDesc(i18n.T(`
		Convert config files between different API versions. Both YAML
		and JSON formats are accepted.

		The command takes filename, directory, or URL as input, and convert it into format
		of version specified by --output-version flag. If target version is not specified or
		not supported, convert to latest version.

		The default output will be printed to stdout in YAML format. One can use -o option
		to change to output destination.`))

	convert_example = templates.Examples(i18n.T(`
		# Convert 'pod.yaml' to latest version and print to stdout.
		kubectl convert -f pod.yaml

		# Convert the live state of the resource specified by 'pod.yaml' to the latest version
		# and print to stdout in JSON format.
		kubectl convert -f pod.yaml --local -o json

		# Convert all files under current directory to latest version and create them all.
		kubectl convert -f . | kubectl create -f -`))
)

// NewCmdConvert creates a command object for the generic "convert" action, which
// translates the config file into a given version.
func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	options := NewConvertOptions(ioStreams)

	cmd := &cobra.Command{
		Use: "convert -f FILENAME",
		DisableFlagsInUseLine: true,
		Short:   i18n.T("Convert config files between different API versions"),
		Long:    convert_long,
		Example: convert_example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(options.Complete(f, cmd))
			cmdutil.CheckErr(options.RunConvert())
		},
	}

	options.PrintFlags.AddFlags(cmd)

	usage := "to need to get converted."
	cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
	cmd.MarkFlagRequired("filename")
	cmdutil.AddValidateFlags(cmd)
	cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, convert will NOT try to contact api-server but run locally.")
	cmd.Flags().String("output-version", "", i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1').)"))
	return cmd
}

// ConvertOptions have the data required to perform the convert operation
type ConvertOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   printers.ResourcePrinterFunc

	resource.FilenameOptions

	builder *resource.Builder
	local   bool

	genericclioptions.IOStreams
	specifiedOutputVersion schema.GroupVersion
}

func NewConvertOptions(ioStreams genericclioptions.IOStreams) *ConvertOptions {
	return &ConvertOptions{
		PrintFlags: genericclioptions.NewPrintFlags("converted").WithTypeSetter(scheme.Scheme).WithDefaultOutput("yaml"),
		local:      true,
		IOStreams:  ioStreams,
	}
}

// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
// defaultVersion is never mutated.  Nil simply allows clean passing in common usage from client.Config
func outputVersion(cmd *cobra.Command) (schema.GroupVersion, error) {
	outputVersionString := cmdutil.GetFlagString(cmd, "output-version")
	if len(outputVersionString) == 0 {
		return schema.GroupVersion{}, nil
	}

	return schema.ParseGroupVersion(outputVersionString)
}

// Complete collects information required to run Convert command from command line.
func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err error) {
	o.specifiedOutputVersion, err = outputVersion(cmd)
	if err != nil {
		return err
	}

	// build the builder
	o.builder = f.NewBuilder().
		WithScheme(scheme.Scheme).
		LocalParam(o.local)
	if !o.local {
		schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
		if err != nil {
			return err
		}
		o.builder.Schema(schema)
	}

	cmdNamespace, _, err := f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	o.builder.NamespaceParam(cmdNamespace).
		ContinueOnError().
		FilenameParam(false, &o.FilenameOptions).
		Flatten()

	// build the printer
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}
	o.PrintObj = printer.PrintObj
	return nil
}

// RunConvert implements the generic Convert command
func (o *ConvertOptions) RunConvert() error {
	r := o.builder.Do()
	err := r.Err()
	if err != nil {
		return err
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return err
	}

	if len(infos) == 0 {
		return fmt.Errorf("no objects passed to convert")
	}

	objects, err := asVersionedObject(infos, !singleItemImplied, o.specifiedOutputVersion, cmdutil.InternalVersionJSONEncoder())
	if err != nil {
		return err
	}

	if meta.IsListType(objects) {
		listContent, err := meta.ExtractList(objects)
		if err != nil {
			return err
		}
		obj, err := objectListToVersionedObject(listContent, o.specifiedOutputVersion)
		if err != nil {
			return err
		}
		return o.PrintObj(obj, o.Out)
	}

	return o.PrintObj(objects, o.Out)
}

// objectListToVersionedObject receives a list of api objects and a group version
// and squashes the list's items into a single versioned runtime.Object.
func objectListToVersionedObject(objects []runtime.Object, specifiedOutputVersion schema.GroupVersion) (runtime.Object, error) {
	objectList := &api.List{Items: objects}
	targetVersions := []schema.GroupVersion{}
	if !specifiedOutputVersion.Empty() {
		targetVersions = append(targetVersions, specifiedOutputVersion)
	}
	targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})
	converted, err := tryConvert(scheme.Scheme, objectList, targetVersions...)
	if err != nil {
		return nil, err
	}
	return converted, nil
}

// asVersionedObject converts a list of infos into a single object - either a List containing
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObject(infos []*resource.Info, forceList bool, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
	objects, err := asVersionedObjects(infos, specifiedOutputVersion, encoder)
	if err != nil {
		return nil, err
	}

	var object runtime.Object
	if len(objects) == 1 && !forceList {
		object = objects[0]
	} else {
		object = &api.List{Items: objects}
		targetVersions := []schema.GroupVersion{}
		if !specifiedOutputVersion.Empty() {
			targetVersions = append(targetVersions, specifiedOutputVersion)
		}
		targetVersions = append(targetVersions, schema.GroupVersion{Group: "", Version: "v1"})

		converted, err := tryConvert(scheme.Scheme, object, targetVersions...)
		if err != nil {
			return nil, err
		}
		object = converted
	}

	actualVersion := object.GetObjectKind().GroupVersionKind()
	if actualVersion.Version != specifiedOutputVersion.Version {
		defaultVersionInfo := ""
		if len(actualVersion.Version) > 0 {
			defaultVersionInfo = fmt.Sprintf("Defaulting to %q", actualVersion.Version)
		}
		glog.V(1).Infof("info: the output version specified is invalid. %s\n", defaultVersionInfo)
	}
	return object, nil
}

// asVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func asVersionedObjects(infos []*resource.Info, specifiedOutputVersion schema.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
	objects := []runtime.Object{}
	for _, info := range infos {
		if info.Object == nil {
			continue
		}

		targetVersions := []schema.GroupVersion{}
		// objects that are not part of api.Scheme must be converted to JSON
		// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
		if !specifiedOutputVersion.Empty() {
			if _, _, err := scheme.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {
				// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
				data, err := runtime.Encode(encoder, info.Object)
				if err != nil {
					return nil, err
				}
				// TODO: Set ContentEncoding and ContentType.
				objects = append(objects, &runtime.Unknown{Raw: data})
				continue
			}
			targetVersions = append(targetVersions, specifiedOutputVersion)
		} else {
			gvks, _, err := scheme.Scheme.ObjectKinds(info.Object)
			if err == nil {
				for _, gvk := range gvks {
					for _, version := range scheme.Scheme.PrioritizedVersionsForGroup(gvk.Group) {
						targetVersions = append(targetVersions, version)
					}
				}
			}
		}

		converted, err := tryConvert(scheme.Scheme, info.Object, targetVersions...)
		if err != nil {
			return nil, err
		}
		objects = append(objects, converted)
	}
	return objects, nil
}

// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...schema.GroupVersion) (runtime.Object, error) {
	var last error
	for _, version := range versions {
		if version.Empty() {
			return object, nil
		}
		obj, err := converter.ConvertToVersion(object, version)
		if err != nil {
			last = err
			continue
		}
		return obj, nil
	}
	return nil, last
}
<|endoftext|>


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

import (
	"fmt"
	"net"
	"runtime"

	"github.com/golang/glog"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/features"
	"k8s.io/kubernetes/pkg/kubelet/configmap"
	"k8s.io/kubernetes/pkg/kubelet/container"
	"k8s.io/kubernetes/pkg/kubelet/mountpod"
	"k8s.io/kubernetes/pkg/kubelet/secret"
	"k8s.io/kubernetes/pkg/util/io"
	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
)

// NewInitializedVolumePluginMgr returns a new instance of
// volume.VolumePluginMgr initialized with kubelets implementation of the
// volume.VolumeHost interface.
//
// kubelet - used by VolumeHost methods to expose kubelet specific parameters
// plugins - used to initialize volumePluginMgr
func NewInitializedVolumePluginMgr(
	kubelet *Kubelet,
	secretManager secret.Manager,
	configMapManager configmap.Manager,
	plugins []volume.VolumePlugin,
	prober volume.DynamicPluginProber) (*volume.VolumePluginMgr, error) {

	mountPodManager, err := mountpod.NewManager(kubelet.getRootDir(), kubelet.podManager)
	if err != nil {
		return nil, err
	}
	kvh := &kubeletVolumeHost{
		kubelet:          kubelet,
		volumePluginMgr:  volume.VolumePluginMgr{},
		secretManager:    secretManager,
		configMapManager: configMapManager,
		mountPodManager:  mountPodManager,
	}

	if err := kvh.volumePluginMgr.InitPlugins(plugins, prober, kvh); err != nil {
		return nil, fmt.Errorf(
			"Could not initialize volume plugins for KubeletVolumePluginMgr: %v",
			err)
	}

	return &kvh.volumePluginMgr, nil
}

// Compile-time check to ensure kubeletVolumeHost implements the VolumeHost interface
var _ volume.VolumeHost = &kubeletVolumeHost{}

func (kvh *kubeletVolumeHost) GetPluginDir(pluginName string) string {
	return kvh.kubelet.getPluginDir(pluginName)
}

type kubeletVolumeHost struct {
	kubelet          *Kubelet
	volumePluginMgr  volume.VolumePluginMgr
	secretManager    secret.Manager
	configMapManager configmap.Manager
	mountPodManager  mountpod.Manager
}

func (kvh *kubeletVolumeHost) GetVolumeDevicePluginDir(pluginName string) string {
	return kvh.kubelet.getVolumeDevicePluginDir(pluginName)
}

func (kvh *kubeletVolumeHost) GetPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
	dir := kvh.kubelet.getPodVolumeDir(podUID, pluginName, volumeName)
	if runtime.GOOS == "windows" {
		dir = util.GetWindowsPath(dir)
	}
	return dir
}

func (kvh *kubeletVolumeHost) GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string {
	return kvh.kubelet.getPodVolumeDeviceDir(podUID, pluginName)
}

func (kvh *kubeletVolumeHost) GetPodPluginDir(podUID types.UID, pluginName string) string {
	return kvh.kubelet.getPodPluginDir(podUID, pluginName)
}

func (kvh *kubeletVolumeHost) GetKubeClient() clientset.Interface {
	return kvh.kubelet.kubeClient
}

func (kvh *kubeletVolumeHost) NewWrapperMounter(
	volName string,
	spec volume.Spec,
	pod *v1.Pod,
	opts volume.VolumeOptions) (volume.Mounter, error) {
	// The name of wrapper volume is set to "wrapped_{wrapped_volume_name}"
	wrapperVolumeName := "wrapped_" + volName
	if spec.Volume != nil {
		spec.Volume.Name = wrapperVolumeName
	}

	return kvh.kubelet.newVolumeMounterFromPlugins(&spec, pod, opts)
}

func (kvh *kubeletVolumeHost) NewWrapperUnmounter(volName string, spec volume.Spec, podUID types.UID) (volume.Unmounter, error) {
	// The name of wrapper volume is set to "wrapped_{wrapped_volume_name}"
	wrapperVolumeName := "wrapped_" + volName
	if spec.Volume != nil {
		spec.Volume.Name = wrapperVolumeName
	}

	plugin, err := kvh.kubelet.volumePluginMgr.FindPluginBySpec(&spec)
	if err != nil {
		return nil, err
	}

	return plugin.NewUnmounter(spec.Name(), podUID)
}

func (kvh *kubeletVolumeHost) GetCloudProvider() cloudprovider.Interface {
	return kvh.kubelet.cloud
}

func (kvh *kubeletVolumeHost) GetMounter(pluginName string) mount.Interface {
	exec, err := kvh.getMountExec(pluginName)
	if err != nil {
		glog.V(2).Info("Error finding mount pod for plugin %s: %s", pluginName, err.Error())
		// Use the default mounter
		exec = nil
	}
	if exec == nil {
		return kvh.kubelet.mounter
	}
	return mount.NewExecMounter(exec, kvh.kubelet.mounter)
}

func (kvh *kubeletVolumeHost) GetWriter() io.Writer {
	return kvh.kubelet.writer
}

func (kvh *kubeletVolumeHost) GetHostName() string {
	return kvh.kubelet.hostname
}

func (kvh *kubeletVolumeHost) GetHostIP() (net.IP, error) {
	return kvh.kubelet.GetHostIP()
}

func (kvh *kubeletVolumeHost) GetNodeAllocatable() (v1.ResourceList, error) {
	node, err := kvh.kubelet.getNodeAnyWay()
	if err != nil {
		return nil, fmt.Errorf("error retrieving node: %v", err)
	}
	return node.Status.Allocatable, nil
}

func (kvh *kubeletVolumeHost) GetSecretFunc() func(namespace, name string) (*v1.Secret, error) {
	return kvh.secretManager.GetSecret
}

func (kvh *kubeletVolumeHost) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) {
	return kvh.configMapManager.GetConfigMap
}

func (kvh *kubeletVolumeHost) GetNodeLabels() (map[string]string, error) {
	node, err := kvh.kubelet.GetNode()
	if err != nil {
		return nil, fmt.Errorf("error retrieving node: %v", err)
	}
	return node.Labels, nil
}

func (kvh *kubeletVolumeHost) GetNodeName() types.NodeName {
	return kvh.kubelet.nodeName
}

func (kvh *kubeletVolumeHost) GetExec(pluginName string) mount.Exec {
	exec, err := kvh.getMountExec(pluginName)
	if err != nil {
		glog.V(2).Info("Error finding mount pod for plugin %s: %s", pluginName, err.Error())
		// Use the default exec
		exec = nil
	}
	if exec == nil {
		return mount.NewOsExec()
	}
	return exec
}

// getMountExec returns mount.Exec implementation that leads to pod with mount
// utilities. It returns nil,nil when there is no such pod and default mounter /
// os.Exec should be used.
func (kvh *kubeletVolumeHost) getMountExec(pluginName string) (mount.Exec, error) {
	if !utilfeature.DefaultFeatureGate.Enabled(features.MountContainers) {
		glog.V(5).Infof("using default mounter/exec for %s", pluginName)
		return nil, nil
	}

	pod, container, err := kvh.mountPodManager.GetMountPod(pluginName)
	if err != nil {
		return nil, err
	}
	if pod == nil {
		// Use default mounter/exec for this plugin
		glog.V(5).Infof("using default mounter/exec for %s", pluginName)
		return nil, nil
	}
	glog.V(5).Infof("using container %s/%s/%s to execute mount utilites for %s", pod.Namespace, pod.Name, container, pluginName)
	return &containerExec{
		pod:           pod,
		containerName: container,
		kl:            kvh.kubelet,
	}, nil
}

// containerExec is implementation of mount.Exec that executes commands in given
// container in given pod.
type containerExec struct {
	pod           *v1.Pod
	containerName string
	kl            *Kubelet
}

var _ mount.Exec = &containerExec{}

func (e *containerExec) Run(cmd string, args ...string) ([]byte, error) {
	cmdline := append([]string{cmd}, args...)
	glog.V(5).Infof("Exec mounter running in pod %s/%s/%s: %v", e.pod.Namespace, e.pod.Name, e.containerName, cmdline)
	return e.kl.RunInContainer(container.GetPodFullName(e.pod), e.pod.UID, e.containerName, cmdline)
}
<commit_msg>fix glog.Info in volumn_host<commit_after>/*
Copyright 2016 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 kubelet

import (
	"fmt"
	"net"
	"runtime"

	"github.com/golang/glog"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/pkg/cloudprovider"
	"k8s.io/kubernetes/pkg/features"
	"k8s.io/kubernetes/pkg/kubelet/configmap"
	"k8s.io/kubernetes/pkg/kubelet/container"
	"k8s.io/kubernetes/pkg/kubelet/mountpod"
	"k8s.io/kubernetes/pkg/kubelet/secret"
	"k8s.io/kubernetes/pkg/util/io"
	"k8s.io/kubernetes/pkg/util/mount"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
)

// NewInitializedVolumePluginMgr returns a new instance of
// volume.VolumePluginMgr initialized with kubelets implementation of the
// volume.VolumeHost interface.
//
// kubelet - used by VolumeHost methods to expose kubelet specific parameters
// plugins - used to initialize volumePluginMgr
func NewInitializedVolumePluginMgr(
	kubelet *Kubelet,
	secretManager secret.Manager,
	configMapManager configmap.Manager,
	plugins []volume.VolumePlugin,
	prober volume.DynamicPluginProber) (*volume.VolumePluginMgr, error) {

	mountPodManager, err := mountpod.NewManager(kubelet.getRootDir(), kubelet.podManager)
	if err != nil {
		return nil, err
	}
	kvh := &kubeletVolumeHost{
		kubelet:          kubelet,
		volumePluginMgr:  volume.VolumePluginMgr{},
		secretManager:    secretManager,
		configMapManager: configMapManager,
		mountPodManager:  mountPodManager,
	}

	if err := kvh.volumePluginMgr.InitPlugins(plugins, prober, kvh); err != nil {
		return nil, fmt.Errorf(
			"Could not initialize volume plugins for KubeletVolumePluginMgr: %v",
			err)
	}

	return &kvh.volumePluginMgr, nil
}

// Compile-time check to ensure kubeletVolumeHost implements the VolumeHost interface
var _ volume.VolumeHost = &kubeletVolumeHost{}

func (kvh *kubeletVolumeHost) GetPluginDir(pluginName string) string {
	return kvh.kubelet.getPluginDir(pluginName)
}

type kubeletVolumeHost struct {
	kubelet          *Kubelet
	volumePluginMgr  volume.VolumePluginMgr
	secretManager    secret.Manager
	configMapManager configmap.Manager
	mountPodManager  mountpod.Manager
}

func (kvh *kubeletVolumeHost) GetVolumeDevicePluginDir(pluginName string) string {
	return kvh.kubelet.getVolumeDevicePluginDir(pluginName)
}

func (kvh *kubeletVolumeHost) GetPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
	dir := kvh.kubelet.getPodVolumeDir(podUID, pluginName, volumeName)
	if runtime.GOOS == "windows" {
		dir = util.GetWindowsPath(dir)
	}
	return dir
}

func (kvh *kubeletVolumeHost) GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string {
	return kvh.kubelet.getPodVolumeDeviceDir(podUID, pluginName)
}

func (kvh *kubeletVolumeHost) GetPodPluginDir(podUID types.UID, pluginName string) string {
	return kvh.kubelet.getPodPluginDir(podUID, pluginName)
}

func (kvh *kubeletVolumeHost) GetKubeClient() clientset.Interface {
	return kvh.kubelet.kubeClient
}

func (kvh *kubeletVolumeHost) NewWrapperMounter(
	volName string,
	spec volume.Spec,
	pod *v1.Pod,
	opts volume.VolumeOptions) (volume.Mounter, error) {
	// The name of wrapper volume is set to "wrapped_{wrapped_volume_name}"
	wrapperVolumeName := "wrapped_" + volName
	if spec.Volume != nil {
		spec.Volume.Name = wrapperVolumeName
	}

	return kvh.kubelet.newVolumeMounterFromPlugins(&spec, pod, opts)
}

func (kvh *kubeletVolumeHost) NewWrapperUnmounter(volName string, spec volume.Spec, podUID types.UID) (volume.Unmounter, error) {
	// The name of wrapper volume is set to "wrapped_{wrapped_volume_name}"
	wrapperVolumeName := "wrapped_" + volName
	if spec.Volume != nil {
		spec.Volume.Name = wrapperVolumeName
	}

	plugin, err := kvh.kubelet.volumePluginMgr.FindPluginBySpec(&spec)
	if err != nil {
		return nil, err
	}

	return plugin.NewUnmounter(spec.Name(), podUID)
}

func (kvh *kubeletVolumeHost) GetCloudProvider() cloudprovider.Interface {
	return kvh.kubelet.cloud
}

func (kvh *kubeletVolumeHost) GetMounter(pluginName string) mount.Interface {
	exec, err := kvh.getMountExec(pluginName)
	if err != nil {
		glog.V(2).Infof("Error finding mount pod for plugin %s: %s", pluginName, err.Error())
		// Use the default mounter
		exec = nil
	}
	if exec == nil {
		return kvh.kubelet.mounter
	}
	return mount.NewExecMounter(exec, kvh.kubelet.mounter)
}

func (kvh *kubeletVolumeHost) GetWriter() io.Writer {
	return kvh.kubelet.writer
}

func (kvh *kubeletVolumeHost) GetHostName() string {
	return kvh.kubelet.hostname
}

func (kvh *kubeletVolumeHost) GetHostIP() (net.IP, error) {
	return kvh.kubelet.GetHostIP()
}

func (kvh *kubeletVolumeHost) GetNodeAllocatable() (v1.ResourceList, error) {
	node, err := kvh.kubelet.getNodeAnyWay()
	if err != nil {
		return nil, fmt.Errorf("error retrieving node: %v", err)
	}
	return node.Status.Allocatable, nil
}

func (kvh *kubeletVolumeHost) GetSecretFunc() func(namespace, name string) (*v1.Secret, error) {
	return kvh.secretManager.GetSecret
}

func (kvh *kubeletVolumeHost) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) {
	return kvh.configMapManager.GetConfigMap
}

func (kvh *kubeletVolumeHost) GetNodeLabels() (map[string]string, error) {
	node, err := kvh.kubelet.GetNode()
	if err != nil {
		return nil, fmt.Errorf("error retrieving node: %v", err)
	}
	return node.Labels, nil
}

func (kvh *kubeletVolumeHost) GetNodeName() types.NodeName {
	return kvh.kubelet.nodeName
}

func (kvh *kubeletVolumeHost) GetExec(pluginName string) mount.Exec {
	exec, err := kvh.getMountExec(pluginName)
	if err != nil {
		glog.V(2).Infof("Error finding mount pod for plugin %s: %s", pluginName, err.Error())
		// Use the default exec
		exec = nil
	}
	if exec == nil {
		return mount.NewOsExec()
	}
	return exec
}

// getMountExec returns mount.Exec implementation that leads to pod with mount
// utilities. It returns nil,nil when there is no such pod and default mounter /
// os.Exec should be used.
func (kvh *kubeletVolumeHost) getMountExec(pluginName string) (mount.Exec, error) {
	if !utilfeature.DefaultFeatureGate.Enabled(features.MountContainers) {
		glog.V(5).Infof("using default mounter/exec for %s", pluginName)
		return nil, nil
	}

	pod, container, err := kvh.mountPodManager.GetMountPod(pluginName)
	if err != nil {
		return nil, err
	}
	if pod == nil {
		// Use default mounter/exec for this plugin
		glog.V(5).Infof("using default mounter/exec for %s", pluginName)
		return nil, nil
	}
	glog.V(5).Infof("using container %s/%s/%s to execute mount utilites for %s", pod.Namespace, pod.Name, container, pluginName)
	return &containerExec{
		pod:           pod,
		containerName: container,
		kl:            kvh.kubelet,
	}, nil
}

// containerExec is implementation of mount.Exec that executes commands in given
// container in given pod.
type containerExec struct {
	pod           *v1.Pod
	containerName string
	kl            *Kubelet
}

var _ mount.Exec = &containerExec{}

func (e *containerExec) Run(cmd string, args ...string) ([]byte, error) {
	cmdline := append([]string{cmd}, args...)
	glog.V(5).Infof("Exec mounter running in pod %s/%s/%s: %v", e.pod.Namespace, e.pod.Name, e.containerName, cmdline)
	return e.kl.RunInContainer(container.GetPodFullName(e.pod), e.pod.UID, e.containerName, cmdline)
}
<|endoftext|>


<commit_before>// Copyright 2018 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 mountinfo

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

const (
	// FilesystemType names for filesystem which are used in /proc/pid/mountinfo
	FilesystemTypeBPFFS   = "bpf"
	FilesystemTypeCgroup2 = "cgroup2"

	mountInfoFilepath = "/proc/self/mountinfo"
)

// MountInfo is a struct representing information from /proc/pid/mountinfo. More
// information about file syntax:
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
type MountInfo struct {
	MountID        int64
	ParentID       int64
	StDev          string
	Root           string
	MountPoint     string
	MountOptions   string
	OptionalFields []string
	FilesystemType string
	MountSource    string
	SuperOptions   string
}

// parseMountInfoFile returns a slice of *MountInfo with information parsed from
// the given reader
func parseMountInfoFile(r io.Reader) ([]*MountInfo, error) {
	var result []*MountInfo

	scanner := bufio.NewScanner(r)
	scanner.Split(bufio.ScanLines)

	for scanner.Scan() {
		mountInfoRaw := scanner.Text()

		// Optional fields (which are on the 7th position) are separated
		// from the rest of fields by "-" character. The number of
		// optional fields can be greater or equal to 1.
		mountInfoSeparated := strings.Split(mountInfoRaw, " - ")
		if len(mountInfoSeparated) != 2 {
			return nil, fmt.Errorf("invalid mountinfo entry which has more that one '-' separator: %s", mountInfoRaw)
		}

		// Extract fields from both sides of mountinfo
		mountInfoLeft := strings.Split(strings.TrimSpace(mountInfoSeparated[0]), " ")
		mountInfoRight := strings.Split(strings.TrimSpace(mountInfoSeparated[1]), " ")

		// Before '-' separator there should be 6 fields and unknown
		// number of optional fields
		if len(mountInfoLeft) < 6 {
			return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw)
		}
		// After '-' separator there should be 3 fields
		if len(mountInfoRight) != 3 {
			return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw)
		}

		mountID, err := strconv.ParseInt(mountInfoLeft[0], 10, 64)
		if err != nil {
			return nil, err
		}

		parentID, err := strconv.ParseInt(mountInfoLeft[1], 10, 64)
		if err != nil {
			return nil, err
		}

		// Extract optional fields, which start from 7th position
		var optionalFields []string
		for i := 6; i < len(mountInfoLeft); i++ {
			optionalFields = append(optionalFields, mountInfoLeft[i])
		}

		result = append(result, &MountInfo{
			MountID:        mountID,
			ParentID:       parentID,
			StDev:          mountInfoLeft[2],
			Root:           mountInfoLeft[3],
			MountPoint:     mountInfoLeft[4],
			MountOptions:   mountInfoLeft[5],
			OptionalFields: optionalFields,
			FilesystemType: mountInfoRight[0],
			MountSource:    mountInfoRight[1],
			SuperOptions:   mountInfoRight[2],
		})
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return result, nil
}

// GetMountInfo returns a slice of *MountInfo with information parsed from
// /proc/self/mountinfo
func GetMountInfo() ([]*MountInfo, error) {
	fMounts, err := os.Open(mountInfoFilepath)
	if err != nil {
		return nil, fmt.Errorf("failed to open mount information at %s: %s", mountInfoFilepath, err)
	}
	defer fMounts.Close()

	return parseMountInfoFile(fMounts)
}

func isMountFS(mountInfos []*MountInfo, mntType string, mapRoot string) (bool, bool) {
	var mapRootMountInfo *MountInfo

	for _, mountInfo := range mountInfos {
		if mountInfo.MountPoint == mapRoot {
			mapRootMountInfo = mountInfo
			break
		}
	}

	if mapRootMountInfo == nil {
		return false, false
	}

	if mapRootMountInfo.FilesystemType == mntType {
		return true, true
	}
	return true, false
}

// IsMountFS returns two boolean values:checks whether the current mapRoot:
// - whether the current mapRoot has any mount
// - whether that mount's filesystem is of type mntType
func IsMountFS(mntType string, mapRoot string) (bool, bool, error) {
	mountInfos, err := GetMountInfo()
	if err != nil {
		return false, false, err
	}

	mounted, mntTypeInstance := isMountFS(mountInfos, mntType, mapRoot)
	return mounted, mntTypeInstance, nil
}
<commit_msg>mountinfo: remove useless TrimSpace<commit_after>// Copyright 2018 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 mountinfo

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

const (
	// FilesystemType names for filesystem which are used in /proc/pid/mountinfo
	FilesystemTypeBPFFS   = "bpf"
	FilesystemTypeCgroup2 = "cgroup2"

	mountInfoFilepath = "/proc/self/mountinfo"
)

// MountInfo is a struct representing information from /proc/pid/mountinfo. More
// information about file syntax:
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt
type MountInfo struct {
	MountID        int64
	ParentID       int64
	StDev          string
	Root           string
	MountPoint     string
	MountOptions   string
	OptionalFields []string
	FilesystemType string
	MountSource    string
	SuperOptions   string
}

// parseMountInfoFile returns a slice of *MountInfo with information parsed from
// the given reader
func parseMountInfoFile(r io.Reader) ([]*MountInfo, error) {
	var result []*MountInfo

	scanner := bufio.NewScanner(r)
	scanner.Split(bufio.ScanLines)

	for scanner.Scan() {
		mountInfoRaw := scanner.Text()

		// Optional fields (which are on the 7th position) are separated
		// from the rest of fields by "-" character. The number of
		// optional fields can be greater or equal to 1.
		mountInfoSeparated := strings.Split(mountInfoRaw, " - ")
		if len(mountInfoSeparated) != 2 {
			return nil, fmt.Errorf("invalid mountinfo entry which has more that one '-' separator: %s", mountInfoRaw)
		}

		// Extract fields from both sides of mountinfo
		mountInfoLeft := strings.Split(mountInfoSeparated[0], " ")
		mountInfoRight := strings.Split(mountInfoSeparated[1], " ")

		// Before '-' separator there should be 6 fields and unknown
		// number of optional fields
		if len(mountInfoLeft) < 6 {
			return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw)
		}
		// After '-' separator there should be 3 fields
		if len(mountInfoRight) != 3 {
			return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw)
		}

		mountID, err := strconv.ParseInt(mountInfoLeft[0], 10, 64)
		if err != nil {
			return nil, err
		}

		parentID, err := strconv.ParseInt(mountInfoLeft[1], 10, 64)
		if err != nil {
			return nil, err
		}

		// Extract optional fields, which start from 7th position
		var optionalFields []string
		for i := 6; i < len(mountInfoLeft); i++ {
			optionalFields = append(optionalFields, mountInfoLeft[i])
		}

		result = append(result, &MountInfo{
			MountID:        mountID,
			ParentID:       parentID,
			StDev:          mountInfoLeft[2],
			Root:           mountInfoLeft[3],
			MountPoint:     mountInfoLeft[4],
			MountOptions:   mountInfoLeft[5],
			OptionalFields: optionalFields,
			FilesystemType: mountInfoRight[0],
			MountSource:    mountInfoRight[1],
			SuperOptions:   mountInfoRight[2],
		})
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return result, nil
}

// GetMountInfo returns a slice of *MountInfo with information parsed from
// /proc/self/mountinfo
func GetMountInfo() ([]*MountInfo, error) {
	fMounts, err := os.Open(mountInfoFilepath)
	if err != nil {
		return nil, fmt.Errorf("failed to open mount information at %s: %s", mountInfoFilepath, err)
	}
	defer fMounts.Close()

	return parseMountInfoFile(fMounts)
}

func isMountFS(mountInfos []*MountInfo, mntType string, mapRoot string) (bool, bool) {
	var mapRootMountInfo *MountInfo

	for _, mountInfo := range mountInfos {
		if mountInfo.MountPoint == mapRoot {
			mapRootMountInfo = mountInfo
			break
		}
	}

	if mapRootMountInfo == nil {
		return false, false
	}

	if mapRootMountInfo.FilesystemType == mntType {
		return true, true
	}
	return true, false
}

// IsMountFS returns two boolean values:checks whether the current mapRoot:
// - whether the current mapRoot has any mount
// - whether that mount's filesystem is of type mntType
func IsMountFS(mntType string, mapRoot string) (bool, bool, error) {
	mountInfos, err := GetMountInfo()
	if err != nil {
		return false, false, err
	}

	mounted, mntTypeInstance := isMountFS(mountInfos, mntType, mapRoot)
	return mounted, mntTypeInstance, nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// 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 bgp

import (
	"fmt"
	"strconv"
	"strings"
)

const AS_TRANS = 23456

const BGP_PORT = 179

type FSMState int

const (
	BGP_FSM_IDLE FSMState = iota
	BGP_FSM_CONNECT
	BGP_FSM_ACTIVE
	BGP_FSM_OPENSENT
	BGP_FSM_OPENCONFIRM
	BGP_FSM_ESTABLISHED
)

// partially taken from http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
type Protocol int

const (
	Unknown Protocol = iota
	ICMP             = 0x01
	IGMP             = 0x02
	TCP              = 0x06
	EGP              = 0x08
	IGP              = 0x09
	UDP              = 0x11
	RSVP             = 0x2e
	GRE              = 0x2f
	OSPF             = 0x59
	IPIP             = 0x5e
	PIM              = 0x67
	SCTP             = 0x84
)

var ProtocolNameMap = map[Protocol]string{
	Unknown: "unknown",
	ICMP:    "icmp",
	IGMP:    "igmp",
	TCP:     "tcp",
	EGP:     "egp",
	IGP:     "igp",
	UDP:     "udp",
	RSVP:    "rsvp",
	GRE:     "gre",
	OSPF:    "ospf",
	IPIP:    "ipip",
	PIM:     "pim",
	SCTP:    "sctp",
}

func (p Protocol) String() string {
	name, ok := ProtocolNameMap[p]
	if !ok {
		return fmt.Sprintf("%d", p)
	}
	return name
}

type TCPFlag int

const (
	_               TCPFlag = iota
	TCP_FLAG_FIN            = 0x01
	TCP_FLAG_SYN            = 0x02
	TCP_FLAG_RST            = 0x04
	TCP_FLAG_PUSH           = 0x08
	TCP_FLAG_ACK            = 0x10
	TCP_FLAG_URGENT         = 0x20
	TCP_FLAG_ECE            = 0x40
	TCP_FLAG_CWR            = 0x80
)

var TCPFlagNameMap = map[TCPFlag]string{
	TCP_FLAG_FIN:    "F",
	TCP_FLAG_SYN:    "S",
	TCP_FLAG_RST:    "R",
	TCP_FLAG_PUSH:   "P",
	TCP_FLAG_ACK:    "A",
	TCP_FLAG_URGENT: "U",
	TCP_FLAG_CWR:    "C",
	TCP_FLAG_ECE:    "E",
}

// Prepares a sorted list of flags because map iterations does not happen
// in a consistent order in Golang.
var TCPSortedFlags = []TCPFlag{
	TCP_FLAG_FIN,
	TCP_FLAG_SYN,
	TCP_FLAG_RST,
	TCP_FLAG_PUSH,
	TCP_FLAG_ACK,
	TCP_FLAG_URGENT,
	TCP_FLAG_ECE,
	TCP_FLAG_CWR,
}

func (f TCPFlag) String() string {
	flags := make([]string, 0, len(TCPSortedFlags))
	for _, v := range TCPSortedFlags {
		if f&v > 0 {
			flags = append(flags, TCPFlagNameMap[v])
		}
	}
	return strings.Join(flags, "")
}

type BitmaskFlagOp uint8

const (
	BITMASK_FLAG_OP_OR        BitmaskFlagOp = iota
	BITMASK_FLAG_OP_MATCH                   = 0x01
	BITMASK_FLAG_OP_NOT                     = 0x02
	BITMASK_FLAG_OP_NOT_MATCH               = 0x03
	BITMASK_FLAG_OP_AND                     = 0x40
	BITMASK_FLAG_OP_END                     = 0x80
)

var BitmaskFlagOpNameMap = map[BitmaskFlagOp]string{
	BITMASK_FLAG_OP_OR:    " ",
	BITMASK_FLAG_OP_AND:   "&",
	BITMASK_FLAG_OP_END:   "E",
	BITMASK_FLAG_OP_NOT:   "!",
	BITMASK_FLAG_OP_MATCH: "=",
}

// Note: Meaning of "" is different from that of the numeric operator because
// RFC5575 says if the Match bit in the bitmask operand is set, it should be
// "strictly" matching against the given value.
var BitmaskFlagOpValueMap = map[string]BitmaskFlagOp{
	" ":  BITMASK_FLAG_OP_OR,
	"":   BITMASK_FLAG_OP_OR,
	"==": BITMASK_FLAG_OP_MATCH,
	"=":  BITMASK_FLAG_OP_MATCH,
	"!":  BITMASK_FLAG_OP_NOT,
	"!=": BITMASK_FLAG_OP_NOT_MATCH,
	"=!": BITMASK_FLAG_OP_NOT_MATCH, // For the backward compatibility
	"&":  BITMASK_FLAG_OP_AND,
	"E":  BITMASK_FLAG_OP_END,
}

func (f BitmaskFlagOp) String() string {
	ops := make([]string, 0, 3)
	if f&BITMASK_FLAG_OP_AND > 0 {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_AND])
	} else {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_OR])
	}
	if f&BITMASK_FLAG_OP_NOT > 0 {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_NOT])
	}
	if f&BITMASK_FLAG_OP_MATCH > 0 {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_MATCH])
	}
	return strings.Join(ops, "")
}

type FragmentFlag int

const (
	FRAG_FLAG_NOT   FragmentFlag = iota
	FRAG_FLAG_DONT               = 0x01
	FRAG_FLAG_IS                 = 0x02
	FRAG_FLAG_FIRST              = 0x04
	FRAG_FLAG_LAST               = 0x08
)

var FragmentFlagNameMap = map[FragmentFlag]string{
	FRAG_FLAG_NOT:   "not-a-fragment",
	FRAG_FLAG_DONT:  "dont-fragment",
	FRAG_FLAG_IS:    "is-fragment",
	FRAG_FLAG_FIRST: "first-fragment",
	FRAG_FLAG_LAST:  "last-fragment",
}

// Prepares a sorted list of flags because map iterations does not happen
// in a consistent order in Golang.
var FragmentSortedFlags = []FragmentFlag{
	FRAG_FLAG_NOT,
	FRAG_FLAG_DONT,
	FRAG_FLAG_IS,
	FRAG_FLAG_FIRST,
	FRAG_FLAG_LAST,
}

func (f FragmentFlag) String() string {
	flags := make([]string, 0, len(FragmentSortedFlags))
	for _, v := range FragmentSortedFlags {
		if f&v > 0 {
			flags = append(flags, FragmentFlagNameMap[v])
		}
	}
	// Note: If multiple bits are set, joins them with "+".
	return strings.Join(flags, "+")
}

type DECNumOp uint8

const (
	DEC_NUM_OP_TRUE   DECNumOp = iota // true always with END bit set
	DEC_NUM_OP_EQ              = 0x01
	DEC_NUM_OP_GT              = 0x02
	DEC_NUM_OP_GT_EQ           = 0x03
	DEC_NUM_OP_LT              = 0x04
	DEC_NUM_OP_LT_EQ           = 0x05
	DEC_NUM_OP_NOT_EQ          = 0x06
	DEC_NUM_OP_FALSE           = 0x07 // false always with END bit set
	DEC_NUM_OP_OR              = 0x00
	DEC_NUM_OP_AND             = 0x40
	DEC_NUM_OP_END             = 0x80
)

var DECNumOpNameMap = map[DECNumOp]string{
	DEC_NUM_OP_TRUE:   "true",
	DEC_NUM_OP_EQ:     "==",
	DEC_NUM_OP_GT:     ">",
	DEC_NUM_OP_GT_EQ:  ">=",
	DEC_NUM_OP_LT:     "<",
	DEC_NUM_OP_LT_EQ:  "<=",
	DEC_NUM_OP_NOT_EQ: "!=",
	DEC_NUM_OP_FALSE:  "false",
	//DEC_NUM_OP_OR:   " ", // duplicate with DEC_NUM_OP_TRUE
	DEC_NUM_OP_AND: "&",
	DEC_NUM_OP_END: "E",
}

var DECNumOpValueMap = map[string]DECNumOp{
	"true":  DEC_NUM_OP_TRUE,
	"":      DEC_NUM_OP_EQ,
	"==":    DEC_NUM_OP_EQ,
	"=":     DEC_NUM_OP_EQ,
	">":     DEC_NUM_OP_GT,
	">=":    DEC_NUM_OP_GT_EQ,
	"<":     DEC_NUM_OP_LT,
	"<=":    DEC_NUM_OP_LT_EQ,
	"!=":    DEC_NUM_OP_NOT_EQ,
	"=!":    DEC_NUM_OP_NOT_EQ,
	"!":     DEC_NUM_OP_NOT_EQ,
	"false": DEC_NUM_OP_FALSE,
	" ":     DEC_NUM_OP_OR,
	"&":     DEC_NUM_OP_AND,
	"E":     DEC_NUM_OP_END,
}

func (f DECNumOp) String() string {
	ops := make([]string, 0)
	logicFlag := DECNumOp(f & 0xc0) // higher 2 bits
	if logicFlag&DEC_NUM_OP_AND > 0 {
		ops = append(ops, DECNumOpNameMap[DEC_NUM_OP_AND])
	} else {
		ops = append(ops, " ") // DEC_NUM_OP_OR
	}
	// Omits DEC_NUM_OP_END
	cmpFlag := DECNumOp(f & 0x7) // lower 3 bits
	for v, s := range DECNumOpNameMap {
		if cmpFlag == v {
			ops = append(ops, s)
			break
		}
	}
	return strings.Join(ops, "")
}

// Potentially taken from https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
type EthernetType int

const (
	IPv4            EthernetType = 0x0800
	ARP             EthernetType = 0x0806
	RARP            EthernetType = 0x8035
	VMTP            EthernetType = 0x805B
	APPLE_TALK      EthernetType = 0x809B
	AARP            EthernetType = 0x80F3
	IPX             EthernetType = 0x8137
	SNMP            EthernetType = 0x814C
	NET_BIOS        EthernetType = 0x8191
	XTP             EthernetType = 0x817D
	IPv6            EthernetType = 0x86DD
	PPPoE_DISCOVERY EthernetType = 0x8863
	PPPoE_SESSION   EthernetType = 0x8864
	LOOPBACK        EthernetType = 0x9000
)

var EthernetTypeNameMap = map[EthernetType]string{
	IPv4:            "ipv4",
	ARP:             "arp",
	RARP:            "rarp",
	VMTP:            "vmtp",
	APPLE_TALK:      "apple-talk",
	AARP:            "aarp",
	IPX:             "ipx",
	SNMP:            "snmp",
	NET_BIOS:        "net-bios",
	XTP:             "xtp",
	IPv6:            "ipv6",
	PPPoE_DISCOVERY: "pppoe-discovery",
	PPPoE_SESSION:   "pppoe-session",
	LOOPBACK:        "loopback",
}

func (t EthernetType) String() string {
	if name, ok := EthernetTypeNameMap[t]; ok {
		return name
	}
	return strconv.Itoa(int(t))
}
<commit_msg>replaced fmt.Sprintf with strconv.Itoa for Protocol.String()<commit_after>// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// 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 bgp

import (
	"strconv"
	"strings"
)

const AS_TRANS = 23456

const BGP_PORT = 179

type FSMState int

const (
	BGP_FSM_IDLE FSMState = iota
	BGP_FSM_CONNECT
	BGP_FSM_ACTIVE
	BGP_FSM_OPENSENT
	BGP_FSM_OPENCONFIRM
	BGP_FSM_ESTABLISHED
)

// partially taken from http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
type Protocol int

const (
	Unknown Protocol = iota
	ICMP             = 0x01
	IGMP             = 0x02
	TCP              = 0x06
	EGP              = 0x08
	IGP              = 0x09
	UDP              = 0x11
	RSVP             = 0x2e
	GRE              = 0x2f
	OSPF             = 0x59
	IPIP             = 0x5e
	PIM              = 0x67
	SCTP             = 0x84
)

var ProtocolNameMap = map[Protocol]string{
	Unknown: "unknown",
	ICMP:    "icmp",
	IGMP:    "igmp",
	TCP:     "tcp",
	EGP:     "egp",
	IGP:     "igp",
	UDP:     "udp",
	RSVP:    "rsvp",
	GRE:     "gre",
	OSPF:    "ospf",
	IPIP:    "ipip",
	PIM:     "pim",
	SCTP:    "sctp",
}

func (p Protocol) String() string {
	name, ok := ProtocolNameMap[p]
	if !ok {
		return strconv.Itoa(int(p))
	}
	return name
}

type TCPFlag int

const (
	_               TCPFlag = iota
	TCP_FLAG_FIN            = 0x01
	TCP_FLAG_SYN            = 0x02
	TCP_FLAG_RST            = 0x04
	TCP_FLAG_PUSH           = 0x08
	TCP_FLAG_ACK            = 0x10
	TCP_FLAG_URGENT         = 0x20
	TCP_FLAG_ECE            = 0x40
	TCP_FLAG_CWR            = 0x80
)

var TCPFlagNameMap = map[TCPFlag]string{
	TCP_FLAG_FIN:    "F",
	TCP_FLAG_SYN:    "S",
	TCP_FLAG_RST:    "R",
	TCP_FLAG_PUSH:   "P",
	TCP_FLAG_ACK:    "A",
	TCP_FLAG_URGENT: "U",
	TCP_FLAG_CWR:    "C",
	TCP_FLAG_ECE:    "E",
}

// Prepares a sorted list of flags because map iterations does not happen
// in a consistent order in Golang.
var TCPSortedFlags = []TCPFlag{
	TCP_FLAG_FIN,
	TCP_FLAG_SYN,
	TCP_FLAG_RST,
	TCP_FLAG_PUSH,
	TCP_FLAG_ACK,
	TCP_FLAG_URGENT,
	TCP_FLAG_ECE,
	TCP_FLAG_CWR,
}

func (f TCPFlag) String() string {
	flags := make([]string, 0, len(TCPSortedFlags))
	for _, v := range TCPSortedFlags {
		if f&v > 0 {
			flags = append(flags, TCPFlagNameMap[v])
		}
	}
	return strings.Join(flags, "")
}

type BitmaskFlagOp uint8

const (
	BITMASK_FLAG_OP_OR        BitmaskFlagOp = iota
	BITMASK_FLAG_OP_MATCH                   = 0x01
	BITMASK_FLAG_OP_NOT                     = 0x02
	BITMASK_FLAG_OP_NOT_MATCH               = 0x03
	BITMASK_FLAG_OP_AND                     = 0x40
	BITMASK_FLAG_OP_END                     = 0x80
)

var BitmaskFlagOpNameMap = map[BitmaskFlagOp]string{
	BITMASK_FLAG_OP_OR:    " ",
	BITMASK_FLAG_OP_AND:   "&",
	BITMASK_FLAG_OP_END:   "E",
	BITMASK_FLAG_OP_NOT:   "!",
	BITMASK_FLAG_OP_MATCH: "=",
}

// Note: Meaning of "" is different from that of the numeric operator because
// RFC5575 says if the Match bit in the bitmask operand is set, it should be
// "strictly" matching against the given value.
var BitmaskFlagOpValueMap = map[string]BitmaskFlagOp{
	" ":  BITMASK_FLAG_OP_OR,
	"":   BITMASK_FLAG_OP_OR,
	"==": BITMASK_FLAG_OP_MATCH,
	"=":  BITMASK_FLAG_OP_MATCH,
	"!":  BITMASK_FLAG_OP_NOT,
	"!=": BITMASK_FLAG_OP_NOT_MATCH,
	"=!": BITMASK_FLAG_OP_NOT_MATCH, // For the backward compatibility
	"&":  BITMASK_FLAG_OP_AND,
	"E":  BITMASK_FLAG_OP_END,
}

func (f BitmaskFlagOp) String() string {
	ops := make([]string, 0, 3)
	if f&BITMASK_FLAG_OP_AND > 0 {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_AND])
	} else {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_OR])
	}
	if f&BITMASK_FLAG_OP_NOT > 0 {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_NOT])
	}
	if f&BITMASK_FLAG_OP_MATCH > 0 {
		ops = append(ops, BitmaskFlagOpNameMap[BITMASK_FLAG_OP_MATCH])
	}
	return strings.Join(ops, "")
}

type FragmentFlag int

const (
	FRAG_FLAG_NOT   FragmentFlag = iota
	FRAG_FLAG_DONT               = 0x01
	FRAG_FLAG_IS                 = 0x02
	FRAG_FLAG_FIRST              = 0x04
	FRAG_FLAG_LAST               = 0x08
)

var FragmentFlagNameMap = map[FragmentFlag]string{
	FRAG_FLAG_NOT:   "not-a-fragment",
	FRAG_FLAG_DONT:  "dont-fragment",
	FRAG_FLAG_IS:    "is-fragment",
	FRAG_FLAG_FIRST: "first-fragment",
	FRAG_FLAG_LAST:  "last-fragment",
}

// Prepares a sorted list of flags because map iterations does not happen
// in a consistent order in Golang.
var FragmentSortedFlags = []FragmentFlag{
	FRAG_FLAG_NOT,
	FRAG_FLAG_DONT,
	FRAG_FLAG_IS,
	FRAG_FLAG_FIRST,
	FRAG_FLAG_LAST,
}

func (f FragmentFlag) String() string {
	flags := make([]string, 0, len(FragmentSortedFlags))
	for _, v := range FragmentSortedFlags {
		if f&v > 0 {
			flags = append(flags, FragmentFlagNameMap[v])
		}
	}
	// Note: If multiple bits are set, joins them with "+".
	return strings.Join(flags, "+")
}

type DECNumOp uint8

const (
	DEC_NUM_OP_TRUE   DECNumOp = iota // true always with END bit set
	DEC_NUM_OP_EQ              = 0x01
	DEC_NUM_OP_GT              = 0x02
	DEC_NUM_OP_GT_EQ           = 0x03
	DEC_NUM_OP_LT              = 0x04
	DEC_NUM_OP_LT_EQ           = 0x05
	DEC_NUM_OP_NOT_EQ          = 0x06
	DEC_NUM_OP_FALSE           = 0x07 // false always with END bit set
	DEC_NUM_OP_OR              = 0x00
	DEC_NUM_OP_AND             = 0x40
	DEC_NUM_OP_END             = 0x80
)

var DECNumOpNameMap = map[DECNumOp]string{
	DEC_NUM_OP_TRUE:   "true",
	DEC_NUM_OP_EQ:     "==",
	DEC_NUM_OP_GT:     ">",
	DEC_NUM_OP_GT_EQ:  ">=",
	DEC_NUM_OP_LT:     "<",
	DEC_NUM_OP_LT_EQ:  "<=",
	DEC_NUM_OP_NOT_EQ: "!=",
	DEC_NUM_OP_FALSE:  "false",
	//DEC_NUM_OP_OR:   " ", // duplicate with DEC_NUM_OP_TRUE
	DEC_NUM_OP_AND: "&",
	DEC_NUM_OP_END: "E",
}

var DECNumOpValueMap = map[string]DECNumOp{
	"true":  DEC_NUM_OP_TRUE,
	"":      DEC_NUM_OP_EQ,
	"==":    DEC_NUM_OP_EQ,
	"=":     DEC_NUM_OP_EQ,
	">":     DEC_NUM_OP_GT,
	">=":    DEC_NUM_OP_GT_EQ,
	"<":     DEC_NUM_OP_LT,
	"<=":    DEC_NUM_OP_LT_EQ,
	"!=":    DEC_NUM_OP_NOT_EQ,
	"=!":    DEC_NUM_OP_NOT_EQ,
	"!":     DEC_NUM_OP_NOT_EQ,
	"false": DEC_NUM_OP_FALSE,
	" ":     DEC_NUM_OP_OR,
	"&":     DEC_NUM_OP_AND,
	"E":     DEC_NUM_OP_END,
}

func (f DECNumOp) String() string {
	ops := make([]string, 0)
	logicFlag := DECNumOp(f & 0xc0) // higher 2 bits
	if logicFlag&DEC_NUM_OP_AND > 0 {
		ops = append(ops, DECNumOpNameMap[DEC_NUM_OP_AND])
	} else {
		ops = append(ops, " ") // DEC_NUM_OP_OR
	}
	// Omits DEC_NUM_OP_END
	cmpFlag := DECNumOp(f & 0x7) // lower 3 bits
	for v, s := range DECNumOpNameMap {
		if cmpFlag == v {
			ops = append(ops, s)
			break
		}
	}
	return strings.Join(ops, "")
}

// Potentially taken from https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
type EthernetType int

const (
	IPv4            EthernetType = 0x0800
	ARP             EthernetType = 0x0806
	RARP            EthernetType = 0x8035
	VMTP            EthernetType = 0x805B
	APPLE_TALK      EthernetType = 0x809B
	AARP            EthernetType = 0x80F3
	IPX             EthernetType = 0x8137
	SNMP            EthernetType = 0x814C
	NET_BIOS        EthernetType = 0x8191
	XTP             EthernetType = 0x817D
	IPv6            EthernetType = 0x86DD
	PPPoE_DISCOVERY EthernetType = 0x8863
	PPPoE_SESSION   EthernetType = 0x8864
	LOOPBACK        EthernetType = 0x9000
)

var EthernetTypeNameMap = map[EthernetType]string{
	IPv4:            "ipv4",
	ARP:             "arp",
	RARP:            "rarp",
	VMTP:            "vmtp",
	APPLE_TALK:      "apple-talk",
	AARP:            "aarp",
	IPX:             "ipx",
	SNMP:            "snmp",
	NET_BIOS:        "net-bios",
	XTP:             "xtp",
	IPv6:            "ipv6",
	PPPoE_DISCOVERY: "pppoe-discovery",
	PPPoE_SESSION:   "pppoe-session",
	LOOPBACK:        "loopback",
}

func (t EthernetType) String() string {
	if name, ok := EthernetTypeNameMap[t]; ok {
		return name
	}
	return strconv.Itoa(int(t))
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative Authors

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

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

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

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"k8s.io/apimachinery/pkg/types"

	rtesting "knative.dev/pkg/reconciler/testing"
	"knative.dev/serving/pkg/activator"
	"knative.dev/serving/pkg/activator/util"
)

func TestContextHandler(t *testing.T) {
	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(t)
	defer cancel()
	revID := types.NamespacedName{Namespace: testNamespace, Name: testRevName}
	revision := revision(revID.Namespace, revID.Name)
	revisionInformer(ctx, revision)

	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if got := util.RevisionFrom(r.Context()); got != revision {
			t.Errorf("revisionFrom() = %v, want %v", got, revision)
		}

		if got := util.RevIDFrom(r.Context()); got != revID {
			t.Errorf("revIDFrom() = %v, want %v", got, revID)
		}
	})

	handler := NewContextHandler(ctx, baseHandler)
	resp := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString(""))
	req.Header.Set(activator.RevisionHeaderNamespace, revID.Namespace)
	req.Header.Set(activator.RevisionHeaderName, revID.Name)
	handler.ServeHTTP(resp, req)

	if got, want := resp.Code, http.StatusOK; got != want {
		t.Errorf("StatusCode = %d, want %d, body: %s", got, want, resp.Body.String())
	}
}

func TestContextHandlerError(t *testing.T) {
	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(t)
	defer cancel()
	revID := types.NamespacedName{Namespace: testNamespace, Name: testRevName}
	revision := revision(revID.Namespace, revID.Name)
	revisionInformer(ctx, revision)

	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

	handler := NewContextHandler(ctx, baseHandler)
	resp := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString(""))
	req.Header.Set(activator.RevisionHeaderNamespace, "foospace")
	req.Header.Set(activator.RevisionHeaderName, "fooname")
	handler.ServeHTTP(resp, req)

	if got, want := resp.Code, http.StatusNotFound; got != want {
		t.Errorf("StatusCode = %d, want %d", got, want)
	}

	if got, want := resp.Body.String(), errMsg(`revision.serving.knative.dev "fooname" not found`); got != want {
		t.Errorf("Body = %q, want %q", got, want)
	}
}

func errMsg(msg string) string {
	return fmt.Sprintf("Error getting active endpoint: %s\n", msg)
}
<commit_msg>Add benchmark for context handler (#7047)<commit_after>/*
Copyright 2020 The Knative Authors

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

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

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

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"k8s.io/apimachinery/pkg/types"

	rtesting "knative.dev/pkg/reconciler/testing"
	"knative.dev/serving/pkg/activator"
	"knative.dev/serving/pkg/activator/util"
)

func TestContextHandler(t *testing.T) {
	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(t)
	defer cancel()
	revID := types.NamespacedName{Namespace: testNamespace, Name: testRevName}
	revision := revision(revID.Namespace, revID.Name)
	revisionInformer(ctx, revision)

	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if got := util.RevisionFrom(r.Context()); got != revision {
			t.Errorf("revisionFrom() = %v, want %v", got, revision)
		}

		if got := util.RevIDFrom(r.Context()); got != revID {
			t.Errorf("revIDFrom() = %v, want %v", got, revID)
		}
	})

	handler := NewContextHandler(ctx, baseHandler)
	resp := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString(""))
	req.Header.Set(activator.RevisionHeaderNamespace, revID.Namespace)
	req.Header.Set(activator.RevisionHeaderName, revID.Name)
	handler.ServeHTTP(resp, req)

	if got, want := resp.Code, http.StatusOK; got != want {
		t.Errorf("StatusCode = %d, want %d, body: %s", got, want, resp.Body.String())
	}
}

func TestContextHandlerError(t *testing.T) {
	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(t)
	defer cancel()
	revID := types.NamespacedName{Namespace: testNamespace, Name: testRevName}
	revision := revision(revID.Namespace, revID.Name)
	revisionInformer(ctx, revision)

	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

	handler := NewContextHandler(ctx, baseHandler)
	resp := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString(""))
	req.Header.Set(activator.RevisionHeaderNamespace, "foospace")
	req.Header.Set(activator.RevisionHeaderName, "fooname")
	handler.ServeHTTP(resp, req)

	if got, want := resp.Code, http.StatusNotFound; got != want {
		t.Errorf("StatusCode = %d, want %d", got, want)
	}

	if got, want := resp.Body.String(), errMsg(`revision.serving.knative.dev "fooname" not found`); got != want {
		t.Errorf("Body = %q, want %q", got, want)
	}
}

func BenchmarkContextHandler(b *testing.B) {
	tests := []struct {
		label        string
		revisionName string
	}{{
		label:        "context handler success",
		revisionName: testRevName,
	}, {
		label:        "context handler failure",
		revisionName: "fake",
	}}
	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(&testing.T{})
	defer cancel()
	revision := revision(testNamespace, testRevName)
	revisionInformer(ctx, revision)

	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

	handler := NewContextHandler(ctx, baseHandler)
	req := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
	req.Header.Set(activator.RevisionHeaderNamespace, testNamespace)

	for _, test := range tests {
		req.Header.Set(activator.RevisionHeaderName, test.revisionName)
		b.Run(fmt.Sprintf("%s-sequential", test.label), func(b *testing.B) {
			resp := httptest.NewRecorder()
			for j := 0; j < b.N; j++ {
				handler.ServeHTTP(resp, req)
			}
		})
		b.Run(fmt.Sprintf("%s-parallel", test.label), func(b *testing.B) {
			b.RunParallel(func(pb *testing.PB) {
				resp := httptest.NewRecorder()
				for pb.Next() {
					handler.ServeHTTP(resp, req)
				}
			})
		})
	}
}

func errMsg(msg string) string {
	return fmt.Sprintf("Error getting active endpoint: %s\n", msg)
}
<|endoftext|>


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

package control

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"
	"text/tabwriter"
	"time"

	"gvisor.googlesource.com/gvisor/pkg/abi/linux"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs/host"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
	ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
	"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
	"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
	"gvisor.googlesource.com/gvisor/pkg/urpc"
)

// Proc includes task-related functions.
//
// At the moment, this is limited to exec support.
type Proc struct {
	Kernel *kernel.Kernel
}

// ExecArgs is the set of arguments to exec.
type ExecArgs struct {
	// Filename is the filename to load.
	//
	// If this is provided as "", then the file will be guessed via Argv[0].
	Filename string `json:"filename"`

	// Argv is a list of arguments.
	Argv []string `json:"argv"`

	// Envv is a list of environment variables.
	Envv []string `json:"envv"`

	// Root defines the root directory for the new process. A reference on
	// Root must be held for the lifetime of the ExecArgs. If Root is nil,
	// it will default to the VFS root.
	Root *fs.Dirent

	// WorkingDirectory defines the working directory for the new process.
	WorkingDirectory string `json:"wd"`

	// KUID is the UID to run with in the root user namespace. Defaults to
	// root if not set explicitly.
	KUID auth.KUID

	// KGID is the GID to run with in the root user namespace. Defaults to
	// the root group if not set explicitly.
	KGID auth.KGID

	// ExtraKGIDs is the list of additional groups to which the user
	// belongs.
	ExtraKGIDs []auth.KGID

	// Capabilities is the list of capabilities to give to the process.
	Capabilities *auth.TaskCapabilities

	// StdioIsPty indicates that FDs 0, 1, and 2 are connected to a host
	// pty fd.
	StdioIsPty bool

	// FilePayload determines the files to give to the new process.
	urpc.FilePayload
}

// Exec runs a new task.
func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
	newTG, err := proc.execAsync(args)
	if err != nil {
		return err
	}

	// Wait for completion.
	newTG.WaitExited()
	*waitStatus = newTG.ExitStatus().Status()
	return nil
}

// ExecAsync runs a new task, but doesn't wait for it to finish. It is defined
// as a function rather than a method to avoid exposing execAsync as an RPC.
func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, error) {
	return proc.execAsync(args)
}

func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, error) {
	// Import file descriptors.
	l := limits.NewLimitSet()
	fdm := proc.Kernel.NewFDMap()
	defer fdm.DecRef()

	creds := auth.NewUserCredentials(
		args.KUID,
		args.KGID,
		args.ExtraKGIDs,
		args.Capabilities,
		proc.Kernel.RootUserNamespace())

	initArgs := kernel.CreateProcessArgs{
		Filename:                args.Filename,
		Argv:                    args.Argv,
		Envv:                    args.Envv,
		WorkingDirectory:        args.WorkingDirectory,
		Root:                    args.Root,
		Credentials:             creds,
		FDMap:                   fdm,
		Umask:                   0022,
		Limits:                  l,
		MaxSymlinkTraversals:    linux.MaxSymlinkTraversals,
		UTSNamespace:            proc.Kernel.RootUTSNamespace(),
		IPCNamespace:            proc.Kernel.RootIPCNamespace(),
		AbstractSocketNamespace: proc.Kernel.RootAbstractSocketNamespace(),
	}
	ctx := initArgs.NewContext(proc.Kernel)

	if initArgs.Filename == "" {
		// Get the full path to the filename from the PATH env variable.
		paths := fs.GetPath(initArgs.Envv)
		f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
		if err != nil {
			return nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
		}
		initArgs.Filename = f
	}

	mounter := fs.FileOwnerFromContext(ctx)
	for appFD, f := range args.FilePayload.Files {
		enableIoctl := args.StdioIsPty && appFD <= 2

		// Import the given file FD. This dups the FD as well.
		file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
		if err != nil {
			return nil, err
		}
		defer file.DecRef()

		// We're done with this file.
		f.Close()

		if err := fdm.NewFDAt(kdefs.FD(appFD), file, kernel.FDFlags{}, l); err != nil {
			return nil, err
		}
	}

	return proc.Kernel.CreateProcess(initArgs)
}

// PsArgs is the set of arguments to ps.
type PsArgs struct {
	// JSON will force calls to Ps to return the result as a JSON payload.
	JSON bool
}

// Ps provides a process listing for the running kernel.
func (proc *Proc) Ps(args *PsArgs, out *string) error {
	var p []*Process
	if e := Processes(proc.Kernel, &p); e != nil {
		return e
	}
	if !args.JSON {
		*out = ProcessListToTable(p)
	} else {
		s, e := ProcessListToJSON(p)
		if e != nil {
			return e
		}
		*out = s
	}
	return nil
}

// Process contains information about a single process in a Sandbox.
// TODO: Implement TTY field.
type Process struct {
	UID auth.KUID       `json:"uid"`
	PID kernel.ThreadID `json:"pid"`
	// Parent PID
	PPID kernel.ThreadID `json:"ppid"`
	// Processor utilization
	C int32 `json:"c"`
	// Start time
	STime string `json:"stime"`
	// CPU time
	Time string `json:"time"`
	// Executable shortname (e.g. "sh" for /bin/sh)
	Cmd string `json:"cmd"`
}

// ProcessListToTable prints a table with the following format:
// UID       PID       PPID      C         STIME     TIME       CMD
// 0         1         0         0         14:04     505262ns   tail
func ProcessListToTable(pl []*Process) string {
	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 10, 1, 3, ' ', 0)
	fmt.Fprint(tw, "UID\tPID\tPPID\tC\tSTIME\tTIME\tCMD")
	for _, d := range pl {
		fmt.Fprintf(tw, "\n%d\t%d\t%d\t%d\t%s\t%s\t%s",
			d.UID,
			d.PID,
			d.PPID,
			d.C,
			d.STime,
			d.Time,
			d.Cmd)
	}
	tw.Flush()
	return buf.String()
}

// ProcessListToJSON will return the JSON representation of ps.
func ProcessListToJSON(pl []*Process) (string, error) {
	b, err := json.Marshal(pl)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal process list %v: %v", pl, err)
	}
	return string(b), nil
}

// PrintPIDsJSON prints a JSON object containing only the PIDs in pl. This
// behavior is the same as runc's.
func PrintPIDsJSON(pl []*Process) (string, error) {
	pids := make([]kernel.ThreadID, 0, len(pl))
	for _, d := range pl {
		pids = append(pids, d.PID)
	}
	b, err := json.Marshal(pids)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal PIDs %v: %v", pids, err)
	}
	return string(b), nil
}

// Processes retrieves information about processes running in the sandbox.
func Processes(k *kernel.Kernel, out *[]*Process) error {
	ts := k.TaskSet()
	now := k.RealtimeClock().Now()
	for _, tg := range ts.Root.ThreadGroups() {
		pid := ts.Root.IDOfThreadGroup(tg)
		// If tg has already been reaped ignore it.
		if pid == 0 {
			continue
		}

		*out = append(*out, &Process{
			UID: tg.Leader().Credentials().EffectiveKUID,
			PID: pid,
			// If Parent is null (i.e. tg is the init process), PPID will be 0.
			PPID:  ts.Root.IDOfTask(tg.Leader().Parent()),
			STime: formatStartTime(now, tg.Leader().StartTime()),
			C:     percentCPU(tg.CPUStats(), tg.Leader().StartTime(), now),
			Time:  tg.CPUStats().SysTime.String(),
			Cmd:   tg.Leader().Name(),
		})
	}
	sort.Slice(*out, func(i, j int) bool { return (*out)[i].PID < (*out)[j].PID })
	return nil
}

// formatStartTime formats startTime depending on the current time:
// - If startTime was today, HH:MM is used.
// - If startTime was not today but was this year, MonDD is used (e.g. Jan02)
// - If startTime was not this year, the year is used.
func formatStartTime(now, startTime ktime.Time) string {
	nowS, nowNs := now.Unix()
	n := time.Unix(nowS, nowNs)
	startTimeS, startTimeNs := startTime.Unix()
	st := time.Unix(startTimeS, startTimeNs)
	format := "15:04"
	if st.YearDay() != n.YearDay() {
		format = "Jan02"
	}
	if st.Year() != n.Year() {
		format = "2006"
	}
	return st.Format(format)
}

func percentCPU(stats usage.CPUStats, startTime, now ktime.Time) int32 {
	// Note: In procps, there is an option to include child CPU stats. As
	// it is disabled by default, we do not include them.
	total := stats.UserTime + stats.SysTime
	lifetime := now.Sub(startTime)
	if lifetime <= 0 {
		return 0
	}
	percentCPU := total * 100 / lifetime
	// Cap at 99% since procps does the same.
	if percentCPU > 99 {
		percentCPU = 99
	}
	return int32(percentCPU)
}
<commit_msg>initArgs must hold a reference on the Root if it is not nil.<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"
	"text/tabwriter"
	"time"

	"gvisor.googlesource.com/gvisor/pkg/abi/linux"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
	"gvisor.googlesource.com/gvisor/pkg/sentry/fs/host"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
	"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
	ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
	"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
	"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
	"gvisor.googlesource.com/gvisor/pkg/urpc"
)

// Proc includes task-related functions.
//
// At the moment, this is limited to exec support.
type Proc struct {
	Kernel *kernel.Kernel
}

// ExecArgs is the set of arguments to exec.
type ExecArgs struct {
	// Filename is the filename to load.
	//
	// If this is provided as "", then the file will be guessed via Argv[0].
	Filename string `json:"filename"`

	// Argv is a list of arguments.
	Argv []string `json:"argv"`

	// Envv is a list of environment variables.
	Envv []string `json:"envv"`

	// Root defines the root directory for the new process. A reference on
	// Root must be held for the lifetime of the ExecArgs. If Root is nil,
	// it will default to the VFS root.
	Root *fs.Dirent

	// WorkingDirectory defines the working directory for the new process.
	WorkingDirectory string `json:"wd"`

	// KUID is the UID to run with in the root user namespace. Defaults to
	// root if not set explicitly.
	KUID auth.KUID

	// KGID is the GID to run with in the root user namespace. Defaults to
	// the root group if not set explicitly.
	KGID auth.KGID

	// ExtraKGIDs is the list of additional groups to which the user
	// belongs.
	ExtraKGIDs []auth.KGID

	// Capabilities is the list of capabilities to give to the process.
	Capabilities *auth.TaskCapabilities

	// StdioIsPty indicates that FDs 0, 1, and 2 are connected to a host
	// pty fd.
	StdioIsPty bool

	// FilePayload determines the files to give to the new process.
	urpc.FilePayload
}

// Exec runs a new task.
func (proc *Proc) Exec(args *ExecArgs, waitStatus *uint32) error {
	newTG, err := proc.execAsync(args)
	if err != nil {
		return err
	}

	// Wait for completion.
	newTG.WaitExited()
	*waitStatus = newTG.ExitStatus().Status()
	return nil
}

// ExecAsync runs a new task, but doesn't wait for it to finish. It is defined
// as a function rather than a method to avoid exposing execAsync as an RPC.
func ExecAsync(proc *Proc, args *ExecArgs) (*kernel.ThreadGroup, error) {
	return proc.execAsync(args)
}

func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, error) {
	// Import file descriptors.
	l := limits.NewLimitSet()
	fdm := proc.Kernel.NewFDMap()
	defer fdm.DecRef()

	creds := auth.NewUserCredentials(
		args.KUID,
		args.KGID,
		args.ExtraKGIDs,
		args.Capabilities,
		proc.Kernel.RootUserNamespace())

	initArgs := kernel.CreateProcessArgs{
		Filename:                args.Filename,
		Argv:                    args.Argv,
		Envv:                    args.Envv,
		WorkingDirectory:        args.WorkingDirectory,
		Root:                    args.Root,
		Credentials:             creds,
		FDMap:                   fdm,
		Umask:                   0022,
		Limits:                  l,
		MaxSymlinkTraversals:    linux.MaxSymlinkTraversals,
		UTSNamespace:            proc.Kernel.RootUTSNamespace(),
		IPCNamespace:            proc.Kernel.RootIPCNamespace(),
		AbstractSocketNamespace: proc.Kernel.RootAbstractSocketNamespace(),
	}
	if initArgs.Root != nil {
		// initArgs must hold a reference on Root. This ref is dropped
		// in CreateProcess.
		initArgs.Root.IncRef()
	}
	ctx := initArgs.NewContext(proc.Kernel)

	if initArgs.Filename == "" {
		// Get the full path to the filename from the PATH env variable.
		paths := fs.GetPath(initArgs.Envv)
		f, err := proc.Kernel.RootMountNamespace().ResolveExecutablePath(ctx, initArgs.WorkingDirectory, initArgs.Argv[0], paths)
		if err != nil {
			return nil, fmt.Errorf("error finding executable %q in PATH %v: %v", initArgs.Argv[0], paths, err)
		}
		initArgs.Filename = f
	}

	mounter := fs.FileOwnerFromContext(ctx)
	for appFD, f := range args.FilePayload.Files {
		enableIoctl := args.StdioIsPty && appFD <= 2

		// Import the given file FD. This dups the FD as well.
		file, err := host.ImportFile(ctx, int(f.Fd()), mounter, enableIoctl)
		if err != nil {
			return nil, err
		}
		defer file.DecRef()

		// We're done with this file.
		f.Close()

		if err := fdm.NewFDAt(kdefs.FD(appFD), file, kernel.FDFlags{}, l); err != nil {
			return nil, err
		}
	}

	return proc.Kernel.CreateProcess(initArgs)
}

// PsArgs is the set of arguments to ps.
type PsArgs struct {
	// JSON will force calls to Ps to return the result as a JSON payload.
	JSON bool
}

// Ps provides a process listing for the running kernel.
func (proc *Proc) Ps(args *PsArgs, out *string) error {
	var p []*Process
	if e := Processes(proc.Kernel, &p); e != nil {
		return e
	}
	if !args.JSON {
		*out = ProcessListToTable(p)
	} else {
		s, e := ProcessListToJSON(p)
		if e != nil {
			return e
		}
		*out = s
	}
	return nil
}

// Process contains information about a single process in a Sandbox.
// TODO: Implement TTY field.
type Process struct {
	UID auth.KUID       `json:"uid"`
	PID kernel.ThreadID `json:"pid"`
	// Parent PID
	PPID kernel.ThreadID `json:"ppid"`
	// Processor utilization
	C int32 `json:"c"`
	// Start time
	STime string `json:"stime"`
	// CPU time
	Time string `json:"time"`
	// Executable shortname (e.g. "sh" for /bin/sh)
	Cmd string `json:"cmd"`
}

// ProcessListToTable prints a table with the following format:
// UID       PID       PPID      C         STIME     TIME       CMD
// 0         1         0         0         14:04     505262ns   tail
func ProcessListToTable(pl []*Process) string {
	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 10, 1, 3, ' ', 0)
	fmt.Fprint(tw, "UID\tPID\tPPID\tC\tSTIME\tTIME\tCMD")
	for _, d := range pl {
		fmt.Fprintf(tw, "\n%d\t%d\t%d\t%d\t%s\t%s\t%s",
			d.UID,
			d.PID,
			d.PPID,
			d.C,
			d.STime,
			d.Time,
			d.Cmd)
	}
	tw.Flush()
	return buf.String()
}

// ProcessListToJSON will return the JSON representation of ps.
func ProcessListToJSON(pl []*Process) (string, error) {
	b, err := json.Marshal(pl)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal process list %v: %v", pl, err)
	}
	return string(b), nil
}

// PrintPIDsJSON prints a JSON object containing only the PIDs in pl. This
// behavior is the same as runc's.
func PrintPIDsJSON(pl []*Process) (string, error) {
	pids := make([]kernel.ThreadID, 0, len(pl))
	for _, d := range pl {
		pids = append(pids, d.PID)
	}
	b, err := json.Marshal(pids)
	if err != nil {
		return "", fmt.Errorf("couldn't marshal PIDs %v: %v", pids, err)
	}
	return string(b), nil
}

// Processes retrieves information about processes running in the sandbox.
func Processes(k *kernel.Kernel, out *[]*Process) error {
	ts := k.TaskSet()
	now := k.RealtimeClock().Now()
	for _, tg := range ts.Root.ThreadGroups() {
		pid := ts.Root.IDOfThreadGroup(tg)
		// If tg has already been reaped ignore it.
		if pid == 0 {
			continue
		}

		*out = append(*out, &Process{
			UID: tg.Leader().Credentials().EffectiveKUID,
			PID: pid,
			// If Parent is null (i.e. tg is the init process), PPID will be 0.
			PPID:  ts.Root.IDOfTask(tg.Leader().Parent()),
			STime: formatStartTime(now, tg.Leader().StartTime()),
			C:     percentCPU(tg.CPUStats(), tg.Leader().StartTime(), now),
			Time:  tg.CPUStats().SysTime.String(),
			Cmd:   tg.Leader().Name(),
		})
	}
	sort.Slice(*out, func(i, j int) bool { return (*out)[i].PID < (*out)[j].PID })
	return nil
}

// formatStartTime formats startTime depending on the current time:
// - If startTime was today, HH:MM is used.
// - If startTime was not today but was this year, MonDD is used (e.g. Jan02)
// - If startTime was not this year, the year is used.
func formatStartTime(now, startTime ktime.Time) string {
	nowS, nowNs := now.Unix()
	n := time.Unix(nowS, nowNs)
	startTimeS, startTimeNs := startTime.Unix()
	st := time.Unix(startTimeS, startTimeNs)
	format := "15:04"
	if st.YearDay() != n.YearDay() {
		format = "Jan02"
	}
	if st.Year() != n.Year() {
		format = "2006"
	}
	return st.Format(format)
}

func percentCPU(stats usage.CPUStats, startTime, now ktime.Time) int32 {
	// Note: In procps, there is an option to include child CPU stats. As
	// it is disabled by default, we do not include them.
	total := stats.UserTime + stats.SysTime
	lifetime := now.Sub(startTime)
	if lifetime <= 0 {
		return 0
	}
	percentCPU := total * 100 / lifetime
	// Cap at 99% since procps does the same.
	if percentCPU > 99 {
		percentCPU = 99
	}
	return int32(percentCPU)
}
<|endoftext|>