<commit_before>// +build linux

package fs

import (
	"bytes"
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/moby/sys/mountinfo"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
	"github.com/opencontainers/runc/libcontainer/configs"
	libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
)

type CpusetGroup struct {
}

func (s *CpusetGroup) Name() string {
	return "cpuset"
}

func (s *CpusetGroup) Apply(d *cgroupData) error {
	dir, err := d.path("cpuset")
	if err != nil && !cgroups.IsNotFound(err) {
		return err
	}
	return s.ApplyDir(dir, d.config, d.pid)
}

func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
	if cgroup.Resources.CpusetCpus != "" {
		if err := fscommon.WriteFile(path, "cpuset.cpus", cgroup.Resources.CpusetCpus); err != nil {
			return err
		}
	}
	if cgroup.Resources.CpusetMems != "" {
		if err := fscommon.WriteFile(path, "cpuset.mems", cgroup.Resources.CpusetMems); err != nil {
			return err
		}
	}
	return nil
}

func (s *CpusetGroup) Remove(d *cgroupData) error {
	return removePath(d.path("cpuset"))
}

func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
	return nil
}

// Get the source mount point of directory passed in as argument.
func getMount(dir string) (string, error) {
	mi, err := mountinfo.GetMounts(mountinfo.ParentsFilter(dir))
	if err != nil {
		return "", err
	}
	if len(mi) < 1 {
		return "", fmt.Errorf("Can't find mount point of %s", dir)
	}

	// find the longest mount point
	var idx, maxlen int
	for i := range mi {
		if len(mi[i].Mountpoint) > maxlen {
			maxlen = len(mi[i].Mountpoint)
			idx = i
		}
	}

	return mi[idx].Mountpoint, nil
}

func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error {
	// This might happen if we have no cpuset cgroup mounted.
	// Just do nothing and don't fail.
	if dir == "" {
		return nil
	}
	root, err := getMount(dir)
	if err != nil {
		return err
	}
	root = filepath.Dir(root)
	// 'ensureParent' start with parent because we don't want to
	// explicitly inherit from parent, it could conflict with
	// 'cpuset.cpu_exclusive'.
	if err := s.ensureParent(filepath.Dir(dir), root); err != nil {
		return err
	}
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	// We didn't inherit cpuset configs from parent, but we have
	// to ensure cpuset configs are set before moving task into the
	// cgroup.
	// The logic is, if user specified cpuset configs, use these
	// specified configs, otherwise, inherit from parent. This makes
	// cpuset configs work correctly with 'cpuset.cpu_exclusive', and
	// keep backward compatibility.
	if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
		return err
	}

	// because we are not using d.join we need to place the pid into the procs file
	// unlike the other subsystems
	return cgroups.WriteCgroupProc(dir, pid)
}

func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
	if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
		return
	}
	if mems, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.mems")); err != nil {
		return
	}
	return cpus, mems, nil
}

// ensureParent makes sure that the parent directory of current is created
// and populated with the proper cpus and mems files copied from
// it's parent.
func (s *CpusetGroup) ensureParent(current, root string) error {
	parent := filepath.Dir(current)
	if libcontainerUtils.CleanPath(parent) == root {
		return nil
	}
	// Avoid infinite recursion.
	if parent == current {
		return errors.New("cpuset: cgroup parent path outside cgroup root")
	}
	if err := s.ensureParent(parent, root); err != nil {
		return err
	}
	if err := os.MkdirAll(current, 0755); err != nil {
		return err
	}
	return s.copyIfNeeded(current, parent)
}

// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
// directory to the current directory if the file's contents are 0
func (s *CpusetGroup) copyIfNeeded(current, parent string) error {
	var (
		err                      error
		currentCpus, currentMems []byte
		parentCpus, parentMems   []byte
	)

	if currentCpus, currentMems, err = s.getSubsystemSettings(current); err != nil {
		return err
	}
	if parentCpus, parentMems, err = s.getSubsystemSettings(parent); err != nil {
		return err
	}

	if s.isEmpty(currentCpus) {
		if err := fscommon.WriteFile(current, "cpuset.cpus", string(parentCpus)); err != nil {
			return err
		}
	}
	if s.isEmpty(currentMems) {
		if err := fscommon.WriteFile(current, "cpuset.mems", string(parentMems)); err != nil {
			return err
		}
	}
	return nil
}

func (s *CpusetGroup) isEmpty(b []byte) bool {
	return len(bytes.Trim(b, "\n")) == 0
}

func (s *CpusetGroup) ensureCpusAndMems(path string, cgroup *configs.Cgroup) error {
	if err := s.Set(path, cgroup); err != nil {
		return err
	}
	return s.copyIfNeeded(path, filepath.Dir(path))
}
<commit_msg>fix "libcontainer/cgroups/fs/cpuset.go:63:14: undefined: fmt"<commit_after>// +build linux

package fs

import (
	"bytes"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/moby/sys/mountinfo"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
	"github.com/opencontainers/runc/libcontainer/configs"
	libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
	"github.com/pkg/errors"
)

type CpusetGroup struct {
}

func (s *CpusetGroup) Name() string {
	return "cpuset"
}

func (s *CpusetGroup) Apply(d *cgroupData) error {
	dir, err := d.path("cpuset")
	if err != nil && !cgroups.IsNotFound(err) {
		return err
	}
	return s.ApplyDir(dir, d.config, d.pid)
}

func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
	if cgroup.Resources.CpusetCpus != "" {
		if err := fscommon.WriteFile(path, "cpuset.cpus", cgroup.Resources.CpusetCpus); err != nil {
			return err
		}
	}
	if cgroup.Resources.CpusetMems != "" {
		if err := fscommon.WriteFile(path, "cpuset.mems", cgroup.Resources.CpusetMems); err != nil {
			return err
		}
	}
	return nil
}

func (s *CpusetGroup) Remove(d *cgroupData) error {
	return removePath(d.path("cpuset"))
}

func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
	return nil
}

// Get the source mount point of directory passed in as argument.
func getMount(dir string) (string, error) {
	mi, err := mountinfo.GetMounts(mountinfo.ParentsFilter(dir))
	if err != nil {
		return "", err
	}
	if len(mi) < 1 {
		return "", errors.Errorf("Can't find mount point of %s", dir)
	}

	// find the longest mount point
	var idx, maxlen int
	for i := range mi {
		if len(mi[i].Mountpoint) > maxlen {
			maxlen = len(mi[i].Mountpoint)
			idx = i
		}
	}

	return mi[idx].Mountpoint, nil
}

func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error {
	// This might happen if we have no cpuset cgroup mounted.
	// Just do nothing and don't fail.
	if dir == "" {
		return nil
	}
	root, err := getMount(dir)
	if err != nil {
		return err
	}
	root = filepath.Dir(root)
	// 'ensureParent' start with parent because we don't want to
	// explicitly inherit from parent, it could conflict with
	// 'cpuset.cpu_exclusive'.
	if err := s.ensureParent(filepath.Dir(dir), root); err != nil {
		return err
	}
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	// We didn't inherit cpuset configs from parent, but we have
	// to ensure cpuset configs are set before moving task into the
	// cgroup.
	// The logic is, if user specified cpuset configs, use these
	// specified configs, otherwise, inherit from parent. This makes
	// cpuset configs work correctly with 'cpuset.cpu_exclusive', and
	// keep backward compatibility.
	if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
		return err
	}

	// because we are not using d.join we need to place the pid into the procs file
	// unlike the other subsystems
	return cgroups.WriteCgroupProc(dir, pid)
}

func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
	if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
		return
	}
	if mems, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.mems")); err != nil {
		return
	}
	return cpus, mems, nil
}

// ensureParent makes sure that the parent directory of current is created
// and populated with the proper cpus and mems files copied from
// it's parent.
func (s *CpusetGroup) ensureParent(current, root string) error {
	parent := filepath.Dir(current)
	if libcontainerUtils.CleanPath(parent) == root {
		return nil
	}
	// Avoid infinite recursion.
	if parent == current {
		return errors.New("cpuset: cgroup parent path outside cgroup root")
	}
	if err := s.ensureParent(parent, root); err != nil {
		return err
	}
	if err := os.MkdirAll(current, 0755); err != nil {
		return err
	}
	return s.copyIfNeeded(current, parent)
}

// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
// directory to the current directory if the file's contents are 0
func (s *CpusetGroup) copyIfNeeded(current, parent string) error {
	var (
		err                      error
		currentCpus, currentMems []byte
		parentCpus, parentMems   []byte
	)

	if currentCpus, currentMems, err = s.getSubsystemSettings(current); err != nil {
		return err
	}
	if parentCpus, parentMems, err = s.getSubsystemSettings(parent); err != nil {
		return err
	}

	if s.isEmpty(currentCpus) {
		if err := fscommon.WriteFile(current, "cpuset.cpus", string(parentCpus)); err != nil {
			return err
		}
	}
	if s.isEmpty(currentMems) {
		if err := fscommon.WriteFile(current, "cpuset.mems", string(parentMems)); err != nil {
			return err
		}
	}
	return nil
}

func (s *CpusetGroup) isEmpty(b []byte) bool {
	return len(bytes.Trim(b, "\n")) == 0
}

func (s *CpusetGroup) ensureCpusAndMems(path string, cgroup *configs.Cgroup) error {
	if err := s.Set(path, cgroup); err != nil {
		return err
	}
	return s.copyIfNeeded(path, filepath.Dir(path))
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/gauge_messages"
	"github.com/golang/protobuf/proto"
	"sync"
	"time"
)

type specInfoGatherer struct {
	availableSpecs    []*specification
	availableStepsMap map[string]*stepValue
	stepsFromRunner   []string
	specStepMapCache  map[string][]*step
	conceptInfos      []*gauge_messages.ConceptInfo
	mutex             sync.Mutex
}

func (specInfoGatherer *specInfoGatherer) makeListOfAvailableSteps(runner *testRunner) {
	specInfoGatherer.availableStepsMap = make(map[string]*stepValue)
	specInfoGatherer.specStepMapCache = make(map[string][]*step)
	specInfoGatherer.stepsFromRunner = specInfoGatherer.getStepsFromRunner(runner)
	specInfoGatherer.addStepValuesToAvailableSteps(specInfoGatherer.stepsFromRunner)
	newSpecStepMap, conceptInfos := specInfoGatherer.getAllStepsFromSpecs()
	specInfoGatherer.conceptInfos = conceptInfos
	specInfoGatherer.addStepsToAvailableSteps(newSpecStepMap)
	go specInfoGatherer.refreshSteps(config.ApiRefreshInterval())
}

func (specInfoGatherer *specInfoGatherer) getAllStepsFromSpecs() (map[string][]*step, []*gauge_messages.ConceptInfo) {
	specFiles := findSpecsFilesIn(common.SpecsDirectoryName)
	dictionary, _ := createConceptsDictionary(true)
	specInfoGatherer.availableSpecs = specInfoGatherer.parseSpecFiles(specFiles, dictionary)
	return specInfoGatherer.findAvailableStepsInSpecs(specInfoGatherer.availableSpecs), specInfoGatherer.createConceptInfos(dictionary)
}

func (specInfoGatherer *specInfoGatherer) createConceptInfos(dictionary *conceptDictionary) []*gauge_messages.ConceptInfo {
	conceptInfos := make([]*gauge_messages.ConceptInfo, 0)
	for _, concept := range dictionary.conceptsMap {
		stepValue, err := extractStepValueAndParams(concept.conceptStep.lineText, concept.conceptStep.hasInlineTable)
		if err != nil {
			continue
		}
		conceptInfos = append(conceptInfos, &gauge_messages.ConceptInfo{StepValue: convertToProtoStepValue(stepValue), Filepath: proto.String(concept.fileName), LineNumber: proto.Int(concept.conceptStep.lineNo)})
	}
	return conceptInfos
}

func (specInfoGatherer *specInfoGatherer) refreshSteps(seconds time.Duration) {
	for {
		time.Sleep(seconds)
		specInfoGatherer.mutex.Lock()
		specInfoGatherer.availableStepsMap = make(map[string]*stepValue, 0)
		specInfoGatherer.addStepValuesToAvailableSteps(specInfoGatherer.stepsFromRunner)
		newSpecStepMap, conceptInfos := specInfoGatherer.getAllStepsFromSpecs()
		specInfoGatherer.conceptInfos = conceptInfos
		specInfoGatherer.addStepsToAvailableSteps(newSpecStepMap)
		specInfoGatherer.mutex.Unlock()
	}
}

func (specInfoGatherer *specInfoGatherer) getStepsFromRunner(runner *testRunner) []string {
	steps := make([]string, 0)
	if runner == nil {
		runner, connErr := startRunnerAndMakeConnection(getProjectManifest())
		if connErr == nil {
			steps = append(steps, requestForSteps(runner)...)
			apiLog.Debug("Steps got from runner: %v", steps)
			runner.kill()
		}
		if connErr != nil {
			apiLog.Error("Runner connection failed: %s", connErr)
		}

	} else {
		steps = append(steps, requestForSteps(runner)...)
		apiLog.Debug("Steps got from runner: %v", steps)
	}
	return steps
}

func (specInfoGatherer *specInfoGatherer) parseSpecFiles(specFiles []string, dictionary *conceptDictionary) []*specification {
	specs := make([]*specification, 0)
	for _, file := range specFiles {
		specContent, err := common.ReadFileContents(file)
		if err != nil {
			apiLog.Error("Failed to read file content: %s %s", file, err)
			continue
		}
		parser := new(specParser)
		specification, result := parser.parse(specContent, dictionary)

		if !result.ok {
			apiLog.Error("Spec Parse failure: %s %s", result.fileName, result.error)
			continue
		}
		specification.fileName = file
		specs = append(specs, specification)
	}
	return specs
}

func (specInfoGatherer *specInfoGatherer) findAvailableStepsInSpecs(specs []*specification) map[string][]*step {
	specStepsMap := make(map[string][]*step)
	for _, spec := range specs {
		stepsInSpec := make([]*step, 0)
		stepsInSpec = append(stepsInSpec, spec.contexts...)
		for _, scenario := range spec.scenarios {
			stepsInSpec = append(stepsInSpec, scenario.steps...)
		}
		specStepsMap[spec.fileName] = stepsInSpec
	}
	return specStepsMap
}

func (specInfoGatherer *specInfoGatherer) addStepsToAvailableSteps(newSpecStepsMap map[string][]*step) {
	specInfoGatherer.updateCache(newSpecStepsMap)
	for _, steps := range specInfoGatherer.specStepMapCache {
		for _, step := range steps {
			if step.isConcept {
				continue
			}
			stepValue, err := extractStepValueAndParams(step.lineText, step.hasInlineTable)
			if err == nil {
				if _, ok := specInfoGatherer.availableStepsMap[stepValue.stepValue]; !ok {
					specInfoGatherer.availableStepsMap[stepValue.stepValue] = stepValue
				}
			}
		}
	}

}

func (specInfoGatherer *specInfoGatherer) updateCache(newSpecStepsMap map[string][]*step) {
	for fileName, specsteps := range newSpecStepsMap {
		specInfoGatherer.specStepMapCache[fileName] = specsteps
	}
}

func (specInfoGatherer *specInfoGatherer) addStepValuesToAvailableSteps(stepValues []string) {
	for _, step := range stepValues {
		specInfoGatherer.addToAvailableSteps(step)
	}
}

func (specInfoGatherer *specInfoGatherer) addToAvailableSteps(stepText string) {
	stepValue, err := extractStepValueAndParams(stepText, false)
	if err == nil {
		if _, ok := specInfoGatherer.availableStepsMap[stepValue.stepValue]; !ok {
			specInfoGatherer.availableStepsMap[stepValue.stepValue] = stepValue
		}
	}
}

func (specInfoGatherer *specInfoGatherer) getAvailableSteps() []*stepValue {
	if specInfoGatherer.availableStepsMap == nil {
		specInfoGatherer.makeListOfAvailableSteps(nil)
	}
	specInfoGatherer.mutex.Lock()
	steps := make([]*stepValue, 0)
	for _, stepValue := range specInfoGatherer.availableStepsMap {
		steps = append(steps, stepValue)
	}
	specInfoGatherer.mutex.Unlock()
	return steps
}
<commit_msg>added steps used only in concepts to autocomplete. Fixes #36<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/gauge_messages"
	"github.com/golang/protobuf/proto"
	"sync"
	"time"
)

type specInfoGatherer struct {
	availableSpecs    []*specification
	availableStepsMap map[string]*stepValue
	stepsFromRunner   []string
	specStepMapCache  map[string][]*step
	conceptInfos      []*gauge_messages.ConceptInfo
	mutex             sync.Mutex
}

func (specInfoGatherer *specInfoGatherer) makeListOfAvailableSteps(runner *testRunner) {
	specInfoGatherer.availableStepsMap = make(map[string]*stepValue)
	specInfoGatherer.specStepMapCache = make(map[string][]*step)
	specInfoGatherer.stepsFromRunner = specInfoGatherer.getStepsFromRunner(runner)
	specInfoGatherer.addStepValuesToAvailableSteps(specInfoGatherer.stepsFromRunner)
	newSpecStepMap, conceptInfos := specInfoGatherer.getAllStepsFromSpecs()
	specInfoGatherer.conceptInfos = conceptInfos
	specInfoGatherer.addStepsToAvailableSteps(newSpecStepMap)

	conceptStepsMap := specInfoGatherer.getAllStepsFromConcepts()
	specInfoGatherer.addStepsToAvailableSteps(conceptStepsMap)

	go specInfoGatherer.refreshSteps(config.ApiRefreshInterval())
}

func (specInfoGatherer *specInfoGatherer) getAllStepsFromSpecs() (map[string][]*step, []*gauge_messages.ConceptInfo) {
	specFiles := findSpecsFilesIn(common.SpecsDirectoryName)
	dictionary, _ := createConceptsDictionary(true)
	specInfoGatherer.availableSpecs = specInfoGatherer.parseSpecFiles(specFiles, dictionary)
	return specInfoGatherer.findAvailableStepsInSpecs(specInfoGatherer.availableSpecs), specInfoGatherer.createConceptInfos(dictionary)
}

func (specInfoGatherer *specInfoGatherer) getAllStepsFromConcepts() map[string][]*step {
	allStepsInConcepts := make(map[string][]*step, 0)
	conceptFiles := findConceptFiles()
	for _, conceptFile := range conceptFiles {
		fileText, fileReadErr := common.ReadFileContents(conceptFile)
		if fileReadErr != nil {
			apiLog.Error("failed to read concept file %s", conceptFile)
			continue
		}
		concepts, err := new(conceptParser).parse(fileText)
		if err != nil {
			apiLog.Error("Spec Parse failure: line no: %s, %s", err.lineNo, err.message)
			continue
		}
		conceptSteps := make([]*step, 0)
		for _, concept := range concepts {
			for _, conceptStep := range concept.conceptSteps {
				conceptSteps = append(conceptSteps, conceptStep)
			}
		}
		allStepsInConcepts[conceptFile] = conceptSteps
	}
	return allStepsInConcepts
}
func (specInfoGatherer *specInfoGatherer) createConceptInfos(dictionary *conceptDictionary) []*gauge_messages.ConceptInfo {
	conceptInfos := make([]*gauge_messages.ConceptInfo, 0)
	for _, concept := range dictionary.conceptsMap {
		stepValue, err := extractStepValueAndParams(concept.conceptStep.lineText, concept.conceptStep.hasInlineTable)
		if err != nil {
			continue
		}
		conceptInfos = append(conceptInfos, &gauge_messages.ConceptInfo{StepValue: convertToProtoStepValue(stepValue), Filepath: proto.String(concept.fileName), LineNumber: proto.Int(concept.conceptStep.lineNo)})
	}
	return conceptInfos
}

func (specInfoGatherer *specInfoGatherer) refreshSteps(seconds time.Duration) {
	for {
		time.Sleep(seconds)
		specInfoGatherer.mutex.Lock()
		specInfoGatherer.availableStepsMap = make(map[string]*stepValue, 0)
		specInfoGatherer.addStepValuesToAvailableSteps(specInfoGatherer.stepsFromRunner)
		newSpecStepMap, conceptInfos := specInfoGatherer.getAllStepsFromSpecs()
		specInfoGatherer.conceptInfos = conceptInfos
		specInfoGatherer.addStepsToAvailableSteps(newSpecStepMap)

		conceptStepsMap := specInfoGatherer.getAllStepsFromConcepts()
		specInfoGatherer.addStepsToAvailableSteps(conceptStepsMap)

		specInfoGatherer.mutex.Unlock()
	}
}

func (specInfoGatherer *specInfoGatherer) getStepsFromRunner(runner *testRunner) []string {
	steps := make([]string, 0)
	if runner == nil {
		runner, connErr := startRunnerAndMakeConnection(getProjectManifest())
		if connErr == nil {
			steps = append(steps, requestForSteps(runner)...)
			apiLog.Debug("Steps got from runner: %v", steps)
			runner.kill()
		}
		if connErr != nil {
			apiLog.Error("Runner connection failed: %s", connErr)
		}

	} else {
		steps = append(steps, requestForSteps(runner)...)
		apiLog.Debug("Steps got from runner: %v", steps)
	}
	return steps
}

func (specInfoGatherer *specInfoGatherer) parseSpecFiles(specFiles []string, dictionary *conceptDictionary) []*specification {
	specs := make([]*specification, 0)
	for _, file := range specFiles {
		specContent, err := common.ReadFileContents(file)
		if err != nil {
			apiLog.Error("Failed to read file content: %s %s", file, err)
			continue
		}
		parser := new(specParser)
		specification, result := parser.parse(specContent, dictionary)

		if !result.ok {
			apiLog.Error("Spec Parse failure: %s %s", result.fileName, result.error)
			continue
		}
		specification.fileName = file
		specs = append(specs, specification)
	}
	return specs
}

func (specInfoGatherer *specInfoGatherer) findAvailableStepsInSpecs(specs []*specification) map[string][]*step {
	specStepsMap := make(map[string][]*step)
	for _, spec := range specs {
		stepsInSpec := make([]*step, 0)
		stepsInSpec = append(stepsInSpec, spec.contexts...)
		for _, scenario := range spec.scenarios {
			stepsInSpec = append(stepsInSpec, scenario.steps...)
		}
		specStepsMap[spec.fileName] = stepsInSpec
	}
	return specStepsMap
}

func (specInfoGatherer *specInfoGatherer) addStepsToAvailableSteps(newSpecStepsMap map[string][]*step) {
	specInfoGatherer.updateCache(newSpecStepsMap)
	for _, steps := range specInfoGatherer.specStepMapCache {
		for _, step := range steps {
			if step.isConcept {
				continue
			}
			stepValue, err := extractStepValueAndParams(step.lineText, step.hasInlineTable)
			if err == nil {
				if _, ok := specInfoGatherer.availableStepsMap[stepValue.stepValue]; !ok {
					specInfoGatherer.availableStepsMap[stepValue.stepValue] = stepValue
				}
			}
		}
	}

}

func (specInfoGatherer *specInfoGatherer) updateCache(newSpecStepsMap map[string][]*step) {
	for fileName, specsteps := range newSpecStepsMap {
		specInfoGatherer.specStepMapCache[fileName] = specsteps
	}
}

func (specInfoGatherer *specInfoGatherer) addStepValuesToAvailableSteps(stepValues []string) {
	for _, step := range stepValues {
		specInfoGatherer.addToAvailableSteps(step)
	}
}

func (specInfoGatherer *specInfoGatherer) addToAvailableSteps(stepText string) {
	stepValue, err := extractStepValueAndParams(stepText, false)
	if err == nil {
		if _, ok := specInfoGatherer.availableStepsMap[stepValue.stepValue]; !ok {
			specInfoGatherer.availableStepsMap[stepValue.stepValue] = stepValue
		}
	}
}

func (specInfoGatherer *specInfoGatherer) getAvailableSteps() []*stepValue {
	if specInfoGatherer.availableStepsMap == nil {
		specInfoGatherer.makeListOfAvailableSteps(nil)
	}
	specInfoGatherer.mutex.Lock()
	steps := make([]*stepValue, 0)
	for _, stepValue := range specInfoGatherer.availableStepsMap {
		steps = append(steps, stepValue)
	}
	specInfoGatherer.mutex.Unlock()
	return steps
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
	"strconv"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/storage"
)

var (
	fixVolumePath       = flag.String("dir", "/tmp", "data directory to store files")
	fixVolumeCollection = flag.String("collection", "", "the volume collection name")
	fixVolumeId         = flag.Int("volumeId", -1, "a volume id. The volume should already exist in the dir. The volume index file should not exist.")
	targetReplica       = flag.String("replication", "", "If just empty, only print out current replication setting.")
	targetTTL           = flag.String("ttl", "", "If just empty, only print out current ttl setting.")
)

/*
This is to change replication factor in .dat file header. Need to shut down the volume servers
that has those volumes.

1. fix the .dat file in place
	// just see the replication setting
	go run change_replication.go -volumeId=9 -dir=/Users/chrislu/Downloads
		Current Volume Replication: 000
	// fix the replication setting
	go run change_replication.go -volumeId=9 -dir=/Users/chrislu/Downloads -replication 001
		Current Volume Replication: 000
		Changing to: 001
		Done.

2. copy the fixed .dat and related .idx files to some remote server
3. restart volume servers or start new volume servers.
*/
func main() {
	flag.Parse()
	fileName := strconv.Itoa(*fixVolumeId)
	if *fixVolumeCollection != "" {
		fileName = *fixVolumeCollection + "_" + fileName
	}
	datFile, err := os.OpenFile(path.Join(*fixVolumePath, fileName+".dat"), os.O_RDWR, 0644)
	if err != nil {
		glog.Fatalf("Open Volume Data File [ERROR]: %v", err)
	}
	defer datFile.Close()

	superBlock, err := storage.ReadSuperBlock(datFile)

	if err != nil {
		glog.Fatalf("cannot parse existing super block: %v", err)
	}

	fmt.Printf("Current Volume Replication: %s\n", superBlock.ReplicaPlacement)
	fmt.Printf("Current Volume TTL: %s\n", superBlock.Ttl.String())

	hasChange := false

	if *targetReplica != "" {
		replica, err := storage.NewReplicaPlacementFromString(*targetReplica)

		if err != nil {
			glog.Fatalf("cannot parse target replica %s: %v", *targetReplica, err)
		}

		fmt.Printf("Changing replication to: %s\n", replica)

		superBlock.ReplicaPlacement = replica
		hasChange = true
	}

	if *targetTTL != "" {
		ttl, err := storage.ReadTTL(*targetTTL)

		if err != nil {
			glog.Fatalf("cannot parse target ttl %s: %v", *targetTTL, err)
		}

		fmt.Printf("Changing ttl to: %s\n", ttl)

		superBlock.Ttl = ttl
		hasChange = true
	}

	if hasChange {

		header = superBlock.Bytes()

		if n, e := datFile.WriteAt(header, 0); n == 0 || e != nil {
			glog.Fatalf("cannot write super block: %v", e)
		}

		fmt.Println("Change Applied.")
	}

}
<commit_msg>fix the tool for change replication factor<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
	"strconv"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/storage"
)

var (
	fixVolumePath       = flag.String("dir", "/tmp", "data directory to store files")
	fixVolumeCollection = flag.String("collection", "", "the volume collection name")
	fixVolumeId         = flag.Int("volumeId", -1, "a volume id. The volume should already exist in the dir. The volume index file should not exist.")
	targetReplica       = flag.String("replication", "", "If just empty, only print out current replication setting.")
	targetTTL           = flag.String("ttl", "", "If just empty, only print out current ttl setting.")
)

/*
This is to change replication factor in .dat file header. Need to shut down the volume servers
that has those volumes.

1. fix the .dat file in place
	// just see the replication setting
	go run change_replication.go -volumeId=9 -dir=/Users/chrislu/Downloads
		Current Volume Replication: 000
	// fix the replication setting
	go run change_replication.go -volumeId=9 -dir=/Users/chrislu/Downloads -replication 001
		Current Volume Replication: 000
		Changing to: 001
		Done.

2. copy the fixed .dat and related .idx files to some remote server
3. restart volume servers or start new volume servers.
*/
func main() {
	flag.Parse()
	fileName := strconv.Itoa(*fixVolumeId)
	if *fixVolumeCollection != "" {
		fileName = *fixVolumeCollection + "_" + fileName
	}
	datFile, err := os.OpenFile(path.Join(*fixVolumePath, fileName+".dat"), os.O_RDWR, 0644)
	if err != nil {
		glog.Fatalf("Open Volume Data File [ERROR]: %v", err)
	}
	defer datFile.Close()

	superBlock, err := storage.ReadSuperBlock(datFile)

	if err != nil {
		glog.Fatalf("cannot parse existing super block: %v", err)
	}

	fmt.Printf("Current Volume Replication: %s\n", superBlock.ReplicaPlacement)
	fmt.Printf("Current Volume TTL: %s\n", superBlock.Ttl.String())

	hasChange := false

	if *targetReplica != "" {
		replica, err := storage.NewReplicaPlacementFromString(*targetReplica)

		if err != nil {
			glog.Fatalf("cannot parse target replica %s: %v", *targetReplica, err)
		}

		fmt.Printf("Changing replication to: %s\n", replica)

		superBlock.ReplicaPlacement = replica
		hasChange = true
	}

	if *targetTTL != "" {
		ttl, err := storage.ReadTTL(*targetTTL)

		if err != nil {
			glog.Fatalf("cannot parse target ttl %s: %v", *targetTTL, err)
		}

		fmt.Printf("Changing ttl to: %s\n", ttl)

		superBlock.Ttl = ttl
		hasChange = true
	}

	if hasChange {

		header := superBlock.Bytes()

		if n, e := datFile.WriteAt(header, 0); n == 0 || e != nil {
			glog.Fatalf("cannot write super block: %v", e)
		}

		fmt.Println("Change Applied.")
	}

}
<|endoftext|>


<commit_before>package main_test

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"

	"github.com/concourse/time-resource/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Out", func() {
	var tmpdir string
	var source string

	var outCmd *exec.Cmd
	var now time.Time

	BeforeEach(func() {
		var err error

		tmpdir, err = ioutil.TempDir("", "out-source")
		Expect(err).NotTo(HaveOccurred())

		source = path.Join(tmpdir, "out-dir")

		outCmd = exec.Command(outPath, source)
		now = time.Now().UTC()
	})

	AfterEach(func() {
		os.RemoveAll(tmpdir)
	})

	Context("when executed", func() {
		var source map[string]interface{}
		var response models.OutResponse

		BeforeEach(func() {
			source = map[string]interface{}{}
			response = models.OutResponse{}
		})

		JustBeforeEach(func() {
			stdin, err := outCmd.StdinPipe()
			Expect(err).NotTo(HaveOccurred())

			session, err := gexec.Start(outCmd, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			err = json.NewEncoder(stdin).Encode(map[string]interface{}{
				"source": source,
			})
			Expect(err).NotTo(HaveOccurred())

			<-session.Exited
			Expect(session.ExitCode()).To(Equal(0))

			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when a location is specified", func() {
			var loc *time.Location

			BeforeEach(func() {
				var err error
				loc, err = time.LoadLocation("America/Indiana/Indianapolis")
				Expect(err).ToNot(HaveOccurred())

				source["location"] = loc.String()

				now = now.In(loc)
			})

			It("reports specified location's current time(offset: -0400) as the version", func() {
				contained := strings.Contains(response.Version.Time.String(), "-0400")
				Expect(contained).To(BeTrue())
			})
		})
		Context("when a location is not specified", func() {
			It("reports the current time(offset: 0000) as the version", func() {
				contained := strings.Contains(response.Version.Time.String(), "0000")
				Expect(contained).To(BeTrue())
			})
		})
	})
})
<commit_msg>add version samles to test<commit_after>package main_test

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"

	"github.com/concourse/time-resource/models"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Out", func() {
	var tmpdir string
	var source string

	var outCmd *exec.Cmd
	var now time.Time

	BeforeEach(func() {
		var err error

		tmpdir, err = ioutil.TempDir("", "out-source")
		Expect(err).NotTo(HaveOccurred())

		source = path.Join(tmpdir, "out-dir")

		outCmd = exec.Command(outPath, source)
		now = time.Now().UTC()
	})

	AfterEach(func() {
		os.RemoveAll(tmpdir)
	})

	Context("when executed", func() {
		var source map[string]interface{}
		var response models.OutResponse

		BeforeEach(func() {
			source = map[string]interface{}{}
			response = models.OutResponse{}
		})

		JustBeforeEach(func() {
			stdin, err := outCmd.StdinPipe()
			Expect(err).NotTo(HaveOccurred())

			session, err := gexec.Start(outCmd, GinkgoWriter, GinkgoWriter)
			Expect(err).NotTo(HaveOccurred())

			err = json.NewEncoder(stdin).Encode(map[string]interface{}{
				"source": source,
			})
			Expect(err).NotTo(HaveOccurred())

			<-session.Exited
			Expect(session.ExitCode()).To(Equal(0))

			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).NotTo(HaveOccurred())
		})

		Context("when a location is specified", func() {
			var loc *time.Location

			BeforeEach(func() {
				var err error
				loc, err = time.LoadLocation("America/Indiana/Indianapolis")
				Expect(err).ToNot(HaveOccurred())

				source["location"] = loc.String()

				now = now.In(loc)
			})

			It("reports specified location's current time(offset: -0400) as the version", func() {
				// An example of response.Version.Time.String() is
				// 2019-04-03 14:53:10.951241 -0400 EDT
				contained := strings.Contains(response.Version.Time.String(), "-0400")
				Expect(contained).To(BeTrue())
			})
		})
		Context("when a location is not specified", func() {
			It("reports the current time(offset: 0000) as the version", func() {
				// An example of response.Version.Time.String() is
				// 2019-04-03 18:53:10.964705 +0000 UTC
				contained := strings.Contains(response.Version.Time.String(), "0000")
				Expect(contained).To(BeTrue())
			})
		})
	})
})
<|endoftext|>


<commit_before>package parser

import (
	"fmt"

	"github.com/Zac-Garby/pluto/token"
)

type Error struct {
	Message    string
	Start, End token.Position
}

func (p *Parser) err(msg string, start, end token.Position) {
	err := Error{
		Message: msg,
		Start:   start,
		End:     end,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) defaultErr(msg string) {
	err := Error{
		Message: msg,
		Start:   p.cur.Start,
		End:     p.cur.End,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) peekErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.peek.Type)

		p.err(msg, p.peek.Start, p.peek.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.peek.Type)
		p.err(msg, p.peek.Start, p.peek.End)
	}
}

func (p *Parser) curErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.cur.Type)

		p.err(msg, p.cur.Start, p.cur.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.cur.Type)
		p.err(msg, p.cur.Start, p.cur.End)
	}
}

func (p *Parser) unexpectedTokenErr(t token.Type) {
	msg := fmt.Sprintf("unexpected token: %s", t)
	p.defaultErr(msg)
}

func (p *Parser) printError(index int) {
	err := p.Errors[index]

	fmt.Printf("%s → %s\t- %s\n", err.Start.String(), err.End.String(), err.Message)
}

func (p *Parser) PrintErrors() {
	for i := range p.Errors {
		p.printError(i)
	}
}
<commit_msg>Remove an unnecessary hyphen<commit_after>package parser

import (
	"fmt"

	"github.com/Zac-Garby/pluto/token"
)

type Error struct {
	Message    string
	Start, End token.Position
}

func (p *Parser) err(msg string, start, end token.Position) {
	err := Error{
		Message: msg,
		Start:   start,
		End:     end,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) defaultErr(msg string) {
	err := Error{
		Message: msg,
		Start:   p.cur.Start,
		End:     p.cur.End,
	}

	p.Errors = append(p.Errors, err)
}

func (p *Parser) peekErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.peek.Type)

		p.err(msg, p.peek.Start, p.peek.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.peek.Type)
		p.err(msg, p.peek.Start, p.peek.End)
	}
}

func (p *Parser) curErr(ts ...token.Type) {
	if len(ts) > 1 {
		msg := "expected either "

		for i, t := range ts {
			msg += string(t)

			if i+1 < len(ts) {
				msg += ", "
			} else if i < len(ts) {
				msg += ", or "
			}
		}

		msg += ", but got " + string(p.cur.Type)

		p.err(msg, p.cur.Start, p.cur.End)
	} else if len(ts) == 1 {
		msg := fmt.Sprintf("expected %s, but got %s", ts[0], p.cur.Type)
		p.err(msg, p.cur.Start, p.cur.End)
	}
}

func (p *Parser) unexpectedTokenErr(t token.Type) {
	msg := fmt.Sprintf("unexpected token: %s", t)
	p.defaultErr(msg)
}

func (p *Parser) printError(index int) {
	err := p.Errors[index]

	fmt.Printf("%s → %s\t%s\n", err.Start.String(), err.End.String(), err.Message)
}

func (p *Parser) PrintErrors() {
	for i := range p.Errors {
		p.printError(i)
	}
}
<|endoftext|>


<commit_before>package uuid

import (
	"github.com/stretchr/testify/assert"
	"regexp"
	"strings"
	"testing"
	"fmt"
)

const (
	clean                   = `[0-9a-f]{8}[0-9a-f]{4}[1-5][0-9a-f]{3}[0-9a-f]{4}[0-9a-f]{12}`
	cleanHexPattern         = `^` + clean + `$`
	curlyHexPattern         = `^\{` + clean + `\}$`
	bracketHexPattern       = `^\(` + clean + `\)$`
	hyphen                  = `[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}`
	cleanHyphenHexPattern   = `^` + hyphen + `$`
	curlyHyphenHexPattern   = `^\{` + hyphen + `\}$`
	bracketHyphenHexPattern = `^\(` + hyphen + `\)$`
	urnHexPattern           = `^urn:uuid:` + hyphen + `$`
)

var (
	formats = []Format{
		CanonicalCurly,
		Hex,
		HexCurly,
		HexBracket,
		Canonical,
		CanonicalBracket,
		Urn,
	}
	patterns = []string{
		curlyHyphenHexPattern,
		cleanHexPattern,
		curlyHexPattern,
		bracketHexPattern,
		cleanHyphenHexPattern,
		bracketHyphenHexPattern,
		urnHexPattern,
	}
)

func TestSwitchFormat(t *testing.T) {
	ids := []UUID{NewV4(), NewV4()}

	// Reset default
	SwitchFormat(Canonical)

	for _, u := range ids {
		for i := range formats {
			SwitchFormat(formats[i])
			assert.True(t, regexp.MustCompile(patterns[i]).MatchString(u.String()), "Format %s must compile pattern %s", formats[i], patterns[i])
			outputLn(u)
		}
	}

	assert.True(t, didSwitchFormatPanic(""), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatPanic("%c%c%c%x%x%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatPanic("%x%X%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatPanic("%x%x%x%x%x%%%%"), "Switch format should panic when format invalid")

	// Reset default
	SwitchFormat(Canonical)
}

func didSwitchFormatPanic(pFormat string) bool {
	return func() (didPanic bool) {
		defer func() {
			if recover() != nil {
				didPanic = true
			}
		}()

		SwitchFormat(Format(pFormat))
		return
	}()
}

func TestSwitchFormatToUpper(t *testing.T) {
	ids := []UUID{NewV4(), NewV4()}

	// Reset default
	SwitchFormat(Canonical)

	for _, u := range ids {
		for i := range formats {
			SwitchFormatToUpper(formats[i])
			assert.True(t, regexp.MustCompile(strings.ToUpper(patterns[i])).MatchString(u.String()), "Format %s must compile pattern %s", formats[i], patterns[i])
			outputLn(u)
		}
	}

	assert.True(t, didSwitchFormatToUpperPanic(""), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatToUpperPanic("%c%c%c%x%x%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatToUpperPanic("%x%X%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatToUpperPanic("%x%x%x%x%x%%%%"), "Switch format should panic when format invalid")

	// Reset default
	SwitchFormat(Canonical)
}

func didSwitchFormatToUpperPanic(pFormat string) bool {
	return func() (didPanic bool) {
		defer func() {
			if recover() != nil {
				didPanic = true
			}
		}()

		SwitchFormatToUpper(Format(pFormat))
		return
	}()
}

func TestFormatter(t *testing.T) {
	ids := []UUID{NewV4(), NewV4()}

	for _, u := range ids {
		for i := range formats {
			assert.True(t, regexp.MustCompile(patterns[i]).MatchString(Formatter(u, formats[i])), "Format must compile")
			outputLn(Formatter(u, formats[i]))
		}
	}

	for k, v := range namespaces {
		s := Formatter(k, Canonical)
		assert.Equal(t, v, s, "Should match")

		s = Formatter(k, Format(strings.ToUpper(string(Canonical))))
		assert.Equal(t, strings.ToUpper(v), s, "Should match")
	}

	assert.True(t, didFormatterPanic(""), "Should panic when format invalid")
	assert.True(t, didFormatterPanic("%c%c%c%x%x%x"), "Should panic when format invalid")
	assert.True(t, didFormatterPanic("%x%X%x"), "Should panic when format invalid")
	assert.True(t, didFormatterPanic("%x%x%x%x%x%%%%"), "Should panic when format invalid")

}

func didFormatterPanic(pFormat string) bool {
	return func() (didPanic bool) {
		defer func() {
			if recover() != nil {
				didPanic = true
			}
		}()

		Formatter(NameSpaceDNS, Format(pFormat))
		return
	}()
}

// *******************************************************

func BenchmarkFormatter(b *testing.B) {
	id := NewV2(DomainGroup)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Formatter(id, "{%X-%X-%X-%x-%X}")
	}
	b.StopTimer()
	b.ReportAllocs()
}

func TestNewV12(t *testing.T) {
	for i:= 0; i < 2048; i++ {
		  fmt.Println(NewV1())
	}
}
<commit_msg>Improve uniqueness test<commit_after>package uuid

import (
	"github.com/stretchr/testify/assert"
	"regexp"
	"strings"
	"testing"
	"fmt"
)

const (
	clean                   = `[0-9a-f]{8}[0-9a-f]{4}[1-5][0-9a-f]{3}[0-9a-f]{4}[0-9a-f]{12}`
	cleanHexPattern         = `^` + clean + `$`
	curlyHexPattern         = `^\{` + clean + `\}$`
	bracketHexPattern       = `^\(` + clean + `\)$`
	hyphen                  = `[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}`
	cleanHyphenHexPattern   = `^` + hyphen + `$`
	curlyHyphenHexPattern   = `^\{` + hyphen + `\}$`
	bracketHyphenHexPattern = `^\(` + hyphen + `\)$`
	urnHexPattern           = `^urn:uuid:` + hyphen + `$`
)

var (
	formats = []Format{
		CanonicalCurly,
		Hex,
		HexCurly,
		HexBracket,
		Canonical,
		CanonicalBracket,
		Urn,
	}
	patterns = []string{
		curlyHyphenHexPattern,
		cleanHexPattern,
		curlyHexPattern,
		bracketHexPattern,
		cleanHyphenHexPattern,
		bracketHyphenHexPattern,
		urnHexPattern,
	}
)

func TestSwitchFormat(t *testing.T) {
	ids := []UUID{NewV4(), NewV4()}

	// Reset default
	SwitchFormat(Canonical)

	for _, u := range ids {
		for i := range formats {
			SwitchFormat(formats[i])
			assert.True(t, regexp.MustCompile(patterns[i]).MatchString(u.String()), "Format %s must compile pattern %s", formats[i], patterns[i])
			outputLn(u)
		}
	}

	assert.True(t, didSwitchFormatPanic(""), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatPanic("%c%c%c%x%x%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatPanic("%x%X%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatPanic("%x%x%x%x%x%%%%"), "Switch format should panic when format invalid")

	// Reset default
	SwitchFormat(Canonical)
}

func didSwitchFormatPanic(pFormat string) bool {
	return func() (didPanic bool) {
		defer func() {
			if recover() != nil {
				didPanic = true
			}
		}()

		SwitchFormat(Format(pFormat))
		return
	}()
}

func TestSwitchFormatToUpper(t *testing.T) {
	ids := []UUID{NewV4(), NewV4()}

	// Reset default
	SwitchFormat(Canonical)

	for _, u := range ids {
		for i := range formats {
			SwitchFormatToUpper(formats[i])
			assert.True(t, regexp.MustCompile(strings.ToUpper(patterns[i])).MatchString(u.String()), "Format %s must compile pattern %s", formats[i], patterns[i])
			outputLn(u)
		}
	}

	assert.True(t, didSwitchFormatToUpperPanic(""), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatToUpperPanic("%c%c%c%x%x%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatToUpperPanic("%x%X%x"), "Switch format should panic when format invalid")
	assert.True(t, didSwitchFormatToUpperPanic("%x%x%x%x%x%%%%"), "Switch format should panic when format invalid")

	// Reset default
	SwitchFormat(Canonical)
}

func didSwitchFormatToUpperPanic(pFormat string) bool {
	return func() (didPanic bool) {
		defer func() {
			if recover() != nil {
				didPanic = true
			}
		}()

		SwitchFormatToUpper(Format(pFormat))
		return
	}()
}

func TestFormatter(t *testing.T) {
	ids := []UUID{NewV4(), NewV4()}

	for _, u := range ids {
		for i := range formats {
			assert.True(t, regexp.MustCompile(patterns[i]).MatchString(Formatter(u, formats[i])), "Format must compile")
			outputLn(Formatter(u, formats[i]))
		}
	}

	for k, v := range namespaces {
		s := Formatter(k, Canonical)
		assert.Equal(t, v, s, "Should match")

		s = Formatter(k, Format(strings.ToUpper(string(Canonical))))
		assert.Equal(t, strings.ToUpper(v), s, "Should match")
	}

	assert.True(t, didFormatterPanic(""), "Should panic when format invalid")
	assert.True(t, didFormatterPanic("%c%c%c%x%x%x"), "Should panic when format invalid")
	assert.True(t, didFormatterPanic("%x%X%x"), "Should panic when format invalid")
	assert.True(t, didFormatterPanic("%x%x%x%x%x%%%%"), "Should panic when format invalid")

}

func didFormatterPanic(pFormat string) bool {
	return func() (didPanic bool) {
		defer func() {
			if recover() != nil {
				didPanic = true
			}
		}()

		Formatter(NameSpaceDNS, Format(pFormat))
		return
	}()
}

// *******************************************************

func BenchmarkFormatter(b *testing.B) {
	id := NewV2(DomainGroup)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		Formatter(id, "{%X-%X-%X-%x-%X}")
	}
	b.StopTimer()
	b.ReportAllocs()
}

<|endoftext|>


<commit_before>package rpc

import (
	"bufio"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/rpc"
	"time"
)

type gobClientCodec struct {
	rwc    io.ReadWriteCloser
	dec    *gob.Decoder
	enc    *gob.Encoder
	encBuf *bufio.Writer
}

func (c *gobClientCodec) WriteRequest(r *rpc.Request, body interface{}) (err error) {
	if err = TimeoutCoder(c.enc.Encode, r, "client write request"); err != nil {
		return
	}
	if err = TimeoutCoder(c.enc.Encode, body, "client write request body"); err != nil {
		return
	}
	return c.encBuf.Flush()
}

func (c *gobClientCodec) ReadResponseHeader(r *rpc.Response) error {
	return c.dec.Decode(r)
}

func (c *gobClientCodec) ReadResponseBody(body interface{}) error {
	return c.dec.Decode(body)
}

func (c *gobClientCodec) Close() error {
	return c.rwc.Close()
}

// Call 调用
func Call(addr string, serviceMethod string, args interface{}, reply interface{}) error {
	bts, _ := json.Marshal(args)
	log.Printf("RPC call %s %s %s ", addr, serviceMethod, string(bts))
	conn, err := net.DialTimeout("tcp", addr, time.Second*10)
	if err != nil {
		return err
	}
	encBuf := bufio.NewWriter(conn)
	codec := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
	c := rpc.NewClientWithCodec(codec)
	err = c.Call(serviceMethod, args, reply)
	errC := c.Close()
	if err != nil && errC != nil {
		return fmt.Errorf("%s %s", err, errC)
	}
	if err != nil {
		return err
	}
	return errC

}
<commit_msg>修复rpc客户端请求堵塞<commit_after>package rpc

import (
	"bufio"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/rpc"
	"time"
)

type gobClientCodec struct {
	rwc    io.ReadWriteCloser
	dec    *gob.Decoder
	enc    *gob.Encoder
	encBuf *bufio.Writer
}

func (c *gobClientCodec) WriteRequest(r *rpc.Request, body interface{}) (err error) {
	if err = TimeoutCoder(c.enc.Encode, r, "client write request"); err != nil {
		return
	}
	if err = TimeoutCoder(c.enc.Encode, body, "client write request body"); err != nil {
		return
	}
	return c.encBuf.Flush()
}

func (c *gobClientCodec) ReadResponseHeader(r *rpc.Response) error {
	return TimeoutCoder(c.dec.Decode, r, "client read response header")
}

func (c *gobClientCodec) ReadResponseBody(body interface{}) error {
	return TimeoutCoder(c.dec.Decode, body, "client read response body")
}

func (c *gobClientCodec) Close() error {
	return c.rwc.Close()
}

// Call 调用
func Call(addr string, serviceMethod string, args interface{}, reply interface{}) error {
	bts, _ := json.Marshal(args)
	log.Printf("RPC call %s %s %s ", addr, serviceMethod, string(bts))
	conn, err := net.DialTimeout("tcp", addr, time.Second*10)
	if err != nil {
		return err
	}
	encBuf := bufio.NewWriter(conn)
	codec := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
	c := rpc.NewClientWithCodec(codec)
	err = c.Call(serviceMethod, args, reply)
	errC := c.Close()
	if err != nil && errC != nil {
		return fmt.Errorf("%s %s", err, errC)
	}
	if err != nil {
		return err
	}
	return errC

}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gc

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"testing"
)

// TestAssembly checks to make sure the assembly generated for
// functions contains certain expected instructions.
// Note: this test will fail if -ssa=0.
func TestAssembly(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	if runtime.GOOS == "windows" {
		// TODO: remove if we can get "go tool compile -S" to work on windows.
		t.Skipf("skipping test: recursive windows compile not working")
	}
	dir, err := ioutil.TempDir("", "TestAssembly")
	if err != nil {
		t.Fatalf("could not create directory: %v", err)
	}
	defer os.RemoveAll(dir)

	for _, test := range asmTests {
		asm := compileToAsm(dir, test.arch, fmt.Sprintf(template, test.function))
		// Get rid of code for "".init. Also gets rid of type algorithms & other junk.
		if i := strings.Index(asm, "\n\"\".init "); i >= 0 {
			asm = asm[:i+1]
		}
		for _, r := range test.regexps {
			if b, err := regexp.MatchString(r, asm); !b || err != nil {
				t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, test.function, asm)
			}
		}
	}
}

// compile compiles the package pkg for architecture arch and
// returns the generated assembly.  dir is a scratch directory.
func compileToAsm(dir, arch, pkg string) string {
	// Create source.
	src := filepath.Join(dir, "test.go")
	f, err := os.Create(src)
	if err != nil {
		panic(err)
	}
	f.Write([]byte(pkg))
	f.Close()

	var stdout, stderr bytes.Buffer
	cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
	cmd.Env = append([]string{"GOARCH=" + arch}, os.Environ()...)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	if s := stderr.String(); s != "" {
		panic(fmt.Errorf("Stderr = %s\nWant empty", s))
	}
	return stdout.String()
}

// template to convert a function to a full file
const template = `
package main
%s
`

type asmTest struct {
	// architecture to compile to
	arch string
	// function to compile
	function string
	// regexps that must match the generated assembly
	regexps []string
}

var asmTests = [...]asmTest{
	{"amd64", `
func f(x int) int {
	return x * 64
}
`,
		[]string{"\tSHLQ\t\\$6,"},
	},
	{"amd64", `
func f(x int) int {
	return x * 96
}`,
		[]string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
	},
}
<commit_msg>cmd/compile: fix TestAssembly on Plan 9<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gc

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"testing"
)

// TestAssembly checks to make sure the assembly generated for
// functions contains certain expected instructions.
// Note: this test will fail if -ssa=0.
func TestAssembly(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	if runtime.GOOS == "windows" {
		// TODO: remove if we can get "go tool compile -S" to work on windows.
		t.Skipf("skipping test: recursive windows compile not working")
	}
	dir, err := ioutil.TempDir("", "TestAssembly")
	if err != nil {
		t.Fatalf("could not create directory: %v", err)
	}
	defer os.RemoveAll(dir)

	for _, test := range asmTests {
		asm := compileToAsm(dir, test.arch, fmt.Sprintf(template, test.function))
		// Get rid of code for "".init. Also gets rid of type algorithms & other junk.
		if i := strings.Index(asm, "\n\"\".init "); i >= 0 {
			asm = asm[:i+1]
		}
		for _, r := range test.regexps {
			if b, err := regexp.MatchString(r, asm); !b || err != nil {
				t.Errorf("expected:%s\ngo:%s\nasm:%s\n", r, test.function, asm)
			}
		}
	}
}

// compile compiles the package pkg for architecture arch and
// returns the generated assembly.  dir is a scratch directory.
func compileToAsm(dir, arch, pkg string) string {
	// Create source.
	src := filepath.Join(dir, "test.go")
	f, err := os.Create(src)
	if err != nil {
		panic(err)
	}
	f.Write([]byte(pkg))
	f.Close()

	var stdout, stderr bytes.Buffer
	cmd := exec.Command("go", "tool", "compile", "-S", "-o", filepath.Join(dir, "out.o"), src)
	cmd.Env = mergeEnvLists([]string{"GOARCH=" + arch}, os.Environ())
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
	if s := stderr.String(); s != "" {
		panic(fmt.Errorf("Stderr = %s\nWant empty", s))
	}
	return stdout.String()
}

// template to convert a function to a full file
const template = `
package main
%s
`

type asmTest struct {
	// architecture to compile to
	arch string
	// function to compile
	function string
	// regexps that must match the generated assembly
	regexps []string
}

var asmTests = [...]asmTest{
	{"amd64", `
func f(x int) int {
	return x * 64
}
`,
		[]string{"\tSHLQ\t\\$6,"},
	},
	{"amd64", `
func f(x int) int {
	return x * 96
}`,
		[]string{"\tSHLQ\t\\$5,", "\tLEAQ\t\\(.*\\)\\(.*\\*2\\),"},
	},
}

// mergeEnvLists merges the two environment lists such that
// variables with the same name in "in" replace those in "out".
// This always returns a newly allocated slice.
func mergeEnvLists(in, out []string) []string {
	out = append([]string(nil), out...)
NextVar:
	for _, inkv := range in {
		k := strings.SplitAfterN(inkv, "=", 2)[0]
		for i, outkv := range out {
			if strings.HasPrefix(outkv, k) {
				out[i] = inkv
				continue NextVar
			}
		}
		out = append(out, inkv)
	}
	return out
}
<|endoftext|>


<commit_before>package metadata

import (
	"github.com/jcloudpub/speedy/chunkmaster/util"
	"time"
)

type Chunkserver struct {
	Id					string `json:"-"`
	GroupId				uint16
	Ip					string
	Port				int
	Status				int
	GlobalStatus    	int
	TotalFreeSpace		int64
	MaxFreeSpace		int64
	PendingWrites		int
	WritingCount   		int
	ReadingCount 		uint32
	TotalChunks 		uint32
	ConnectionsCount 	uint32
	DataDir				string
	UpdateTime			time.Time `json:"-"`
}

type Chunkservers []*Chunkserver

type MetaDataDriver interface {
	Close() error

	AddChunkserver(chunkserver *Chunkserver) error
	UpdateChunkserverStatus(chunkserver *Chunkserver, preStatus int, status int) error
	IsExistChunkserver(chunkServer *Chunkserver) (bool, error)
	//UpdateChunkserver(chunkserver *Chunkserver) error
	UpdateChunkserverInfo(chunkserver *Chunkserver, preStatus int, status int) error
	ListChunkserver() (Chunkservers, error)
	//UpdateChunkserverAbortCount(ip string, port, status, errStatus int) error
	UpdateChunkserverNORMAL(ip string, port, status, count int) error
	UpdateChunkserverERROR(ip string, port, status, count int) error

	GetFid() (uint64, error)
	UpdateFid(fid uint64) error
}

func GenChunkserver(jsonMap map[string]interface{}) (*Chunkserver, error) {
	chunkserver := new(Chunkserver)

	ip, err := util.CheckMapString(jsonMap, "Ip")
	if err != nil {
		return nil, err
	}
	chunkserver.Ip = ip

	port, err := util.CheckMapInt(jsonMap, "Port")
	if err != nil {
		return nil, err
	}
	chunkserver.Port = port

	/*
	status, err := util.CheckMapInt(jsonMap, "Status")
	if err != nil {
		return nil, err
	}
	*/

	//chunkserver.Status = status

	groupId, err := util.CheckMapUInt16(jsonMap, "GroupId")
	if err != nil {
		return nil, err
	}
	chunkserver.GroupId = groupId

	maxFreeSpace, err := util.CheckMapInt64(jsonMap, "MaxFreeSpace")
	if err != nil {
		return nil, err
	}
	chunkserver.MaxFreeSpace = maxFreeSpace

	totalFreeSpace, err := util.CheckMapInt64(jsonMap, "TotalFreeSpace")
	if err != nil {
		return nil, err
	}
	chunkserver.TotalFreeSpace = totalFreeSpace

	pendingWrites, err := util.CheckMapInt(jsonMap, "PendingWrites")
	if err != nil {
		return nil, err
	}
	chunkserver.PendingWrites = pendingWrites

	writtingCount, err := util.CheckMapInt(jsonMap, "WritingCount")
	if err != nil {
		return nil, err
	}
	chunkserver.WritingCount = writtingCount

	dataDir, err := util.CheckMapString(jsonMap, "DataDir")
	if err != nil {
		return nil, err
	}
	chunkserver.DataDir = dataDir

	readCount, err := util.CheckMapUInt32(jsonMap, "ReadingCount")
	if err != nil {
		return nil, err
	}
	chunkserver.ReadingCount = readCount

	totalChunks, err := util.CheckMapUInt32(jsonMap, "TotalChunks")
	if err != nil {
		return nil, err
	}
	chunkserver.TotalChunks = totalChunks

	connectionsCount, err := util.CheckMapUInt32(jsonMap, "ConnectionsCount")
	if err != nil {
		return nil, err
	}
	chunkserver.ConnectionsCount = connectionsCount

	return chunkserver, nil
}
<commit_msg>delete useless code<commit_after>package metadata

import (
	"github.com/jcloudpub/speedy/chunkmaster/util"
	"time"
)

type Chunkserver struct {
	Id					string `json:"-"`
	GroupId				uint16
	Ip					string
	Port				int
	Status				int
	GlobalStatus    	int
	TotalFreeSpace		int64
	MaxFreeSpace		int64
	PendingWrites		int
	WritingCount   		int
	ReadingCount 		uint32
	TotalChunks 		uint32
	ConnectionsCount 	uint32
	DataDir				string
	UpdateTime			time.Time `json:"-"`
}

type Chunkservers []*Chunkserver

type MetaDataDriver interface {
	Close() error

	AddChunkserver(chunkserver *Chunkserver) error
	UpdateChunkserverStatus(chunkserver *Chunkserver, preStatus int, status int) error
	IsExistChunkserver(chunkServer *Chunkserver) (bool, error)
	UpdateChunkserverInfo(chunkserver *Chunkserver, preStatus int, status int) error
	ListChunkserver() (Chunkservers, error)
	UpdateChunkserverNORMAL(ip string, port, status, count int) error
	UpdateChunkserverERROR(ip string, port, status, count int) error

	GetFid() (uint64, error)
	UpdateFid(fid uint64) error
}

func GenChunkserver(jsonMap map[string]interface{}) (*Chunkserver, error) {
	chunkserver := new(Chunkserver)

	ip, err := util.CheckMapString(jsonMap, "Ip")
	if err != nil {
		return nil, err
	}
	chunkserver.Ip = ip

	port, err := util.CheckMapInt(jsonMap, "Port")
	if err != nil {
		return nil, err
	}
	chunkserver.Port = port

	/*
	status, err := util.CheckMapInt(jsonMap, "Status")
	if err != nil {
		return nil, err
	}
	*/

	//chunkserver.Status = status

	groupId, err := util.CheckMapUInt16(jsonMap, "GroupId")
	if err != nil {
		return nil, err
	}
	chunkserver.GroupId = groupId

	maxFreeSpace, err := util.CheckMapInt64(jsonMap, "MaxFreeSpace")
	if err != nil {
		return nil, err
	}
	chunkserver.MaxFreeSpace = maxFreeSpace

	totalFreeSpace, err := util.CheckMapInt64(jsonMap, "TotalFreeSpace")
	if err != nil {
		return nil, err
	}
	chunkserver.TotalFreeSpace = totalFreeSpace

	pendingWrites, err := util.CheckMapInt(jsonMap, "PendingWrites")
	if err != nil {
		return nil, err
	}
	chunkserver.PendingWrites = pendingWrites

	writtingCount, err := util.CheckMapInt(jsonMap, "WritingCount")
	if err != nil {
		return nil, err
	}
	chunkserver.WritingCount = writtingCount

	dataDir, err := util.CheckMapString(jsonMap, "DataDir")
	if err != nil {
		return nil, err
	}
	chunkserver.DataDir = dataDir

	readCount, err := util.CheckMapUInt32(jsonMap, "ReadingCount")
	if err != nil {
		return nil, err
	}
	chunkserver.ReadingCount = readCount

	totalChunks, err := util.CheckMapUInt32(jsonMap, "TotalChunks")
	if err != nil {
		return nil, err
	}
	chunkserver.TotalChunks = totalChunks

	connectionsCount, err := util.CheckMapUInt32(jsonMap, "ConnectionsCount")
	if err != nil {
		return nil, err
	}
	chunkserver.ConnectionsCount = connectionsCount

	return chunkserver, nil
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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.

// Implements php/composer-install buildpack.
// The composer-install buildpack installs the composer dependency manager.
package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/runtime"
	"github.com/buildpacks/libcnb"
)

var (
	composerLayer    = "composer"
	composerJSON     = "composer.json"
	composerSetup    = "composer-setup"
	composerVer      = "2.1.3"
	versionKey       = "version"
	composerSigURL   = "https://composer.github.io/installer.sig"
	composerSetupURL = "https://getcomposer.org/installer"
)

func main() {
	gcp.Main(detectFn, buildFn)
}

func detectFn(ctx *gcp.Context) (gcp.DetectResult, error) {
	composerJSONExists, err := ctx.FileExists(composerJSON)
	if err != nil {
		return nil, err
	}
	if !composerJSONExists {
		return gcp.OptOutFileNotFound(composerJSON), nil
	}
	return gcp.OptInFileFound(composerJSON), nil
}

func buildFn(ctx *gcp.Context) error {
	l := ctx.Layer(composerLayer, gcp.BuildLayer, gcp.CacheLayer)

	ctx.AddBOMEntry(libcnb.BOMEntry{
		Name:     composerLayer,
		Metadata: map[string]interface{}{"version": composerVer},
		Build:    true,
	})

	// Check the metadata in the cache layer to determine if we need to proceed.
	metaVersion := ctx.GetMetadata(l, versionKey)
	if composerVer == metaVersion {
		ctx.CacheHit(composerLayer)
		ctx.Logf("composer binary cache hit, skipping installation.")
		return nil
	}
	ctx.CacheMiss(composerLayer)
	ctx.ClearLayer(l)

	// download the installer
	installer, err := os.CreateTemp(l.Path, fmt.Sprintf("%s-*.php", composerSetup))
	if err != nil {
		return fmt.Errorf("creating temp file: %w", err)
	}
	defer os.Remove(installer.Name())

	if err := runtime.FetchURL(composerSetupURL, installer); err != nil {
		return fmt.Errorf("failed to download composer installer from %s: %w", composerSetupURL, err)
	}

	// verify the installer hash
	var expectedSHABuf bytes.Buffer
	if err := runtime.FetchURL(composerSigURL, io.Writer(&expectedSHABuf)); err != nil {
		return fmt.Errorf("failed to fetch the installer signature from %s: %w", composerSigURL, err)
	}
	expectedSHA := expectedSHABuf.String()
	actualSHACmd := fmt.Sprintf("php -r \"echo hash_file('sha384', '%s');\"", installer.Name())
	actualSHA := ctx.Exec([]string{"bash", "-c", actualSHACmd}).Stdout
	if actualSHA != expectedSHA {
		return fmt.Errorf("invalid composer installer found at %q: checksum for composer installer, %q, does not match expected checksum of %q", composerSetupURL, actualSHA, expectedSHA)
	}

	// run the installer
	ctx.Logf("installing Composer v%s", composerVer)
	clBin := filepath.Join(l.Path, "bin")
	ctx.MkdirAll(clBin, 0755)
	installCmd := fmt.Sprintf("php %s --install-dir %s --filename composer --version %s", installer.Name(), clBin, composerVer)
	ctx.Exec([]string{"bash", "-c", installCmd})

	ctx.SetMetadata(l, versionKey, composerVer)
	return nil
}
<commit_msg>Add an erronously missing check for error on ctx.MkdirAll(...)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      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.

// Implements php/composer-install buildpack.
// The composer-install buildpack installs the composer dependency manager.
package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/runtime"
	"github.com/buildpacks/libcnb"
)

var (
	composerLayer    = "composer"
	composerJSON     = "composer.json"
	composerSetup    = "composer-setup"
	composerVer      = "2.1.3"
	versionKey       = "version"
	composerSigURL   = "https://composer.github.io/installer.sig"
	composerSetupURL = "https://getcomposer.org/installer"
)

func main() {
	gcp.Main(detectFn, buildFn)
}

func detectFn(ctx *gcp.Context) (gcp.DetectResult, error) {
	composerJSONExists, err := ctx.FileExists(composerJSON)
	if err != nil {
		return nil, err
	}
	if !composerJSONExists {
		return gcp.OptOutFileNotFound(composerJSON), nil
	}
	return gcp.OptInFileFound(composerJSON), nil
}

func buildFn(ctx *gcp.Context) error {
	l := ctx.Layer(composerLayer, gcp.BuildLayer, gcp.CacheLayer)

	ctx.AddBOMEntry(libcnb.BOMEntry{
		Name:     composerLayer,
		Metadata: map[string]interface{}{"version": composerVer},
		Build:    true,
	})

	// Check the metadata in the cache layer to determine if we need to proceed.
	metaVersion := ctx.GetMetadata(l, versionKey)
	if composerVer == metaVersion {
		ctx.CacheHit(composerLayer)
		ctx.Logf("composer binary cache hit, skipping installation.")
		return nil
	}
	ctx.CacheMiss(composerLayer)
	ctx.ClearLayer(l)

	// download the installer
	installer, err := os.CreateTemp(l.Path, fmt.Sprintf("%s-*.php", composerSetup))
	if err != nil {
		return fmt.Errorf("creating temp file: %w", err)
	}
	defer os.Remove(installer.Name())

	if err := runtime.FetchURL(composerSetupURL, installer); err != nil {
		return fmt.Errorf("failed to download composer installer from %s: %w", composerSetupURL, err)
	}

	// verify the installer hash
	var expectedSHABuf bytes.Buffer
	if err := runtime.FetchURL(composerSigURL, io.Writer(&expectedSHABuf)); err != nil {
		return fmt.Errorf("failed to fetch the installer signature from %s: %w", composerSigURL, err)
	}
	expectedSHA := expectedSHABuf.String()
	actualSHACmd := fmt.Sprintf("php -r \"echo hash_file('sha384', '%s');\"", installer.Name())
	actualSHA := ctx.Exec([]string{"bash", "-c", actualSHACmd}).Stdout
	if actualSHA != expectedSHA {
		return fmt.Errorf("invalid composer installer found at %q: checksum for composer installer, %q, does not match expected checksum of %q", composerSetupURL, actualSHA, expectedSHA)
	}

	// run the installer
	ctx.Logf("installing Composer v%s", composerVer)
	clBin := filepath.Join(l.Path, "bin")
	if err := ctx.MkdirAll(clBin, 0755); err != nil {
		return fmt.Errorf("creating bin folder: %w", err)
	}
	installCmd := fmt.Sprintf("php %s --install-dir %s --filename composer --version %s", installer.Name(), clBin, composerVer)
	ctx.Exec([]string{"bash", "-c", installCmd})

	ctx.SetMetadata(l, versionKey, composerVer)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 xgfone
//
// 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 strings2

import (
	"bytes"
	"encoding/json"
	"strings"
	"testing"
)

func TestBuilder_TruncateBefore(t *testing.T) {
	b := NewBuilder(32)
	b.WriteString("abcdefg")
	b.TruncateBefore(3)
	if b.String() != "defg" {
		t.Error(b.String())
	} else if b.TruncateBefore(10); b.String() != "" {
		t.Error(b.String())
	}
}

func TestBuilder_TruncateAfter(t *testing.T) {
	b := NewBuilder(32)
	b.WriteString("abcdefg")
	b.TruncateAfter(3)
	if b.String() != "abcd" {
		t.Error(b.String())
	} else if b.TruncateAfter(10); b.String() != "" {
		t.Error(b.String())
	}
}

func TestBuilder_WriteRune(t *testing.T) {
	b := NewBuilderString("abc")
	b.WriteRune(rune('d'))
	b.WriteRune(rune('中'))
	if b.String() != "abcd中" {
		t.Error(b.String())
	}
}

func TestBuilder_TrimNewline(t *testing.T) {
	b := NewBuilderString("abcd   \n\n\n")
	b.TrimNewline()
	if b.String() != "abcd   " {
		t.Error(b.String())
	}
}

func TestBuilder_WriteTo(t *testing.T) {
	b := NewBuilder(32)
	b.AppendInt(123)

	buf := bytes.NewBuffer(nil)
	b.WriteTo(buf)

	if buf.String() != "123" {
		t.Error(buf.String())
	} else if b.String() != "123" {
		t.Error(b.String())
	}
}

func TestBuilder_AppendJSON(t *testing.T) {
	b := NewBuilder(32)
	b.AppendJSON(`a"b`)

	if b.String() != `"a\"b"` {
		t.Error(b.String())
	}

	b.Reset()
	b.AppendJSON("ab")
	if b.String() != `"ab"` {
		t.Error(b.String())
	}

	b.Reset()
	b.AppendJSON([]interface{}{1, "a", 2, "c", 3, `"d"`})
	if b.String() != `[1,"a",2,"c",3,"\"d\""]` {
		t.Error(b.String())
	}

	b.Reset()
	b.AppendJSON(map[string]interface{}{"a": 123, "b": `"b"`, "c": `c`})
	var ms map[string]interface{}
	if err := json.Unmarshal(b.Bytes(), &ms); err != nil {
		t.Error(b.String(), err)
	} else if len(ms) != 3 {
		t.Error(ms)
	} else if v, _ := ms["a"].(float64); v != 123 {
		t.Error(ms)
	} else if v, _ := ms["b"].(string); v != `"b"` {
		t.Error(ms)
	} else if v, _ := ms["c"].(string); v != "c" {
		t.Error(ms)
	}
}

func TestBuilder_AppendAny(t *testing.T) {
	b := NewBuilder(64)
	b.AppendAny([]int{1, 2, 3})
	b.WriteByte('\n')
	b.AppendAny([]string{"a", "b", "c"})
	b.WriteByte('\n')
	b.AppendAny([]interface{}{4, "x", 5, "y"})
	b.WriteByte('\n')
	b.AppendAny(map[string]interface{}{"k1": "v1", "k2": 789})

	ss := strings.Split(b.String(), "\n")
	if len(ss) != 4 {
		t.Error(b.String())
	} else if ss[0] != "[1 2 3]" {
		t.Error(ss[0])
	} else if ss[1] != "[a b c]" {
		t.Error(ss[1])
	} else if ss[2] != "[4 x 5 y]" {
		t.Error(ss[2])
	} else if ss[3] != "map[k1:v1 k2:789]" {
		t.Error(ss[3])
	}
}

func TestBuilder_AppendAnyFmt(t *testing.T) {
	type st struct {
		Name string
		Age  int
	}

	b := NewBuilder(64)
	b.AppendAny([]int{1, 2, 3})
	b.WriteByte('\n')
	b.AppendAny([]string{"a", "b", "c"})
	b.WriteByte('\n')
	b.AppendAny([]interface{}{4, "x", 5, "y"})
	b.WriteByte('\n')
	b.AppendAny(map[string]interface{}{"k1": "v1", "k2": 789})
	b.WriteByte('\n')
	b.AppendAnyFmt(st{"Aaron", 123})

	ss := strings.Split(b.String(), "\n")
	if len(ss) != 5 {
		t.Error(b.String())
	} else if ss[0] != "[1 2 3]" {
		t.Error(ss[0])
	} else if ss[1] != "[a b c]" {
		t.Error(ss[1])
	} else if ss[2] != "[4 x 5 y]" {
		t.Error(ss[2])
	} else if ss[3] != "map[k1:v1 k2:789]" {
		t.Error(ss[3])
	} else if ss[4] != "{Name:Aaron Age:123}" {
		t.Error(ss[4])
	}
}
<commit_msg>update string builder test<commit_after>// Copyright 2019 xgfone
//
// 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 strings2

import (
	"bytes"
	"encoding/json"
	"strings"
	"testing"
)

func TestBuilder_TruncateBefore(t *testing.T) {
	b := NewBuilder(32)
	b.WriteString("abcdefg")
	b.TruncateBefore(3)
	if b.String() != "defg" {
		t.Error(b.String())
	} else if b.TruncateBefore(10); b.String() != "" {
		t.Error(b.String())
	}
}

func TestBuilder_TruncateAfter(t *testing.T) {
	b := NewBuilder(32)
	b.WriteString("abcdefg")
	b.TruncateAfter(3)
	if b.String() != "abcd" {
		t.Error(b.String())
	} else if b.TruncateAfter(10); b.String() != "" {
		t.Error(b.String())
	}
}

func TestBuilder_WriteRune(t *testing.T) {
	b := NewBuilderString("abc")
	b.WriteRune(rune('d'))
	b.WriteRune(rune('中'))
	if b.String() != "abcd中" {
		t.Error(b.String())
	}
}

func TestBuilder_TrimNewline(t *testing.T) {
	b := NewBuilderString("abcd   \n\n\n")
	b.TrimNewline()
	if b.String() != "abcd   " {
		t.Error(b.String())
	}
}

func TestBuilder_WriteTo(t *testing.T) {
	b := NewBuilder(32)
	b.AppendInt(123)

	buf := bytes.NewBuffer(nil)
	b.WriteTo(buf)

	if buf.String() != "123" {
		t.Error(buf.String())
	} else if b.String() != "123" {
		t.Error(b.String())
	}
}

func TestBuilder_AppendJSON(t *testing.T) {
	b := NewBuilder(32)
	b.AppendJSON(`a"b`)

	if b.String() != `"a\"b"` {
		t.Error(b.String())
	}

	b.Reset()
	b.AppendJSON("ab")
	if b.String() != `"ab"` {
		t.Error(b.String())
	}

	b.Reset()
	b.AppendJSON([]interface{}{1, "a", 2, "c", 3, `"d"`})
	if b.String() != `[1,"a",2,"c",3,"\"d\""]` {
		t.Error(b.String())
	}

	b.Reset()
	b.AppendJSON(map[string]interface{}{"a": 123, "b": `"b"`, "c": `c`})
	var ms map[string]interface{}
	if err := json.Unmarshal(b.Bytes(), &ms); err != nil {
		t.Error(b.String(), err)
	} else if len(ms) != 3 {
		t.Error(ms)
	} else if v, _ := ms["a"].(float64); v != 123 {
		t.Error(ms)
	} else if v, _ := ms["b"].(string); v != `"b"` {
		t.Error(ms)
	} else if v, _ := ms["c"].(string); v != "c" {
		t.Error(ms)
	}
}

func TestBuilder_AppendAny(t *testing.T) {
	b := NewBuilder(64)
	b.AppendAny([]int{1, 2, 3})
	b.WriteByte('\n')
	b.AppendAny([]string{"a", "b", "c"})
	b.WriteByte('\n')
	b.AppendAny([]interface{}{4, "x", 5, "y"})
	b.WriteByte('\n')
	b.AppendAny(map[string]interface{}{"k1": "v1", "k2": 789})

	ss := strings.Split(b.String(), "\n")
	if len(ss) != 4 {
		t.Error(b.String())
	} else if ss[0] != "[1 2 3]" {
		t.Error(ss[0])
	} else if ss[1] != "[a b c]" {
		t.Error(ss[1])
	} else if ss[2] != "[4 x 5 y]" {
		t.Error(ss[2])
	} else if ss[3] != "map[k1:v1 k2:789]" {
		t.Error(ss[3])
	}
}

func TestBuilder_AppendAnyFmt(t *testing.T) {
	type st struct {
		Name string
		Age  int
	}

	b := NewBuilder(64)
	b.AppendAnyFmt([]int{1, 2, 3})
	b.WriteByte('\n')
	b.AppendAnyFmt([]string{"a", "b", "c"})
	b.WriteByte('\n')
	b.AppendAnyFmt([]interface{}{4, "x", 5, "y"})
	b.WriteByte('\n')
	b.AppendAnyFmt(map[string]interface{}{"k1": "v1", "k2": 789})
	b.WriteByte('\n')
	b.AppendAnyFmt(st{"Aaron", 123})

	ss := strings.Split(b.String(), "\n")
	if len(ss) != 5 {
		t.Error(b.String())
	} else if ss[0] != "[1 2 3]" {
		t.Error(ss[0])
	} else if ss[1] != "[a b c]" {
		t.Error(ss[1])
	} else if ss[2] != "[4 x 5 y]" {
		t.Error(ss[2])
	} else if ss[3] != "map[k1:v1 k2:789]" {
		t.Error(ss[3])
	} else if ss[4] != "{Name:Aaron Age:123}" {
		t.Error(ss[4])
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Ceph-CSI 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 cephfs

import (
	"context"
	"fmt"
	"path"
	"strings"

	"github.com/ceph/ceph-csi/internal/util"

	fsAdmin "github.com/ceph/go-ceph/cephfs/admin"
)

var (
	// clusterAdditionalInfo contains information regarding if resize is
	// supported in the particular cluster and subvolumegroup is
	// created or not.
	// Subvolumegroup creation and volume resize decisions are
	// taken through this additional cluster information.
	clusterAdditionalInfo = make(map[string]*localClusterState)
)

const (
	cephEntityClientPrefix = "client."

	// modeAllRWX can be used for setting permissions to Read-Write-eXecute
	// for User, Group and Other.
	modeAllRWX = 0777
)

// Subvolume holds subvolume information. This includes only the needed members
// from fsAdmin.SubVolumeInfo.
type Subvolume struct {
	BytesQuota int64
	Path       string
	Features   []string
}

func getVolumeRootPathCephDeprecated(volID volumeID) string {
	return path.Join("/", "csi-volumes", string(volID))
}

func getVolumeRootPathCeph(ctx context.Context, volOptions *volumeOptions, cr *util.Credentials, volID volumeID) (string, error) {
	stdout, stderr, err := util.ExecCommand(
		ctx,
		"ceph",
		"fs",
		"subvolume",
		"getpath",
		volOptions.FsName,
		string(volID),
		"--group_name",
		volOptions.SubvolumeGroup,
		"-m", volOptions.Monitors,
		"-c", util.CephConfigPath,
		"-n", cephEntityClientPrefix+cr.ID,
		"--keyfile="+cr.KeyFile)

	if err != nil {
		util.ErrorLog(ctx, "failed to get the rootpath for the vol %s: %s (stdError: %s)", string(volID), err, stderr)
		if strings.Contains(stderr, volumeNotFound) {
			return "", util.JoinErrors(ErrVolumeNotFound, err)
		}

		return "", err
	}
	return strings.TrimSuffix(stdout, "\n"), nil
}

func (vo *volumeOptions) getSubVolumeInfo(ctx context.Context, volID volumeID) (*Subvolume, error) {
	fsa, err := vo.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not fetch metadata pool for %s:", vo.FsName, err)
		return nil, err
	}

	info, err := fsa.SubVolumeInfo(vo.FsName, vo.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get subvolume info for the vol %s: %s", string(volID), err)
		if strings.HasPrefix(err.Error(), volumeNotFound) {
			return nil, ErrVolumeNotFound
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			return nil, ErrInvalidCommand
		}

		return nil, err
	}

	subvol := Subvolume{
		// only set BytesQuota when it is of type ByteCount
		Path:     info.Path,
		Features: make([]string, len(info.Features)),
	}
	bc, ok := info.BytesQuota.(fsAdmin.ByteCount)
	if !ok {
		// we ignore info.BytesQuota == Infinite and just continue
		// without returning quota information
		if info.BytesQuota != fsAdmin.Infinite {
			return nil, fmt.Errorf("subvolume %s has unsupported quota: %v", string(volID), info.BytesQuota)
		}
	} else {
		subvol.BytesQuota = int64(bc)
	}
	for i, feature := range info.Features {
		subvol.Features[i] = string(feature)
	}

	return &subvol, nil
}

type localClusterState struct {
	// set true if cluster supports resize functionality.
	resizeSupported bool
	// set true once a subvolumegroup is created
	// for corresponding cluster.
	subVolumeGroupCreated bool
}

func createVolume(ctx context.Context, volOptions *volumeOptions, volID volumeID, bytesQuota int64) error {
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent := clusterAdditionalInfo[volOptions.ClusterID]; !keyPresent {
		clusterAdditionalInfo[volOptions.ClusterID] = &localClusterState{}
	}

	ca, err := volOptions.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not create subvolume %s: %s", string(volID), err)
		return err
	}

	// create subvolumegroup if not already created for the cluster.
	if !clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated {
		opts := fsAdmin.SubVolumeGroupOptions{}
		err = ca.CreateSubVolumeGroup(volOptions.FsName, volOptions.SubvolumeGroup, &opts)
		if err != nil {
			util.ErrorLog(ctx, "failed to create subvolume group %s, for the vol %s: %s", volOptions.SubvolumeGroup, string(volID), err)
			return err
		}
		util.DebugLog(ctx, "cephfs: created subvolume group %s", volOptions.SubvolumeGroup)
		clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated = true
	}

	opts := fsAdmin.SubVolumeOptions{
		Size: fsAdmin.ByteCount(bytesQuota),
		Mode: modeAllRWX,
	}
	if volOptions.Pool != "" {
		opts.PoolLayout = volOptions.Pool
	}

	// FIXME: check if the right credentials are used ("-n", cephEntityClientPrefix + cr.ID)
	err = ca.CreateSubVolume(volOptions.FsName, volOptions.SubvolumeGroup, string(volID), &opts)
	if err != nil {
		util.ErrorLog(ctx, "failed to create subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		return err
	}

	return nil
}

// resizeVolume will try to use ceph fs subvolume resize command to resize the
// subvolume. If the command is not available as a fallback it will use
// CreateVolume to resize the subvolume.
func (vo *volumeOptions) resizeVolume(ctx context.Context, volID volumeID, bytesQuota int64) error {
	// keyPresent checks whether corresponding clusterID key is present in clusterAdditionalInfo
	var keyPresent bool
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent = clusterAdditionalInfo[vo.ClusterID]; !keyPresent {
		clusterAdditionalInfo[vo.ClusterID] = &localClusterState{}
	}
	// resize subvolume when either it's supported, or when corresponding
	// clusterID key was not present.
	if clusterAdditionalInfo[vo.ClusterID].resizeSupported || !keyPresent {
		fsa, err := vo.conn.GetFSAdmin()
		if err != nil {
			util.ErrorLog(ctx, "could not get FSAdmin, can not resize volume %s:", vo.FsName, err)
			return err
		}

		_, err = fsa.ResizeSubVolume(vo.FsName, vo.SubvolumeGroup, string(volID), fsAdmin.ByteCount(bytesQuota), true)
		if err == nil {
			clusterAdditionalInfo[vo.ClusterID].resizeSupported = true
			return nil
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			util.ErrorLog(ctx, "failed to resize subvolume %s in fs %s: %s", string(volID), vo.FsName, err)
			return err
		}
	}
	clusterAdditionalInfo[vo.ClusterID].resizeSupported = false
	return createVolume(ctx, vo, volID, bytesQuota)
}

func purgeVolume(ctx context.Context, volID volumeID, cr *util.Credentials, volOptions *volumeOptions, force bool) error {
	arg := []string{
		"fs",
		"subvolume",
		"rm",
		volOptions.FsName,
		string(volID),
		"--group_name",
		volOptions.SubvolumeGroup,
		"-m", volOptions.Monitors,
		"-c", util.CephConfigPath,
		"-n", cephEntityClientPrefix + cr.ID,
		"--keyfile=" + cr.KeyFile,
	}
	if force {
		arg = append(arg, "--force")
	}
	if checkSubvolumeHasFeature("snapshot-retention", volOptions.Features) {
		arg = append(arg, "--retain-snapshots")
	}

	err := execCommandErr(ctx, "ceph", arg...)
	if err != nil {
		util.ErrorLog(ctx, "failed to purge subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		if strings.Contains(err.Error(), volumeNotEmpty) {
			return util.JoinErrors(ErrVolumeHasSnapshots, err)
		}
		if strings.Contains(err.Error(), volumeNotFound) {
			return util.JoinErrors(ErrVolumeNotFound, err)
		}
		return err
	}

	return nil
}

// checkSubvolumeHasFeature verifies if the referred subvolume has
// the required feature.
func checkSubvolumeHasFeature(feature string, subVolFeatures []string) bool {
	// The subvolume "features" are based on the internal version of the subvolume.
	// Verify if subvolume supports the required feature.
	for _, subvolFeature := range subVolFeatures {
		if subvolFeature == feature {
			return true
		}
	}
	return false
}
<commit_msg>cephfs: implement getVolumeRootPathCeph with go-ceph<commit_after>/*
Copyright 2018 The Ceph-CSI 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 cephfs

import (
	"context"
	"fmt"
	"path"
	"strings"

	"github.com/ceph/ceph-csi/internal/util"

	fsAdmin "github.com/ceph/go-ceph/cephfs/admin"
)

var (
	// clusterAdditionalInfo contains information regarding if resize is
	// supported in the particular cluster and subvolumegroup is
	// created or not.
	// Subvolumegroup creation and volume resize decisions are
	// taken through this additional cluster information.
	clusterAdditionalInfo = make(map[string]*localClusterState)
)

const (
	cephEntityClientPrefix = "client."

	// modeAllRWX can be used for setting permissions to Read-Write-eXecute
	// for User, Group and Other.
	modeAllRWX = 0777
)

// Subvolume holds subvolume information. This includes only the needed members
// from fsAdmin.SubVolumeInfo.
type Subvolume struct {
	BytesQuota int64
	Path       string
	Features   []string
}

func getVolumeRootPathCephDeprecated(volID volumeID) string {
	return path.Join("/", "csi-volumes", string(volID))
}

func getVolumeRootPathCeph(ctx context.Context, volOptions *volumeOptions, cr *util.Credentials, volID volumeID) (string, error) {
	fsa, err := volOptions.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin err %s", err)
		return "", err
	}
	svPath, err := fsa.SubVolumePath(volOptions.FsName, volOptions.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get the rootpath for the vol %s: %s", string(volID), err)
		if strings.Contains(err.Error(), volumeNotFound) {
			return "", util.JoinErrors(ErrVolumeNotFound, err)
		}
		return "", err
	}
	return svPath, nil
}

func (vo *volumeOptions) getSubVolumeInfo(ctx context.Context, volID volumeID) (*Subvolume, error) {
	fsa, err := vo.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not fetch metadata pool for %s:", vo.FsName, err)
		return nil, err
	}

	info, err := fsa.SubVolumeInfo(vo.FsName, vo.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get subvolume info for the vol %s: %s", string(volID), err)
		if strings.HasPrefix(err.Error(), volumeNotFound) {
			return nil, ErrVolumeNotFound
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			return nil, ErrInvalidCommand
		}

		return nil, err
	}

	subvol := Subvolume{
		// only set BytesQuota when it is of type ByteCount
		Path:     info.Path,
		Features: make([]string, len(info.Features)),
	}
	bc, ok := info.BytesQuota.(fsAdmin.ByteCount)
	if !ok {
		// we ignore info.BytesQuota == Infinite and just continue
		// without returning quota information
		if info.BytesQuota != fsAdmin.Infinite {
			return nil, fmt.Errorf("subvolume %s has unsupported quota: %v", string(volID), info.BytesQuota)
		}
	} else {
		subvol.BytesQuota = int64(bc)
	}
	for i, feature := range info.Features {
		subvol.Features[i] = string(feature)
	}

	return &subvol, nil
}

type localClusterState struct {
	// set true if cluster supports resize functionality.
	resizeSupported bool
	// set true once a subvolumegroup is created
	// for corresponding cluster.
	subVolumeGroupCreated bool
}

func createVolume(ctx context.Context, volOptions *volumeOptions, volID volumeID, bytesQuota int64) error {
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent := clusterAdditionalInfo[volOptions.ClusterID]; !keyPresent {
		clusterAdditionalInfo[volOptions.ClusterID] = &localClusterState{}
	}

	ca, err := volOptions.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not create subvolume %s: %s", string(volID), err)
		return err
	}

	// create subvolumegroup if not already created for the cluster.
	if !clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated {
		opts := fsAdmin.SubVolumeGroupOptions{}
		err = ca.CreateSubVolumeGroup(volOptions.FsName, volOptions.SubvolumeGroup, &opts)
		if err != nil {
			util.ErrorLog(ctx, "failed to create subvolume group %s, for the vol %s: %s", volOptions.SubvolumeGroup, string(volID), err)
			return err
		}
		util.DebugLog(ctx, "cephfs: created subvolume group %s", volOptions.SubvolumeGroup)
		clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated = true
	}

	opts := fsAdmin.SubVolumeOptions{
		Size: fsAdmin.ByteCount(bytesQuota),
		Mode: modeAllRWX,
	}
	if volOptions.Pool != "" {
		opts.PoolLayout = volOptions.Pool
	}

	// FIXME: check if the right credentials are used ("-n", cephEntityClientPrefix + cr.ID)
	err = ca.CreateSubVolume(volOptions.FsName, volOptions.SubvolumeGroup, string(volID), &opts)
	if err != nil {
		util.ErrorLog(ctx, "failed to create subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		return err
	}

	return nil
}

// resizeVolume will try to use ceph fs subvolume resize command to resize the
// subvolume. If the command is not available as a fallback it will use
// CreateVolume to resize the subvolume.
func (vo *volumeOptions) resizeVolume(ctx context.Context, volID volumeID, bytesQuota int64) error {
	// keyPresent checks whether corresponding clusterID key is present in clusterAdditionalInfo
	var keyPresent bool
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent = clusterAdditionalInfo[vo.ClusterID]; !keyPresent {
		clusterAdditionalInfo[vo.ClusterID] = &localClusterState{}
	}
	// resize subvolume when either it's supported, or when corresponding
	// clusterID key was not present.
	if clusterAdditionalInfo[vo.ClusterID].resizeSupported || !keyPresent {
		fsa, err := vo.conn.GetFSAdmin()
		if err != nil {
			util.ErrorLog(ctx, "could not get FSAdmin, can not resize volume %s:", vo.FsName, err)
			return err
		}

		_, err = fsa.ResizeSubVolume(vo.FsName, vo.SubvolumeGroup, string(volID), fsAdmin.ByteCount(bytesQuota), true)
		if err == nil {
			clusterAdditionalInfo[vo.ClusterID].resizeSupported = true
			return nil
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			util.ErrorLog(ctx, "failed to resize subvolume %s in fs %s: %s", string(volID), vo.FsName, err)
			return err
		}
	}
	clusterAdditionalInfo[vo.ClusterID].resizeSupported = false
	return createVolume(ctx, vo, volID, bytesQuota)
}

func purgeVolume(ctx context.Context, volID volumeID, cr *util.Credentials, volOptions *volumeOptions, force bool) error {
	arg := []string{
		"fs",
		"subvolume",
		"rm",
		volOptions.FsName,
		string(volID),
		"--group_name",
		volOptions.SubvolumeGroup,
		"-m", volOptions.Monitors,
		"-c", util.CephConfigPath,
		"-n", cephEntityClientPrefix + cr.ID,
		"--keyfile=" + cr.KeyFile,
	}
	if force {
		arg = append(arg, "--force")
	}
	if checkSubvolumeHasFeature("snapshot-retention", volOptions.Features) {
		arg = append(arg, "--retain-snapshots")
	}

	err := execCommandErr(ctx, "ceph", arg...)
	if err != nil {
		util.ErrorLog(ctx, "failed to purge subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		if strings.Contains(err.Error(), volumeNotEmpty) {
			return util.JoinErrors(ErrVolumeHasSnapshots, err)
		}
		if strings.Contains(err.Error(), volumeNotFound) {
			return util.JoinErrors(ErrVolumeNotFound, err)
		}
		return err
	}

	return nil
}

// checkSubvolumeHasFeature verifies if the referred subvolume has
// the required feature.
func checkSubvolumeHasFeature(feature string, subVolFeatures []string) bool {
	// The subvolume "features" are based on the internal version of the subvolume.
	// Verify if subvolume supports the required feature.
	for _, subvolFeature := range subVolFeatures {
		if subvolFeature == feature {
			return true
		}
	}
	return false
}
<|endoftext|>