<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"syscall"

	"github.com/systemboot/systemboot/pkg/bootconfig"
	"github.com/systemboot/systemboot/pkg/storage"
)

// TODO backward compatibility for BIOS mode with partition type 0xee
// TODO use a proper parser for grub config (see grub.go)

var (
	flagBaseMountPoint = flag.String("m", "/mnt", "Base mount point where to mount partitions")
	flagDryRun         = flag.Bool("dryrun", false, "Do not actually kexec into the boot config")
	flagDebug          = flag.Bool("d", false, "Print debug output")
	flagGrubMode       = flag.Bool("grub", false, "Use GRUB mode, i.e. look for valid Grub/Grub2 configuration in default locations to boot a kernel. GRUB mode ignores -kernel/-initramfs/-cmdline")
	flagKernelPath     = flag.String("kernel", "", "Specify the path of the kernel to execute. If using -grub, this argument is ignored")
	flagInitramfsPath  = flag.String("initramfs", "", "Specify the path of the initramfs to load. If using -grub, this argument is ignored")
	flagKernelCmdline  = flag.String("cmdline", "", "Specify the kernel command line. If using -grub, this argument is ignored")
	flagDeviceGUID     = flag.String("guid", "", "GUID of the device where the kernel (and optionally initramfs) are located. Ignored if -grub is set or if -kernel is not specified")
)

var debug = func(string, ...interface{}) {}

// mountByGUID looks for a partition with the given GUID, and tries to mount it
// in a subdirectory under the specified mount point. The subdirectory has the
// same name of the device (e.g. /your/base/mountpoint/sda1).
// The specified filesystems will be used in the mount attempts.
// If more than one partition is found with the given GUID, the first that is
// found is used.
// This function returns a storage.Mountpoint object, or an error if any.
func mountByGUID(devices []storage.BlockDev, filesystems []string, guid, baseMountpoint string) (*storage.Mountpoint, error) {
	log.Printf("Looking for partition with GUID %s", guid)
	partitions, err := storage.PartitionsByGUID(devices, guid)
	if err != nil || len(partitions) == 0 {
		return nil, fmt.Errorf("Error looking up for partition with GUID %s", guid)
	}
	log.Printf("Partitions with GUID %s: %+v", guid, partitions)
	if len(partitions) > 1 {
		log.Printf("Warning: more than one partition found with the given GUID. Using the first one")
	}
	dev := partitions[0]
	mountpath := path.Join(baseMountpoint, dev.Name)
	devname := path.Join("/dev", dev.Name)
	mountpoint, err := storage.Mount(devname, mountpath, filesystems)
	if err != nil {
		return nil, fmt.Errorf("mountByGUID: cannot mount %s (GUID %s) on %s: %v", devname, guid, mountpath, err)
	}
	return mountpoint, nil
}

// BootGrubMode tries to boot a kernel in GRUB mode. GRUB mode means:
// * look for the partition with the specified GUID, and mount it
// * if no GUID is specified, mount all of the specified devices
// * try to mount the device(s) using any of the kernel-supported filesystems
// * look for a GRUB configuration in various well-known locations
// * build a list of valid boot configurations from the found GRUB configuration files
// * try to boot every valid boot configuration until one succeeds
//
// The first parameter, `devices` is a list of storage.BlockDev . The function
// will look for bootable configurations on these devices
// The second parameter, `baseMountPoint`, is the directory where the mount
// points for each device will be created.
// The third parameter, `guid`, is the partition GUID to look for. If it is an
// empty string, will search boot configurations on all of the specified devices
// instead.
// The fourth parameter, `dryrun`, will not boot the found configurations if set
// to true.
func BootGrubMode(devices []storage.BlockDev, baseMountpoint string, guid string, dryrun bool) error {
	// get a list of supported file systems for real devices (i.e. skip nodev)
	debug("Getting list of supported filesystems")
	filesystems, err := storage.GetSupportedFilesystems()
	if err != nil {
		log.Fatal(err)
	}
	debug("Supported file systems: %v", filesystems)

	var mounted []storage.Mountpoint
	if guid == "" {
		// try mounting all the available devices, with all the supported file
		// systems
		debug("trying to mount all the available block devices with all the supported file system types")
		mounted = make([]storage.Mountpoint, 0)
		for _, dev := range devices {
			devname := path.Join("/dev", dev.Name)
			mountpath := path.Join(baseMountpoint, dev.Name)
			if mountpoint, err := storage.Mount(devname, mountpath, filesystems); err != nil {
				debug("Failed to mount %s on %s: %v", devname, mountpath, err)
			} else {
				mounted = append(mounted, *mountpoint)
			}
		}
		log.Printf("mounted: %+v", mounted)
		defer func() {
			// clean up
			for _, mountpoint := range mounted {
				syscall.Unmount(mountpoint.Path, syscall.MNT_DETACH)
			}
		}()
	} else {
		mount, err := mountByGUID(devices, filesystems, guid, baseMountpoint)
		if err != nil {
			return err
		}
		mounted = []storage.Mountpoint{*mount}
	}

	// search for a valid grub config and extracts the boot configuration
	bootconfigs := make([]bootconfig.BootConfig, 0)
	for _, mountpoint := range mounted {
		bootconfigs = append(bootconfigs, ScanGrubConfigs(mountpoint.Path)...)
	}
	log.Printf("Found %d boot configs", len(bootconfigs))
	for _, cfg := range bootconfigs {
		debug("%+v", cfg)
	}
	if len(bootconfigs) == 0 {
		return fmt.Errorf("No boot configuration found")
	}

	if dryrun {
		cfg := bootconfigs[0]
		debug("Dry-run mode: will not boot the found configuration")
		debug("Boot configuration: %+v", cfg)
		return nil
	}

	// try to kexec into every boot config kernel until one succeeds
	for _, cfg := range bootconfigs {
		debug("Trying boot configuration %+v", cfg)
		if err := cfg.Boot(); err != nil {
			log.Printf("Failed to boot kernel %s: %v", cfg.Kernel, err)
		}
	}
	// if we reach this point, no boot configuration succeeded
	log.Print("No boot configuration succeeded")

	return nil
}

// BootPathMode tries to boot a kernel in PATH mode. This means:
// * look for a partition with the given GUID and mount it
// * look for the kernel and initramfs in the provided locations
// * boot the kernel with the provided command line
//
// The first parameter, `devices` is a list of storage.BlockDev . The function
// will look for bootable configurations on these devices
// The second parameter, `baseMountPoint`, is the directory where the mount
// points for each device will be created.
// The third parameter, `guid`, is the partition GUID to look for.
// The fourth parameter, `dryrun`, will not boot the found configurations if set
// to true.
func BootPathMode(devices []storage.BlockDev, baseMountpoint string, guid string, dryrun bool) error {
	debug("Getting list of supported filesystems")
	filesystems, err := storage.GetSupportedFilesystems()
	if err != nil {
		log.Fatal(err)
	}
	debug("Supported file systems: %v", filesystems)

	mount, err := mountByGUID(devices, filesystems, guid, baseMountpoint)
	if err != nil {
		return err
	}

	fullKernelPath := path.Join(mount.Path, *flagKernelPath)
	fullInitramfsPath := path.Join(mount.Path, *flagInitramfsPath)
	cfg := bootconfig.BootConfig{
		Kernel:     fullKernelPath,
		Initramfs:  fullInitramfsPath,
		KernelArgs: *flagKernelCmdline,
	}
	debug("Trying boot configuration %+v", cfg)
	if dryrun {
		log.Printf("Dry-run, will not actually boot")
	} else {
		if err := cfg.Boot(); err != nil {
			return fmt.Errorf("Failed to boot kernel %s: %v", cfg.Kernel, err)
		}
	}
	return nil
}

func main() {
	flag.Parse()
	if *flagGrubMode && *flagKernelPath != "" {
		log.Fatal("Options -grub and -kernel are mutually exclusive")
	}
	if *flagDebug {
		debug = log.Printf
	}

	// Get all the available block devices
	devices, err := storage.GetBlockStats()
	if err != nil {
		log.Fatal(err)
	}
	// print partition info
	if *flagDebug {
		for _, dev := range devices {
			log.Printf("Device: %+v", dev)
			table, err := storage.GetGPTTable(dev)
			if err != nil {
				continue
			}
			log.Printf("  Table: %+v", table)
			for _, part := range table.Partitions {
				log.Printf("    Partition: %+v\n", part)
				if !part.IsEmpty() {
					log.Printf("      UUID: %s\n", part.Type.String())
				}
			}
		}
	}

	// TODO boot from EFI system partitions. See storage.FilterEFISystemPartitions

	if *flagGrubMode {
		if err := BootGrubMode(devices, *flagBaseMountPoint, *flagDeviceGUID, *flagDryRun); err != nil {
			log.Fatal(err)
		}
	} else if *flagKernelPath != "" {
		if err := BootPathMode(devices, *flagBaseMountPoint, *flagDeviceGUID, *flagDryRun); err != nil {
			log.Fatal(err)
		}
	} else {
		log.Fatal("You must specify either -grub or -kernel")
	}
	os.Exit(1)
}
<commit_msg>add config flag to selct bootconfig<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"syscall"

	"github.com/systemboot/systemboot/pkg/bootconfig"
	"github.com/systemboot/systemboot/pkg/storage"
)

// TODO backward compatibility for BIOS mode with partition type 0xee
// TODO use a proper parser for grub config (see grub.go)

var (
	flagBaseMountPoint = flag.String("m", "/mnt", "Base mount point where to mount partitions")
	flagDryRun         = flag.Bool("dryrun", false, "Do not actually kexec into the boot config")
	flagDebug          = flag.Bool("d", false, "Print debug output")
	flagConfigIdx      = flag.Int("config", -1, "Specify the index of the configuration to boot. The order is determined by the menu entries in the Grub config")
	flagGrubMode       = flag.Bool("grub", false, "Use GRUB mode, i.e. look for valid Grub/Grub2 configuration in default locations to boot a kernel. GRUB mode ignores -kernel/-initramfs/-cmdline")
	flagKernelPath     = flag.String("kernel", "", "Specify the path of the kernel to execute. If using -grub, this argument is ignored")
	flagInitramfsPath  = flag.String("initramfs", "", "Specify the path of the initramfs to load. If using -grub, this argument is ignored")
	flagKernelCmdline  = flag.String("cmdline", "", "Specify the kernel command line. If using -grub, this argument is ignored")
	flagDeviceGUID     = flag.String("guid", "", "GUID of the device where the kernel (and optionally initramfs) are located. Ignored if -grub is set or if -kernel is not specified")
)

var debug = func(string, ...interface{}) {}

// mountByGUID looks for a partition with the given GUID, and tries to mount it
// in a subdirectory under the specified mount point. The subdirectory has the
// same name of the device (e.g. /your/base/mountpoint/sda1).
// The specified filesystems will be used in the mount attempts.
// If more than one partition is found with the given GUID, the first that is
// found is used.
// This function returns a storage.Mountpoint object, or an error if any.
func mountByGUID(devices []storage.BlockDev, filesystems []string, guid, baseMountpoint string) (*storage.Mountpoint, error) {
	log.Printf("Looking for partition with GUID %s", guid)
	partitions, err := storage.PartitionsByGUID(devices, guid)
	if err != nil || len(partitions) == 0 {
		return nil, fmt.Errorf("Error looking up for partition with GUID %s", guid)
	}
	log.Printf("Partitions with GUID %s: %+v", guid, partitions)
	if len(partitions) > 1 {
		log.Printf("Warning: more than one partition found with the given GUID. Using the first one")
	}
	dev := partitions[0]
	mountpath := path.Join(baseMountpoint, dev.Name)
	devname := path.Join("/dev", dev.Name)
	mountpoint, err := storage.Mount(devname, mountpath, filesystems)
	if err != nil {
		return nil, fmt.Errorf("mountByGUID: cannot mount %s (GUID %s) on %s: %v", devname, guid, mountpath, err)
	}
	return mountpoint, nil
}

// BootGrubMode tries to boot a kernel in GRUB mode. GRUB mode means:
// * look for the partition with the specified GUID, and mount it
// * if no GUID is specified, mount all of the specified devices
// * try to mount the device(s) using any of the kernel-supported filesystems
// * look for a GRUB configuration in various well-known locations
// * build a list of valid boot configurations from the found GRUB configuration files
// * try to boot every valid boot configuration until one succeeds
//
// The first parameter, `devices` is a list of storage.BlockDev . The function
// will look for bootable configurations on these devices
// The second parameter, `baseMountPoint`, is the directory where the mount
// points for each device will be created.
// The third parameter, `guid`, is the partition GUID to look for. If it is an
// empty string, will search boot configurations on all of the specified devices
// instead.
// The fourth parameter, `dryrun`, will not boot the found configurations if set
// to true.
func BootGrubMode(devices []storage.BlockDev, baseMountpoint string, guid string, dryrun bool, configIdx int) error {
	// get a list of supported file systems for real devices (i.e. skip nodev)
	debug("Getting list of supported filesystems")
	filesystems, err := storage.GetSupportedFilesystems()
	if err != nil {
		log.Fatal(err)
	}
	debug("Supported file systems: %v", filesystems)

	var mounted []storage.Mountpoint
	if guid == "" {
		// try mounting all the available devices, with all the supported file
		// systems
		debug("trying to mount all the available block devices with all the supported file system types")
		mounted = make([]storage.Mountpoint, 0)
		for _, dev := range devices {
			devname := path.Join("/dev", dev.Name)
			mountpath := path.Join(baseMountpoint, dev.Name)
			if mountpoint, err := storage.Mount(devname, mountpath, filesystems); err != nil {
				debug("Failed to mount %s on %s: %v", devname, mountpath, err)
			} else {
				mounted = append(mounted, *mountpoint)
			}
		}
		log.Printf("mounted: %+v", mounted)
		defer func() {
			// clean up
			for _, mountpoint := range mounted {
				syscall.Unmount(mountpoint.Path, syscall.MNT_DETACH)
			}
		}()
	} else {
		mount, err := mountByGUID(devices, filesystems, guid, baseMountpoint)
		if err != nil {
			return err
		}
		mounted = []storage.Mountpoint{*mount}
	}

	// search for a valid grub config and extracts the boot configuration
	bootconfigs := make([]bootconfig.BootConfig, 0)
	for _, mountpoint := range mounted {
		bootconfigs = append(bootconfigs, ScanGrubConfigs(mountpoint.Path)...)
	}
	if len(bootconfigs) == 0 {
		return fmt.Errorf("No boot configuration found")
	}
	log.Printf("Found %d boot configs", len(bootconfigs))
	for _, cfg := range bootconfigs {
		debug("%+v", cfg)
	}
	for n, cfg := range bootconfigs {
		log.Printf("  %d: %s\n", n, cfg.Name)
	}
	if configIdx > -1 {
		for n, cfg := range bootconfigs {
			if configIdx == n {
				if dryrun {
					debug("Dry-run mode: will not boot the found configuration")
					debug("Boot configuration: %+v", cfg)
					return nil
				}
				if err := cfg.Boot(); err != nil {
					log.Printf("Failed to boot kernel %s: %v", cfg.Kernel, err)
				}
			}
		}
		log.Printf("Invalid arg -config %d: there are only %d bootconfigs available\n", configIdx, len(bootconfigs))
		return nil
	}
	if dryrun {
		cfg := bootconfigs[0]
		debug("Dry-run mode: will not boot the found configuration")
		debug("Boot configuration: %+v", cfg)
		return nil
	}

	// try to kexec into every boot config kernel until one succeeds
	for _, cfg := range bootconfigs {
		debug("Trying boot configuration %+v", cfg)
		if err := cfg.Boot(); err != nil {
			log.Printf("Failed to boot kernel %s: %v", cfg.Kernel, err)
		}
	}
	// if we reach this point, no boot configuration succeeded
	log.Print("No boot configuration succeeded")

	return nil
}

// BootPathMode tries to boot a kernel in PATH mode. This means:
// * look for a partition with the given GUID and mount it
// * look for the kernel and initramfs in the provided locations
// * boot the kernel with the provided command line
//
// The first parameter, `devices` is a list of storage.BlockDev . The function
// will look for bootable configurations on these devices
// The second parameter, `baseMountPoint`, is the directory where the mount
// points for each device will be created.
// The third parameter, `guid`, is the partition GUID to look for.
// The fourth parameter, `dryrun`, will not boot the found configurations if set
// to true.
func BootPathMode(devices []storage.BlockDev, baseMountpoint string, guid string, dryrun bool) error {
	debug("Getting list of supported filesystems")
	filesystems, err := storage.GetSupportedFilesystems()
	if err != nil {
		log.Fatal(err)
	}
	debug("Supported file systems: %v", filesystems)

	mount, err := mountByGUID(devices, filesystems, guid, baseMountpoint)
	if err != nil {
		return err
	}

	fullKernelPath := path.Join(mount.Path, *flagKernelPath)
	fullInitramfsPath := path.Join(mount.Path, *flagInitramfsPath)
	cfg := bootconfig.BootConfig{
		Kernel:     fullKernelPath,
		Initramfs:  fullInitramfsPath,
		KernelArgs: *flagKernelCmdline,
	}
	debug("Trying boot configuration %+v", cfg)
	if dryrun {
		log.Printf("Dry-run, will not actually boot")
	} else {
		if err := cfg.Boot(); err != nil {
			return fmt.Errorf("Failed to boot kernel %s: %v", cfg.Kernel, err)
		}
	}
	return nil
}

func main() {
	flag.Parse()
	if *flagGrubMode && *flagKernelPath != "" {
		log.Fatal("Options -grub and -kernel are mutually exclusive")
	}
	if *flagDebug {
		debug = log.Printf
	}

	// Get all the available block devices
	devices, err := storage.GetBlockStats()
	if err != nil {
		log.Fatal(err)
	}
	// print partition info
	if *flagDebug {
		for _, dev := range devices {
			log.Printf("Device: %+v", dev)
			table, err := storage.GetGPTTable(dev)
			if err != nil {
				continue
			}
			log.Printf("  Table: %+v", table)
			for _, part := range table.Partitions {
				log.Printf("    Partition: %+v\n", part)
				if !part.IsEmpty() {
					log.Printf("      UUID: %s\n", part.Type.String())
				}
			}
		}
	}

	// TODO boot from EFI system partitions. See storage.FilterEFISystemPartitions

	if *flagGrubMode {
		if err := BootGrubMode(devices, *flagBaseMountPoint, *flagDeviceGUID, *flagDryRun, *flagConfigIdx); err != nil {
			log.Fatal(err)
		}
	} else if *flagKernelPath != "" {
		if err := BootPathMode(devices, *flagBaseMountPoint, *flagDeviceGUID, *flagDryRun); err != nil {
			log.Fatal(err)
		}
	} else {
		log.Fatal("You must specify either -grub or -kernel")
	}
	os.Exit(1)
}
<|endoftext|>


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

package repository

import (
	"context"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/models/db"
	issues_model "code.gitea.io/gitea/models/issues"
	repo_model "code.gitea.io/gitea/models/repo"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/notification"
	repo_module "code.gitea.io/gitea/modules/repository"
)

// GenerateIssueLabels generates issue labels from a template repository
func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
	templateLabels, err := issues_model.GetLabelsByRepoID(ctx, templateRepo.ID, "", db.ListOptions{})
	if err != nil {
		return err
	}
	// Prevent insert being called with an empty slice which would result in
	// err "no element on slice when insert".
	if len(templateLabels) == 0 {
		return nil
	}

	newLabels := make([]*issues_model.Label, 0, len(templateLabels))
	for _, templateLabel := range templateLabels {
		newLabels = append(newLabels, &issues_model.Label{
			RepoID:      generateRepo.ID,
			Name:        templateLabel.Name,
			Description: templateLabel.Description,
			Color:       templateLabel.Color,
		})
	}
	return db.Insert(ctx, newLabels)
}

// GenerateRepository generates a repository from a template
func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.Repository, opts repo_module.GenerateRepoOptions) (_ *repo_model.Repository, err error) {
	if !doer.IsAdmin && !owner.CanCreateRepo() {
		return nil, repo_model.ErrReachLimitOfRepo{
			Limit: owner.MaxRepoCreation,
		}
	}

	var generateRepo *repo_model.Repository
	if err = db.WithTx(func(ctx context.Context) error {
		generateRepo, err = repo_module.GenerateRepository(ctx, doer, owner, templateRepo, opts)
		if err != nil {
			return err
		}

		// Git Content
		if opts.GitContent && !templateRepo.IsEmpty {
			if err = repo_module.GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Topics
		if opts.Topics {
			if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Git Hooks
		if opts.GitHooks {
			if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Webhooks
		if opts.Webhooks {
			if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Avatar
		if opts.Avatar && len(templateRepo.Avatar) > 0 {
			if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Issue Labels
		if opts.IssueLabels {
			if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		return nil
	}); err != nil {
		if generateRepo != nil && generateRepo.ID > 0 {
			if errDelete := models.DeleteRepository(doer, owner.ID, generateRepo.ID); errDelete != nil {
				log.Error("Rollback deleteRepository: %v", errDelete)
			}
		}
		return nil, err
	}

	notification.NotifyCreateRepository(doer, owner, generateRepo)

	return generateRepo, nil
}
<commit_msg>Remove manual rollback for failed generated repositories (#20639)<commit_after>// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repository

import (
	"context"

	"code.gitea.io/gitea/models/db"
	issues_model "code.gitea.io/gitea/models/issues"
	repo_model "code.gitea.io/gitea/models/repo"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/notification"
	repo_module "code.gitea.io/gitea/modules/repository"
)

// GenerateIssueLabels generates issue labels from a template repository
func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
	templateLabels, err := issues_model.GetLabelsByRepoID(ctx, templateRepo.ID, "", db.ListOptions{})
	if err != nil {
		return err
	}
	// Prevent insert being called with an empty slice which would result in
	// err "no element on slice when insert".
	if len(templateLabels) == 0 {
		return nil
	}

	newLabels := make([]*issues_model.Label, 0, len(templateLabels))
	for _, templateLabel := range templateLabels {
		newLabels = append(newLabels, &issues_model.Label{
			RepoID:      generateRepo.ID,
			Name:        templateLabel.Name,
			Description: templateLabel.Description,
			Color:       templateLabel.Color,
		})
	}
	return db.Insert(ctx, newLabels)
}

// GenerateRepository generates a repository from a template
func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.Repository, opts repo_module.GenerateRepoOptions) (_ *repo_model.Repository, err error) {
	if !doer.IsAdmin && !owner.CanCreateRepo() {
		return nil, repo_model.ErrReachLimitOfRepo{
			Limit: owner.MaxRepoCreation,
		}
	}

	var generateRepo *repo_model.Repository
	if err = db.WithTx(func(ctx context.Context) error {
		generateRepo, err = repo_module.GenerateRepository(ctx, doer, owner, templateRepo, opts)
		if err != nil {
			return err
		}

		// Git Content
		if opts.GitContent && !templateRepo.IsEmpty {
			if err = repo_module.GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Topics
		if opts.Topics {
			if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Git Hooks
		if opts.GitHooks {
			if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Webhooks
		if opts.Webhooks {
			if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Avatar
		if opts.Avatar && len(templateRepo.Avatar) > 0 {
			if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		// Issue Labels
		if opts.IssueLabels {
			if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
				return err
			}
		}

		return nil
	}); err != nil {
		return nil, err
	}

	notification.NotifyCreateRepository(doer, owner, generateRepo)

	return generateRepo, nil
}
<|endoftext|>


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

package varsutil

import (
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/sessionctx/variable"
	"github.com/pingcap/tidb/util/types"
)

// GetSessionSystemVar gets a system variable.
// If it is a session only variable, use the default value defined in code.
// Returns error if there is no such variable.
func GetSessionSystemVar(s *variable.SessionVars, key string) (string, error) {
	key = strings.ToLower(key)
	sysVar := variable.SysVars[key]
	if sysVar == nil {
		return "", variable.UnknownSystemVar
	}
	sVal, ok := s.Systems[key]
	if ok {
		return sVal, nil
	}
	if sysVar.Scope&variable.ScopeGlobal == 0 {
		// None-Global variable can use pre-defined default value.
		return sysVar.Value, nil
	}
	gVal, err := s.GlobalVarsAccessor.GetGlobalSysVar(key)
	if err != nil {
		return "", errors.Trace(err)
	}
	s.Systems[key] = gVal
	return gVal, nil
}

// GetGlobalSystemVar gets a global system variable.
func GetGlobalSystemVar(s *variable.SessionVars, key string) (string, error) {
	key = strings.ToLower(key)
	sysVar := variable.SysVars[key]
	if sysVar == nil {
		return "", variable.UnknownSystemVar
	}
	if sysVar.Scope == variable.ScopeSession {
		return "", variable.ErrIncorrectScope
	} else if sysVar.Scope == variable.ScopeNone {
		return sysVar.Value, nil
	}
	return s.GlobalVarsAccessor.GetGlobalSysVar(key)
}

// epochShiftBits is used to reserve logical part of the timestamp.
const epochShiftBits = 18

// SetSessionSystemVar sets system variable and updates SessionVars states.
func SetSessionSystemVar(vars *variable.SessionVars, name string, value types.Datum) error {
	name = strings.ToLower(name)
	sysVar := variable.SysVars[name]
	if sysVar == nil {
		return variable.UnknownSystemVar
	}
	if value.IsNull() {
		if name != variable.CharacterSetResults {
			return variable.ErrCantSetToNull
		}
		delete(vars.Systems, name)
		return nil
	}
	sVal, err := value.ToString()
	if err != nil {
		return errors.Trace(err)
	}
	switch name {
	case variable.TimeZone:
		vars.TimeZone = parseTimeZone(sVal)
	case variable.SQLModeVar:
		sVal = strings.ToUpper(sVal)
		if strings.Contains(sVal, "STRICT_TRANS_TABLES") || strings.Contains(sVal, "STRICT_ALL_TABLES") {
			vars.StrictSQLMode = true
		} else {
			vars.StrictSQLMode = false
		}
	case variable.TiDBSnapshot:
		err = setSnapshotTS(vars, sVal)
		if err != nil {
			return errors.Trace(err)
		}
	case variable.AutocommitVar:
		isAutocommit := strings.EqualFold(sVal, "ON") || sVal == "1"
		vars.SetStatusFlag(mysql.ServerStatusAutocommit, isAutocommit)
		if isAutocommit {
			vars.SetStatusFlag(mysql.ServerStatusInTrans, false)
		}
	case variable.TiDBSkipConstraintCheck:
		vars.SkipConstraintCheck = (sVal == "1")
	case variable.TiDBSkipDDLWait:
		vars.SkipDDLWait = (sVal == "1")
	}
	vars.Systems[name] = sVal
	return nil
}

func parseTimeZone(s string) *time.Location {
	if s == "SYSTEM" {
		// TODO: Support global time_zone variable, it should be set to global time_zone value.
		return time.Local
	}

	loc, err := time.LoadLocation(s)
	if err == nil {
		return loc
	}

	// The value can be given as a string indicating an offset from UTC, such as '+10:00' or '-6:00'.
	if strings.HasPrefix(s, "+") || strings.HasPrefix(s, "-") {
		d, err := types.ParseDuration(s[1:], 0)
		if err == nil {
			return time.FixedZone("UTC", int(d.Duration/time.Second))
		}
	}

	return nil
}

func setSnapshotTS(s *variable.SessionVars, sVal string) error {
	if sVal == "" {
		s.SnapshotTS = 0
		return nil
	}
	t, err := types.ParseTime(sVal, mysql.TypeTimestamp, types.MaxFsp)
	if err != nil {
		return errors.Trace(err)
	}
	// TODO: Consider time_zone variable.
	t1, err := t.Time.GoTime(time.Local)
	ts := (t1.UnixNano() / int64(time.Millisecond)) << epochShiftBits
	s.SnapshotTS = uint64(ts)
	return errors.Trace(err)
}
<commit_msg>sessionctx/varsutil: fix format str in sysvar error msg (#2572)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package varsutil

import (
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/sessionctx/variable"
	"github.com/pingcap/tidb/util/types"
)

// GetSessionSystemVar gets a system variable.
// If it is a session only variable, use the default value defined in code.
// Returns error if there is no such variable.
func GetSessionSystemVar(s *variable.SessionVars, key string) (string, error) {
	key = strings.ToLower(key)
	sysVar := variable.SysVars[key]
	if sysVar == nil {
		return "", variable.UnknownSystemVar.GenByArgs(key)
	}
	sVal, ok := s.Systems[key]
	if ok {
		return sVal, nil
	}
	if sysVar.Scope&variable.ScopeGlobal == 0 {
		// None-Global variable can use pre-defined default value.
		return sysVar.Value, nil
	}
	gVal, err := s.GlobalVarsAccessor.GetGlobalSysVar(key)
	if err != nil {
		return "", errors.Trace(err)
	}
	s.Systems[key] = gVal
	return gVal, nil
}

// GetGlobalSystemVar gets a global system variable.
func GetGlobalSystemVar(s *variable.SessionVars, key string) (string, error) {
	key = strings.ToLower(key)
	sysVar := variable.SysVars[key]
	if sysVar == nil {
		return "", variable.UnknownSystemVar.GenByArgs(key)
	}
	if sysVar.Scope == variable.ScopeSession {
		return "", variable.ErrIncorrectScope
	} else if sysVar.Scope == variable.ScopeNone {
		return sysVar.Value, nil
	}
	return s.GlobalVarsAccessor.GetGlobalSysVar(key)
}

// epochShiftBits is used to reserve logical part of the timestamp.
const epochShiftBits = 18

// SetSessionSystemVar sets system variable and updates SessionVars states.
func SetSessionSystemVar(vars *variable.SessionVars, name string, value types.Datum) error {
	name = strings.ToLower(name)
	sysVar := variable.SysVars[name]
	if sysVar == nil {
		return variable.UnknownSystemVar
	}
	if value.IsNull() {
		if name != variable.CharacterSetResults {
			return variable.ErrCantSetToNull
		}
		delete(vars.Systems, name)
		return nil
	}
	sVal, err := value.ToString()
	if err != nil {
		return errors.Trace(err)
	}
	switch name {
	case variable.TimeZone:
		vars.TimeZone = parseTimeZone(sVal)
	case variable.SQLModeVar:
		sVal = strings.ToUpper(sVal)
		if strings.Contains(sVal, "STRICT_TRANS_TABLES") || strings.Contains(sVal, "STRICT_ALL_TABLES") {
			vars.StrictSQLMode = true
		} else {
			vars.StrictSQLMode = false
		}
	case variable.TiDBSnapshot:
		err = setSnapshotTS(vars, sVal)
		if err != nil {
			return errors.Trace(err)
		}
	case variable.AutocommitVar:
		isAutocommit := strings.EqualFold(sVal, "ON") || sVal == "1"
		vars.SetStatusFlag(mysql.ServerStatusAutocommit, isAutocommit)
		if isAutocommit {
			vars.SetStatusFlag(mysql.ServerStatusInTrans, false)
		}
	case variable.TiDBSkipConstraintCheck:
		vars.SkipConstraintCheck = (sVal == "1")
	case variable.TiDBSkipDDLWait:
		vars.SkipDDLWait = (sVal == "1")
	}
	vars.Systems[name] = sVal
	return nil
}

func parseTimeZone(s string) *time.Location {
	if s == "SYSTEM" {
		// TODO: Support global time_zone variable, it should be set to global time_zone value.
		return time.Local
	}

	loc, err := time.LoadLocation(s)
	if err == nil {
		return loc
	}

	// The value can be given as a string indicating an offset from UTC, such as '+10:00' or '-6:00'.
	if strings.HasPrefix(s, "+") || strings.HasPrefix(s, "-") {
		d, err := types.ParseDuration(s[1:], 0)
		if err == nil {
			return time.FixedZone("UTC", int(d.Duration/time.Second))
		}
	}

	return nil
}

func setSnapshotTS(s *variable.SessionVars, sVal string) error {
	if sVal == "" {
		s.SnapshotTS = 0
		return nil
	}
	t, err := types.ParseTime(sVal, mysql.TypeTimestamp, types.MaxFsp)
	if err != nil {
		return errors.Trace(err)
	}
	// TODO: Consider time_zone variable.
	t1, err := t.Time.GoTime(time.Local)
	ts := (t1.UnixNano() / int64(time.Millisecond)) << epochShiftBits
	s.SnapshotTS = uint64(ts)
	return errors.Trace(err)
}
<|endoftext|>


<commit_before>// +build darwin

package test

import (
	"os"
	"testing"
)

func TestIntegration__date(t *testing.T) {
	cmd := Command("date", "-u", "-r", "0").Trim()
	cmd.EqualT(t, "Thu Jan  1 00:00:00 UTC 1970")

	ans := `Command:
  date -u -r 0
Output:
  Thu Jan  1 00:00:00 UTC 1970`
	if cmd.String() != ans {
		t.Errorf("cmd.String() = '%s'", cmd.String())
	}
}

func TestIntegration__unknown(t *testing.T) {
	cmd := CertManage("other").Trim()
	cmd.FailedT(t)
}

func TestIntegration__list(t *testing.T) {
	cmd := CertManage("list", "-count").Trim()
	cmd.CmpFnT(t, func(i int) bool { return i > 1 })
}

func TestIntegration__backup(t *testing.T) {
	cmd := CertManage("backup").Trim()
	cmd.EqualT(t, "Backup completed successfully")
}

// TODO(adam): Need to run -whitelist and -restore

// Firefox tests
func TestIntegration__firefox(t *testing.T) {
	if !online(t) {
		t.Skip("offline, can't run firefox tests (no NSS setup)")
	}

	// Make a request using the Keychain to get it ready
	// travis needs this
	cmd := Command("curl", "-s", "-o", os.DevNull, "https://google.com")
	cmd.SuccessT(t)

	// Verify firefox has found certificates
	cmd = CertManage("list", "-app", "firefox", "-count").Trim()
	cmd.SuccessT(t)
	cmd.CmpFnT(t, func(i int) bool { return i > 1 })
}
<commit_msg>test/osx: Ignore firefox tests for now<commit_after>// +build darwin

package test

import (
	// "strings"
	"testing"
)

func TestIntegration__date(t *testing.T) {
	cmd := Command("date", "-u", "-r", "0").Trim()
	cmd.EqualT(t, "Thu Jan  1 00:00:00 UTC 1970")

	ans := `Command:
  date -u -r 0
Output:
  Thu Jan  1 00:00:00 UTC 1970`
	if cmd.String() != ans {
		t.Errorf("cmd.String() = '%s'", cmd.String())
	}
}

func TestIntegration__unknown(t *testing.T) {
	cmd := CertManage("other").Trim()
	cmd.FailedT(t)
}

func TestIntegration__list(t *testing.T) {
	cmd := CertManage("list", "-count").Trim()
	cmd.CmpFnT(t, func(i int) bool { return i > 1 })
}

func TestIntegration__backup(t *testing.T) {
	cmd := CertManage("backup").Trim()
	cmd.EqualT(t, "Backup completed successfully")
}

// TODO(adam): Need to run -whitelist and -restore

// Firefox tests
// func TestIntegration__firefox(t *testing.T) {
// 	if !online(t) {
// 		t.Skip("offline, can't run firefox tests (no NSS setup)")
// 	}

// 	// Make a request using the Keychain to get it ready
// 	// travis needs this
// 	cmd := Command("timeout", strings.Split("15s firefox --headless https://google.com 2>&1 >> /var/log/firefox.log", " ")...)
// 	cmd.SuccessT(t)

// 	// Verify firefox has found certificates
// 	cmd = CertManage("list", "-app", "firefox", "-count").Trim()
// 	cmd.SuccessT(t)
// 	cmd.CmpFnT(t, func(i int) bool { return i > 1 })
// }
<|endoftext|>


<commit_before>package cluster

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd/shared/version"
)

// Numeric type codes identifying different kind of entities.
const (
	TypeContainer = iota
	TypeImage
	TypeProfile
	TypeProject
)

// EntityNames associates an entity code to its name.
var EntityNames = map[int]string{
	TypeContainer: "container",
	TypeImage:     "image",
	TypeProfile:   "profile",
	TypeProject:   "project",
}

// EntityTypes associates an entity name to its type code.
var EntityTypes = map[string]int{}

// EntityURIs associates an entity code to its URI pattern.
var EntityURIs = map[int]string{
	TypeContainer: "/" + version.APIVersion + "/containers/%s?project=%s",
	TypeImage:     "/" + version.APIVersion + "/images/%s",
	TypeProfile:   "/" + version.APIVersion + "/profiles/%s?project=%s",
	TypeProject:   "/" + version.APIVersion + "/projects/%s",
}

// EntityFormatURIs associates an entity code to a formatter function that can be
// used to format its URI.
var EntityFormatURIs = map[int]func(a ...interface{}) string{
	TypeContainer: func(a ...interface{}) string {
		uri := fmt.Sprintf(EntityURIs[TypeContainer], a[1], a[0])
		if a[0] == "default" {
			return strings.Split(uri, fmt.Sprintf("?project=%s", a[0]))[0]
		}

		return uri
	},
	TypeProfile: func(a ...interface{}) string {
		uri := fmt.Sprintf(EntityURIs[TypeProfile], a[1], a[0])
		if a[0] == "default" {
			return strings.Split(uri, fmt.Sprintf("?project=%s", a[0]))[0]
		}

		return uri
	},
	TypeProject: func(a ...interface{}) string {
		uri := fmt.Sprintf(EntityURIs[TypeProject], a[0])
		return uri
	},
}

func init() {
	for code, name := range EntityNames {
		EntityTypes[name] = code
	}
}
<commit_msg>lxd/db/cluster: Extend list of entities<commit_after>package cluster

import (
	"fmt"
	"strings"

	"github.com/lxc/lxd/shared/version"
)

// Numeric type codes identifying different kind of entities.
const (
	TypeContainer             = 0
	TypeImage                 = 1
	TypeProfile               = 2
	TypeProject               = 3
	TypeCertificate           = 4
	TypeInstance              = 5
	TypeInstanceBackup        = 6
	TypeInstanceSnapshot      = 7
	TypeNetwork               = 8
	TypeNetworkACL            = 9
	TypeNode                  = 10
	TypeOperation             = 11
	TypeStoragePool           = 12
	TypeStorageVolume         = 13
	TypeStorageVolumeBackup   = 14
	TypeStorageVolumeSnapshot = 15
	TypeWarning               = 16
)

// EntityNames associates an entity code to its name.
var EntityNames = map[int]string{
	TypeContainer:             "container",
	TypeImage:                 "image",
	TypeProfile:               "profile",
	TypeProject:               "project",
	TypeCertificate:           "certificate",
	TypeInstance:              "instance",
	TypeInstanceBackup:        "instance backup",
	TypeInstanceSnapshot:      "instance snapshot",
	TypeNetwork:               "network",
	TypeNetworkACL:            "network acl",
	TypeNode:                  "node",
	TypeOperation:             "operation",
	TypeStoragePool:           "storage pool",
	TypeStorageVolume:         "storage volume",
	TypeStorageVolumeBackup:   "storage volume backup",
	TypeStorageVolumeSnapshot: "storage volume snapshot",
	TypeWarning:               "warning",
}

// EntityTypes associates an entity name to its type code.
var EntityTypes = map[string]int{}

// EntityURIs associates an entity code to its URI pattern.
var EntityURIs = map[int]string{
	TypeContainer:             "/" + version.APIVersion + "/containers/%s?project=%s",
	TypeImage:                 "/" + version.APIVersion + "/images/%s",
	TypeProfile:               "/" + version.APIVersion + "/profiles/%s?project=%s",
	TypeProject:               "/" + version.APIVersion + "/projects/%s",
	TypeCertificate:           "/" + version.APIVersion + "/certificates/%s",
	TypeInstance:              "/" + version.APIVersion + "/instances/%s?project=%s",
	TypeInstanceBackup:        "/" + version.APIVersion + "/instances/%s/backups/%s?project=%s",
	TypeInstanceSnapshot:      "/" + version.APIVersion + "/instances/%s/snapshots/%s?project=%s",
	TypeNetwork:               "/" + version.APIVersion + "/networks/%s?project=%s",
	TypeNetworkACL:            "/" + version.APIVersion + "/network-acls/%s?project=%s",
	TypeNode:                  "/" + version.APIVersion + "/cluster/members/%s",
	TypeOperation:             "/" + version.APIVersion + "/operations/%s",
	TypeStoragePool:           "/" + version.APIVersion + "/storage-pools/%s",
	TypeStorageVolume:         "/" + version.APIVersion + "/storage-pools/%s/volumes/%s/%s?project=%s",
	TypeStorageVolumeBackup:   "/" + version.APIVersion + "/storage-pools/%s/volumes/%s/%s/backups/%s?project=%s",
	TypeStorageVolumeSnapshot: "/" + version.APIVersion + "/storage-pools/%s/volumes/%s/%s/snapshots/%s?project=%s",
	TypeWarning:               "/" + version.APIVersion + "/warnings/%s",
}

// EntityFormatURIs associates an entity code to a formatter function that can be
// used to format its URI.
var EntityFormatURIs = map[int]func(a ...interface{}) string{
	TypeContainer: func(a ...interface{}) string {
		uri := fmt.Sprintf(EntityURIs[TypeContainer], a[1], a[0])
		if a[0] == "default" {
			return strings.Split(uri, fmt.Sprintf("?project=%s", a[0]))[0]
		}

		return uri
	},
	TypeProfile: func(a ...interface{}) string {
		uri := fmt.Sprintf(EntityURIs[TypeProfile], a[1], a[0])
		if a[0] == "default" {
			return strings.Split(uri, fmt.Sprintf("?project=%s", a[0]))[0]
		}

		return uri
	},
	TypeProject: func(a ...interface{}) string {
		uri := fmt.Sprintf(EntityURIs[TypeProject], a[0])
		return uri
	},
}

func init() {
	for code, name := range EntityNames {
		EntityTypes[name] = code
	}
}
<|endoftext|>


<commit_before>package storage

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

type mockBackend struct {
	name   string
	state  *state.State
	logger logger.Logger
}

func (b *mockBackend) DaemonState() *state.State {
	return b.state
}

func (b *mockBackend) ID() int64 {
	return -1
}

func (b *mockBackend) Name() string {
	return b.name
}

func (b *mockBackend) Driver() drivers.Driver {
	return nil
}

func (b *mockBackend) MigrationTypes(contentType drivers.ContentType) []migration.Type {
	return []migration.Type{
		{
			FSType:   migration.MigrationFSType_RSYNC,
			Features: []string{"xattrs", "delete", "compress", "bidirectional"},
		},
	}
}

func (b *mockBackend) GetResources() (*api.ResourcesStoragePool, error) {
	return nil, nil
}

func (b *mockBackend) Delete(localOnly bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) Mount() (bool, error) {
	return true, nil
}

func (b *mockBackend) Unmount() (bool, error) {
	return true, nil
}

func (b *mockBackend) CreateInstance(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameInstance(i Instance, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteInstance(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) GetInstanceUsage(i Instance) (int64, error) {
	return 0, nil
}

func (b *mockBackend) SetInstanceQuota(i Instance, size string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MountInstance(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) UnmountInstance(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) GetInstanceDisk(i Instance) (string, error) {
	return "", nil
}

func (b *mockBackend) CreateInstanceSnapshot(i Instance, name string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteInstanceSnapshot(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) EnsureImage(fingerprint string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteImage(fingerprint string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateCustomVolume(volName, desc string, config map[string]string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateCustomVolumeFromCopy(volName, desc string, config map[string]string, srcPoolName, srcVolName string, srcVolOnly bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameCustomVolume(volName string, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) UpdateCustomVolume(volName, newDesc string, newConfig map[string]string, op *operations.Operation) error {
	return ErrNotImplemented
}

func (b *mockBackend) DeleteCustomVolume(volName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MigrateCustomVolume(conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateCustomVolumeFromMigration(conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) GetCustomVolumeUsage(volName string) (int64, error) {
	return 0, nil
}

func (b *mockBackend) MountCustomVolume(volName string, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) UnmountCustomVolume(volName string, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) CreateCustomVolumeSnapshot(volName string, newSnapshotName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameCustomVolumeSnapshot(volName string, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteCustomVolumeSnapshot(volName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RestoreCustomVolume(volName string, snapshotName string, op *operations.Operation) error {
	return nil
}
<commit_msg>lxd/storage/backend/mock: RefreshInstance placeholder<commit_after>package storage

import (
	"io"

	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

type mockBackend struct {
	name   string
	state  *state.State
	logger logger.Logger
}

func (b *mockBackend) DaemonState() *state.State {
	return b.state
}

func (b *mockBackend) ID() int64 {
	return -1
}

func (b *mockBackend) Name() string {
	return b.name
}

func (b *mockBackend) Driver() drivers.Driver {
	return nil
}

func (b *mockBackend) MigrationTypes(contentType drivers.ContentType) []migration.Type {
	return []migration.Type{
		{
			FSType:   migration.MigrationFSType_RSYNC,
			Features: []string{"xattrs", "delete", "compress", "bidirectional"},
		},
	}
}

func (b *mockBackend) GetResources() (*api.ResourcesStoragePool, error) {
	return nil, nil
}

func (b *mockBackend) Delete(localOnly bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) Mount() (bool, error) {
	return true, nil
}

func (b *mockBackend) Unmount() (bool, error) {
	return true, nil
}

func (b *mockBackend) CreateInstance(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameInstance(i Instance, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteInstance(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RefreshInstance(i instance.Instance, src instance.Instance, srcSnapshots []instance.Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) GetInstanceUsage(i Instance) (int64, error) {
	return 0, nil
}

func (b *mockBackend) SetInstanceQuota(i Instance, size string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MountInstance(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) UnmountInstance(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) GetInstanceDisk(i Instance) (string, error) {
	return "", nil
}

func (b *mockBackend) CreateInstanceSnapshot(i Instance, name string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteInstanceSnapshot(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) EnsureImage(fingerprint string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteImage(fingerprint string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateCustomVolume(volName, desc string, config map[string]string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateCustomVolumeFromCopy(volName, desc string, config map[string]string, srcPoolName, srcVolName string, srcVolOnly bool, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameCustomVolume(volName string, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) UpdateCustomVolume(volName, newDesc string, newConfig map[string]string, op *operations.Operation) error {
	return ErrNotImplemented
}

func (b *mockBackend) DeleteCustomVolume(volName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) MigrateCustomVolume(conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) CreateCustomVolumeFromMigration(conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) GetCustomVolumeUsage(volName string) (int64, error) {
	return 0, nil
}

func (b *mockBackend) MountCustomVolume(volName string, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) UnmountCustomVolume(volName string, op *operations.Operation) (bool, error) {
	return true, nil
}

func (b *mockBackend) CreateCustomVolumeSnapshot(volName string, newSnapshotName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RenameCustomVolumeSnapshot(volName string, newName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) DeleteCustomVolumeSnapshot(volName string, op *operations.Operation) error {
	return nil
}

func (b *mockBackend) RestoreCustomVolume(volName string, snapshotName string, op *operations.Operation) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"

	"github.com/lxc/lxd/shared"
)

var storagePoolConfigKeys = map[string]func(value string) error{
	// valid drivers: btrfs
	// (Note, that we can't be smart in detecting mount options since a lot
	// of filesystems come with their own additional ones (e.g.
	// "user_subvol_rm_allowed" for btrfs or "zfsutils" for zfs). So
	// shared.IsAny() must do.)
	"btrfs.mount_options": shared.IsAny,

	// valid drivers: ceph
	"ceph.cluster_name":  shared.IsAny,
	"ceph.osd.pool_name": shared.IsAny,
	"ceph.osd.pg_num": func(value string) error {
		if value == "" {
			return nil
		}

		_, err := shared.ParseByteSizeString(value)
		return err
	},
	"ceph.rbd.clone_copy": shared.IsBool,
	"ceph.user.name":      shared.IsAny,

	// valid drivers: lvm
	"lvm.thinpool_name": shared.IsAny,
	"lvm.use_thinpool":  shared.IsBool,
	"lvm.vg_name":       shared.IsAny,

	// valid drivers: btrfs, lvm, zfs
	"size": func(value string) error {
		if value == "" {
			return nil
		}

		_, err := shared.ParseByteSizeString(value)
		return err
	},

	// valid drivers: btrfs, dir, lvm, zfs
	"source": shared.IsAny,

	// Using it as an indicator whether we created the pool or are just
	// re-using it. Note that the valid drivers only list ceph for now. This
	// approach is however generalizable. It's just that we currently don't
	// really need it for the other drivers.
	// valid drivers: ceph
	"volatile.pool.pristine":  shared.IsAny,
	"volatile.initial_source": shared.IsAny,

	// valid drivers: ceph, lvm
	"volume.block.filesystem": func(value string) error {
		return shared.IsOneOf(value, []string{"ext4", "xfs"})
	},
	"volume.block.mount_options": shared.IsAny,

	// valid drivers: ceph, lvm
	"volume.size": func(value string) error {
		if value == "" {
			return nil
		}

		_, err := shared.ParseByteSizeString(value)
		return err
	},

	// valid drivers: zfs
	"volume.zfs.remove_snapshots": shared.IsBool,
	"volume.zfs.use_refquota":     shared.IsBool,

	// valid drivers: zfs
	"zfs.clone_copy": shared.IsBool,
	"zfs.pool_name":  shared.IsAny,
	"rsync.bwlimit":  shared.IsAny,
}

func storagePoolValidateConfig(name string, driver string, config map[string]string) error {
	err := func(value string) error {
		return shared.IsOneOf(value, supportedStoragePoolDrivers)
	}(driver)
	if err != nil {
		return err
	}

	if driver == "lvm" {
		v, ok := config["lvm.use_thinpool"]
		if ok && !shared.IsTrue(v) && config["lvm.thinpool_name"] != "" {
			return fmt.Errorf("the key \"lvm.use_thinpool\" cannot be set to a false value when \"lvm.thinpool_name\" is set for LVM storage pools")
		}
	}

	v, ok := config["rsync.bwlimit"]
	if ok && v != "" {
		_, err := shared.ParseByteSizeString(v)
		if err != nil {
			return err
		}
	}

	// Check whether the config properties for the driver container sane
	// values.
	for key, val := range config {
		// User keys are not validated.
		if strings.HasPrefix(key, "user.") {
			continue
		}

		prfx := strings.HasPrefix
		if driver == "dir" || driver == "ceph" {
			if key == "size" {
				return fmt.Errorf("the key %s cannot be used with %s storage pools", key, strings.ToUpper(driver))
			}
		}

		if driver != "lvm" && driver != "ceph" {
			if prfx(key, "lvm.") || prfx(key, "volume.block.") || key == "volume.size" {
				return fmt.Errorf("the key %s cannot be used with %s storage pools", key, strings.ToUpper(driver))
			}
		}

		if driver != "zfs" {
			if prfx(key, "volume.zfs.") || prfx(key, "zfs.") {
				return fmt.Errorf("the key %s cannot be used with %s storage pools", key, strings.ToUpper(driver))
			}
		}

		// Validate storage pool config keys.
		validator, ok := storagePoolConfigKeys[key]
		if !ok {
			return fmt.Errorf("Invalid storage pool configuration key: %s", key)
		}

		err := validator(val)
		if err != nil {
			return err
		}
	}

	return nil
}

func storagePoolFillDefault(name string, driver string, config map[string]string) error {
	if driver == "dir" {
		if config["size"] != "" {
			return fmt.Errorf("The \"size\" property does not apply to %s storage pools", driver)
		}
	} else {
		if config["size"] == "" {
			st := syscall.Statfs_t{}
			err := syscall.Statfs(shared.VarPath(), &st)
			if err != nil {
				return fmt.Errorf("Couldn't statfs %s: %s", shared.VarPath(), err)
			}

			/* choose 15 GB < x < 100GB, where x is 20% of the disk size */
			size := uint64(st.Frsize) * st.Blocks / (1024 * 1024 * 1024) / 5
			if size > 100 {
				size = 100
			}
			if size < 15 {
				size = 15
			}
			config["size"] = strconv.FormatUint(uint64(size), 10) + "GB"
		} else {
			_, err := shared.ParseByteSizeString(config["size"])
			if err != nil {
				return err
			}
		}
	}

	if driver == "lvm" {
		// We use thin pools per default.
		useThinpool := true
		if config["lvm.use_thinpool"] != "" {
			useThinpool = shared.IsTrue(config["lvm.use_thinpool"])
		}

		if useThinpool && config["lvm.thinpool_name"] == "" {
			// Unchangeable pool property: Set unconditionally.
			config["lvm.thinpool_name"] = "LXDThinpool"
		}
	}

	if driver == "lvm" || driver == "ceph" {
		if config["volume.size"] != "" {
			_, err := shared.ParseByteSizeString(config["volume.size"])
			if err != nil {
				return err
			}
		}
	}

	return nil
}
<commit_msg>storage: enable "volume.size" for {btrfs,zfs}<commit_after>package main

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"

	"github.com/lxc/lxd/shared"
)

var storagePoolConfigKeys = map[string]func(value string) error{
	// valid drivers: btrfs
	// (Note, that we can't be smart in detecting mount options since a lot
	// of filesystems come with their own additional ones (e.g.
	// "user_subvol_rm_allowed" for btrfs or "zfsutils" for zfs). So
	// shared.IsAny() must do.)
	"btrfs.mount_options": shared.IsAny,

	// valid drivers: ceph
	"ceph.cluster_name":  shared.IsAny,
	"ceph.osd.pool_name": shared.IsAny,
	"ceph.osd.pg_num": func(value string) error {
		if value == "" {
			return nil
		}

		_, err := shared.ParseByteSizeString(value)
		return err
	},
	"ceph.rbd.clone_copy": shared.IsBool,
	"ceph.user.name":      shared.IsAny,

	// valid drivers: lvm
	"lvm.thinpool_name": shared.IsAny,
	"lvm.use_thinpool":  shared.IsBool,
	"lvm.vg_name":       shared.IsAny,

	// valid drivers: btrfs, lvm, zfs
	"size": func(value string) error {
		if value == "" {
			return nil
		}

		_, err := shared.ParseByteSizeString(value)
		return err
	},

	// valid drivers: btrfs, dir, lvm, zfs
	"source": shared.IsAny,

	// Using it as an indicator whether we created the pool or are just
	// re-using it. Note that the valid drivers only list ceph for now. This
	// approach is however generalizable. It's just that we currently don't
	// really need it for the other drivers.
	// valid drivers: ceph
	"volatile.pool.pristine":  shared.IsAny,
	"volatile.initial_source": shared.IsAny,

	// valid drivers: ceph, lvm
	"volume.block.filesystem": func(value string) error {
		return shared.IsOneOf(value, []string{"ext4", "xfs"})
	},
	"volume.block.mount_options": shared.IsAny,

	// valid drivers: ceph, lvm
	"volume.size": func(value string) error {
		if value == "" {
			return nil
		}

		_, err := shared.ParseByteSizeString(value)
		return err
	},

	// valid drivers: zfs
	"volume.zfs.remove_snapshots": shared.IsBool,
	"volume.zfs.use_refquota":     shared.IsBool,

	// valid drivers: zfs
	"zfs.clone_copy": shared.IsBool,
	"zfs.pool_name":  shared.IsAny,
	"rsync.bwlimit":  shared.IsAny,
}

func storagePoolValidateConfig(name string, driver string, config map[string]string) error {
	err := func(value string) error {
		return shared.IsOneOf(value, supportedStoragePoolDrivers)
	}(driver)
	if err != nil {
		return err
	}

	if driver == "lvm" {
		v, ok := config["lvm.use_thinpool"]
		if ok && !shared.IsTrue(v) && config["lvm.thinpool_name"] != "" {
			return fmt.Errorf("the key \"lvm.use_thinpool\" cannot be set to a false value when \"lvm.thinpool_name\" is set for LVM storage pools")
		}
	}

	v, ok := config["rsync.bwlimit"]
	if ok && v != "" {
		_, err := shared.ParseByteSizeString(v)
		if err != nil {
			return err
		}
	}

	// Check whether the config properties for the driver container sane
	// values.
	for key, val := range config {
		// User keys are not validated.
		if strings.HasPrefix(key, "user.") {
			continue
		}

		prfx := strings.HasPrefix
		if driver == "dir" || driver == "ceph" {
			if key == "size" {
				return fmt.Errorf("the key %s cannot be used with %s storage pools", key, strings.ToUpper(driver))
			}
		}

		if driver != "lvm" && driver != "ceph" {
			if prfx(key, "lvm.") || prfx(key, "volume.block.") || key == "volume.size" {
				return fmt.Errorf("the key %s cannot be used with %s storage pools", key, strings.ToUpper(driver))
			}
		}

		if driver != "zfs" {
			if prfx(key, "volume.zfs.") || prfx(key, "zfs.") {
				return fmt.Errorf("the key %s cannot be used with %s storage pools", key, strings.ToUpper(driver))
			}
		}

		// Validate storage pool config keys.
		validator, ok := storagePoolConfigKeys[key]
		if !ok {
			return fmt.Errorf("Invalid storage pool configuration key: %s", key)
		}

		err := validator(val)
		if err != nil {
			return err
		}
	}

	return nil
}

func storagePoolFillDefault(name string, driver string, config map[string]string) error {
	if driver == "dir" {
		if config["size"] != "" {
			return fmt.Errorf("The \"size\" property does not apply to %s storage pools", driver)
		}
	} else {
		if config["size"] == "" {
			st := syscall.Statfs_t{}
			err := syscall.Statfs(shared.VarPath(), &st)
			if err != nil {
				return fmt.Errorf("Couldn't statfs %s: %s", shared.VarPath(), err)
			}

			/* choose 15 GB < x < 100GB, where x is 20% of the disk size */
			size := uint64(st.Frsize) * st.Blocks / (1024 * 1024 * 1024) / 5
			if size > 100 {
				size = 100
			}
			if size < 15 {
				size = 15
			}
			config["size"] = strconv.FormatUint(uint64(size), 10) + "GB"
		} else {
			_, err := shared.ParseByteSizeString(config["size"])
			if err != nil {
				return err
			}
		}
	}

	if driver == "lvm" {
		// We use thin pools per default.
		useThinpool := true
		if config["lvm.use_thinpool"] != "" {
			useThinpool = shared.IsTrue(config["lvm.use_thinpool"])
		}

		if useThinpool && config["lvm.thinpool_name"] == "" {
			// Unchangeable pool property: Set unconditionally.
			config["lvm.thinpool_name"] = "LXDThinpool"
		}
	}

	if driver == "btrfs" || driver == "ceph" || driver == "lvm" || driver == "zfs" {
		if config["volume.size"] != "" {
			_, err := shared.ParseByteSizeString(config["volume.size"])
			if err != nil {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package dispatcher

import (
	"sync"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"github.com/docker/swarm-v2/api"
	"github.com/docker/swarm-v2/pkg/heartbeat"
)

type registeredNode struct {
	SessionID string
	Heartbeat *heartbeat.Heartbeat
	Tasks     []string
	Node      *api.Node
	mu        sync.Mutex
}

// checkSessionID determines if the SessionID has changed and returns the
// appropriate GRPC error code.
//
// This may not belong here in the future.
func (rn *registeredNode) checkSessionID(sessionID string) error {
	rn.mu.Lock()
	defer rn.mu.Unlock()

	// Before each message send, we need to check the nodes sessionID hasn't
	// changed. If it has, we will the stream and make the node
	// re-register.
	if rn.SessionID != sessionID {
		return grpc.Errorf(codes.InvalidArgument, ErrSessionInvalid.Error())
	}

	return nil
}

type nodeStore struct {
	nodes map[string]*registeredNode
	mu    sync.RWMutex
}

func newNodeStore() *nodeStore {
	return &nodeStore{
		nodes: make(map[string]*registeredNode),
	}
}

// Add adds new node, it replaces existing without notification.
func (s *nodeStore) Add(rn *registeredNode) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if existRn, ok := s.nodes[rn.Node.ID]; ok {
		existRn.Heartbeat.Stop()
		delete(s.nodes, rn.Node.ID)
	}
	s.nodes[rn.Node.ID] = rn
}

func (s *nodeStore) Get(id string) (*registeredNode, error) {
	s.mu.RLock()
	rn, ok := s.nodes[id]
	s.mu.RUnlock()
	if !ok {
		return nil, grpc.Errorf(codes.NotFound, ErrNodeNotRegistered.Error())
	}
	return rn, nil
}

func (s *nodeStore) GetWithSession(id, sid string) (*registeredNode, error) {
	s.mu.RLock()
	rn, ok := s.nodes[id]
	s.mu.RUnlock()
	if !ok {
		return nil, grpc.Errorf(codes.NotFound, ErrNodeNotRegistered.Error())
	}
	return rn, rn.checkSessionID(sid)
}

func (s *nodeStore) Delete(id string) {
	s.mu.Lock()
	if rn, ok := s.nodes[id]; ok {
		delete(s.nodes, id)
		rn.Heartbeat.Stop()
	}
	s.mu.Unlock()
}
<commit_msg>dispatcher: remove unused field<commit_after>package dispatcher

import (
	"sync"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"github.com/docker/swarm-v2/api"
	"github.com/docker/swarm-v2/pkg/heartbeat"
)

type registeredNode struct {
	SessionID string
	Heartbeat *heartbeat.Heartbeat
	Node      *api.Node
	mu        sync.Mutex
}

// checkSessionID determines if the SessionID has changed and returns the
// appropriate GRPC error code.
//
// This may not belong here in the future.
func (rn *registeredNode) checkSessionID(sessionID string) error {
	rn.mu.Lock()
	defer rn.mu.Unlock()

	// Before each message send, we need to check the nodes sessionID hasn't
	// changed. If it has, we will the stream and make the node
	// re-register.
	if rn.SessionID != sessionID {
		return grpc.Errorf(codes.InvalidArgument, ErrSessionInvalid.Error())
	}

	return nil
}

type nodeStore struct {
	nodes map[string]*registeredNode
	mu    sync.RWMutex
}

func newNodeStore() *nodeStore {
	return &nodeStore{
		nodes: make(map[string]*registeredNode),
	}
}

// Add adds new node, it replaces existing without notification.
func (s *nodeStore) Add(rn *registeredNode) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if existRn, ok := s.nodes[rn.Node.ID]; ok {
		existRn.Heartbeat.Stop()
		delete(s.nodes, rn.Node.ID)
	}
	s.nodes[rn.Node.ID] = rn
}

func (s *nodeStore) Get(id string) (*registeredNode, error) {
	s.mu.RLock()
	rn, ok := s.nodes[id]
	s.mu.RUnlock()
	if !ok {
		return nil, grpc.Errorf(codes.NotFound, ErrNodeNotRegistered.Error())
	}
	return rn, nil
}

func (s *nodeStore) GetWithSession(id, sid string) (*registeredNode, error) {
	s.mu.RLock()
	rn, ok := s.nodes[id]
	s.mu.RUnlock()
	if !ok {
		return nil, grpc.Errorf(codes.NotFound, ErrNodeNotRegistered.Error())
	}
	return rn, rn.checkSessionID(sid)
}

func (s *nodeStore) Delete(id string) {
	s.mu.Lock()
	if rn, ok := s.nodes[id]; ok {
		delete(s.nodes, id)
		rn.Heartbeat.Stop()
	}
	s.mu.Unlock()
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package logs

import (
	"os"
	"time"

	"github.com/go-logr/logr"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Inspired from https://github.com/go-logr/zapr, some functions is copy from the repo.

var (
	// JSONLogger is global json log format logr
	JSONLogger logr.Logger

	// timeNow stubbed out for testing
	timeNow = time.Now
)

// zapLogger is a logr.Logger that uses Zap to record log.
type zapLogger struct {
	// NB: this looks very similar to zap.SugaredLogger, but
	// deals with our desire to have multiple verbosity levels.
	l   *zap.Logger
	lvl int
}

// implement logr.Logger
var _ logr.Logger = &zapLogger{}

// Enabled should always return true
func (l *zapLogger) Enabled() bool {
	return true
}

// Info write message to error level log
func (l *zapLogger) Info(msg string, keysAndVals ...interface{}) {
	entry := zapcore.Entry{
		Time:    timeNow(),
		Message: msg,
	}
	checkedEntry := l.l.Core().Check(entry, nil)
	checkedEntry.Write(l.handleFields(keysAndVals)...)
}

// dPanic write message to DPanicLevel level log
// we need implement this because unit test case need stub time.Now
// otherwise the ts field always changed
func (l *zapLogger) dPanic(msg string) {
	entry := zapcore.Entry{
		Level:   zapcore.DPanicLevel,
		Time:    timeNow(),
		Message: msg,
	}
	checkedEntry := l.l.Core().Check(entry, nil)
	checkedEntry.Write(zap.Int("v", l.lvl))
}

// handleFields converts a bunch of arbitrary key-value pairs into Zap fields.  It takes
// additional pre-converted Zap fields, for use with automatically attached fields, like
// `error`.
func (l *zapLogger) handleFields(args []interface{}, additional ...zap.Field) []zap.Field {
	// a slightly modified version of zap.SugaredLogger.sweetenFields
	if len(args) == 0 {
		// fast-return if we have no suggared fields.
		return append(additional, zap.Int("v", l.lvl))
	}

	// unlike Zap, we can be pretty sure users aren't passing structured
	// fields (since logr has no concept of that), so guess that we need a
	// little less space.
	fields := make([]zap.Field, 0, len(args)/2+len(additional)+1)
	fields = append(fields, zap.Int("v", l.lvl))
	for i := 0; i < len(args)-1; i += 2 {
		// check just in case for strongly-typed Zap fields, which is illegal (since
		// it breaks implementation agnosticism), so we can give a better error message.
		if _, ok := args[i].(zap.Field); ok {
			l.dPanic("strongly-typed Zap Field passed to logr")
			break
		}

		// process a key-value pair,
		// ensuring that the key is a string
		key, val := args[i], args[i+1]
		keyStr, isString := key.(string)
		if !isString {
			// if the key isn't a string, stop logging
			l.dPanic("non-string key argument passed to logging, ignoring all later arguments")
			break
		}

		fields = append(fields, zap.Any(keyStr, val))
	}

	return append(fields, additional...)
}

// Error write log message to error level
func (l *zapLogger) Error(err error, msg string, keysAndVals ...interface{}) {
	entry := zapcore.Entry{
		Level:   zapcore.ErrorLevel,
		Time:    timeNow(),
		Message: msg,
	}
	checkedEntry := l.l.Core().Check(entry, nil)
	checkedEntry.Write(l.handleFields(keysAndVals, handleError(err))...)
}

// V return info logr.Logger  with specified level
func (l *zapLogger) V(level int) logr.Logger {
	return &zapLogger{
		lvl: l.lvl + level,
		l:   l.l,
	}
}

// WithValues return logr.Logger with some keys And Values
func (l *zapLogger) WithValues(keysAndValues ...interface{}) logr.Logger {
	l.l = l.l.With(l.handleFields(keysAndValues)...)
	return l
}

// WithName return logger Named with specified name
func (l *zapLogger) WithName(name string) logr.Logger {
	l.l = l.l.Named(name)
	return l
}

// encoderConfig config zap encodetime format
var encoderConfig = zapcore.EncoderConfig{
	MessageKey: "msg",

	TimeKey:        "ts",
	EncodeTime:     zapcore.EpochMillisTimeEncoder,
	EncodeDuration: zapcore.StringDurationEncoder,
}

// NewJSONLogger creates a new json logr.Logger using the given Zap Logger to log.
func NewJSONLogger(w zapcore.WriteSyncer) logr.Logger {
	l, _ := zap.NewProduction()
	if w == nil {
		w = os.Stdout
	}
	log := l.WithOptions(zap.AddCallerSkip(1),
		zap.WrapCore(
			func(zapcore.Core) zapcore.Core {
				return zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(w), zapcore.DebugLevel)
			}))
	return &zapLogger{
		l: log,
	}
}

func handleError(err error) zap.Field {
	return zap.NamedError("err", err)
}

func init() {
	JSONLogger = NewJSONLogger(nil)
}
<commit_msg>Disable zap sampling and cleanup config<commit_after>/*
Copyright 2020 The Kubernetes Authors.

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

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

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

package logs

import (
	"time"

	"github.com/go-logr/logr"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Inspired from https://github.com/go-logr/zapr, some functions is copy from the repo.

var (
	// JSONLogger is global json log format logr
	JSONLogger logr.Logger

	// timeNow stubbed out for testing
	timeNow = time.Now
)

func init() {
	JSONLogger = NewJSONLogger(nil)
}

// NewJSONLogger creates a new json logr.Logger using the given Zap Logger to log.
func NewJSONLogger(w zapcore.WriteSyncer) logr.Logger {
	l, _ := zapConfig.Build()
	l = l.WithOptions(zap.AddCallerSkip(1))
	if w != nil {
		l = l.WithOptions(zap.WrapCore(
			func(zapcore.Core) zapcore.Core {
				return zapcore.NewCore(zapcore.NewJSONEncoder(zapConfig.EncoderConfig), zapcore.AddSync(w), zapcore.DebugLevel)
			}))
	}
	return &zapLogger{l: l}
}

var zapConfig = zap.Config{
	Level:       zap.NewAtomicLevelAt(zap.DebugLevel),
	Development: false,
	Sampling:    nil,
	Encoding:    "json",
	EncoderConfig: zapcore.EncoderConfig{
		MessageKey:     "msg",
		TimeKey:        "ts",
		EncodeTime:     zapcore.EpochMillisTimeEncoder,
		EncodeDuration: zapcore.StringDurationEncoder,
	},
	OutputPaths:      []string{"stdout"},
	ErrorOutputPaths: []string{"stderr"},
}

// zapLogger is a logr.Logger that uses Zap to record log.
type zapLogger struct {
	// NB: this looks very similar to zap.SugaredLogger, but
	// deals with our desire to have multiple verbosity levels.
	l   *zap.Logger
	lvl int
}

// zapLogger implement logr.Logger
var _ logr.Logger = &zapLogger{}

// Enabled should always return true
func (l *zapLogger) Enabled() bool {
	return true
}

// Info write message to error level log
func (l *zapLogger) Info(msg string, keysAndVals ...interface{}) {
	entry := zapcore.Entry{
		Time:    timeNow(),
		Message: msg,
	}
	checkedEntry := l.l.Core().Check(entry, nil)
	checkedEntry.Write(l.handleFields(keysAndVals)...)
}

// dPanic write message to DPanicLevel level log
// we need implement this because unit test case need stub time.Now
// otherwise the ts field always changed
func (l *zapLogger) dPanic(msg string) {
	entry := zapcore.Entry{
		Level:   zapcore.DPanicLevel,
		Time:    timeNow(),
		Message: msg,
	}
	checkedEntry := l.l.Core().Check(entry, nil)
	checkedEntry.Write(zap.Int("v", l.lvl))
}

// handleFields converts a bunch of arbitrary key-value pairs into Zap fields.  It takes
// additional pre-converted Zap fields, for use with automatically attached fields, like
// `error`.
func (l *zapLogger) handleFields(args []interface{}, additional ...zap.Field) []zap.Field {
	// a slightly modified version of zap.SugaredLogger.sweetenFields
	if len(args) == 0 {
		// fast-return if we have no suggared fields.
		return append(additional, zap.Int("v", l.lvl))
	}

	// unlike Zap, we can be pretty sure users aren't passing structured
	// fields (since logr has no concept of that), so guess that we need a
	// little less space.
	fields := make([]zap.Field, 0, len(args)/2+len(additional)+1)
	fields = append(fields, zap.Int("v", l.lvl))
	for i := 0; i < len(args)-1; i += 2 {
		// check just in case for strongly-typed Zap fields, which is illegal (since
		// it breaks implementation agnosticism), so we can give a better error message.
		if _, ok := args[i].(zap.Field); ok {
			l.dPanic("strongly-typed Zap Field passed to logr")
			break
		}

		// process a key-value pair,
		// ensuring that the key is a string
		key, val := args[i], args[i+1]
		keyStr, isString := key.(string)
		if !isString {
			// if the key isn't a string, stop logging
			l.dPanic("non-string key argument passed to logging, ignoring all later arguments")
			break
		}

		fields = append(fields, zap.Any(keyStr, val))
	}

	return append(fields, additional...)
}

// Error write log message to error level
func (l *zapLogger) Error(err error, msg string, keysAndVals ...interface{}) {
	entry := zapcore.Entry{
		Level:   zapcore.ErrorLevel,
		Time:    timeNow(),
		Message: msg,
	}
	checkedEntry := l.l.Core().Check(entry, nil)
	checkedEntry.Write(l.handleFields(keysAndVals, handleError(err))...)
}

func handleError(err error) zap.Field {
	return zap.NamedError("err", err)
}

// V return info logr.Logger  with specified level
func (l *zapLogger) V(level int) logr.Logger {
	return &zapLogger{
		lvl: l.lvl + level,
		l:   l.l,
	}
}

// WithValues return logr.Logger with some keys And Values
func (l *zapLogger) WithValues(keysAndValues ...interface{}) logr.Logger {
	l.l = l.l.With(l.handleFields(keysAndValues)...)
	return l
}

// WithName return logger Named with specified name
func (l *zapLogger) WithName(name string) logr.Logger {
	l.l = l.l.Named(name)
	return l
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	//"runtime"
	//"github.com/intel-hpdd/go-lustre/llapi"
	"path"
	"path/filepath"
)

const LustreNoStripeSize = 10 * 1000000000
const Lustre5StripeSize = 100 * 1000000000
const Lustre10StripeSize = 1000 * 1000000000

type LustreDatastore struct {
	id             string
	mountPath      string
	canWrite       bool
	skipFilesNewer int
	skipFilesOlder int
	MDSNum         int
}

func (l LustreDatastore) GetId() string {
	return l.id
}

func (l LustreDatastore) GetMountPath() string {
	return l.mountPath
}

func (l LustreDatastore) GetSkipFilesNewer() int {
	return l.skipFilesNewer
}

func (l LustreDatastore) GetSkipFilesOlder() int {
	return l.skipFilesOlder
}

func (l LustreDatastore) GetLocalFilepath(filePath string) string {
	return path.Join(l.mountPath, filePath)
}

func (l LustreDatastore) GetMetadata(filePath string) (os.FileInfo, error) {
	return os.Lstat(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Readlink(filePath string) (string, error) {
	return os.Readlink(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Symlink(pointTo, filePath string) error {
	return os.Symlink(pointTo, path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Remove(filePath string) error {
	return os.Remove(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) RemoveAll(filePath string) error {
	return rmDir(path.Join(l.mountPath, filePath))
}

func rmDir(absPath string) error {
	logger.Debugf("Deleting folder: %v", absPath)
	dirsChan := make(chan string, 1000001)
	filesChan := make(chan string, 1000001)

	var wg sync.WaitGroup

	cmdDirName := "lfs"
	cmdDirArgs := []string{"find", absPath, "-maxdepth", "1", "-type", "d"}

	cmdDir := exec.Command(cmdDirName, cmdDirArgs...)
	cmdDirReader, err := cmdDir.StdoutPipe()
	if err != nil {
		logger.Errorf("Error running lustre find: %v", err)
		return err
	}

	scannerDir := bufio.NewScanner(cmdDirReader)

	err = cmdDir.Start()
	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		defer close(dirsChan)
		defer wg.Done()

		for scannerDir.Scan() {
			newDir := scannerDir.Text()
			if newDir != absPath {
				dirsChan <- newDir
			}
		}
		cmdDir.Wait()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for dir := range dirsChan {
			err := rmDir(dir)
			if err != nil {
				logger.Errorf("Error processing folder %s: %v", dir, err)
			}
		}
	}()

	cmdFileName := "lfs"
	cmdFileArgs := []string{"find", absPath, "-maxdepth", "1", "!", "-type", "d"}

	cmdFile := exec.Command(cmdFileName, cmdFileArgs...)
	cmdFileReader, err := cmdFile.StdoutPipe()
	if err != nil {
		logger.Errorf("Error running lustre find: %v", err)
		return err
	}

	scannerFile := bufio.NewScanner(cmdFileReader)

	err = cmdFile.Start()
	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		defer close(filesChan)
		defer wg.Done()

		for scannerFile.Scan() {
			newFile := scannerFile.Text()
			filesChan <- newFile
		}
		cmdFile.Wait()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for file := range filesChan {
			os.Remove(file)
			atomic.AddUint64(&FilesRemovedCount, 1)
		}
	}()

	wg.Wait()

	os.Remove(absPath)

	logger.Debugf("Successfully deleted folder: %v", absPath)
	return nil
}

func (l LustreDatastore) Open(filePath string) (io.ReadCloser, error) {
	return os.Open(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Create(filePath string, meta os.FileInfo) (io.WriteCloser, error) {
	if meta.Size() > LustreNoStripeSize {
		cmdName := "/usr/bin/lfs"
		var cmdArgs []string
		if meta.Size() < Lustre5StripeSize {
			cmdArgs = []string{"setstripe", "-c", "5", path.Join(l.mountPath, filePath)}
			logger.Debugf("Striping %s across 5", filePath)
		} else if meta.Size() < Lustre10StripeSize {
			cmdArgs = []string{"setstripe", "-c", "10", path.Join(l.mountPath, filePath)}
			logger.Debugf("Striping %s across 10", filePath)
		} else {
			cmdArgs = []string{"setstripe", "-c", "50", path.Join(l.mountPath, filePath)}
			logger.Debugf("Striping %s across 50", filePath)
		}

		_, err := exec.Command(cmdName, cmdArgs...).Output()
		if err != nil {
			logger.Errorf("Error creating striped file: %v", err)
			return nil, err
		}
	}
	return os.OpenFile(path.Join(l.mountPath, filePath), os.O_CREATE|os.O_RDWR|os.O_TRUNC, meta.Mode())
}

func (l LustreDatastore) Lchown(filePath string, uid, gid int) error {
	return os.Lchown(path.Join(l.mountPath, filePath), uid, gid)
}

func (l LustreDatastore) Chmod(filePath string, perm os.FileMode) error {
	return os.Chmod(path.Join(l.mountPath, filePath), perm)
}

func (l LustreDatastore) Mkdir(dirPath string, perm os.FileMode) error {
	if l.MDSNum == 0 || path.Dir(dirPath) != "." {
		return os.Mkdir(path.Join(l.mountPath, dirPath), perm)
	} else {
		cmdName := "/usr/bin/lfs"
		// cmdArgs := []string{"setdirstripe", "-i", strconv.Itoa(rand.Intn(l.MDSNum)), path.Join(l.mountPath, dirPath)}
		cmdArgs := []string{"setdirstripe", "-i", strconv.Itoa(l.MDSNum), path.Join(l.mountPath, dirPath)}
		_, err := exec.Command(cmdName, cmdArgs...).Output()
		if err != nil {
			return err
		}
		err = os.Chmod(path.Join(l.mountPath, dirPath), perm)
		return err
	}
}

func (l LustreDatastore) Chtimes(dirPath string, atime time.Time, mtime time.Time) error {
	return os.Chtimes(path.Join(l.mountPath, dirPath), atime, mtime)
}

func (l LustreDatastore) ListDir(dirPath string, listFiles bool) (chan []string, error) {
	outchan := make(chan []string)

	curDir := path.Join(l.mountPath, dirPath)

	cmdName := "/usr/bin/lfs"
	cmdArgs := []string{"find", curDir, "-maxdepth", "1", "!", "-type", "d"}
	if !listFiles {
		cmdArgs = []string{"find", curDir, "-maxdepth", "1", "-type", "d"}
	}

	var cmd *exec.Cmd
	var scanner *bufio.Scanner
	ran := false

	for !ran {
		cmd = exec.Command(cmdName, cmdArgs...)

		cmdReader, err := cmd.StdoutPipe()
		if err != nil {
			return nil, err
		}
		scanner = bufio.NewScanner(cmdReader)

		err = cmd.Start()
		if err != nil {
			cmd.Wait()
			logger.Debugf("Error running lustre find: %v", err)
			if err.Error() != "fork/exec /usr/bin/lfs: errno 513" {
				return nil, err
			}
		}
		ran = true
	}

	go func(outchan chan []string) {
		defer close(outchan)
		defer cmd.Wait()
		if !listFiles {
			for scanner.Scan() {
				folder := scanner.Text()
				if folder != curDir {
					rel, err := filepath.Rel(l.mountPath, folder)
					if err != nil {
						logger.Errorf("Error resolving folder %s: %v", folder, err)
						continue
					}
					outchan <- []string{rel}
				}
			}
		} else {
			var filesBuf []string
			for scanner.Scan() {
				if len(filesBuf) == FileChunks {
					outchan <- filesBuf
					filesBuf = nil
				}

				file := scanner.Text()
				rel, err := filepath.Rel(l.mountPath, file)
				if err != nil {
					logger.Errorf("Error resolving file %s: %v", file, err)
					continue
				}

				filesBuf = append(filesBuf, rel)
			}
			if len(filesBuf) > 0 {
				outchan <- filesBuf
			}
		}
	}(outchan)

	return outchan, nil
}
<commit_msg>Debugging for error files resolving<commit_after>package main

import (
	"bufio"
	"io"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	//"runtime"
	//"github.com/intel-hpdd/go-lustre/llapi"
	"path"
	"path/filepath"
)

const LustreNoStripeSize = 10 * 1000000000
const Lustre5StripeSize = 100 * 1000000000
const Lustre10StripeSize = 1000 * 1000000000

type LustreDatastore struct {
	id             string
	mountPath      string
	canWrite       bool
	skipFilesNewer int
	skipFilesOlder int
	MDSNum         int
}

func (l LustreDatastore) GetId() string {
	return l.id
}

func (l LustreDatastore) GetMountPath() string {
	return l.mountPath
}

func (l LustreDatastore) GetSkipFilesNewer() int {
	return l.skipFilesNewer
}

func (l LustreDatastore) GetSkipFilesOlder() int {
	return l.skipFilesOlder
}

func (l LustreDatastore) GetLocalFilepath(filePath string) string {
	return path.Join(l.mountPath, filePath)
}

func (l LustreDatastore) GetMetadata(filePath string) (os.FileInfo, error) {
	return os.Lstat(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Readlink(filePath string) (string, error) {
	return os.Readlink(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Symlink(pointTo, filePath string) error {
	return os.Symlink(pointTo, path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Remove(filePath string) error {
	return os.Remove(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) RemoveAll(filePath string) error {
	return rmDir(path.Join(l.mountPath, filePath))
}

func rmDir(absPath string) error {
	logger.Debugf("Deleting folder: %v", absPath)
	dirsChan := make(chan string, 1000001)
	filesChan := make(chan string, 1000001)

	var wg sync.WaitGroup

	cmdDirName := "lfs"
	cmdDirArgs := []string{"find", absPath, "-maxdepth", "1", "-type", "d"}

	cmdDir := exec.Command(cmdDirName, cmdDirArgs...)
	cmdDirReader, err := cmdDir.StdoutPipe()
	if err != nil {
		logger.Errorf("Error running lustre find: %v", err)
		return err
	}

	scannerDir := bufio.NewScanner(cmdDirReader)

	err = cmdDir.Start()
	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		defer close(dirsChan)
		defer wg.Done()

		for scannerDir.Scan() {
			newDir := scannerDir.Text()
			if newDir != absPath {
				dirsChan <- newDir
			}
		}
		cmdDir.Wait()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for dir := range dirsChan {
			err := rmDir(dir)
			if err != nil {
				logger.Errorf("Error processing folder %s: %v", dir, err)
			}
		}
	}()

	cmdFileName := "lfs"
	cmdFileArgs := []string{"find", absPath, "-maxdepth", "1", "!", "-type", "d"}

	cmdFile := exec.Command(cmdFileName, cmdFileArgs...)
	cmdFileReader, err := cmdFile.StdoutPipe()
	if err != nil {
		logger.Errorf("Error running lustre find: %v", err)
		return err
	}

	scannerFile := bufio.NewScanner(cmdFileReader)

	err = cmdFile.Start()
	if err != nil {
		return err
	}

	wg.Add(1)
	go func() {
		defer close(filesChan)
		defer wg.Done()

		for scannerFile.Scan() {
			newFile := scannerFile.Text()
			filesChan <- newFile
		}
		cmdFile.Wait()
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		for file := range filesChan {
			os.Remove(file)
			atomic.AddUint64(&FilesRemovedCount, 1)
		}
	}()

	wg.Wait()

	os.Remove(absPath)

	logger.Debugf("Successfully deleted folder: %v", absPath)
	return nil
}

func (l LustreDatastore) Open(filePath string) (io.ReadCloser, error) {
	return os.Open(path.Join(l.mountPath, filePath))
}

func (l LustreDatastore) Create(filePath string, meta os.FileInfo) (io.WriteCloser, error) {
	if meta.Size() > LustreNoStripeSize {
		cmdName := "/usr/bin/lfs"
		var cmdArgs []string
		if meta.Size() < Lustre5StripeSize {
			cmdArgs = []string{"setstripe", "-c", "5", path.Join(l.mountPath, filePath)}
			logger.Debugf("Striping %s across 5", filePath)
		} else if meta.Size() < Lustre10StripeSize {
			cmdArgs = []string{"setstripe", "-c", "10", path.Join(l.mountPath, filePath)}
			logger.Debugf("Striping %s across 10", filePath)
		} else {
			cmdArgs = []string{"setstripe", "-c", "50", path.Join(l.mountPath, filePath)}
			logger.Debugf("Striping %s across 50", filePath)
		}

		_, err := exec.Command(cmdName, cmdArgs...).Output()
		if err != nil {
			logger.Errorf("Error creating striped file: %v", err)
			return nil, err
		}
	}
	return os.OpenFile(path.Join(l.mountPath, filePath), os.O_CREATE|os.O_RDWR|os.O_TRUNC, meta.Mode())
}

func (l LustreDatastore) Lchown(filePath string, uid, gid int) error {
	return os.Lchown(path.Join(l.mountPath, filePath), uid, gid)
}

func (l LustreDatastore) Chmod(filePath string, perm os.FileMode) error {
	return os.Chmod(path.Join(l.mountPath, filePath), perm)
}

func (l LustreDatastore) Mkdir(dirPath string, perm os.FileMode) error {
	if l.MDSNum == 0 || path.Dir(dirPath) != "." {
		return os.Mkdir(path.Join(l.mountPath, dirPath), perm)
	} else {
		cmdName := "/usr/bin/lfs"
		// cmdArgs := []string{"setdirstripe", "-i", strconv.Itoa(rand.Intn(l.MDSNum)), path.Join(l.mountPath, dirPath)}
		cmdArgs := []string{"setdirstripe", "-i", strconv.Itoa(l.MDSNum), path.Join(l.mountPath, dirPath)}
		_, err := exec.Command(cmdName, cmdArgs...).Output()
		if err != nil {
			return err
		}
		err = os.Chmod(path.Join(l.mountPath, dirPath), perm)
		return err
	}
}

func (l LustreDatastore) Chtimes(dirPath string, atime time.Time, mtime time.Time) error {
	return os.Chtimes(path.Join(l.mountPath, dirPath), atime, mtime)
}

func (l LustreDatastore) ListDir(dirPath string, listFiles bool) (chan []string, error) {
	outchan := make(chan []string)

	curDir := path.Join(l.mountPath, dirPath)

	cmdName := "/usr/bin/lfs"
	cmdArgs := []string{"find", curDir, "-maxdepth", "1", "!", "-type", "d"}
	if !listFiles {
		cmdArgs = []string{"find", curDir, "-maxdepth", "1", "-type", "d"}
	}

	var cmd *exec.Cmd
	var scanner *bufio.Scanner
	ran := false

	for !ran {
		cmd = exec.Command(cmdName, cmdArgs...)

		cmdReader, err := cmd.StdoutPipe()
		if err != nil {
			return nil, err
		}
		scanner = bufio.NewScanner(cmdReader)

		err = cmd.Start()
		if err != nil {
			cmd.Wait()
			logger.Debugf("Error running lustre find: %v", err)
			if err.Error() != "fork/exec /usr/bin/lfs: errno 513" {
				return nil, err
			}
		}
		ran = true
	}

	go func(outchan chan []string) {
		defer close(outchan)
		defer cmd.Wait()
		if !listFiles {
			for scanner.Scan() {
				folder := scanner.Text()
				if folder != curDir {
					rel, err := filepath.Rel(l.mountPath, folder)
					if err != nil {
						logger.Errorf("Error resolving folder %s in %s: %v", folder, curDir, err)
						continue
					}
					outchan <- []string{rel}
				}
			}
		} else {
			var filesBuf []string
			for scanner.Scan() {
				if len(filesBuf) == FileChunks {
					outchan <- filesBuf
					filesBuf = nil
				}

				file := scanner.Text()
				rel, err := filepath.Rel(l.mountPath, file)
				if err != nil {
					logger.Errorf("Error resolving file %s in %s: %v", file, curDir, err)
					continue
				}

				filesBuf = append(filesBuf, rel)
			}
			if len(filesBuf) > 0 {
				outchan <- filesBuf
			}
		}
	}(outchan)

	return outchan, nil
}
<|endoftext|>