<commit_before>// +build !providerless

/*
Copyright 2018 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 awsebs

import (
	"os"
	"path/filepath"
	"testing"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utiltesting "k8s.io/client-go/util/testing"
	"k8s.io/kubernetes/pkg/volume"
	volumetest "k8s.io/kubernetes/pkg/volume/testing"
)

const (
	testVolName    = "vol-1234"
	testPVName     = "pv1"
	testGlobalPath = "plugins/kubernetes.io/aws-ebs/volumeDevices/vol-1234"
	testPodPath    = "pods/poduid/volumeDevices/kubernetes.io~aws-ebs"
)

func TestGetVolumeSpecFromGlobalMapPath(t *testing.T) {
	// make our test path for fake GlobalMapPath
	// /tmp symbolized our pluginDir
	// /tmp/testGlobalPathXXXXX/plugins/kubernetes.io/gce-pd/volumeDevices/pdVol1
	tmpVDir, err := utiltesting.MkTmpdir("awsBlockTest")
	if err != nil {
		t.Fatalf("can't make a temp dir: %v", err)
	}
	//deferred clean up
	defer os.RemoveAll(tmpVDir)

	expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath)

	plugMgr := volume.VolumePluginMgr{}
	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpVDir, nil, nil))
	plug, err := plugMgr.FindMapperPluginByName(awsElasticBlockStorePluginName)
	if err != nil {
		os.RemoveAll(tmpVDir)
		t.Fatalf("Can't find the plugin by name: %q", awsElasticBlockStorePluginName)
	}

	//Bad Path
	badspec, err := plug.(*awsElasticBlockStorePlugin).getVolumeSpecFromGlobalMapPath("", "")
	if badspec != nil || err == nil {
		t.Fatalf("Expected not to get spec from GlobalMapPath but did")
	}

	// Good Path
	spec, err := plug.(*awsElasticBlockStorePlugin).getVolumeSpecFromGlobalMapPath("myVolume", expectedGlobalPath)
	if spec == nil || err != nil {
		t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v - %v", &specMode, block)
	}
	if spec.PersistentVolume.Name != "myVolume" {
		t.Errorf("Invalid PV name from GlobalMapPath spec: %s", spec.PersistentVolume.Name)
	}
	if spec.PersistentVolume.Spec.AWSElasticBlockStore.VolumeID != testVolName {
		t.Errorf("Invalid volumeID from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.AWSElasticBlockStore.VolumeID)
	}
	block := v1.PersistentVolumeBlock
	specMode := spec.PersistentVolume.Spec.VolumeMode
	if specMode == nil {
		t.Fatalf("Failed to get spec from GlobalMapPath: %v", err)
	}
	if *specMode != block {
		t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v - %v", *specMode, block)
	}
}

func getTestVolume(readOnly bool, isBlock bool) *volume.Spec {
	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: testPVName,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeSource: v1.PersistentVolumeSource{
				AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
					VolumeID: testVolName,
				},
			},
		},
	}

	if isBlock {
		blockMode := v1.PersistentVolumeBlock
		pv.Spec.VolumeMode = &blockMode
	}
	return volume.NewSpecFromPersistentVolume(pv, readOnly)
}

func TestGetPodAndPluginMapPaths(t *testing.T) {
	tmpVDir, err := utiltesting.MkTmpdir("awsBlockTest")
	if err != nil {
		t.Fatalf("can't make a temp dir: %v", err)
	}
	//deferred clean up
	defer os.RemoveAll(tmpVDir)

	expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath)
	expectedPodPath := filepath.Join(tmpVDir, testPodPath)

	spec := getTestVolume(false, true /*isBlock*/)
	plugMgr := volume.VolumePluginMgr{}
	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpVDir, nil, nil))
	plug, err := plugMgr.FindMapperPluginByName(awsElasticBlockStorePluginName)
	if err != nil {
		os.RemoveAll(tmpVDir)
		t.Fatalf("Can't find the plugin by name: %q", awsElasticBlockStorePluginName)
	}
	if plug.GetPluginName() != awsElasticBlockStorePluginName {
		t.Fatalf("Wrong name: %s", plug.GetPluginName())
	}
	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
	mapper, err := plug.NewBlockVolumeMapper(spec, pod, volume.VolumeOptions{})
	if err != nil {
		t.Fatalf("Failed to make a new Mounter: %v", err)
	}
	if mapper == nil {
		t.Fatalf("Got a nil Mounter")
	}

	//GetGlobalMapPath
	gMapPath, err := mapper.GetGlobalMapPath(spec)
	if err != nil || len(gMapPath) == 0 {
		t.Fatalf("Invalid path from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.GCEPersistentDisk.PDName)
	}
	if gMapPath != expectedGlobalPath {
		t.Fatalf("Failed to get GlobalMapPath: %s %s", gMapPath, expectedGlobalPath)
	}

	//GetPodDeviceMapPath
	gDevicePath, gVolName := mapper.GetPodDeviceMapPath()
	if gDevicePath != expectedPodPath {
		t.Errorf("Got unexpected pod path: %s, expected %s", gDevicePath, expectedPodPath)
	}
	if gVolName != testPVName {
		t.Errorf("Got unexpected volNamne: %s, expected %s", gVolName, testPVName)
	}
}
<commit_msg>Code review changes<commit_after>// +build !providerless

/*
Copyright 2018 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 awsebs

import (
	"os"
	"path/filepath"
	"testing"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utiltesting "k8s.io/client-go/util/testing"
	"k8s.io/kubernetes/pkg/volume"
	volumetest "k8s.io/kubernetes/pkg/volume/testing"
)

const (
	testVolName    = "vol-1234"
	testPVName     = "pv1"
	testGlobalPath = "plugins/kubernetes.io/aws-ebs/volumeDevices/vol-1234"
	testPodPath    = "pods/poduid/volumeDevices/kubernetes.io~aws-ebs"
)

func TestGetVolumeSpecFromGlobalMapPath(t *testing.T) {
	// make our test path for fake GlobalMapPath
	// /tmp symbolized our pluginDir
	// /tmp/testGlobalPathXXXXX/plugins/kubernetes.io/gce-pd/volumeDevices/pdVol1
	tmpVDir, err := utiltesting.MkTmpdir("awsBlockTest")
	if err != nil {
		t.Fatalf("can't make a temp dir: %v", err)
	}
	//deferred clean up
	defer os.RemoveAll(tmpVDir)

	expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath)

	plugMgr := volume.VolumePluginMgr{}
	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpVDir, nil, nil))
	plug, err := plugMgr.FindMapperPluginByName(awsElasticBlockStorePluginName)
	if err != nil {
		os.RemoveAll(tmpVDir)
		t.Fatalf("Can't find the plugin by name: %q", awsElasticBlockStorePluginName)
	}

	//Bad Path
	badspec, err := plug.(*awsElasticBlockStorePlugin).getVolumeSpecFromGlobalMapPath("", "")
	if badspec != nil || err == nil {
		t.Fatalf("Expected not to get spec from GlobalMapPath but did")
	}

	// Good Path
	spec, err := plug.(*awsElasticBlockStorePlugin).getVolumeSpecFromGlobalMapPath("myVolume", expectedGlobalPath)
	if spec == nil || err != nil {
		t.Fatalf("Failed to get spec from GlobalMapPath: %v", err)
	}
	if spec.PersistentVolume.Name != "myVolume" {
		t.Errorf("Invalid PV name from GlobalMapPath spec: %s", spec.PersistentVolume.Name)
	}
	if spec.PersistentVolume.Spec.AWSElasticBlockStore.VolumeID != testVolName {
		t.Errorf("Invalid volumeID from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.AWSElasticBlockStore.VolumeID)
	}
	block := v1.PersistentVolumeBlock
	specMode := spec.PersistentVolume.Spec.VolumeMode
	if specMode == nil {
		t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v - %v", specMode, block)
	}
	if *specMode != block {
		t.Errorf("Invalid volumeMode from GlobalMapPath spec: %v - %v", *specMode, block)
	}
}

func getTestVolume(readOnly bool, isBlock bool) *volume.Spec {
	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: testPVName,
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeSource: v1.PersistentVolumeSource{
				AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
					VolumeID: testVolName,
				},
			},
		},
	}

	if isBlock {
		blockMode := v1.PersistentVolumeBlock
		pv.Spec.VolumeMode = &blockMode
	}
	return volume.NewSpecFromPersistentVolume(pv, readOnly)
}

func TestGetPodAndPluginMapPaths(t *testing.T) {
	tmpVDir, err := utiltesting.MkTmpdir("awsBlockTest")
	if err != nil {
		t.Fatalf("can't make a temp dir: %v", err)
	}
	//deferred clean up
	defer os.RemoveAll(tmpVDir)

	expectedGlobalPath := filepath.Join(tmpVDir, testGlobalPath)
	expectedPodPath := filepath.Join(tmpVDir, testPodPath)

	spec := getTestVolume(false, true /*isBlock*/)
	plugMgr := volume.VolumePluginMgr{}
	plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, volumetest.NewFakeVolumeHost(t, tmpVDir, nil, nil))
	plug, err := plugMgr.FindMapperPluginByName(awsElasticBlockStorePluginName)
	if err != nil {
		os.RemoveAll(tmpVDir)
		t.Fatalf("Can't find the plugin by name: %q", awsElasticBlockStorePluginName)
	}
	if plug.GetPluginName() != awsElasticBlockStorePluginName {
		t.Fatalf("Wrong name: %s", plug.GetPluginName())
	}
	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
	mapper, err := plug.NewBlockVolumeMapper(spec, pod, volume.VolumeOptions{})
	if err != nil {
		t.Fatalf("Failed to make a new Mounter: %v", err)
	}
	if mapper == nil {
		t.Fatalf("Got a nil Mounter")
	}

	//GetGlobalMapPath
	gMapPath, err := mapper.GetGlobalMapPath(spec)
	if err != nil || len(gMapPath) == 0 {
		t.Fatalf("Invalid path from GlobalMapPath spec: %s", spec.PersistentVolume.Spec.GCEPersistentDisk.PDName)
	}
	if gMapPath != expectedGlobalPath {
		t.Fatalf("Failed to get GlobalMapPath: %s %s", gMapPath, expectedGlobalPath)
	}

	//GetPodDeviceMapPath
	gDevicePath, gVolName := mapper.GetPodDeviceMapPath()
	if gDevicePath != expectedPodPath {
		t.Errorf("Got unexpected pod path: %s, expected %s", gDevicePath, expectedPodPath)
	}
	if gVolName != testPVName {
		t.Errorf("Got unexpected volNamne: %s, expected %s", gVolName, testPVName)
	}
}
<|endoftext|>


<commit_before>package progress

import (
	"fmt"
	"time"
)

type DownloadProgress struct {
	From int64 `json:"From"`
	To   int64 `json:"To"`
	Pos  int64 `json:"Pos"`

	BytesLoaded uint64    `json:"-"`
	Speed       uint64    `json:"-"`
	MiddleSpeed uint64    `json:"-"`
	LastTime    time.Time `json:"-"`
	StartTime   time.Time `json:"-"`

	ConnectTime time.Duration `json:"-"`
	IsLoading   bool          `json:"-"`
}

func (s *DownloadProgress) StartSpeed() {
	s.Speed = 0
	s.MiddleSpeed = 0
	s.BytesLoaded = 0
	s.StartTime = time.Now()
	s.LastTime = time.Now()
}

func (s *DownloadProgress) EndSpeed() {
	s.Speed = 0
	s.MiddleSpeed = 0
	s.BytesLoaded = 0
}

func (s *DownloadProgress) GetSpeed() (uint64, uint64) {
	return s.Speed, s.MiddleSpeed
}

func (s *DownloadProgress) MessureSpeed(realc int) {
	s.BytesLoaded += uint64(realc)

	delta := time.Since(s.StartTime).Seconds()
	if time.Since(s.LastTime).Seconds() > 0.1 {
		s.LastTime = time.Now()
		s.Speed = uint64(float64(s.BytesLoaded) / delta)
		s.MiddleSpeed = (s.MiddleSpeed + s.Speed) / 2
	}
	if time.Since(s.StartTime).Seconds() > 5 {
		s.StartTime = time.Now()
		s.LastTime = time.Now()
		s.BytesLoaded = 0
	}
}

func (s *DownloadProgress) Complete() bool {
	return s.Pos >= s.To && s.To > 0
}

func (s *DownloadProgress) GetPercent() int {
	if s.To < 1 {
		return -1
	}

	loaded := s.Pos - s.From
	all := s.To - s.From

	return int(loaded * 100 / all)
}

func (s DownloadProgress) String() string {
	_, sm := s.GetSpeed()
	return fmt.Sprintf("P: %v %v %v %v%% %v/s", s.From, s.Pos, s.To, s.GetPercent(), ByteSize(uint64(sm)))
}
<commit_msg>change aver speed calc<commit_after>package progress

import (
	"fmt"
	"time"
)

type DownloadProgress struct {
	From int64 `json:"From"`
	To   int64 `json:"To"`
	Pos  int64 `json:"Pos"`

	BytesLoaded uint64    `json:"-"`
	Speed       uint64    `json:"-"`
	MiddleSpeed uint64    `json:"-"`
	LastTime    time.Time `json:"-"`
	StartTime   time.Time `json:"-"`

	ConnectTime time.Duration `json:"-"`
	IsLoading   bool          `json:"-"`
}

func (s *DownloadProgress) StartSpeed() {
	s.Speed = 0
	s.MiddleSpeed = 0
	s.BytesLoaded = 0
	s.StartTime = time.Now()
	s.LastTime = time.Now()
}

func (s *DownloadProgress) EndSpeed() {
	s.Speed = 0
	s.MiddleSpeed = 0
	s.BytesLoaded = 0
}

func (s *DownloadProgress) GetSpeed() (uint64, uint64) {
	return s.Speed, s.MiddleSpeed
}

func (s *DownloadProgress) MessureSpeed(realc int) {
	s.BytesLoaded += uint64(realc)

	delta := time.Since(s.StartTime).Seconds()
	if time.Since(s.LastTime).Seconds() > 0.1 {
		s.LastTime = time.Now()
		lstSpeed := s.Speed
		s.Speed = uint64(float64(s.BytesLoaded) / delta)
		s.MiddleSpeed = (s.MiddleSpeed + (s.Speed+lstSpeed)/2) / 2
	}
	if time.Since(s.StartTime).Seconds() > 5 {
		s.StartTime = time.Now()
		s.LastTime = time.Now()
		s.BytesLoaded = 0
	}
}

func (s *DownloadProgress) Complete() bool {
	return s.Pos >= s.To && s.To > 0
}

func (s *DownloadProgress) GetPercent() int {
	if s.To < 1 {
		return -1
	}

	loaded := s.Pos - s.From
	all := s.To - s.From

	return int(loaded * 100 / all)
}

func (s DownloadProgress) String() string {
	_, sm := s.GetSpeed()
	return fmt.Sprintf("P: %v %v %v %v%% %v/s", s.From, s.Pos, s.To, s.GetPercent(), ByteSize(uint64(sm)))
}
<|endoftext|>


<commit_before>package main

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

	"./blob"
	"./inet"
)

type FrameHead interface {
	RawHeader() []byte
}

type stringer interface {
	String() string
}

type FrameBody interface {
	// This should be ignored if HasHeader() is false.
	ModuleFrame

	FrameHead

	stringer
}

type ModuleFrame interface {
	HasHeader() bool

	// ParseHead takes a frame of bytes and returns two subsets or two nils and a
	// parsing error. The two subsets of `frame` which a module should return are:
	// - that beginning subset which the module identified as its own header
	// - the remainder subset which the module identified as its own header
	ParseHead() (FrameHead, FrameBody, error)
}

func decodeHexStream(input io.Reader) ([]byte, error) {
	chars, e := ioutil.ReadAll(os.Stdin)
	if e != nil {
		return nil, fmt.Errorf("reading from stdin: %s", e)
	}
	if len(chars) < 0 {
		return nil, fmt.Errorf("require non-zero fake datagram, got on chars\n")
	}

	onLeftHalf := true
	bytes := make([]byte, 0, 256)
	for _, char := range chars {
		ch := strings.ToUpper(strings.TrimSpace(string(char)))
		if len(ch) == 0 {
			continue
		}
		if len(ch) > 1 {
			return nil, fmt.Errorf(
				"got non ASCII char (some UTF-8 rune?); at col %d: '%s'", len(bytes), ch)
		}

		hex := byte(ch[0])
		if hex < 0 || hex > 255 {
			return nil, fmt.Errorf(
				"expected hex val of [0,f]; at col %d: '%s'", len(bytes), hex)
		}

		if onLeftHalf {
			bytes = append(bytes, hex<<4)
		} else {
			bytes[len(bytes)-1] = bytes[len(bytes)-1] & (hex << 4)
		}
		onLeftHalf = !onLeftHalf
	}

	return bytes, nil
}

func mustIntFromHex(hex byte) int {
	resp, e := getIntFromHex(hex)
	if e != nil {
		panic(e)
	}
	return resp
}

// Given a hex char, returns its corresponding integer: a value in [0,15]
func getIntFromHex(hex byte) (int, error) {
	// Char-to-rune table:
	//
	// | char    code point
	// +-------------------
	// | [0,9] = [48,57]
	// | [A,F] = [65,70]
	// | [a,f] = [97,102]

	runePoint := rune(hex)
	val := getValidHex(runePoint)
	if val == -1 {
		return -1, fmt.Errorf("invalid hex, got: '%c'", runePoint)
	}
	return val, nil // TODO(zacsh) finish converting to int [0,15]
}

func isValidHex(rn rune) bool { return getValidHex(rn) != -1 }

func getValidHex(rn rune) int {
	switch {
	case (rn >= 48 && rn <= 57): /*[0,9]*/
		fmt.Fprintf(os.Stderr, "think '%c' is [0,9]\n", rn) // TODO(zacsh) debugging; remove
	case (rn >= 65 && rn <= 70): /*[A,F]*/
		fmt.Fprintf(os.Stderr, "think '%c' is [A,F]\n", rn) // TODO(zacsh) debugging; remove
	case (rn >= 97 && rn <= 102): /*[a,f]*/
		fmt.Fprintf(os.Stderr, "think '%c' is [a,f]\n", rn) // TODO(zacsh) debugging; remove
	default:
		return -1
	}
	fmt.Fprintf(os.Stderr, "think '%c' IS hex; returning %d\n", rn, int(rn)) // TODO(zacsh) debugging; remove
	return int(rn)
}

func main() {
	bytes, e := decodeHexStream(os.Stdin)
	if e != nil {
		log.Fatalf("decoding hex from stdin: %v", e)
	}
	log.Printf("\nprocessing %d bytes of input...\n", len(bytes))

	ethFrame := inet.EthFrame{Blob: blob.ByteBlob{Data: bytes}}
	_, ipFrame, e := ethFrame.ParseHead()
	if e != nil {
		log.Fatalf("parsing ethernet frame: %s", e)
	}
	log.Printf("ethernet frame:\n%v\n", ethFrame.String())

	_, payload, e := ipFrame.ParseHead()
	if e != nil {
		log.Fatalf("parsing ip frame: %s", e)
	}
	log.Printf("ip frame:\n%v\n", ipFrame.String())
	log.Printf("above-IP level payload (eg: application-bound):\n%s\n", payload)
}
<commit_msg>utilizing neatly packaged hex decoder<commit_after>package main

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

	"./blob"
	"./inet"
	"github.com/jzacsh/hexint"
)

type FrameHead interface {
	RawHeader() []byte
}

type stringer interface {
	String() string
}

type FrameBody interface {
	// This should be ignored if HasHeader() is false.
	ModuleFrame

	FrameHead

	stringer
}

type ModuleFrame interface {
	HasHeader() bool

	// ParseHead takes a frame of bytes and returns two subsets or two nils and a
	// parsing error. The two subsets of `frame` which a module should return are:
	// - that beginning subset which the module identified as its own header
	// - the remainder subset which the module identified as its own header
	ParseHead() (FrameHead, FrameBody, error)
}

func decodeHexStream(input io.Reader) ([]byte, error) {
	chars, e := ioutil.ReadAll(os.Stdin)
	if e != nil {
		return nil, fmt.Errorf("reading from stdin: %s", e)
	}
	if len(chars) < 0 {
		return nil, fmt.Errorf("require non-zero fake datagram, got on chars\n")
	}

	onLeftHalf := true
	bytes := make([]byte, 0, 256)
	for _, char := range chars {
		ch := strings.TrimSpace(string(char))
		if len(ch) == 0 {
			continue
		}
		if len(ch) > 1 {
			return nil, fmt.Errorf(
				"got non ASCII char (some UTF-8 rune?); at col %d: '%s'", len(bytes), ch)
		}

		hex := byte(ch[0])
		integer, e := hexint.DecodeInt(hex)
		if e != nil {
			return nil, fmt.Errorf("input at [octet #%d] '%c': %v", len(bytes), hex, e)
		}

		if onLeftHalf {
			bytes = append(bytes, byte(integer<<4))
		} else {
			bytes[len(bytes)-1] = bytes[len(bytes)-1] & byte(integer<<4)
		}
		onLeftHalf = !onLeftHalf
	}

	return bytes, nil
}

func mustIntFromHex(hex byte) int {
	resp, e := getIntFromHex(hex)
	if e != nil {
		panic(e)
	}
	return resp
}

// Given a hex char, returns its corresponding integer: a value in [0,15]
func getIntFromHex(hex byte) (int, error) {
	// Char-to-rune table:
	//
	// | char    code point
	// +-------------------
	// | [0,9] = [48,57]
	// | [A,F] = [65,70]
	// | [a,f] = [97,102]

	runePoint := rune(hex)
	val := getValidHex(runePoint)
	if val == -1 {
		return -1, fmt.Errorf("invalid hex, got: '%c'", runePoint)
	}
	return val, nil // TODO(zacsh) finish converting to int [0,15]
}

func isValidHex(rn rune) bool { return getValidHex(rn) != -1 }

func getValidHex(rn rune) int {
	switch {
	case (rn >= 48 && rn <= 57): /*[0,9]*/
		fmt.Fprintf(os.Stderr, "think '%c' is [0,9]\n", rn) // TODO(zacsh) debugging; remove
	case (rn >= 65 && rn <= 70): /*[A,F]*/
		fmt.Fprintf(os.Stderr, "think '%c' is [A,F]\n", rn) // TODO(zacsh) debugging; remove
	case (rn >= 97 && rn <= 102): /*[a,f]*/
		fmt.Fprintf(os.Stderr, "think '%c' is [a,f]\n", rn) // TODO(zacsh) debugging; remove
	default:
		return -1
	}
	fmt.Fprintf(os.Stderr, "think '%c' IS hex; returning %d\n", rn, int(rn)) // TODO(zacsh) debugging; remove
	return int(rn)
}

func main() {
	bytes, e := decodeHexStream(os.Stdin)
	if e != nil {
		log.Fatalf("decoding hex from stdin: %v", e)
	}
	log.Printf("\nprocessing %d bytes of input...\n", len(bytes))

	ethFrame := inet.EthFrame{Blob: blob.ByteBlob{Data: bytes}}
	_, ipFrame, e := ethFrame.ParseHead()
	if e != nil {
		log.Fatalf("parsing ethernet frame: %s", e)
	}
	log.Printf("ethernet frame:\n%v\n", ethFrame.String())

	_, payload, e := ipFrame.ParseHead()
	if e != nil {
		log.Fatalf("parsing ip frame: %s", e)
	}
	log.Printf("ip frame:\n%v\n", ipFrame.String())
	log.Printf("above-IP level payload (eg: application-bound):\n%s\n", payload)
}
<|endoftext|>


<commit_before>package spoukfw

import (
	"strconv"
	"reflect"
	"time"
)

const (
	defConverter = "[spoukconverter] `%s`\n"
	prefixLogConverter  = "[spoukconverter][logger]"
	ErrorValueNotValidConvert = "Value not valid for converting"

)
var (
	acceptTypes []interface{} = []interface{}{
		"", 0, int64(0),
	}
)
type (
	SpoukConverter struct {
		logger *SpoukLogger
		value   interface{}
		result  interface{}
		stockFu map[string]func()
	}

)
func NewSpoukConverter() *SpoukConverter {
	f := &SpoukConverter{
		stockFu:make(map[string]func()),
	}
	f.logger = NewSpoukLogger(prefixLogConverter, nil)
	f.stockFu["string"] = f.stringToInt
	f.stockFu["string"] = f.stringToInt64
	return f
}
func (c *SpoukConverter) StrToInt() (*SpoukConverter) {
	if f, exists := c.stockFu["string"]; exists {
		f()
	}
	return c
}
func (c *SpoukConverter) StrToInt64() (*SpoukConverter) {
	if f, exists := c.stockFu["string"]; exists {
		f()
	}
	return c
}
//---------------------------------------------------------------------------
//  String to Int64
//---------------------------------------------------------------------------
func (c *SpoukConverter) stringToInt64() {
	c.stringToInt()
	if c.result != nil {
		c.result = int64(c.result.(int))
	} else {
		c.result = nil
	}
}
//---------------------------------------------------------------------------
//  String to Int
//---------------------------------------------------------------------------
func (c *SpoukConverter) stringToInt() {
	if r, err := strconv.Atoi(c.value.(string)); err != nil {
		c.logger.Printf(makeErrorMessage(defConverter, err.Error()).Error())
		c.result = nil
	} else {
		c.result = r
	}
}
//---------------------------------------------------------------------------
//  возвращает результат конвертации
//---------------------------------------------------------------------------
func (c *SpoukConverter) Result() interface{} {
	return c.result
}
//---------------------------------------------------------------------------
//  инциализация вводным значением
//---------------------------------------------------------------------------
func (c *SpoukConverter) Value(value interface{}) (*SpoukConverter) {
	if c.checkValue(value) {
		c.value = value
		return c
	}
	return nil
}
//---------------------------------------------------------------------------
//  проверка типа поступившего значения на возможность конвертации
//---------------------------------------------------------------------------
func (c *SpoukConverter) checkValue(value interface{}) bool {
	tValue := reflect.TypeOf(value)
	for _, x := range acceptTypes {
		if tValue == reflect.TypeOf(x) {
			return true
		}
	}
	c.logger.Printf(makeErrorMessage(defConverter, ErrorValueNotValidConvert).Error())
	return false
}

func (c *SpoukConverter) DirectStringtoInt64(v string) int64 {
	if res, err := strconv.Atoi(v); err != nil {
		c.logger.Printf(makeErrorMessage(defConverter, err.Error()).Error())
		return 0
	} else {
		return int64(res)
	}
}
//---------------------------------------------------------------------------
//  time convert
//---------------------------------------------------------------------------
func (c *SpoukConverter) ConvertHTMLDatetoUnix(date string) (int64, error) {
	result, err := time.Parse("2006-01-01", date)
	if err == nil {
		return result.Unix(), err
	} else {
		return 0, err
	}
}
func (c *SpoukConverter) ConvertUnixTimeToString(unixtime int64) string {
	return time.Unix(unixtime, 0).String()
}<commit_msg>adding converting time html form<commit_after>package spoukfw

import (
	"strconv"
	"reflect"
	"time"
)

const (
	defConverter = "[spoukconverter] `%s`\n"
	prefixLogConverter  = "[spoukconverter][logger]"
	ErrorValueNotValidConvert = "Value not valid for converting"

)
var (
	acceptTypes []interface{} = []interface{}{
		"", 0, int64(0),
	}
)
type (
	SpoukConverter struct {
		logger *SpoukLogger
		value   interface{}
		result  interface{}
		stockFu map[string]func()
	}

)
func NewSpoukConverter() *SpoukConverter {
	f := &SpoukConverter{
		stockFu:make(map[string]func()),
	}
	f.logger = NewSpoukLogger(prefixLogConverter, nil)
	f.stockFu["string"] = f.stringToInt
	f.stockFu["string"] = f.stringToInt64
	return f
}
func (c *SpoukConverter) StrToInt() (*SpoukConverter) {
	if f, exists := c.stockFu["string"]; exists {
		f()
	}
	return c
}
func (c *SpoukConverter) StrToInt64() (*SpoukConverter) {
	if f, exists := c.stockFu["string"]; exists {
		f()
	}
	return c
}
//---------------------------------------------------------------------------
//  String to Int64
//---------------------------------------------------------------------------
func (c *SpoukConverter) stringToInt64() {
	c.stringToInt()
	if c.result != nil {
		c.result = int64(c.result.(int))
	} else {
		c.result = nil
	}
}
//---------------------------------------------------------------------------
//  String to Int
//---------------------------------------------------------------------------
func (c *SpoukConverter) stringToInt() {
	if r, err := strconv.Atoi(c.value.(string)); err != nil {
		c.logger.Printf(makeErrorMessage(defConverter, err.Error()).Error())
		c.result = nil
	} else {
		c.result = r
	}
}
//---------------------------------------------------------------------------
//  возвращает результат конвертации
//---------------------------------------------------------------------------
func (c *SpoukConverter) Result() interface{} {
	return c.result
}
//---------------------------------------------------------------------------
//  инциализация вводным значением
//---------------------------------------------------------------------------
func (c *SpoukConverter) Value(value interface{}) (*SpoukConverter) {
	if c.checkValue(value) {
		c.value = value
		return c
	}
	return nil
}
//---------------------------------------------------------------------------
//  проверка типа поступившего значения на возможность конвертации
//---------------------------------------------------------------------------
func (c *SpoukConverter) checkValue(value interface{}) bool {
	tValue := reflect.TypeOf(value)
	for _, x := range acceptTypes {
		if tValue == reflect.TypeOf(x) {
			return true
		}
	}
	c.logger.Printf(makeErrorMessage(defConverter, ErrorValueNotValidConvert).Error())
	return false
}

func (c *SpoukConverter) DirectStringtoInt64(v string) int64 {
	if res, err := strconv.Atoi(v); err != nil {
		c.logger.Printf(makeErrorMessage(defConverter, err.Error()).Error())
		return 0
	} else {
		return int64(res)
	}
}
//---------------------------------------------------------------------------
//  time convert
//---------------------------------------------------------------------------
func (c *SpoukConverter) ConvertHTMLDatetoUnix(date string) (int64, error) {
	result, err := time.Parse("2006-01-02", date)
	if err == nil {
		return result.Unix(), err
	} else {
		return 0, err
	}
}
func (c *SpoukConverter) ConvertUnixTimeToString(unixtime int64) string {
	return time.Unix(unixtime, 0).String()
}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"strings"
)

func ExtractColumn(logContents string, column uint8) string {
	var buffer bytes.Buffer
    scanner := bufio.NewScanner(strings.NewReader(logContents))

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

        if line == "" {
            continue
        }

		cols := strings.Split(line, " ")

		switch column {
		case 0:
			buffer.WriteString(cols[0])
			buffer.WriteString(" ")
			buffer.WriteString(cols[1])
		case 1:
			buffer.WriteString(cols[2])
		case 2:
			buffer.WriteString(cols[3])

			for j := 4; j < len(cols); j++ {
				buffer.WriteString(" ")
				buffer.WriteString(cols[j])
			}
		}

		buffer.WriteString("\n")
	}

	return buffer.String()
}

func main() {
	var (
		column uint8
		buffer bytes.Buffer
	)

	_, err := fmt.Scan(&column)

	if err != nil {
		return
	}

	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {
		buffer.WriteString(scanner.Text())
		buffer.WriteString("\n")
	}

	fmt.Print(ExtractColumn(buffer.String(), column))
}
<commit_msg>style fix: ran `go fmt' on `extract-column.go'<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"strings"
)

func ExtractColumn(logContents string, column uint8) string {
	var buffer bytes.Buffer
	scanner := bufio.NewScanner(strings.NewReader(logContents))

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

		if line == "" {
			continue
		}

		cols := strings.Split(line, " ")

		switch column {
		case 0:
			buffer.WriteString(cols[0])
			buffer.WriteString(" ")
			buffer.WriteString(cols[1])
		case 1:
			buffer.WriteString(cols[2])
		case 2:
			buffer.WriteString(cols[3])

			for j := 4; j < len(cols); j++ {
				buffer.WriteString(" ")
				buffer.WriteString(cols[j])
			}
		}

		buffer.WriteString("\n")
	}

	return buffer.String()
}

func main() {
	var (
		column uint8
		buffer bytes.Buffer
	)

	_, err := fmt.Scan(&column)

	if err != nil {
		return
	}

	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {
		buffer.WriteString(scanner.Text())
		buffer.WriteString("\n")
	}

	fmt.Print(ExtractColumn(buffer.String(), column))
}
<|endoftext|>


<commit_before>// Copyright 2018 Axel Etcheverry. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package opentracing

import (
	"bytes"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/rs/zerolog/log"
)

// RequestIgnorerFunc is the type of a function for use in
// WithServerRequestIgnorer.
type RequestIgnorerFunc func(*http.Request) bool

// IgnoreNone is a RequestIgnorerFunc which ignores no requests.
func IgnoreNone(*http.Request) bool {
	return false
}

// Handler opentracing
func Handler(tracer opentracing.Tracer, ignore RequestIgnorerFunc) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if ignore(req) {
				next.ServeHTTP(w, req)

				return
			}

			wireContext, err := tracer.Extract(
				opentracing.TextMap,
				opentracing.HTTPHeadersCarrier(req.Header),
			)
			if err != nil {
				log.Debug().Err(err).Msg("trying to extract span")
			}

			path := routeRequestName(req)

			span := tracer.StartSpan(path, ext.RPCServerOption(wireContext))
			defer span.Finish()

			ctx := opentracing.ContextWithSpan(req.Context(), span)

			req = req.WithContext(ctx)

			next.ServeHTTP(w, req)
		})
	}
}

func routeRequestName(req *http.Request) string {
	route := mux.CurrentRoute(req)
	if route != nil {
		tpl, err := route.GetPathTemplate()
		if err == nil {
			return req.Method + " " + massageTemplate(tpl)
		}
	}

	return serverRequestName(req)
}

func serverRequestName(req *http.Request) string {
	var b strings.Builder
	b.Grow(len(req.Method) + len(req.URL.Path) + 1)
	b.WriteString(req.Method)
	b.WriteByte(' ')
	b.WriteString(req.URL.Path)
	return b.String()
}

// massageTemplate removes the regexp patterns from template variables.
func massageTemplate(tpl string) string {
	braces := braceIndices(tpl)
	if len(braces) == 0 {
		return tpl
	}

	buf := bytes.NewBuffer(make([]byte, 0, len(tpl)))
	for i := 0; i < len(tpl); {
		var j int
		if i < braces[0] {
			j = braces[0]
			buf.WriteString(tpl[i:j])
		} else {
			j = braces[1]
			field := tpl[i:j]

			if colon := strings.IndexRune(field, ':'); colon >= 0 {
				buf.WriteString(field[:colon])
				buf.WriteRune('}')
			} else {
				buf.WriteString(field)
			}

			braces = braces[2:]
			if len(braces) == 0 {
				buf.WriteString(tpl[j:])
				break
			}
		}

		i = j
	}

	return buf.String()
}

// Copied/adapted from gorilla/mux. The original version checks
// that the braces are matched up correctly; we assume they are,
// as otherwise the path wouldn't have been registered correctly.
func braceIndices(s string) []int {
	var level, idx int
	var idxs []int

	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '{':
			if level++; level == 1 {
				idx = i
			}
		case '}':
			if level--; level == 0 {
				idxs = append(idxs, idx, i+1)
			}
		}
	}

	return idxs
}
<commit_msg>feat(opentracing): added log fields to span<commit_after>// Copyright 2018 Axel Etcheverry. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package opentracing

import (
	"bytes"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	otlog "github.com/opentracing/opentracing-go/log"
	"github.com/rs/zerolog/log"
)

// RequestIgnorerFunc is the type of a function for use in
// WithServerRequestIgnorer.
type RequestIgnorerFunc func(*http.Request) bool

// IgnoreNone is a RequestIgnorerFunc which ignores no requests.
func IgnoreNone(*http.Request) bool {
	return false
}

// Handler opentracing
func Handler(tracer opentracing.Tracer, ignore RequestIgnorerFunc) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			if ignore(req) {
				next.ServeHTTP(w, req)

				return
			}

			wireContext, err := tracer.Extract(
				opentracing.TextMap,
				opentracing.HTTPHeadersCarrier(req.Header),
			)
			if err != nil {
				log.Debug().Err(err).Msg("trying to extract span")
			}

			path := routeRequestName(req)

			span := tracer.StartSpan(path, ext.RPCServerOption(wireContext))
			defer span.Finish()

			params := mux.Vars(req)

			fields := []otlog.Field{}
			for k, v := range params {
				fields = append(fields, otlog.String(k, v))
			}

			if len(fields) > 0 {
				span.LogFields(fields...)
			}

			ctx := opentracing.ContextWithSpan(req.Context(), span)

			req = req.WithContext(ctx)

			next.ServeHTTP(w, req)
		})
	}
}

func routeRequestName(req *http.Request) string {
	route := mux.CurrentRoute(req)
	if route != nil {
		tpl, err := route.GetPathTemplate()
		if err == nil {
			return req.Method + " " + massageTemplate(tpl)
		}
	}

	return serverRequestName(req)
}

func serverRequestName(req *http.Request) string {
	var b strings.Builder
	b.Grow(len(req.Method) + len(req.URL.Path) + 1)
	b.WriteString(req.Method)
	b.WriteByte(' ')
	b.WriteString(req.URL.Path)
	return b.String()
}

// massageTemplate removes the regexp patterns from template variables.
func massageTemplate(tpl string) string {
	braces := braceIndices(tpl)
	if len(braces) == 0 {
		return tpl
	}

	buf := bytes.NewBuffer(make([]byte, 0, len(tpl)))
	for i := 0; i < len(tpl); {
		var j int
		if i < braces[0] {
			j = braces[0]
			buf.WriteString(tpl[i:j])
		} else {
			j = braces[1]
			field := tpl[i:j]

			if colon := strings.IndexRune(field, ':'); colon >= 0 {
				buf.WriteString(field[:colon])
				buf.WriteRune('}')
			} else {
				buf.WriteString(field)
			}

			braces = braces[2:]
			if len(braces) == 0 {
				buf.WriteString(tpl[j:])
				break
			}
		}

		i = j
	}

	return buf.String()
}

// Copied/adapted from gorilla/mux. The original version checks
// that the braces are matched up correctly; we assume they are,
// as otherwise the path wouldn't have been registered correctly.
func braceIndices(s string) []int {
	var level, idx int
	var idxs []int

	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '{':
			if level++; level == 1 {
				idx = i
			}
		case '}':
			if level--; level == 0 {
				idxs = append(idxs, idx, i+1)
			}
		}
	}

	return idxs
}
<|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 preemption

import (
	"fmt"
	"math"

	"k8s.io/api/core/v1"
	"k8s.io/client-go/tools/record"
	"k8s.io/klog"
	"k8s.io/kubernetes/pkg/api/v1/resource"
	v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
	"k8s.io/kubernetes/pkg/kubelet/events"
	"k8s.io/kubernetes/pkg/kubelet/eviction"
	"k8s.io/kubernetes/pkg/kubelet/lifecycle"
	"k8s.io/kubernetes/pkg/kubelet/metrics"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/kubelet/util/format"
	"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
)

const message = "Preempted in order to admit critical pod"

// CriticalPodAdmissionHandler is an AdmissionFailureHandler that handles admission failure for Critical Pods.
// If the ONLY admission failures are due to insufficient resources, then CriticalPodAdmissionHandler evicts pods
// so that the critical pod can be admitted.  For evictions, the CriticalPodAdmissionHandler evicts a set of pods that
// frees up the required resource requests.  The set of pods is designed to minimize impact, and is prioritized according to the ordering:
// minimal impact for guaranteed pods > minimal impact for burstable pods > minimal impact for besteffort pods.
// minimal impact is defined as follows: fewest pods evicted > fewest total requests of pods.
// finding the fewest total requests of pods is considered besteffort.
type CriticalPodAdmissionHandler struct {
	getPodsFunc eviction.ActivePodsFunc
	killPodFunc eviction.KillPodFunc
	recorder    record.EventRecorder
}

var _ lifecycle.AdmissionFailureHandler = &CriticalPodAdmissionHandler{}

func NewCriticalPodAdmissionHandler(getPodsFunc eviction.ActivePodsFunc, killPodFunc eviction.KillPodFunc, recorder record.EventRecorder) *CriticalPodAdmissionHandler {
	return &CriticalPodAdmissionHandler{
		getPodsFunc: getPodsFunc,
		killPodFunc: killPodFunc,
		recorder:    recorder,
	}
}

// HandleAdmissionFailure gracefully handles admission rejection, and, in some cases,
// to allow admission of the pod despite its previous failure.
func (c *CriticalPodAdmissionHandler) HandleAdmissionFailure(admitPod *v1.Pod, failureReasons []predicates.PredicateFailureReason) (bool, []predicates.PredicateFailureReason, error) {
	if !kubetypes.IsCriticalPod(admitPod) {
		return false, failureReasons, nil
	}
	// InsufficientResourceError is not a reason to reject a critical pod.
	// Instead of rejecting, we free up resources to admit it, if no other reasons for rejection exist.
	nonResourceReasons := []predicates.PredicateFailureReason{}
	resourceReasons := []*admissionRequirement{}
	for _, reason := range failureReasons {
		if r, ok := reason.(*predicates.InsufficientResourceError); ok {
			resourceReasons = append(resourceReasons, &admissionRequirement{
				resourceName: r.ResourceName,
				quantity:     r.GetInsufficientAmount(),
			})
		} else {
			nonResourceReasons = append(nonResourceReasons, reason)
		}
	}
	if len(nonResourceReasons) > 0 {
		// Return only reasons that are not resource related, since critical pods cannot fail admission for resource reasons.
		return false, nonResourceReasons, nil
	}
	err := c.evictPodsToFreeRequests(admitPod, admissionRequirementList(resourceReasons))
	// if no error is returned, preemption succeeded and the pod is safe to admit.
	return err == nil, nil, err
}

// evictPodsToFreeRequests takes a list of insufficient resources, and attempts to free them by evicting pods
// based on requests.  For example, if the only insufficient resource is 200Mb of memory, this function could
// evict a pod with request=250Mb.
func (c *CriticalPodAdmissionHandler) evictPodsToFreeRequests(admitPod *v1.Pod, insufficientResources admissionRequirementList) error {
	podsToPreempt, err := getPodsToPreempt(admitPod, c.getPodsFunc(), insufficientResources)
	if err != nil {
		return fmt.Errorf("preemption: error finding a set of pods to preempt: %v", err)
	}
	klog.Infof("preemption: attempting to evict pods %v, in order to free up resources: %s", podsToPreempt, insufficientResources.toString())
	for _, pod := range podsToPreempt {
		status := v1.PodStatus{
			Phase:   v1.PodFailed,
			Message: message,
			Reason:  events.PreemptContainer,
		}
		// record that we are evicting the pod
		c.recorder.Eventf(pod, v1.EventTypeWarning, events.PreemptContainer, message)
		// this is a blocking call and should only return when the pod and its containers are killed.
		err := c.killPodFunc(pod, status, nil)
		if err != nil {
			klog.Warningf("preemption: pod %s failed to evict %v", format.Pod(pod), err)
			// In future syncPod loops, the kubelet will retry the pod deletion steps that it was stuck on.
			continue
		}
		if len(insufficientResources) > 0 {
			metrics.Preemptions.WithLabelValues(insufficientResources[0].resourceName.String()).Inc()
		} else {
			metrics.Preemptions.WithLabelValues("").Inc()
		}
		klog.Infof("preemption: pod %s evicted successfully", format.Pod(pod))
	}
	return nil
}

// getPodsToPreempt returns a list of pods that could be preempted to free requests >= requirements
func getPodsToPreempt(pod *v1.Pod, pods []*v1.Pod, requirements admissionRequirementList) ([]*v1.Pod, error) {
	bestEffortPods, burstablePods, guaranteedPods := sortPodsByQOS(pod, pods)

	// make sure that pods exist to reclaim the requirements
	unableToMeetRequirements := requirements.subtract(append(append(bestEffortPods, burstablePods...), guaranteedPods...)...)
	if len(unableToMeetRequirements) > 0 {
		return nil, fmt.Errorf("no set of running pods found to reclaim resources: %v", unableToMeetRequirements.toString())
	}
	// find the guaranteed pods we would need to evict if we already evicted ALL burstable and besteffort pods.
	guarateedToEvict, err := getPodsToPreemptByDistance(guaranteedPods, requirements.subtract(append(bestEffortPods, burstablePods...)...))
	if err != nil {
		return nil, err
	}
	// Find the burstable pods we would need to evict if we already evicted ALL besteffort pods, and the required guaranteed pods.
	burstableToEvict, err := getPodsToPreemptByDistance(burstablePods, requirements.subtract(append(bestEffortPods, guarateedToEvict...)...))
	if err != nil {
		return nil, err
	}
	// Find the besteffort pods we would need to evict if we already evicted the required guaranteed and burstable pods.
	bestEffortToEvict, err := getPodsToPreemptByDistance(bestEffortPods, requirements.subtract(append(burstableToEvict, guarateedToEvict...)...))
	if err != nil {
		return nil, err
	}
	return append(append(bestEffortToEvict, burstableToEvict...), guarateedToEvict...), nil
}

// getPodsToPreemptByDistance finds the pods that have pod requests >= admission requirements.
// Chooses pods that minimize "distance" to the requirements.
// If more than one pod exists that fulfills the remaining requirements,
// it chooses the pod that has the "smaller resource request"
// This method, by repeatedly choosing the pod that fulfills as much of the requirements as possible,
// attempts to minimize the number of pods returned.
func getPodsToPreemptByDistance(pods []*v1.Pod, requirements admissionRequirementList) ([]*v1.Pod, error) {
	podsToEvict := []*v1.Pod{}
	// evict pods by shortest distance from remaining requirements, updating requirements every round.
	for len(requirements) > 0 {
		if len(pods) == 0 {
			return nil, fmt.Errorf("no set of running pods found to reclaim resources: %v", requirements.toString())
		}
		// all distances must be less than len(requirements), because the max distance for a single requirement is 1
		bestDistance := float64(len(requirements) + 1)
		bestPodIndex := 0
		// Find the pod with the smallest distance from requirements
		// Or, in the case of two equidistant pods, find the pod with "smaller" resource requests.
		for i, pod := range pods {
			dist := requirements.distance(pod)
			if dist < bestDistance || (bestDistance == dist && smallerResourceRequest(pod, pods[bestPodIndex])) {
				bestDistance = dist
				bestPodIndex = i
			}
		}
		// subtract the pod from requirements, and transfer the pod from input-pods to pods-to-evicted
		requirements = requirements.subtract(pods[bestPodIndex])
		podsToEvict = append(podsToEvict, pods[bestPodIndex])
		pods[bestPodIndex] = pods[len(pods)-1]
		pods = pods[:len(pods)-1]
	}
	return podsToEvict, nil
}

type admissionRequirement struct {
	resourceName v1.ResourceName
	quantity     int64
}

type admissionRequirementList []*admissionRequirement

// distance returns distance of the pods requests from the admissionRequirements.
// The distance is measured by the fraction of the requirement satisfied by the pod,
// so that each requirement is weighted equally, regardless of absolute magnitude.
func (a admissionRequirementList) distance(pod *v1.Pod) float64 {
	dist := float64(0)
	for _, req := range a {
		remainingRequest := float64(req.quantity - resource.GetResourceRequest(pod, req.resourceName))
		if remainingRequest > 0 {
			dist += math.Pow(remainingRequest/float64(req.quantity), 2)
		}
	}
	return dist
}

// subtract returns a new admissionRequirementList containing remaining requirements if the provided pod
// were to be preempted
func (a admissionRequirementList) subtract(pods ...*v1.Pod) admissionRequirementList {
	newList := []*admissionRequirement{}
	for _, req := range a {
		newQuantity := req.quantity
		for _, pod := range pods {
			newQuantity -= resource.GetResourceRequest(pod, req.resourceName)
			if newQuantity <= 0 {
				break
			}
		}
		if newQuantity > 0 {
			newList = append(newList, &admissionRequirement{
				resourceName: req.resourceName,
				quantity:     newQuantity,
			})
		}
	}
	return newList
}

func (a admissionRequirementList) toString() string {
	s := "["
	for _, req := range a {
		s += fmt.Sprintf("(res: %v, q: %d), ", req.resourceName, req.quantity)
	}
	return s + "]"
}

// sortPodsByQOS returns lists containing besteffort, burstable, and guaranteed pods that
// can be preempted by preemptor pod.
func sortPodsByQOS(preemptor *v1.Pod, pods []*v1.Pod) (bestEffort, burstable, guaranteed []*v1.Pod) {
	for _, pod := range pods {
		if kubetypes.Preemptable(preemptor, pod) {
			switch v1qos.GetPodQOS(pod) {
			case v1.PodQOSBestEffort:
				bestEffort = append(bestEffort, pod)
			case v1.PodQOSBurstable:
				burstable = append(burstable, pod)
			case v1.PodQOSGuaranteed:
				guaranteed = append(guaranteed, pod)
			default:
			}
		}
	}

	return
}

// smallerResourceRequest returns true if pod1 has a smaller request than pod2
func smallerResourceRequest(pod1 *v1.Pod, pod2 *v1.Pod) bool {
	priorityList := []v1.ResourceName{
		v1.ResourceMemory,
		v1.ResourceCPU,
	}
	for _, res := range priorityList {
		req1 := resource.GetResourceRequest(pod1, res)
		req2 := resource.GetResourceRequest(pod2, res)
		if req1 < req2 {
			return true
		} else if req1 > req2 {
			return false
		}
	}
	return true
}
<commit_msg>preemption: typo cleanup<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 preemption

import (
	"fmt"
	"math"

	"k8s.io/api/core/v1"
	"k8s.io/client-go/tools/record"
	"k8s.io/klog"
	"k8s.io/kubernetes/pkg/api/v1/resource"
	v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
	"k8s.io/kubernetes/pkg/kubelet/events"
	"k8s.io/kubernetes/pkg/kubelet/eviction"
	"k8s.io/kubernetes/pkg/kubelet/lifecycle"
	"k8s.io/kubernetes/pkg/kubelet/metrics"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/kubelet/util/format"
	"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
)

const message = "Preempted in order to admit critical pod"

// CriticalPodAdmissionHandler is an AdmissionFailureHandler that handles admission failure for Critical Pods.
// If the ONLY admission failures are due to insufficient resources, then CriticalPodAdmissionHandler evicts pods
// so that the critical pod can be admitted.  For evictions, the CriticalPodAdmissionHandler evicts a set of pods that
// frees up the required resource requests.  The set of pods is designed to minimize impact, and is prioritized according to the ordering:
// minimal impact for guaranteed pods > minimal impact for burstable pods > minimal impact for besteffort pods.
// minimal impact is defined as follows: fewest pods evicted > fewest total requests of pods.
// finding the fewest total requests of pods is considered besteffort.
type CriticalPodAdmissionHandler struct {
	getPodsFunc eviction.ActivePodsFunc
	killPodFunc eviction.KillPodFunc
	recorder    record.EventRecorder
}

var _ lifecycle.AdmissionFailureHandler = &CriticalPodAdmissionHandler{}

func NewCriticalPodAdmissionHandler(getPodsFunc eviction.ActivePodsFunc, killPodFunc eviction.KillPodFunc, recorder record.EventRecorder) *CriticalPodAdmissionHandler {
	return &CriticalPodAdmissionHandler{
		getPodsFunc: getPodsFunc,
		killPodFunc: killPodFunc,
		recorder:    recorder,
	}
}

// HandleAdmissionFailure gracefully handles admission rejection, and, in some cases,
// to allow admission of the pod despite its previous failure.
func (c *CriticalPodAdmissionHandler) HandleAdmissionFailure(admitPod *v1.Pod, failureReasons []predicates.PredicateFailureReason) (bool, []predicates.PredicateFailureReason, error) {
	if !kubetypes.IsCriticalPod(admitPod) {
		return false, failureReasons, nil
	}
	// InsufficientResourceError is not a reason to reject a critical pod.
	// Instead of rejecting, we free up resources to admit it, if no other reasons for rejection exist.
	nonResourceReasons := []predicates.PredicateFailureReason{}
	resourceReasons := []*admissionRequirement{}
	for _, reason := range failureReasons {
		if r, ok := reason.(*predicates.InsufficientResourceError); ok {
			resourceReasons = append(resourceReasons, &admissionRequirement{
				resourceName: r.ResourceName,
				quantity:     r.GetInsufficientAmount(),
			})
		} else {
			nonResourceReasons = append(nonResourceReasons, reason)
		}
	}
	if len(nonResourceReasons) > 0 {
		// Return only reasons that are not resource related, since critical pods cannot fail admission for resource reasons.
		return false, nonResourceReasons, nil
	}
	err := c.evictPodsToFreeRequests(admitPod, admissionRequirementList(resourceReasons))
	// if no error is returned, preemption succeeded and the pod is safe to admit.
	return err == nil, nil, err
}

// evictPodsToFreeRequests takes a list of insufficient resources, and attempts to free them by evicting pods
// based on requests.  For example, if the only insufficient resource is 200Mb of memory, this function could
// evict a pod with request=250Mb.
func (c *CriticalPodAdmissionHandler) evictPodsToFreeRequests(admitPod *v1.Pod, insufficientResources admissionRequirementList) error {
	podsToPreempt, err := getPodsToPreempt(admitPod, c.getPodsFunc(), insufficientResources)
	if err != nil {
		return fmt.Errorf("preemption: error finding a set of pods to preempt: %v", err)
	}
	klog.Infof("preemption: attempting to evict pods %v, in order to free up resources: %s", podsToPreempt, insufficientResources.toString())
	for _, pod := range podsToPreempt {
		status := v1.PodStatus{
			Phase:   v1.PodFailed,
			Message: message,
			Reason:  events.PreemptContainer,
		}
		// record that we are evicting the pod
		c.recorder.Eventf(pod, v1.EventTypeWarning, events.PreemptContainer, message)
		// this is a blocking call and should only return when the pod and its containers are killed.
		err := c.killPodFunc(pod, status, nil)
		if err != nil {
			klog.Warningf("preemption: pod %s failed to evict %v", format.Pod(pod), err)
			// In future syncPod loops, the kubelet will retry the pod deletion steps that it was stuck on.
			continue
		}
		if len(insufficientResources) > 0 {
			metrics.Preemptions.WithLabelValues(insufficientResources[0].resourceName.String()).Inc()
		} else {
			metrics.Preemptions.WithLabelValues("").Inc()
		}
		klog.Infof("preemption: pod %s evicted successfully", format.Pod(pod))
	}
	return nil
}

// getPodsToPreempt returns a list of pods that could be preempted to free requests >= requirements
func getPodsToPreempt(pod *v1.Pod, pods []*v1.Pod, requirements admissionRequirementList) ([]*v1.Pod, error) {
	bestEffortPods, burstablePods, guaranteedPods := sortPodsByQOS(pod, pods)

	// make sure that pods exist to reclaim the requirements
	unableToMeetRequirements := requirements.subtract(append(append(bestEffortPods, burstablePods...), guaranteedPods...)...)
	if len(unableToMeetRequirements) > 0 {
		return nil, fmt.Errorf("no set of running pods found to reclaim resources: %v", unableToMeetRequirements.toString())
	}
	// find the guaranteed pods we would need to evict if we already evicted ALL burstable and besteffort pods.
	guaranteedToEvict, err := getPodsToPreemptByDistance(guaranteedPods, requirements.subtract(append(bestEffortPods, burstablePods...)...))
	if err != nil {
		return nil, err
	}
	// Find the burstable pods we would need to evict if we already evicted ALL besteffort pods, and the required guaranteed pods.
	burstableToEvict, err := getPodsToPreemptByDistance(burstablePods, requirements.subtract(append(bestEffortPods, guaranteedToEvict...)...))
	if err != nil {
		return nil, err
	}
	// Find the besteffort pods we would need to evict if we already evicted the required guaranteed and burstable pods.
	bestEffortToEvict, err := getPodsToPreemptByDistance(bestEffortPods, requirements.subtract(append(burstableToEvict, guaranteedToEvict...)...))
	if err != nil {
		return nil, err
	}
	return append(append(bestEffortToEvict, burstableToEvict...), guaranteedToEvict...), nil
}

// getPodsToPreemptByDistance finds the pods that have pod requests >= admission requirements.
// Chooses pods that minimize "distance" to the requirements.
// If more than one pod exists that fulfills the remaining requirements,
// it chooses the pod that has the "smaller resource request"
// This method, by repeatedly choosing the pod that fulfills as much of the requirements as possible,
// attempts to minimize the number of pods returned.
func getPodsToPreemptByDistance(pods []*v1.Pod, requirements admissionRequirementList) ([]*v1.Pod, error) {
	podsToEvict := []*v1.Pod{}
	// evict pods by shortest distance from remaining requirements, updating requirements every round.
	for len(requirements) > 0 {
		if len(pods) == 0 {
			return nil, fmt.Errorf("no set of running pods found to reclaim resources: %v", requirements.toString())
		}
		// all distances must be less than len(requirements), because the max distance for a single requirement is 1
		bestDistance := float64(len(requirements) + 1)
		bestPodIndex := 0
		// Find the pod with the smallest distance from requirements
		// Or, in the case of two equidistant pods, find the pod with "smaller" resource requests.
		for i, pod := range pods {
			dist := requirements.distance(pod)
			if dist < bestDistance || (bestDistance == dist && smallerResourceRequest(pod, pods[bestPodIndex])) {
				bestDistance = dist
				bestPodIndex = i
			}
		}
		// subtract the pod from requirements, and transfer the pod from input-pods to pods-to-evicted
		requirements = requirements.subtract(pods[bestPodIndex])
		podsToEvict = append(podsToEvict, pods[bestPodIndex])
		pods[bestPodIndex] = pods[len(pods)-1]
		pods = pods[:len(pods)-1]
	}
	return podsToEvict, nil
}

type admissionRequirement struct {
	resourceName v1.ResourceName
	quantity     int64
}

type admissionRequirementList []*admissionRequirement

// distance returns distance of the pods requests from the admissionRequirements.
// The distance is measured by the fraction of the requirement satisfied by the pod,
// so that each requirement is weighted equally, regardless of absolute magnitude.
func (a admissionRequirementList) distance(pod *v1.Pod) float64 {
	dist := float64(0)
	for _, req := range a {
		remainingRequest := float64(req.quantity - resource.GetResourceRequest(pod, req.resourceName))
		if remainingRequest > 0 {
			dist += math.Pow(remainingRequest/float64(req.quantity), 2)
		}
	}
	return dist
}

// subtract returns a new admissionRequirementList containing remaining requirements if the provided pod
// were to be preempted
func (a admissionRequirementList) subtract(pods ...*v1.Pod) admissionRequirementList {
	newList := []*admissionRequirement{}
	for _, req := range a {
		newQuantity := req.quantity
		for _, pod := range pods {
			newQuantity -= resource.GetResourceRequest(pod, req.resourceName)
			if newQuantity <= 0 {
				break
			}
		}
		if newQuantity > 0 {
			newList = append(newList, &admissionRequirement{
				resourceName: req.resourceName,
				quantity:     newQuantity,
			})
		}
	}
	return newList
}

func (a admissionRequirementList) toString() string {
	s := "["
	for _, req := range a {
		s += fmt.Sprintf("(res: %v, q: %d), ", req.resourceName, req.quantity)
	}
	return s + "]"
}

// sortPodsByQOS returns lists containing besteffort, burstable, and guaranteed pods that
// can be preempted by preemptor pod.
func sortPodsByQOS(preemptor *v1.Pod, pods []*v1.Pod) (bestEffort, burstable, guaranteed []*v1.Pod) {
	for _, pod := range pods {
		if kubetypes.Preemptable(preemptor, pod) {
			switch v1qos.GetPodQOS(pod) {
			case v1.PodQOSBestEffort:
				bestEffort = append(bestEffort, pod)
			case v1.PodQOSBurstable:
				burstable = append(burstable, pod)
			case v1.PodQOSGuaranteed:
				guaranteed = append(guaranteed, pod)
			default:
			}
		}
	}

	return
}

// smallerResourceRequest returns true if pod1 has a smaller request than pod2
func smallerResourceRequest(pod1 *v1.Pod, pod2 *v1.Pod) bool {
	priorityList := []v1.ResourceName{
		v1.ResourceMemory,
		v1.ResourceCPU,
	}
	for _, res := range priorityList {
		req1 := resource.GetResourceRequest(pod1, res)
		req2 := resource.GetResourceRequest(pod2, res)
		if req1 < req2 {
			return true
		} else if req1 > req2 {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Rook 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 file

import (
	"fmt"

	"github.com/rook/rook/pkg/operator/k8sutil"

	"github.com/pkg/errors"
	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
	"github.com/rook/rook/pkg/clusterd"
	cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
	"github.com/rook/rook/pkg/operator/ceph/file/mds"
	"github.com/rook/rook/pkg/operator/ceph/pool"
)

const (
	dataPoolSuffix     = "data"
	metaDataPoolSuffix = "metadata"
)

// Filesystem represents an instance of a Ceph filesystem (CephFS)
type Filesystem struct {
	Name      string
	Namespace string
}

// createFilesystem creates a Ceph filesystem with metadata servers
func createFilesystem(
	context *clusterd.Context,
	clusterInfo *cephclient.ClusterInfo,
	fs cephv1.CephFilesystem,
	clusterSpec *cephv1.ClusterSpec,
	ownerInfo *k8sutil.OwnerInfo,
	dataDirHostPath string,
) error {
	logger.Infof("start running mdses for filesystem %q", fs.Name)
	c := mds.NewCluster(clusterInfo, context, clusterSpec, fs, ownerInfo, dataDirHostPath)
	if err := c.Start(); err != nil {
		return err
	}

	if len(fs.Spec.DataPools) != 0 {
		f := newFS(fs.Name, fs.Namespace)
		if err := f.doFilesystemCreate(context, clusterInfo, clusterSpec, fs.Spec); err != nil {
			return errors.Wrapf(err, "failed to create filesystem %q", fs.Name)
		}
	}
	if fs.Spec.MetadataServer.ActiveStandby {
		if err := cephclient.AllowStandbyReplay(context, clusterInfo, fs.Name, fs.Spec.MetadataServer.ActiveStandby); err != nil {
			return errors.Wrapf(err, "failed to set allow_standby_replay to filesystem %q", fs.Name)
		}
	}

	// set the number of active mds instances
	if fs.Spec.MetadataServer.ActiveCount > 1 {
		if err := cephclient.SetNumMDSRanks(context, clusterInfo, fs.Name, fs.Spec.MetadataServer.ActiveCount); err != nil {
			logger.Warningf("failed setting active mds count to %d. %v", fs.Spec.MetadataServer.ActiveCount, err)
		}
	}

	return nil
}

// deleteFilesystem deletes the filesystem from Ceph
func deleteFilesystem(
	context *clusterd.Context,
	clusterInfo *cephclient.ClusterInfo,
	fs cephv1.CephFilesystem,
	clusterSpec *cephv1.ClusterSpec,
	ownerInfo *k8sutil.OwnerInfo,
	dataDirHostPath string,
) error {
	c := mds.NewCluster(clusterInfo, context, clusterSpec, fs, ownerInfo, dataDirHostPath)

	// Delete mds CephX keys and configuration in centralized mon database
	replicas := fs.Spec.MetadataServer.ActiveCount * 2
	for i := 0; i < int(replicas); i++ {
		daemonLetterID := k8sutil.IndexToName(i)
		daemonName := fmt.Sprintf("%s-%s", fs.Name, daemonLetterID)

		err := c.DeleteMdsCephObjects(daemonName)
		if err != nil {
			return errors.Wrapf(err, "failed to delete mds ceph objects for filesystem %q", fs.Name)
		}
	}

	// The most important part of deletion is that the filesystem gets removed from Ceph
	// The K8s resources will already be removed with the K8s owner references
	if err := downFilesystem(context, clusterInfo, fs.Name); err != nil {
		// If the fs isn't deleted from Ceph, leave the daemons so it can still be used.
		// Log the error for best effort and continue
		logger.Warningf("continuing to remove filesystem CR even though downing the filesystem failed. %v", err)
	}

	// Permanently remove the filesystem if it was created by rook and the spec does not prevent it.
	if len(fs.Spec.DataPools) != 0 && !fs.Spec.PreserveFilesystemOnDelete {
		if err := cephclient.RemoveFilesystem(context, clusterInfo, fs.Name, fs.Spec.PreservePoolsOnDelete); err != nil {
			// log the error for best effort and continue
			logger.Warningf("continuing to remove filesystem CR even though removal failed. %v", err)
		}
	}
	return nil
}

func validateFilesystem(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, f *cephv1.CephFilesystem) error {
	if f.Name == "" {
		return errors.New("missing name")
	}
	if f.Namespace == "" {
		return errors.New("missing namespace")
	}
	if f.Spec.MetadataServer.ActiveCount < 1 {
		return errors.New("MetadataServer.ActiveCount must be at least 1")
	}
	// No data pool means that we expect the fs to exist already
	if len(f.Spec.DataPools) == 0 {
		return nil
	}
	if err := pool.ValidatePoolSpec(context, clusterInfo, clusterSpec, &f.Spec.MetadataPool); err != nil {
		return errors.Wrap(err, "invalid metadata pool")
	}
	for _, p := range f.Spec.DataPools {
		localpoolSpec := p.PoolSpec
		if err := pool.ValidatePoolSpec(context, clusterInfo, clusterSpec, &localpoolSpec); err != nil {
			return errors.Wrap(err, "Invalid data pool")
		}
	}

	return nil
}

// newFS creates a new instance of the file (MDS) service
func newFS(name, namespace string) *Filesystem {
	return &Filesystem{
		Name:      name,
		Namespace: namespace,
	}
}

// createOrUpdatePools function sets the sizes for MetadataPool and dataPool
func createOrUpdatePools(f *Filesystem, context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, spec cephv1.FilesystemSpec) error {
	// generating the metadata pool's name
	metadataPool := cephv1.NamedPoolSpec{
		Name:     generateMetaDataPoolName(f),
		PoolSpec: spec.MetadataPool,
	}
	err := cephclient.CreatePool(context, clusterInfo, clusterSpec, metadataPool, "")
	if err != nil {
		return errors.Wrapf(err, "failed to update metadata pool %q", metadataPool.Name)
	}
	// generating the data pool's name
	dataPoolNames := generateDataPoolNames(f, spec)
	for i, dataPool := range spec.DataPools {
		dataPool.Name = dataPoolNames[i]
		err := cephclient.CreatePool(context, clusterInfo, clusterSpec, dataPool, "")
		if err != nil {
			return errors.Wrapf(err, "failed to update datapool  %q", dataPool.Name)
		}
	}
	return nil
}

// updateFilesystem ensures that a filesystem which already exists matches the provided spec.
func (f *Filesystem) updateFilesystem(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, spec cephv1.FilesystemSpec) error {
	// Even if the fs already exists, the num active mdses may have changed
	if err := cephclient.SetNumMDSRanks(context, clusterInfo, f.Name, spec.MetadataServer.ActiveCount); err != nil {
		logger.Errorf(
			fmt.Sprintf("failed to set num mds ranks (max_mds) to %d for filesystem %s, still continuing. ", spec.MetadataServer.ActiveCount, f.Name) +
				"this error is not critical, but mdses may not be as failure tolerant as desired. " +
				fmt.Sprintf("USER should verify that the number of active mdses is %d with 'ceph fs get %s'", spec.MetadataServer.ActiveCount, f.Name) +
				fmt.Sprintf(". %v", err),
		)
	}

	if err := createOrUpdatePools(f, context, clusterInfo, clusterSpec, spec); err != nil {
		return errors.Wrap(err, "failed to set pools size")
	}

	dataPoolNames := generateDataPoolNames(f, spec)
	for i := range spec.DataPools {
		if err := cephclient.AddDataPoolToFilesystem(context, clusterInfo, f.Name, dataPoolNames[i]); err != nil {
			return err
		}
	}
	return nil
}

// doFilesystemCreate starts the Ceph file daemons and creates the filesystem in Ceph.
func (f *Filesystem) doFilesystemCreate(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, spec cephv1.FilesystemSpec) error {

	_, err := cephclient.GetFilesystem(context, clusterInfo, f.Name)
	if err == nil {
		logger.Infof("filesystem %q already exists", f.Name)
		return f.updateFilesystem(context, clusterInfo, clusterSpec, spec)
	}
	if len(spec.DataPools) == 0 {
		return errors.New("at least one data pool must be specified")
	}

	fslist, err := cephclient.ListFilesystems(context, clusterInfo)
	if err != nil {
		return errors.Wrap(err, "failed to list existing filesystem(s)")
	}
	// This check prevents from concurrent CephFilesystem CRD trying to create a filesystem
	// Whoever gets to create the Filesystem first wins the race, then we fail if that cluster is not Ceph Pacific and one Filesystem is present
	if len(fslist) > 0 && !clusterInfo.CephVersion.IsAtLeastPacific() {
		return errors.New("multiple filesystems are only supported as of ceph pacific")
	}

	poolNames, err := cephclient.GetPoolNamesByID(context, clusterInfo)
	if err != nil {
		return errors.Wrap(err, "failed to get pool names")
	}

	logger.Infof("creating filesystem %q", f.Name)

	// Make easy to locate a pool by name and avoid repeated searches
	reversedPoolMap := make(map[string]int)
	for key, value := range poolNames {
		reversedPoolMap[value] = key
	}

	metadataPool := cephv1.NamedPoolSpec{
		Name:     generateMetaDataPoolName(f),
		PoolSpec: spec.MetadataPool,
	}
	if _, poolFound := reversedPoolMap[metadataPool.Name]; !poolFound {
		err = cephclient.CreatePool(context, clusterInfo, clusterSpec, metadataPool, "")
		if err != nil {
			return errors.Wrapf(err, "failed to create metadata pool %q", metadataPool.Name)
		}
	}

	dataPoolNames := generateDataPoolNames(f, spec)
	for i, dataPool := range spec.DataPools {
		dataPool.Name = dataPoolNames[i]
		if _, poolFound := reversedPoolMap[dataPool.Name]; !poolFound {
			err = cephclient.CreatePool(context, clusterInfo, clusterSpec, dataPool, "")
			if err != nil {
				return errors.Wrapf(err, "failed to create data pool %q", dataPool.Name)
			}
			if dataPool.IsErasureCoded() {
				// An erasure coded data pool used for a filesystem must allow overwrites
				if err := cephclient.SetPoolProperty(context, clusterInfo, dataPool.Name, "allow_ec_overwrites", "true"); err != nil {
					logger.Warningf("failed to set ec pool property. %v", err)
				}
			}
		}
	}

	// create the filesystem ('fs new' needs to be forced in order to reuse pre-existing pools)
	// if only one pool is created new it won't work (to avoid inconsistencies).
	if err := cephclient.CreateFilesystem(context, clusterInfo, f.Name, metadataPool.Name, dataPoolNames); err != nil {
		return err
	}

	logger.Infof("created filesystem %q on %d data pool(s) and metadata pool %q", f.Name, len(dataPoolNames), metadataPool.Name)
	return nil
}

// downFilesystem marks the filesystem as down and the MDS' as failed
func downFilesystem(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, filesystemName string) error {
	logger.Infof("downing filesystem %q", filesystemName)

	if err := cephclient.FailFilesystem(context, clusterInfo, filesystemName); err != nil {
		return err
	}
	logger.Infof("downed filesystem %q", filesystemName)
	return nil
}

// generateDataPoolName generates DataPool name by prefixing the filesystem name to the constant DataPoolSuffix
// or get predefined name from spec
func generateDataPoolNames(f *Filesystem, spec cephv1.FilesystemSpec) []string {
	var dataPoolNames []string
	for i, pool := range spec.DataPools {
		poolName := ""
		if pool.Name == "" {
			poolName = fmt.Sprintf("%s-%s%d", f.Name, dataPoolSuffix, i)
		} else {
			poolName = fmt.Sprintf("%s-%s", f.Name, pool.Name)
		}
		dataPoolNames = append(dataPoolNames, poolName)
	}
	return dataPoolNames
}

// generateMetaDataPoolName generates MetaDataPool name by prefixing the filesystem name to the constant metaDataPoolSuffix
func generateMetaDataPoolName(f *Filesystem) string {
	return fmt.Sprintf("%s-%s", f.Name, metaDataPoolSuffix)
}
<commit_msg>mds: pool changed to cephpool<commit_after>/*
Copyright 2016 The Rook 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 file

import (
	"fmt"

	"github.com/rook/rook/pkg/operator/k8sutil"

	"github.com/pkg/errors"
	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
	"github.com/rook/rook/pkg/clusterd"
	cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
	"github.com/rook/rook/pkg/operator/ceph/file/mds"
	cephpool "github.com/rook/rook/pkg/operator/ceph/pool"
)

const (
	dataPoolSuffix     = "data"
	metaDataPoolSuffix = "metadata"
)

// Filesystem represents an instance of a Ceph filesystem (CephFS)
type Filesystem struct {
	Name      string
	Namespace string
}

// createFilesystem creates a Ceph filesystem with metadata servers
func createFilesystem(
	context *clusterd.Context,
	clusterInfo *cephclient.ClusterInfo,
	fs cephv1.CephFilesystem,
	clusterSpec *cephv1.ClusterSpec,
	ownerInfo *k8sutil.OwnerInfo,
	dataDirHostPath string,
) error {
	logger.Infof("start running mdses for filesystem %q", fs.Name)
	c := mds.NewCluster(clusterInfo, context, clusterSpec, fs, ownerInfo, dataDirHostPath)
	if err := c.Start(); err != nil {
		return err
	}

	if len(fs.Spec.DataPools) != 0 {
		f := newFS(fs.Name, fs.Namespace)
		if err := f.doFilesystemCreate(context, clusterInfo, clusterSpec, fs.Spec); err != nil {
			return errors.Wrapf(err, "failed to create filesystem %q", fs.Name)
		}
	}
	if fs.Spec.MetadataServer.ActiveStandby {
		if err := cephclient.AllowStandbyReplay(context, clusterInfo, fs.Name, fs.Spec.MetadataServer.ActiveStandby); err != nil {
			return errors.Wrapf(err, "failed to set allow_standby_replay to filesystem %q", fs.Name)
		}
	}

	// set the number of active mds instances
	if fs.Spec.MetadataServer.ActiveCount > 1 {
		if err := cephclient.SetNumMDSRanks(context, clusterInfo, fs.Name, fs.Spec.MetadataServer.ActiveCount); err != nil {
			logger.Warningf("failed setting active mds count to %d. %v", fs.Spec.MetadataServer.ActiveCount, err)
		}
	}

	return nil
}

// deleteFilesystem deletes the filesystem from Ceph
func deleteFilesystem(
	context *clusterd.Context,
	clusterInfo *cephclient.ClusterInfo,
	fs cephv1.CephFilesystem,
	clusterSpec *cephv1.ClusterSpec,
	ownerInfo *k8sutil.OwnerInfo,
	dataDirHostPath string,
) error {
	c := mds.NewCluster(clusterInfo, context, clusterSpec, fs, ownerInfo, dataDirHostPath)

	// Delete mds CephX keys and configuration in centralized mon database
	replicas := fs.Spec.MetadataServer.ActiveCount * 2
	for i := 0; i < int(replicas); i++ {
		daemonLetterID := k8sutil.IndexToName(i)
		daemonName := fmt.Sprintf("%s-%s", fs.Name, daemonLetterID)

		err := c.DeleteMdsCephObjects(daemonName)
		if err != nil {
			return errors.Wrapf(err, "failed to delete mds ceph objects for filesystem %q", fs.Name)
		}
	}

	// The most important part of deletion is that the filesystem gets removed from Ceph
	// The K8s resources will already be removed with the K8s owner references
	if err := downFilesystem(context, clusterInfo, fs.Name); err != nil {
		// If the fs isn't deleted from Ceph, leave the daemons so it can still be used.
		// Log the error for best effort and continue
		logger.Warningf("continuing to remove filesystem CR even though downing the filesystem failed. %v", err)
	}

	// Permanently remove the filesystem if it was created by rook and the spec does not prevent it.
	if len(fs.Spec.DataPools) != 0 && !fs.Spec.PreserveFilesystemOnDelete {
		if err := cephclient.RemoveFilesystem(context, clusterInfo, fs.Name, fs.Spec.PreservePoolsOnDelete); err != nil {
			// log the error for best effort and continue
			logger.Warningf("continuing to remove filesystem CR even though removal failed. %v", err)
		}
	}
	return nil
}

func validateFilesystem(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, f *cephv1.CephFilesystem) error {
	if f.Name == "" {
		return errors.New("missing name")
	}
	if f.Namespace == "" {
		return errors.New("missing namespace")
	}
	if f.Spec.MetadataServer.ActiveCount < 1 {
		return errors.New("MetadataServer.ActiveCount must be at least 1")
	}
	// No data pool means that we expect the fs to exist already
	if len(f.Spec.DataPools) == 0 {
		return nil
	}
	if err := cephpool.ValidatePoolSpec(context, clusterInfo, clusterSpec, &f.Spec.MetadataPool); err != nil {
		return errors.Wrap(err, "invalid metadata pool")
	}
	for _, p := range f.Spec.DataPools {
		localpoolSpec := p.PoolSpec
		if err := cephpool.ValidatePoolSpec(context, clusterInfo, clusterSpec, &localpoolSpec); err != nil {
			return errors.Wrap(err, "Invalid data pool")
		}
	}

	return nil
}

// newFS creates a new instance of the file (MDS) service
func newFS(name, namespace string) *Filesystem {
	return &Filesystem{
		Name:      name,
		Namespace: namespace,
	}
}

// createOrUpdatePools function sets the sizes for MetadataPool and dataPool
func createOrUpdatePools(f *Filesystem, context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, spec cephv1.FilesystemSpec) error {
	// generating the metadata pool's name
	metadataPool := cephv1.NamedPoolSpec{
		Name:     generateMetaDataPoolName(f),
		PoolSpec: spec.MetadataPool,
	}
	err := cephclient.CreatePool(context, clusterInfo, clusterSpec, metadataPool, "")
	if err != nil {
		return errors.Wrapf(err, "failed to update metadata pool %q", metadataPool.Name)
	}
	// generating the data pool's name
	dataPoolNames := generateDataPoolNames(f, spec)
	for i, dataPool := range spec.DataPools {
		dataPool.Name = dataPoolNames[i]
		err := cephclient.CreatePool(context, clusterInfo, clusterSpec, dataPool, "")
		if err != nil {
			return errors.Wrapf(err, "failed to update datapool  %q", dataPool.Name)
		}
	}
	return nil
}

// updateFilesystem ensures that a filesystem which already exists matches the provided spec.
func (f *Filesystem) updateFilesystem(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, spec cephv1.FilesystemSpec) error {
	// Even if the fs already exists, the num active mdses may have changed
	if err := cephclient.SetNumMDSRanks(context, clusterInfo, f.Name, spec.MetadataServer.ActiveCount); err != nil {
		logger.Errorf(
			fmt.Sprintf("failed to set num mds ranks (max_mds) to %d for filesystem %s, still continuing. ", spec.MetadataServer.ActiveCount, f.Name) +
				"this error is not critical, but mdses may not be as failure tolerant as desired. " +
				fmt.Sprintf("USER should verify that the number of active mdses is %d with 'ceph fs get %s'", spec.MetadataServer.ActiveCount, f.Name) +
				fmt.Sprintf(". %v", err),
		)
	}

	if err := createOrUpdatePools(f, context, clusterInfo, clusterSpec, spec); err != nil {
		return errors.Wrap(err, "failed to set pools size")
	}

	dataPoolNames := generateDataPoolNames(f, spec)
	for i := range spec.DataPools {
		if err := cephclient.AddDataPoolToFilesystem(context, clusterInfo, f.Name, dataPoolNames[i]); err != nil {
			return err
		}
	}
	return nil
}

// doFilesystemCreate starts the Ceph file daemons and creates the filesystem in Ceph.
func (f *Filesystem) doFilesystemCreate(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, clusterSpec *cephv1.ClusterSpec, spec cephv1.FilesystemSpec) error {

	_, err := cephclient.GetFilesystem(context, clusterInfo, f.Name)
	if err == nil {
		logger.Infof("filesystem %q already exists", f.Name)
		return f.updateFilesystem(context, clusterInfo, clusterSpec, spec)
	}
	if len(spec.DataPools) == 0 {
		return errors.New("at least one data pool must be specified")
	}

	fslist, err := cephclient.ListFilesystems(context, clusterInfo)
	if err != nil {
		return errors.Wrap(err, "failed to list existing filesystem(s)")
	}
	// This check prevents from concurrent CephFilesystem CRD trying to create a filesystem
	// Whoever gets to create the Filesystem first wins the race, then we fail if that cluster is not Ceph Pacific and one Filesystem is present
	if len(fslist) > 0 && !clusterInfo.CephVersion.IsAtLeastPacific() {
		return errors.New("multiple filesystems are only supported as of ceph pacific")
	}

	poolNames, err := cephclient.GetPoolNamesByID(context, clusterInfo)
	if err != nil {
		return errors.Wrap(err, "failed to get pool names")
	}

	logger.Infof("creating filesystem %q", f.Name)

	// Make easy to locate a pool by name and avoid repeated searches
	reversedPoolMap := make(map[string]int)
	for key, value := range poolNames {
		reversedPoolMap[value] = key
	}

	metadataPool := cephv1.NamedPoolSpec{
		Name:     generateMetaDataPoolName(f),
		PoolSpec: spec.MetadataPool,
	}
	if _, poolFound := reversedPoolMap[metadataPool.Name]; !poolFound {
		err = cephclient.CreatePool(context, clusterInfo, clusterSpec, metadataPool, "")
		if err != nil {
			return errors.Wrapf(err, "failed to create metadata pool %q", metadataPool.Name)
		}
	}

	dataPoolNames := generateDataPoolNames(f, spec)
	for i, dataPool := range spec.DataPools {
		dataPool.Name = dataPoolNames[i]
		if _, poolFound := reversedPoolMap[dataPool.Name]; !poolFound {
			err = cephclient.CreatePool(context, clusterInfo, clusterSpec, dataPool, "")
			if err != nil {
				return errors.Wrapf(err, "failed to create data pool %q", dataPool.Name)
			}
			if dataPool.IsErasureCoded() {
				// An erasure coded data pool used for a filesystem must allow overwrites
				if err := cephclient.SetPoolProperty(context, clusterInfo, dataPool.Name, "allow_ec_overwrites", "true"); err != nil {
					logger.Warningf("failed to set ec pool property. %v", err)
				}
			}
		}
	}

	// create the filesystem ('fs new' needs to be forced in order to reuse pre-existing pools)
	// if only one pool is created new it won't work (to avoid inconsistencies).
	if err := cephclient.CreateFilesystem(context, clusterInfo, f.Name, metadataPool.Name, dataPoolNames); err != nil {
		return err
	}

	logger.Infof("created filesystem %q on %d data pool(s) and metadata pool %q", f.Name, len(dataPoolNames), metadataPool.Name)
	return nil
}

// downFilesystem marks the filesystem as down and the MDS' as failed
func downFilesystem(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, filesystemName string) error {
	logger.Infof("downing filesystem %q", filesystemName)

	if err := cephclient.FailFilesystem(context, clusterInfo, filesystemName); err != nil {
		return err
	}
	logger.Infof("downed filesystem %q", filesystemName)
	return nil
}

// generateDataPoolName generates DataPool name by prefixing the filesystem name to the constant DataPoolSuffix
// or get predefined name from spec
func generateDataPoolNames(f *Filesystem, spec cephv1.FilesystemSpec) []string {
	var dataPoolNames []string
	for i, pool := range spec.DataPools {
		poolName := ""
		if pool.Name == "" {
			poolName = fmt.Sprintf("%s-%s%d", f.Name, dataPoolSuffix, i)
		} else {
			poolName = fmt.Sprintf("%s-%s", f.Name, pool.Name)
		}
		dataPoolNames = append(dataPoolNames, poolName)
	}
	return dataPoolNames
}

// generateMetaDataPoolName generates MetaDataPool name by prefixing the filesystem name to the constant metaDataPoolSuffix
func generateMetaDataPoolName(f *Filesystem) string {
	return fmt.Sprintf("%s-%s", f.Name, metaDataPoolSuffix)
}
<|endoftext|>


<commit_before>// Copyright 2021 The gVisor 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 cgroupfs

import (
	"bytes"
	"fmt"

	"gvisor.dev/gvisor/pkg/bitmap"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/errors/linuxerr"
	"gvisor.dev/gvisor/pkg/hostarch"
	"gvisor.dev/gvisor/pkg/log"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/sync"
	"gvisor.dev/gvisor/pkg/usermem"
)

// +stateify savable
type cpusetController struct {
	controllerCommon
	controllerStateless

	maxCpus uint32
	maxMems uint32

	mu sync.Mutex `state:"nosave"`

	cpus *bitmap.Bitmap
	mems *bitmap.Bitmap
}

var _ controller = (*cpusetController)(nil)

func newCPUSetController(k *kernel.Kernel, fs *filesystem) *cpusetController {
	cores := uint32(k.ApplicationCores())
	cpus := bitmap.New(cores)
	cpus.FlipRange(0, cores)
	mems := bitmap.New(1)
	mems.FlipRange(0, 1)
	c := &cpusetController{
		cpus:    &cpus,
		mems:    &mems,
		maxCpus: uint32(k.ApplicationCores()),
		maxMems: 1, // We always report a single NUMA node.
	}
	c.controllerCommon.init(controllerCPUSet, fs)
	return c
}

// Clone implements controller.Clone.
func (c *cpusetController) Clone() controller {
	cpus := c.cpus.Clone()
	mems := c.mems.Clone()
	new := &cpusetController{
		maxCpus: c.maxCpus,
		maxMems: c.maxMems,
		cpus:    &cpus,
		mems:    &mems,
	}
	new.controllerCommon.cloneFromParent(c)
	return new
}

// AddControlFiles implements controller.AddControlFiles.
func (c *cpusetController) AddControlFiles(ctx context.Context, creds *auth.Credentials, _ *cgroupInode, contents map[string]kernfs.Inode) {
	contents["cpuset.cpus"] = c.fs.newControllerWritableFile(ctx, creds, &cpusData{c: c})
	contents["cpuset.mems"] = c.fs.newControllerWritableFile(ctx, creds, &memsData{c: c})
}

// +stateify savable
type cpusData struct {
	c *cpusetController
}

// Generate implements vfs.DynamicBytesSource.Generate.
func (d *cpusData) Generate(ctx context.Context, buf *bytes.Buffer) error {
	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	fmt.Fprintf(buf, "%s\n", formatBitmap(d.c.cpus))
	return nil
}

// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *cpusData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
	if src.NumBytes() > hostarch.PageSize {
		return 0, linuxerr.EINVAL
	}

	t := kernel.TaskFromContext(ctx)
	buf := t.CopyScratchBuffer(hostarch.PageSize)
	n, err := src.CopyIn(ctx, buf)
	if err != nil {
		return 0, err
	}
	buf = buf[:n]

	b, err := parseBitmap(string(buf), d.c.maxCpus)
	if err != nil {
		log.Warningf("cgroupfs cpuset controller: Failed to parse bitmap: %v", err)
		return 0, linuxerr.EINVAL
	}

	if got, want := b.Maximum(), d.c.maxCpus; got > want {
		log.Warningf("cgroupfs cpuset controller: Attempted to specify cpuset.cpus beyond highest available cpu: got %d, want %d", got, want)
		return 0, linuxerr.EINVAL
	}

	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	d.c.cpus = b
	return int64(n), nil
}

// +stateify savable
type memsData struct {
	c *cpusetController
}

// Generate implements vfs.DynamicBytesSource.Generate.
func (d *memsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	fmt.Fprintf(buf, "%s\n", formatBitmap(d.c.mems))
	return nil
}

// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *memsData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
	if src.NumBytes() > hostarch.PageSize {
		return 0, linuxerr.EINVAL
	}

	t := kernel.TaskFromContext(ctx)
	buf := t.CopyScratchBuffer(hostarch.PageSize)
	n, err := src.CopyIn(ctx, buf)
	if err != nil {
		return 0, err
	}
	buf = buf[:n]

	b, err := parseBitmap(string(buf), d.c.maxMems)
	if err != nil {
		log.Warningf("cgroupfs cpuset controller: Failed to parse bitmap: %v", err)
		return 0, linuxerr.EINVAL
	}

	if got, want := b.Maximum(), d.c.maxMems; got > want {
		log.Warningf("cgroupfs cpuset controller: Attempted to specify cpuset.mems beyond highest available node: got %d, want %d", got, want)
		return 0, linuxerr.EINVAL
	}

	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	d.c.mems = b
	return int64(n), nil
}
<commit_msg>cgroupfs: Synchronize access to cpuset controller bitmaps.<commit_after>// Copyright 2021 The gVisor 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 cgroupfs

import (
	"bytes"
	"fmt"

	"gvisor.dev/gvisor/pkg/bitmap"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/errors/linuxerr"
	"gvisor.dev/gvisor/pkg/hostarch"
	"gvisor.dev/gvisor/pkg/log"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/sync"
	"gvisor.dev/gvisor/pkg/usermem"
)

// +stateify savable
type cpusetController struct {
	controllerCommon
	controllerStateless

	maxCpus uint32
	maxMems uint32

	mu sync.Mutex `state:"nosave"`

	cpus *bitmap.Bitmap
	mems *bitmap.Bitmap
}

var _ controller = (*cpusetController)(nil)

func newCPUSetController(k *kernel.Kernel, fs *filesystem) *cpusetController {
	cores := uint32(k.ApplicationCores())
	cpus := bitmap.New(cores)
	cpus.FlipRange(0, cores)
	mems := bitmap.New(1)
	mems.FlipRange(0, 1)
	c := &cpusetController{
		cpus:    &cpus,
		mems:    &mems,
		maxCpus: uint32(k.ApplicationCores()),
		maxMems: 1, // We always report a single NUMA node.
	}
	c.controllerCommon.init(controllerCPUSet, fs)
	return c
}

// Clone implements controller.Clone.
func (c *cpusetController) Clone() controller {
	c.mu.Lock()
	defer c.mu.Unlock()
	cpus := c.cpus.Clone()
	mems := c.mems.Clone()
	new := &cpusetController{
		maxCpus: c.maxCpus,
		maxMems: c.maxMems,
		cpus:    &cpus,
		mems:    &mems,
	}
	new.controllerCommon.cloneFromParent(c)
	return new
}

// AddControlFiles implements controller.AddControlFiles.
func (c *cpusetController) AddControlFiles(ctx context.Context, creds *auth.Credentials, _ *cgroupInode, contents map[string]kernfs.Inode) {
	contents["cpuset.cpus"] = c.fs.newControllerWritableFile(ctx, creds, &cpusData{c: c})
	contents["cpuset.mems"] = c.fs.newControllerWritableFile(ctx, creds, &memsData{c: c})
}

// +stateify savable
type cpusData struct {
	c *cpusetController
}

// Generate implements vfs.DynamicBytesSource.Generate.
func (d *cpusData) Generate(ctx context.Context, buf *bytes.Buffer) error {
	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	fmt.Fprintf(buf, "%s\n", formatBitmap(d.c.cpus))
	return nil
}

// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *cpusData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
	if src.NumBytes() > hostarch.PageSize {
		return 0, linuxerr.EINVAL
	}

	t := kernel.TaskFromContext(ctx)
	buf := t.CopyScratchBuffer(hostarch.PageSize)
	n, err := src.CopyIn(ctx, buf)
	if err != nil {
		return 0, err
	}
	buf = buf[:n]

	b, err := parseBitmap(string(buf), d.c.maxCpus)
	if err != nil {
		log.Warningf("cgroupfs cpuset controller: Failed to parse bitmap: %v", err)
		return 0, linuxerr.EINVAL
	}

	if got, want := b.Maximum(), d.c.maxCpus; got > want {
		log.Warningf("cgroupfs cpuset controller: Attempted to specify cpuset.cpus beyond highest available cpu: got %d, want %d", got, want)
		return 0, linuxerr.EINVAL
	}

	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	d.c.cpus = b
	return int64(n), nil
}

// +stateify savable
type memsData struct {
	c *cpusetController
}

// Generate implements vfs.DynamicBytesSource.Generate.
func (d *memsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	fmt.Fprintf(buf, "%s\n", formatBitmap(d.c.mems))
	return nil
}

// Write implements vfs.WritableDynamicBytesSource.Write.
func (d *memsData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
	if src.NumBytes() > hostarch.PageSize {
		return 0, linuxerr.EINVAL
	}

	t := kernel.TaskFromContext(ctx)
	buf := t.CopyScratchBuffer(hostarch.PageSize)
	n, err := src.CopyIn(ctx, buf)
	if err != nil {
		return 0, err
	}
	buf = buf[:n]

	b, err := parseBitmap(string(buf), d.c.maxMems)
	if err != nil {
		log.Warningf("cgroupfs cpuset controller: Failed to parse bitmap: %v", err)
		return 0, linuxerr.EINVAL
	}

	if got, want := b.Maximum(), d.c.maxMems; got > want {
		log.Warningf("cgroupfs cpuset controller: Attempted to specify cpuset.mems beyond highest available node: got %d, want %d", got, want)
		return 0, linuxerr.EINVAL
	}

	d.c.mu.Lock()
	defer d.c.mu.Unlock()
	d.c.mems = b
	return int64(n), nil
}
<|endoftext|>


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

import (
	"errors"
	"fmt"
	"log"

	"sigs.k8s.io/kustomize/pkg/gvk"
	"sigs.k8s.io/kustomize/pkg/resmap"
	"sigs.k8s.io/kustomize/pkg/transformers/config"
)

// namePrefixSuffixTransformer contains the prefix, suffix, and the FieldSpecs
// for each field needing a name prefix and suffix.
type namePrefixSuffixTransformer struct {
	prefix           string
	suffix           string
	fieldSpecsToUse  []config.FieldSpec
	fieldSpecsToSkip []config.FieldSpec
}

var _ Transformer = &namePrefixSuffixTransformer{}

var prefixSuffixFieldSpecsToSkip = []config.FieldSpec{
	{
		Gvk: gvk.Gvk{Kind: "CustomResourceDefinition"},
	},
}

// deprecateNamePrefixSuffixFieldSpec will be moved into
// prefixSuffixFieldSpecsToSkip in next release
var deprecateNamePrefixSuffixFieldSpec = config.FieldSpec{
	Gvk: gvk.Gvk{Kind: "Namespace"},
}

// NewNamePrefixSuffixTransformer makes a namePrefixSuffixTransformer.
func NewNamePrefixSuffixTransformer(
	np, ns string, fieldSpecs []config.FieldSpec) (Transformer, error) {
	if len(np) == 0 && len(ns) == 0 {
		return NewNoOpTransformer(), nil
	}
	if fieldSpecs == nil {
		return nil, errors.New("fieldSpecs is not expected to be nil")
	}
	return &namePrefixSuffixTransformer{
		prefix:           np,
		suffix:           ns,
		fieldSpecsToUse:  fieldSpecs,
		fieldSpecsToSkip: prefixSuffixFieldSpecsToSkip}, nil
}

// Transform prepends the name prefix and appends the name suffix.
func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error {
	// Fill map "mf" with entries subject to name modification, and
	// delete these entries from "m", so that for now m retains only
	// the entries whose names will not be modified.
	mf := resmap.ResMap{}
	for id := range m {
		found := false
		for _, path := range o.fieldSpecsToSkip {
			if id.Gvk().IsSelected(&path.Gvk) {
				found = true
				break
			}
		}
		if !found {
			mf[id] = m[id]
			delete(m, id)
		}
	}

	for id := range mf {
		if id.Gvk().IsSelected(&deprecateNamePrefixSuffixFieldSpec.Gvk) {
			log.Println(
				"Adding prefix and suffix to Namespace " +
					"resource will be deprecated in next release.")
		}
		objMap := mf[id].Map()
		for _, path := range o.fieldSpecsToUse {
			if !id.Gvk().IsSelected(&path.Gvk) {
				continue
			}
			err := mutateField(
				objMap,
				path.PathSlice(),
				path.CreateIfNotPresent,
				o.addPrefixSuffix)
			if err != nil {
				return err
			}
			newId := id.CopyWithNewPrefixSuffix(o.prefix, o.suffix)
			m[newId] = mf[id]
		}
	}
	return nil
}

func (o *namePrefixSuffixTransformer) addPrefixSuffix(
	in interface{}) (interface{}, error) {
	s, ok := in.(string)
	if !ok {
		return nil, fmt.Errorf("%#v is expected to be %T", in, s)
	}
	return fmt.Sprintf("%s%s%s", o.prefix, s, o.suffix), nil
}
<commit_msg>remove warning for using prefix/suffix in conjunction with a Namespace<commit_after>/*
Copyright 2018 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 transformers

import (
	"errors"
	"fmt"

	"sigs.k8s.io/kustomize/pkg/gvk"
	"sigs.k8s.io/kustomize/pkg/resmap"
	"sigs.k8s.io/kustomize/pkg/transformers/config"
)

// namePrefixSuffixTransformer contains the prefix, suffix, and the FieldSpecs
// for each field needing a name prefix and suffix.
type namePrefixSuffixTransformer struct {
	prefix           string
	suffix           string
	fieldSpecsToUse  []config.FieldSpec
	fieldSpecsToSkip []config.FieldSpec
}

var _ Transformer = &namePrefixSuffixTransformer{}

var prefixSuffixFieldSpecsToSkip = []config.FieldSpec{
	{
		Gvk: gvk.Gvk{Kind: "CustomResourceDefinition"},
	},
}

// NewNamePrefixSuffixTransformer makes a namePrefixSuffixTransformer.
func NewNamePrefixSuffixTransformer(
	np, ns string, fieldSpecs []config.FieldSpec) (Transformer, error) {
	if len(np) == 0 && len(ns) == 0 {
		return NewNoOpTransformer(), nil
	}
	if fieldSpecs == nil {
		return nil, errors.New("fieldSpecs is not expected to be nil")
	}
	return &namePrefixSuffixTransformer{
		prefix:           np,
		suffix:           ns,
		fieldSpecsToUse:  fieldSpecs,
		fieldSpecsToSkip: prefixSuffixFieldSpecsToSkip}, nil
}

// Transform prepends the name prefix and appends the name suffix.
func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error {
	// Fill map "mf" with entries subject to name modification, and
	// delete these entries from "m", so that for now m retains only
	// the entries whose names will not be modified.
	mf := resmap.ResMap{}
	for id := range m {
		found := false
		for _, path := range o.fieldSpecsToSkip {
			if id.Gvk().IsSelected(&path.Gvk) {
				found = true
				break
			}
		}
		if !found {
			mf[id] = m[id]
			delete(m, id)
		}
	}

	for id := range mf {
		objMap := mf[id].Map()
		for _, path := range o.fieldSpecsToUse {
			if !id.Gvk().IsSelected(&path.Gvk) {
				continue
			}
			err := mutateField(
				objMap,
				path.PathSlice(),
				path.CreateIfNotPresent,
				o.addPrefixSuffix)
			if err != nil {
				return err
			}
			newId := id.CopyWithNewPrefixSuffix(o.prefix, o.suffix)
			m[newId] = mf[id]
		}
	}
	return nil
}

func (o *namePrefixSuffixTransformer) addPrefixSuffix(
	in interface{}) (interface{}, error) {
	s, ok := in.(string)
	if !ok {
		return nil, fmt.Errorf("%#v is expected to be %T", in, s)
	}
	return fmt.Sprintf("%s%s%s", o.prefix, s, o.suffix), nil
}
<|endoftext|>