<commit_before>package qemu

import (
	"context"
	"fmt"
	"log"
	"path/filepath"
	"strings"

	"github.com/hashicorp/go-version"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

// stepRun runs the virtual machine
type stepRun struct {
	BootDrive string
	Message   string
}

type qemuArgsTemplateData struct {
	HTTPIP      string
	HTTPPort    int
	HTTPDir     string
	OutputDir   string
	Name        string
	SSHHostPort int
}

func (s *stepRun) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say(s.Message)

	command, err := getCommandArgs(s.BootDrive, state)
	if err != nil {
		err := fmt.Errorf("Error processing QemuArgs: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if err := driver.Qemu(command...); err != nil {
		err := fmt.Errorf("Error launching VM: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepRun) Cleanup(state multistep.StateBag) {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	if err := driver.Stop(); err != nil {
		ui.Error(fmt.Sprintf("Error shutting down VM: %s", err))
	}
}

func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) {
	config := state.Get("config").(*Config)
	isoPath := state.Get("iso_path").(string)
	vncIP := config.VNCBindAddress
	vncPort := state.Get("vnc_port").(int)
	ui := state.Get("ui").(packer.Ui)
	driver := state.Get("driver").(Driver)
	vmName := config.VMName
	imgPath := filepath.Join(config.OutputDir, vmName)

	defaultArgs := make(map[string]interface{})
	var deviceArgs []string
	var driveArgs []string
	var commHostPort int
	var vnc string

	if !config.VNCUsePassword {
		vnc = fmt.Sprintf("%s:%d", vncIP, vncPort-5900)
	} else {
		vnc = fmt.Sprintf("%s:%d,password", vncIP, vncPort-5900)
	}

	if config.QMPEnable {
		defaultArgs["-qmp"] = fmt.Sprintf("unix:%s,server,nowait", config.QMPSocketPath)
	}

	defaultArgs["-name"] = vmName
	defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType)

	if config.NetBridge == "" {
		if config.CommConfig.Comm.Type != "none" {
			commHostPort = state.Get("commHostPort").(int)
			defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", commHostPort, config.CommConfig.Comm.Port())
		} else {
			defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0")
		}
	} else {
		defaultArgs["-netdev"] = fmt.Sprintf("bridge,id=user.0,br=%s", config.NetBridge)
	}

	rawVersion, err := driver.Version()
	if err != nil {
		return nil, err
	}
	qemuVersion, err := version.NewVersion(rawVersion)
	if err != nil {
		return nil, err
	}
	v2 := version.Must(version.NewVersion("2.0"))

	if qemuVersion.GreaterThanOrEqual(v2) {
		if config.DiskInterface == "virtio-scsi" {
			if config.DiskImage {
				deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0", "scsi-hd,bus=scsi0.0,drive=drive0")
				driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive0,cache=%s,discard=%s,format=%s", imgPath, config.DiskCache, config.DiskDiscard, config.Format)
				if config.DetectZeroes != "off" {
					driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
				}
				driveArgs = append(driveArgs, driveArgumentString)
			} else {
				deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0")
				diskFullPaths := state.Get("qemu_disk_paths").([]string)
				for i, diskFullPath := range diskFullPaths {
					deviceArgs = append(deviceArgs, fmt.Sprintf("scsi-hd,bus=scsi0.0,drive=drive%d", i))
					driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive%d,cache=%s,discard=%s,format=%s", diskFullPath, i, config.DiskCache, config.DiskDiscard, config.Format)
					if config.DetectZeroes != "off" {
						driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
					}
					driveArgs = append(driveArgs, driveArgumentString)
				}
			}
		} else {
			if config.DiskImage {
				driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
				if config.DetectZeroes != "off" {
					driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
				}
				driveArgs = append(driveArgs, driveArgumentString)
			} else {
				diskFullPaths := state.Get("qemu_disk_paths").([]string)
				for _, diskFullPath := range diskFullPaths {
					driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", diskFullPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
					if config.DetectZeroes != "off" {
						driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
					}
					driveArgs = append(driveArgs, driveArgumentString)
				}
			}
		}
	} else {
		driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,cache=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.Format))
	}
	deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice))

	if config.Headless == true {
		vncPortRaw, vncPortOk := state.GetOk("vnc_port")
		vncPass := state.Get("vnc_password")

		if vncPortOk && vncPass != nil && len(vncPass.(string)) > 0 {
			vncPort := vncPortRaw.(int)

			ui.Message(fmt.Sprintf(
				"The VM will be run headless, without a GUI. If you want to\n"+
					"view the screen of the VM, connect via VNC to vnc://%s:%d\n"+
					"with the password: %s", vncIP, vncPort, vncPass))
		} else if vncPortOk {
			vncPort := vncPortRaw.(int)

			ui.Message(fmt.Sprintf(
				"The VM will be run headless, without a GUI. If you want to\n"+
					"view the screen of the VM, connect via VNC without a password to\n"+
					"vnc://%s:%d", vncIP, vncPort))
		} else {
			ui.Message("The VM will be run headless, without a GUI, as configured.\n" +
				"If the run isn't succeeding as you expect, please enable the GUI\n" +
				"to inspect the progress of the build.")
		}
	} else {
		if qemuVersion.GreaterThanOrEqual(v2) {
			if len(config.Display) > 0 {
				if config.Display != "none" {
					defaultArgs["-display"] = config.Display
				}
			} else if !config.UseDefaultDisplay {
				defaultArgs["-display"] = "gtk"
			}
		} else {
			ui.Message("WARNING: The version of qemu  on your host doesn't support display mode.\n" +
				"The display parameter will be ignored.")
		}
	}

	if !config.DiskImage {
		if config.CDROMInterface == "" {
			defaultArgs["-cdrom"] = isoPath
		} else if config.CDROMInterface == "virtio-scsi" {
			driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=none,id=cdrom,media=cdrom", isoPath))
			deviceArgs = append(deviceArgs, "virtio-scsi-device", "scsi-cd,drive=cdrom")
		} else {
			driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,id=cdrom,media=cdrom", isoPath, config.CDROMInterface))
		}
	}

	defaultArgs["-device"] = deviceArgs
	defaultArgs["-drive"] = driveArgs

	defaultArgs["-boot"] = bootDrive
	defaultArgs["-m"] = fmt.Sprintf("%dM", config.MemorySize)
	if config.CpuCount > 1 {
		defaultArgs["-smp"] = fmt.Sprintf("cpus=%d,sockets=%d", config.CpuCount, config.CpuCount)
	}
	defaultArgs["-vnc"] = vnc

	// Append the accelerator to the machine type if it is specified
	if config.Accelerator != "none" {
		defaultArgs["-machine"] = fmt.Sprintf("%s,accel=%s", defaultArgs["-machine"], config.Accelerator)
	} else {
		ui.Message("WARNING: The VM will be started with no hardware acceleration.\n" +
			"The installation may take considerably longer to finish.\n")
	}

	// Determine if we have a floppy disk to attach
	if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
		defaultArgs["-fda"] = floppyPathRaw.(string)
	} else {
		log.Println("Qemu Builder has no floppy files, not attaching a floppy.")
	}

	inArgs := make(map[string][]string)
	if len(config.QemuArgs) > 0 {
		ui.Say("Overriding defaults Qemu arguments with QemuArgs...")

		httpIp := state.Get("http_ip").(string)
		httpPort := state.Get("http_port").(int)
		ictx := config.ctx
		if config.CommConfig.Comm.Type != "none" {
			ictx.Data = qemuArgsTemplateData{
				httpIp,
				httpPort,
				config.HTTPDir,
				config.OutputDir,
				config.VMName,
				commHostPort,
			}
		} else {
			ictx.Data = qemuArgsTemplateData{
				HTTPIP:    httpIp,
				HTTPPort:  httpPort,
				HTTPDir:   config.HTTPDir,
				OutputDir: config.OutputDir,
				Name:      config.VMName,
			}
		}
		newQemuArgs, err := processArgs(config.QemuArgs, &ictx)
		if err != nil {
			return nil, err
		}

		// because qemu supports multiple appearances of the same
		// switch, just different values, each key in the args hash
		// will have an array of string values
		for _, qemuArgs := range newQemuArgs {
			key := qemuArgs[0]
			val := strings.Join(qemuArgs[1:], "")
			if _, ok := inArgs[key]; !ok {
				inArgs[key] = make([]string, 0)
			}
			if len(val) > 0 {
				inArgs[key] = append(inArgs[key], val)
			}
		}
	}

	// get any remaining missing default args from the default settings
	for key := range defaultArgs {
		if _, ok := inArgs[key]; !ok {
			arg := make([]string, 1)
			switch defaultArgs[key].(type) {
			case string:
				arg[0] = defaultArgs[key].(string)
			case []string:
				arg = defaultArgs[key].([]string)
			}
			inArgs[key] = arg
		}
	}

	// Check if we are missing the netDevice #6804
	if x, ok := inArgs["-device"]; ok {
		if !strings.Contains(strings.Join(x, ""), config.NetDevice) {
			inArgs["-device"] = append(inArgs["-device"], fmt.Sprintf("%s,netdev=user.0", config.NetDevice))
		}
	}

	// Flatten to array of strings
	outArgs := make([]string, 0)
	for key, values := range inArgs {
		if len(values) > 0 {
			for idx := range values {
				outArgs = append(outArgs, key, values[idx])
			}
		} else {
			outArgs = append(outArgs, key)
		}
	}

	return outArgs, nil
}

func processArgs(args [][]string, ctx *interpolate.Context) ([][]string, error) {
	var err error

	if args == nil {
		return make([][]string, 0), err
	}

	newArgs := make([][]string, len(args))
	for argsIdx, rowArgs := range args {
		parms := make([]string, len(rowArgs))
		newArgs[argsIdx] = parms
		for i, parm := range rowArgs {
			parms[i], err = interpolate.Render(parm, ctx)
			if err != nil {
				return nil, err
			}
		}
	}

	return newArgs, err
}
<commit_msg>qemu vnc: hardcoded lowerbound leaves negative ports<commit_after>package qemu

import (
	"context"
	"fmt"
	"log"
	"path/filepath"
	"strings"

	"github.com/hashicorp/go-version"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

// stepRun runs the virtual machine
type stepRun struct {
	BootDrive string
	Message   string
}

type qemuArgsTemplateData struct {
	HTTPIP      string
	HTTPPort    int
	HTTPDir     string
	OutputDir   string
	Name        string
	SSHHostPort int
}

func (s *stepRun) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say(s.Message)

	command, err := getCommandArgs(s.BootDrive, state)
	if err != nil {
		err := fmt.Errorf("Error processing QemuArgs: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if err := driver.Qemu(command...); err != nil {
		err := fmt.Errorf("Error launching VM: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepRun) Cleanup(state multistep.StateBag) {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	if err := driver.Stop(); err != nil {
		ui.Error(fmt.Sprintf("Error shutting down VM: %s", err))
	}
}

func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) {
	config := state.Get("config").(*Config)
	isoPath := state.Get("iso_path").(string)
	vncIP := config.VNCBindAddress
	vncPort := state.Get("vnc_port").(int)
	ui := state.Get("ui").(packer.Ui)
	driver := state.Get("driver").(Driver)
	vmName := config.VMName
	imgPath := filepath.Join(config.OutputDir, vmName)

	defaultArgs := make(map[string]interface{})
	var deviceArgs []string
	var driveArgs []string
	var commHostPort int
	var vnc string

	if !config.VNCUsePassword {
		vnc = fmt.Sprintf("%s:%d", vncIP, vncPort-config.VNCPortMin)
	} else {
		vnc = fmt.Sprintf("%s:%d,password", vncIP, vncPort-config.VNCPortMin)
	}

	if config.QMPEnable {
		defaultArgs["-qmp"] = fmt.Sprintf("unix:%s,server,nowait", config.QMPSocketPath)
	}

	defaultArgs["-name"] = vmName
	defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType)

	if config.NetBridge == "" {
		if config.CommConfig.Comm.Type != "none" {
			commHostPort = state.Get("commHostPort").(int)
			defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", commHostPort, config.CommConfig.Comm.Port())
		} else {
			defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0")
		}
	} else {
		defaultArgs["-netdev"] = fmt.Sprintf("bridge,id=user.0,br=%s", config.NetBridge)
	}

	rawVersion, err := driver.Version()
	if err != nil {
		return nil, err
	}
	qemuVersion, err := version.NewVersion(rawVersion)
	if err != nil {
		return nil, err
	}
	v2 := version.Must(version.NewVersion("2.0"))

	if qemuVersion.GreaterThanOrEqual(v2) {
		if config.DiskInterface == "virtio-scsi" {
			if config.DiskImage {
				deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0", "scsi-hd,bus=scsi0.0,drive=drive0")
				driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive0,cache=%s,discard=%s,format=%s", imgPath, config.DiskCache, config.DiskDiscard, config.Format)
				if config.DetectZeroes != "off" {
					driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
				}
				driveArgs = append(driveArgs, driveArgumentString)
			} else {
				deviceArgs = append(deviceArgs, "virtio-scsi-pci,id=scsi0")
				diskFullPaths := state.Get("qemu_disk_paths").([]string)
				for i, diskFullPath := range diskFullPaths {
					deviceArgs = append(deviceArgs, fmt.Sprintf("scsi-hd,bus=scsi0.0,drive=drive%d", i))
					driveArgumentString := fmt.Sprintf("if=none,file=%s,id=drive%d,cache=%s,discard=%s,format=%s", diskFullPath, i, config.DiskCache, config.DiskDiscard, config.Format)
					if config.DetectZeroes != "off" {
						driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
					}
					driveArgs = append(driveArgs, driveArgumentString)
				}
			}
		} else {
			if config.DiskImage {
				driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
				if config.DetectZeroes != "off" {
					driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
				}
				driveArgs = append(driveArgs, driveArgumentString)
			} else {
				diskFullPaths := state.Get("qemu_disk_paths").([]string)
				for _, diskFullPath := range diskFullPaths {
					driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", diskFullPath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format)
					if config.DetectZeroes != "off" {
						driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes)
					}
					driveArgs = append(driveArgs, driveArgumentString)
				}
			}
		}
	} else {
		driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,cache=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.Format))
	}
	deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice))

	if config.Headless == true {
		vncPortRaw, vncPortOk := state.GetOk("vnc_port")
		vncPass := state.Get("vnc_password")

		if vncPortOk && vncPass != nil && len(vncPass.(string)) > 0 {
			vncPort := vncPortRaw.(int)

			ui.Message(fmt.Sprintf(
				"The VM will be run headless, without a GUI. If you want to\n"+
					"view the screen of the VM, connect via VNC to vnc://%s:%d\n"+
					"with the password: %s", vncIP, vncPort, vncPass))
		} else if vncPortOk {
			vncPort := vncPortRaw.(int)

			ui.Message(fmt.Sprintf(
				"The VM will be run headless, without a GUI. If you want to\n"+
					"view the screen of the VM, connect via VNC without a password to\n"+
					"vnc://%s:%d", vncIP, vncPort))
		} else {
			ui.Message("The VM will be run headless, without a GUI, as configured.\n" +
				"If the run isn't succeeding as you expect, please enable the GUI\n" +
				"to inspect the progress of the build.")
		}
	} else {
		if qemuVersion.GreaterThanOrEqual(v2) {
			if len(config.Display) > 0 {
				if config.Display != "none" {
					defaultArgs["-display"] = config.Display
				}
			} else if !config.UseDefaultDisplay {
				defaultArgs["-display"] = "gtk"
			}
		} else {
			ui.Message("WARNING: The version of qemu  on your host doesn't support display mode.\n" +
				"The display parameter will be ignored.")
		}
	}

	if !config.DiskImage {
		if config.CDROMInterface == "" {
			defaultArgs["-cdrom"] = isoPath
		} else if config.CDROMInterface == "virtio-scsi" {
			driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=none,id=cdrom,media=cdrom", isoPath))
			deviceArgs = append(deviceArgs, "virtio-scsi-device", "scsi-cd,drive=cdrom")
		} else {
			driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,id=cdrom,media=cdrom", isoPath, config.CDROMInterface))
		}
	}

	defaultArgs["-device"] = deviceArgs
	defaultArgs["-drive"] = driveArgs

	defaultArgs["-boot"] = bootDrive
	defaultArgs["-m"] = fmt.Sprintf("%dM", config.MemorySize)
	if config.CpuCount > 1 {
		defaultArgs["-smp"] = fmt.Sprintf("cpus=%d,sockets=%d", config.CpuCount, config.CpuCount)
	}
	defaultArgs["-vnc"] = vnc

	// Append the accelerator to the machine type if it is specified
	if config.Accelerator != "none" {
		defaultArgs["-machine"] = fmt.Sprintf("%s,accel=%s", defaultArgs["-machine"], config.Accelerator)
	} else {
		ui.Message("WARNING: The VM will be started with no hardware acceleration.\n" +
			"The installation may take considerably longer to finish.\n")
	}

	// Determine if we have a floppy disk to attach
	if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
		defaultArgs["-fda"] = floppyPathRaw.(string)
	} else {
		log.Println("Qemu Builder has no floppy files, not attaching a floppy.")
	}

	inArgs := make(map[string][]string)
	if len(config.QemuArgs) > 0 {
		ui.Say("Overriding defaults Qemu arguments with QemuArgs...")

		httpIp := state.Get("http_ip").(string)
		httpPort := state.Get("http_port").(int)
		ictx := config.ctx
		if config.CommConfig.Comm.Type != "none" {
			ictx.Data = qemuArgsTemplateData{
				httpIp,
				httpPort,
				config.HTTPDir,
				config.OutputDir,
				config.VMName,
				commHostPort,
			}
		} else {
			ictx.Data = qemuArgsTemplateData{
				HTTPIP:    httpIp,
				HTTPPort:  httpPort,
				HTTPDir:   config.HTTPDir,
				OutputDir: config.OutputDir,
				Name:      config.VMName,
			}
		}
		newQemuArgs, err := processArgs(config.QemuArgs, &ictx)
		if err != nil {
			return nil, err
		}

		// because qemu supports multiple appearances of the same
		// switch, just different values, each key in the args hash
		// will have an array of string values
		for _, qemuArgs := range newQemuArgs {
			key := qemuArgs[0]
			val := strings.Join(qemuArgs[1:], "")
			if _, ok := inArgs[key]; !ok {
				inArgs[key] = make([]string, 0)
			}
			if len(val) > 0 {
				inArgs[key] = append(inArgs[key], val)
			}
		}
	}

	// get any remaining missing default args from the default settings
	for key := range defaultArgs {
		if _, ok := inArgs[key]; !ok {
			arg := make([]string, 1)
			switch defaultArgs[key].(type) {
			case string:
				arg[0] = defaultArgs[key].(string)
			case []string:
				arg = defaultArgs[key].([]string)
			}
			inArgs[key] = arg
		}
	}

	// Check if we are missing the netDevice #6804
	if x, ok := inArgs["-device"]; ok {
		if !strings.Contains(strings.Join(x, ""), config.NetDevice) {
			inArgs["-device"] = append(inArgs["-device"], fmt.Sprintf("%s,netdev=user.0", config.NetDevice))
		}
	}

	// Flatten to array of strings
	outArgs := make([]string, 0)
	for key, values := range inArgs {
		if len(values) > 0 {
			for idx := range values {
				outArgs = append(outArgs, key, values[idx])
			}
		} else {
			outArgs = append(outArgs, key)
		}
	}

	return outArgs, nil
}

func processArgs(args [][]string, ctx *interpolate.Context) ([][]string, error) {
	var err error

	if args == nil {
		return make([][]string, 0), err
	}

	newArgs := make([][]string, len(args))
	for argsIdx, rowArgs := range args {
		parms := make([]string, len(rowArgs))
		newArgs[argsIdx] = parms
		for i, parm := range rowArgs {
			parms[i], err = interpolate.Render(parm, ctx)
			if err != nil {
				return nil, err
			}
		}
	}

	return newArgs, err
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"testing"
)

func BenchmarkLowercaseBytesNoop(b *testing.B) {
	src := []byte("foobarbaz_lowercased_all")
	b.RunParallel(func(pb *testing.PB) {
		s := make([]byte, len(src))
		for pb.Next() {
			copy(s, src)
			lowercaseBytes(s)
		}
	})
}

func BenchmarkLowercaseBytesAll(b *testing.B) {
	src := []byte("FOOBARBAZ_UPPERCASED_ALL")
	b.RunParallel(func(pb *testing.PB) {
		s := make([]byte, len(src))
		for pb.Next() {
			copy(s, src)
			lowercaseBytes(s)
		}
	})
}

func BenchmarkLowercaseBytesMixed(b *testing.B) {
	src := []byte("Foobarbaz_Uppercased_Mix")
	b.RunParallel(func(pb *testing.PB) {
		s := make([]byte, len(src))
		for pb.Next() {
			copy(s, src)
			lowercaseBytes(s)
		}
	})
}

func BenchmarkEqualBytesStr(b *testing.B) {
	s := "foobarbaraz"
	bs := []byte(s)
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			if !EqualBytesStr(bs, s) {
				b.Fatalf("unexpected result: %q != %q", bs, s)
			}
		}
	})
}
<commit_msg>Added benchmark for AppendBytesStr<commit_after>package fasthttp

import (
	"testing"
)

func BenchmarkLowercaseBytesNoop(b *testing.B) {
	src := []byte("foobarbaz_lowercased_all")
	b.RunParallel(func(pb *testing.PB) {
		s := make([]byte, len(src))
		for pb.Next() {
			copy(s, src)
			lowercaseBytes(s)
		}
	})
}

func BenchmarkLowercaseBytesAll(b *testing.B) {
	src := []byte("FOOBARBAZ_UPPERCASED_ALL")
	b.RunParallel(func(pb *testing.PB) {
		s := make([]byte, len(src))
		for pb.Next() {
			copy(s, src)
			lowercaseBytes(s)
		}
	})
}

func BenchmarkLowercaseBytesMixed(b *testing.B) {
	src := []byte("Foobarbaz_Uppercased_Mix")
	b.RunParallel(func(pb *testing.PB) {
		s := make([]byte, len(src))
		for pb.Next() {
			copy(s, src)
			lowercaseBytes(s)
		}
	})
}

func BenchmarkEqualBytesStr(b *testing.B) {
	s := "foobarbaraz"
	bs := []byte(s)
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			if !EqualBytesStr(bs, s) {
				b.Fatalf("unexpected result: %q != %q", bs, s)
			}
		}
	})
}

func BenchmarkAppendBytesStr(b *testing.B) {
	s := "foobarbazbaraz"
	b.RunParallel(func(pb *testing.PB) {
		var dst []byte
		for pb.Next() {
			dst = AppendBytesStr(dst[:0], s)
		}
	})
}
<|endoftext|>


<commit_before>package api

import (
	"time"
)

// ImageExportPost represents the fields required to export a LXD image
//
// swagger:model
//
// API extension: images_push_relay
type ImageExportPost struct {
	// Target server URL
	// Example: https://1.2.3.4:8443
	Target string `json:"target" yaml:"target"`

	// Image receive secret
	// Example: RANDOM-STRING
	Secret string `json:"secret" yaml:"secret"`

	// Remote server certificate
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// List of aliases to set on the image
	Aliases []ImageAlias `json:"aliases" yaml:"aliases"`

	// Project name
	// Example: project1
	//
	// API extension: image_target_project
	Project string `json:"project" yaml:"project"`
}

// ImagesPost represents the fields available for a new LXD image
//
// swagger:model
type ImagesPost struct {
	ImagePut `yaml:",inline"`

	// Original filename of the image
	// Example: lxd.tar.xz
	Filename string `json:"filename" yaml:"filename"`

	// Source of the image
	Source *ImagesPostSource `json:"source" yaml:"source"`

	// Compression algorithm to use when turning an instance into an image
	// Example: gzip
	//
	// API extension: image_compression_algorithm
	CompressionAlgorithm string `json:"compression_algorithm" yaml:"compression_algorithm"`

	// Aliases to add to the image
	// Example: [{"name": "foo"}, {"name": "bar"}]
	//
	// API extension: image_create_aliases
	Aliases []ImageAlias `json:"aliases" yaml:"aliases"`
}

// ImagesPostSource represents the source of a new LXD image
//
// swagger:model
type ImagesPostSource struct {
	ImageSource `yaml:",inline"`

	// Transfer mode (push or pull)
	// Example: pull
	Mode string `json:"mode" yaml:"mode"`

	// Type of image source (instance, snapshot, image or url)
	// Example: instance
	Type string `json:"type" yaml:"type"`

	// Source URL (for type "url")
	// Example: https://some-server.com/some-directory/
	URL string `json:"url" yaml:"url"`

	// Instance name (for type "instance" or "snapshot")
	// Example: c1/snap0
	Name string `json:"name" yaml:"name"`

	// Source image fingerprint (for type "image")
	// Example: 8ae945c52bb2f2df51c923b04022312f99bbb72c356251f54fa89ea7cf1df1d0
	Fingerprint string `json:"fingerprint" yaml:"fingerprint"`

	// Source image server secret token (when downloading private images)
	// Example: RANDOM-STRING
	Secret string `json:"secret" yaml:"secret"`

	// Source project name
	// Example: project1
	//
	// API extension: image_source_project
	Project string `json:"project" yaml:"project"`
}

// ImagePut represents the modifiable fields of a LXD image
//
// swagger:model
type ImagePut struct {
	// Whether the image should auto-update when a new build is available
	// Example: true
	AutoUpdate bool `json:"auto_update" yaml:"auto_update"`

	// Descriptive properties
	// Example: {"os": "Ubuntu", "release": "focal", "variant": "cloud"}
	Properties map[string]string `json:"properties" yaml:"properties"`

	// Whether the image is available to unauthenticated users
	// Example: false
	Public bool `json:"public" yaml:"public"`

	// When the image becomes obsolete
	// Example: 2025-03-23T20:00:00-04:00
	//
	// API extension: images_expiry
	ExpiresAt time.Time `json:"expires_at" yaml:"expires_at"`

	// List of profiles to use when creating from this image (if none provided by user)
	// Example: ["default"]
	//
	// API extension: image_profiles
	Profiles []string `json:"profiles" yaml:"profiles"`
}

// Image represents a LXD image
//
// swagger:model
type Image struct {
	ImagePut `yaml:",inline"`

	// List of aliases
	Aliases []ImageAlias `json:"aliases" yaml:"aliases"`

	// Architecture
	// Example: x86_64
	Architecture string `json:"architecture" yaml:"architecture"`

	// Whether the image is an automatically cached remote image
	// Example: true
	Cached bool `json:"cached" yaml:"cached"`

	// Original filename
	// Example: 06b86454720d36b20f94e31c6812e05ec51c1b568cf3a8abd273769d213394bb.rootfs
	Filename string `json:"filename" yaml:"filename"`

	// Full SHA-256 fingerprint
	// Example: 06b86454720d36b20f94e31c6812e05ec51c1b568cf3a8abd273769d213394bb
	Fingerprint string `json:"fingerprint" yaml:"fingerprint"`

	// Size of the image in bytes
	// Example: 272237676
	Size int64 `json:"size" yaml:"size"`

	// Where the image came from
	UpdateSource *ImageSource `json:"update_source,omitempty" yaml:"update_source,omitempty"`

	// Type of image (container or virtual-machine)
	// Example: container
	//
	// API extension: image_types
	Type string `json:"type" yaml:"type"`

	// When the image was originally created
	// Example: 2021-03-23T20:00:00-04:00
	CreatedAt time.Time `json:"created_at" yaml:"created_at"`

	// Last time the image was used
	// Example: 2021-03-22T20:39:00.575185384-04:00
	LastUsedAt time.Time `json:"last_used_at" yaml:"last_used_at"`

	// When the image was added to this LXD server
	// Example: 2021-03-24T14:18:15.115036787-04:00
	UploadedAt time.Time `json:"uploaded_at" yaml:"uploaded_at"`
}

// Writable converts a full Image struct into a ImagePut struct (filters read-only fields)
func (img *Image) Writable() ImagePut {
	return img.ImagePut
}

// ImageAlias represents an alias from the alias list of a LXD image
//
// swagger:model
type ImageAlias struct {
	// Name of the alias
	// Example: ubuntu-20.04
	Name string `json:"name" yaml:"name"`

	// Description of the alias
	// Example: Our preferred Ubuntu image
	Description string `json:"description" yaml:"description"`
}

// ImageSource represents the source of a LXD image
//
// swagger:model
type ImageSource struct {
	// Source alias to download from
	// Example: focal
	Alias string `json:"alias" yaml:"alias"`

	// Source server certificate (if not trusted by system CA)
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// Source server protocol
	// Example: simplestreams
	Protocol string `json:"protocol" yaml:"protocol"`

	// URL of the source server
	// Example: https://images.linuxcontainers.org
	Server string `json:"server" yaml:"server"`

	// Type of image (container or virtual-machine)
	// Example: container
	//
	// API extension: image_types
	ImageType string `json:"image_type" yaml:"image_type"`
}

// ImageAliasesPost represents a new LXD image alias
//
// swagger:model
type ImageAliasesPost struct {
	ImageAliasesEntry `yaml:",inline"`
}

// ImageAliasesEntryPost represents the required fields to rename a LXD image alias
//
// swagger:model
type ImageAliasesEntryPost struct {
	// Alias name
	// Example: ubuntu-20.04
	Name string `json:"name" yaml:"name"`
}

// ImageAliasesEntryPut represents the modifiable fields of a LXD image alias
//
// swagger:model
type ImageAliasesEntryPut struct {
	// Alias description
	// Example: Our preferred Ubuntu image
	Description string `json:"description" yaml:"description"`

	// Target fingerprint for the alias
	// Example: 06b86454720d36b20f94e31c6812e05ec51c1b568cf3a8abd273769d213394bb
	Target string `json:"target" yaml:"target"`
}

// ImageAliasesEntry represents a LXD image alias
//
// swagger:model
type ImageAliasesEntry struct {
	ImageAliasesEntryPut `yaml:",inline"`

	// Alias name
	// Example: ubuntu-20.04
	Name string `json:"name" yaml:"name"`

	// Alias type (container or virtual-machine)
	// Example: container
	//
	// API extension: image_types
	Type string `json:"type" yaml:"type"`
}

// ImageMetadata represents LXD image metadata (used in image tarball)
//
// swagger:model
type ImageMetadata struct {
	// Architecture name
	// Example: x86_64
	Architecture string `json:"architecture" yaml:"architecture"`

	// Image creation data (as UNIX epoch)
	// Example: 1620655439
	CreationDate int64 `json:"creation_date" yaml:"creation_date"`

	// Image expiry data (as UNIX epoch)
	// Example: 1620685757
	ExpiryDate int64 `json:"expiry_date" yaml:"expiry_date"`

	// Descriptive properties
	// Example: {"os": "Ubuntu", "release": "focal", "variant": "cloud"}
	Properties map[string]string `json:"properties" yaml:"properties"`

	// Template for files in the image
	Templates map[string]*ImageMetadataTemplate `json:"templates" yaml:"templates"`
}

// ImageMetadataTemplate represents a template entry in image metadata (used in image tarball)
//
// swagger:model
type ImageMetadataTemplate struct {
	// When to trigger the template (create, copy or start)
	// Example: create
	When []string `json:"when" yaml:"when"`

	// Whether to trigger only if the file is missing
	// Example: false
	CreateOnly bool `json:"create_only" yaml:"create_only"`

	// The template itself as a valid pongo2 template
	// Example: pongo2-template
	Template string `json:"template" yaml:"template"`

	// Key/value properties to pass to the template
	// Example: {"foo": "bar"}
	Properties map[string]string `json:"properties" yaml:"properties"`
}
<commit_msg>shared/api: Add Profiles field to ImageExportPost<commit_after>package api

import (
	"time"
)

// ImageExportPost represents the fields required to export a LXD image
//
// swagger:model
//
// API extension: images_push_relay
type ImageExportPost struct {
	// Target server URL
	// Example: https://1.2.3.4:8443
	Target string `json:"target" yaml:"target"`

	// Image receive secret
	// Example: RANDOM-STRING
	Secret string `json:"secret" yaml:"secret"`

	// Remote server certificate
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// List of aliases to set on the image
	Aliases []ImageAlias `json:"aliases" yaml:"aliases"`

	// Project name
	// Example: project1
	//
	// API extension: image_target_project
	Project string `json:"project" yaml:"project"`

	// List of profiles to use
	// Example: ["default"]
	//
	// API extension: image_copy_profile
	Profiles []string `json:"profiles" yaml:"profiles"`
}

// ImagesPost represents the fields available for a new LXD image
//
// swagger:model
type ImagesPost struct {
	ImagePut `yaml:",inline"`

	// Original filename of the image
	// Example: lxd.tar.xz
	Filename string `json:"filename" yaml:"filename"`

	// Source of the image
	Source *ImagesPostSource `json:"source" yaml:"source"`

	// Compression algorithm to use when turning an instance into an image
	// Example: gzip
	//
	// API extension: image_compression_algorithm
	CompressionAlgorithm string `json:"compression_algorithm" yaml:"compression_algorithm"`

	// Aliases to add to the image
	// Example: [{"name": "foo"}, {"name": "bar"}]
	//
	// API extension: image_create_aliases
	Aliases []ImageAlias `json:"aliases" yaml:"aliases"`
}

// ImagesPostSource represents the source of a new LXD image
//
// swagger:model
type ImagesPostSource struct {
	ImageSource `yaml:",inline"`

	// Transfer mode (push or pull)
	// Example: pull
	Mode string `json:"mode" yaml:"mode"`

	// Type of image source (instance, snapshot, image or url)
	// Example: instance
	Type string `json:"type" yaml:"type"`

	// Source URL (for type "url")
	// Example: https://some-server.com/some-directory/
	URL string `json:"url" yaml:"url"`

	// Instance name (for type "instance" or "snapshot")
	// Example: c1/snap0
	Name string `json:"name" yaml:"name"`

	// Source image fingerprint (for type "image")
	// Example: 8ae945c52bb2f2df51c923b04022312f99bbb72c356251f54fa89ea7cf1df1d0
	Fingerprint string `json:"fingerprint" yaml:"fingerprint"`

	// Source image server secret token (when downloading private images)
	// Example: RANDOM-STRING
	Secret string `json:"secret" yaml:"secret"`

	// Source project name
	// Example: project1
	//
	// API extension: image_source_project
	Project string `json:"project" yaml:"project"`
}

// ImagePut represents the modifiable fields of a LXD image
//
// swagger:model
type ImagePut struct {
	// Whether the image should auto-update when a new build is available
	// Example: true
	AutoUpdate bool `json:"auto_update" yaml:"auto_update"`

	// Descriptive properties
	// Example: {"os": "Ubuntu", "release": "focal", "variant": "cloud"}
	Properties map[string]string `json:"properties" yaml:"properties"`

	// Whether the image is available to unauthenticated users
	// Example: false
	Public bool `json:"public" yaml:"public"`

	// When the image becomes obsolete
	// Example: 2025-03-23T20:00:00-04:00
	//
	// API extension: images_expiry
	ExpiresAt time.Time `json:"expires_at" yaml:"expires_at"`

	// List of profiles to use when creating from this image (if none provided by user)
	// Example: ["default"]
	//
	// API extension: image_profiles
	Profiles []string `json:"profiles" yaml:"profiles"`
}

// Image represents a LXD image
//
// swagger:model
type Image struct {
	ImagePut `yaml:",inline"`

	// List of aliases
	Aliases []ImageAlias `json:"aliases" yaml:"aliases"`

	// Architecture
	// Example: x86_64
	Architecture string `json:"architecture" yaml:"architecture"`

	// Whether the image is an automatically cached remote image
	// Example: true
	Cached bool `json:"cached" yaml:"cached"`

	// Original filename
	// Example: 06b86454720d36b20f94e31c6812e05ec51c1b568cf3a8abd273769d213394bb.rootfs
	Filename string `json:"filename" yaml:"filename"`

	// Full SHA-256 fingerprint
	// Example: 06b86454720d36b20f94e31c6812e05ec51c1b568cf3a8abd273769d213394bb
	Fingerprint string `json:"fingerprint" yaml:"fingerprint"`

	// Size of the image in bytes
	// Example: 272237676
	Size int64 `json:"size" yaml:"size"`

	// Where the image came from
	UpdateSource *ImageSource `json:"update_source,omitempty" yaml:"update_source,omitempty"`

	// Type of image (container or virtual-machine)
	// Example: container
	//
	// API extension: image_types
	Type string `json:"type" yaml:"type"`

	// When the image was originally created
	// Example: 2021-03-23T20:00:00-04:00
	CreatedAt time.Time `json:"created_at" yaml:"created_at"`

	// Last time the image was used
	// Example: 2021-03-22T20:39:00.575185384-04:00
	LastUsedAt time.Time `json:"last_used_at" yaml:"last_used_at"`

	// When the image was added to this LXD server
	// Example: 2021-03-24T14:18:15.115036787-04:00
	UploadedAt time.Time `json:"uploaded_at" yaml:"uploaded_at"`
}

// Writable converts a full Image struct into a ImagePut struct (filters read-only fields)
func (img *Image) Writable() ImagePut {
	return img.ImagePut
}

// ImageAlias represents an alias from the alias list of a LXD image
//
// swagger:model
type ImageAlias struct {
	// Name of the alias
	// Example: ubuntu-20.04
	Name string `json:"name" yaml:"name"`

	// Description of the alias
	// Example: Our preferred Ubuntu image
	Description string `json:"description" yaml:"description"`
}

// ImageSource represents the source of a LXD image
//
// swagger:model
type ImageSource struct {
	// Source alias to download from
	// Example: focal
	Alias string `json:"alias" yaml:"alias"`

	// Source server certificate (if not trusted by system CA)
	// Example: X509 PEM certificate
	Certificate string `json:"certificate" yaml:"certificate"`

	// Source server protocol
	// Example: simplestreams
	Protocol string `json:"protocol" yaml:"protocol"`

	// URL of the source server
	// Example: https://images.linuxcontainers.org
	Server string `json:"server" yaml:"server"`

	// Type of image (container or virtual-machine)
	// Example: container
	//
	// API extension: image_types
	ImageType string `json:"image_type" yaml:"image_type"`
}

// ImageAliasesPost represents a new LXD image alias
//
// swagger:model
type ImageAliasesPost struct {
	ImageAliasesEntry `yaml:",inline"`
}

// ImageAliasesEntryPost represents the required fields to rename a LXD image alias
//
// swagger:model
type ImageAliasesEntryPost struct {
	// Alias name
	// Example: ubuntu-20.04
	Name string `json:"name" yaml:"name"`
}

// ImageAliasesEntryPut represents the modifiable fields of a LXD image alias
//
// swagger:model
type ImageAliasesEntryPut struct {
	// Alias description
	// Example: Our preferred Ubuntu image
	Description string `json:"description" yaml:"description"`

	// Target fingerprint for the alias
	// Example: 06b86454720d36b20f94e31c6812e05ec51c1b568cf3a8abd273769d213394bb
	Target string `json:"target" yaml:"target"`
}

// ImageAliasesEntry represents a LXD image alias
//
// swagger:model
type ImageAliasesEntry struct {
	ImageAliasesEntryPut `yaml:",inline"`

	// Alias name
	// Example: ubuntu-20.04
	Name string `json:"name" yaml:"name"`

	// Alias type (container or virtual-machine)
	// Example: container
	//
	// API extension: image_types
	Type string `json:"type" yaml:"type"`
}

// ImageMetadata represents LXD image metadata (used in image tarball)
//
// swagger:model
type ImageMetadata struct {
	// Architecture name
	// Example: x86_64
	Architecture string `json:"architecture" yaml:"architecture"`

	// Image creation data (as UNIX epoch)
	// Example: 1620655439
	CreationDate int64 `json:"creation_date" yaml:"creation_date"`

	// Image expiry data (as UNIX epoch)
	// Example: 1620685757
	ExpiryDate int64 `json:"expiry_date" yaml:"expiry_date"`

	// Descriptive properties
	// Example: {"os": "Ubuntu", "release": "focal", "variant": "cloud"}
	Properties map[string]string `json:"properties" yaml:"properties"`

	// Template for files in the image
	Templates map[string]*ImageMetadataTemplate `json:"templates" yaml:"templates"`
}

// ImageMetadataTemplate represents a template entry in image metadata (used in image tarball)
//
// swagger:model
type ImageMetadataTemplate struct {
	// When to trigger the template (create, copy or start)
	// Example: create
	When []string `json:"when" yaml:"when"`

	// Whether to trigger only if the file is missing
	// Example: false
	CreateOnly bool `json:"create_only" yaml:"create_only"`

	// The template itself as a valid pongo2 template
	// Example: pongo2-template
	Template string `json:"template" yaml:"template"`

	// Key/value properties to pass to the template
	// Example: {"foo": "bar"}
	Properties map[string]string `json:"properties" yaml:"properties"`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/cixtor/slackapi"
)

// MonitorRealTimeMessages prints all the supported websocket events.
func MonitorRealTimeMessages(client *slackapi.SlackAPI) {
	rtm, err := client.NewRTM()

	if err != nil {
		fmt.Println("RTM error;", err)
		return
	}

	go rtm.ManageEvents()

	for msg := range rtm.Events {
		switch event := msg.Data.(type) {
		case *slackapi.HelloEvent:
			fmt.Println("hello; connection established")

		case *slackapi.PresenceChangeEvent:
			fmt.Println("presence;", event.User, "=>", event.Presence)

		case *slackapi.MessageEvent:
			fmt.Printf("message; %s@%s: %#v\n", event.User, event.Channel, event.Text)

		case *slackapi.ErrorEvent:
			fmt.Println("error;", event.Text)

		case *slackapi.ReconnectURLEvent:
			fmt.Println("reconnect;", event.URL)

		default:
			fmt.Printf("%s; %#v\n", msg.Type, msg.Data)
		}
	}
}
<commit_msg>Add example of how to stop the RTM connection programmatically<commit_after>package main

import (
	"fmt"
	"github.com/cixtor/slackapi"
)

// MonitorRealTimeMessages prints all the supported websocket events.
func MonitorRealTimeMessages(client *slackapi.SlackAPI) {
	rtm, err := client.NewRTM()

	if err != nil {
		fmt.Println("RTM error;", err)
		return
	}

	go rtm.ManageEvents()

	for msg := range rtm.Events {
		switch event := msg.Data.(type) {
		case *slackapi.HelloEvent:
			fmt.Println("hello; connection established")

		case *slackapi.PresenceChangeEvent:
			fmt.Println("presence;", event.User, "=>", event.Presence)

		case *slackapi.MessageEvent:
			if event.Text == "stop" {
				rtm.Stop()
			} else {
				fmt.Printf(
					"message; %s@%s: %#v\n",
					event.User,
					event.Channel,
					event.Text)
			}

		case *slackapi.ErrorEvent:
			fmt.Println("error;", event.Text)

		case *slackapi.ReconnectURLEvent:
			fmt.Println("reconnect;", event.URL)

		default:
			fmt.Printf("%s; %#v\n", msg.Type, msg.Data)
		}
	}

	fmt.Println("stopped")
}
<|endoftext|>


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

// Package cmd_test ensures the end to end works as intended.
package cmd_test

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/googlecodelabs/tools/claat/cmd"
)

func TestExportCodelabMemory(t *testing.T) {
	/*
		Test Plan: Ensure ExportCodelabMemory and ExportCodelab can generate identical
		artifact on valid cases with a few difference e.g. removal of "source"
		metadata field.
	*/
	tests := []struct {
		name     string
		filePath string
	}{
		{
			name:     "Multiple Steps",
			filePath: "testdata/simple-2-steps.md",
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			tmp, err := ioutil.TempDir("", "TestExportCodelabMemory-*")
			if err != nil {
				t.Fatal(err)
			}

			defer os.RemoveAll(tmp)

			testFile, err := ioutil.ReadFile(test.filePath)
			if err != nil {
				t.Fatal(err)
			}

			testContent := ioutil.NopCloser(bytes.NewReader(testFile))
			gotBytes := bytes.NewBuffer([]byte{})
			opts := cmd.CmdExportOptions{
				Expenv:   "web",
				Output:   tmp,
				Tmplout:  "devsite",
				GlobalGA: "UA-99999999-99",
			}

			// Given the same markdown input, ExportCodelabMemory should have the same output content as ExportCodelab
			wantMeta, err := cmd.ExportCodelab(test.filePath, nil, opts)
			if err != nil {
				t.Fatal(err)
			}

			generatedFolder := path.Join(tmp, wantMeta.ID)
			files, err := ioutil.ReadDir(generatedFolder)
			if err != nil {
				t.Fatal(err)
			}

			t.Logf("ExportCodelab generated files under %q", generatedFolder)
			for _, f := range files {
				t.Logf("Name: %s, IsDir: %v, Size: %d", f.Name(), f.IsDir(), f.Size())
			}

			wantBytes, err := ioutil.ReadFile(path.Join(tmp, wantMeta.ID, "index.html"))
			if err != nil {
				t.Fatal(err)
			}

			gotMeta, err := cmd.ExportCodelabMemory(testContent, gotBytes, opts)
			if err != nil {
				t.Errorf("ExportCodelabMemory got error %q, want nil", err)
			}

			// Because the In-Memory codelab doesn't have the source, when comparing, we remove Source
			wantMeta.Source = ""
			if !reflect.DeepEqual(wantMeta, gotMeta) {
				t.Errorf("ExportCodelabMemory returns metadata:\n%+v\nwant:\n%+v\n", gotMeta, wantMeta)
			}

			wantContent := filterIgnoredLinePrefix(string(wantBytes))
			gotContent := filterIgnoredLinePrefix(string(gotBytes.Bytes()))
			if diff := cmp.Diff(wantContent, gotContent); diff != "" {
				t.Errorf("ExportCodelabMemory returns diff: %s", diff)
			}
		})
	}
}

func filterIgnoredLinePrefix(content string) string {
	// ignoredLinePrefix is used because
	// 1. InMemory Export method doesn't have a file to begin with
	// 2. Some expected bugs to be resolved.
	ignoredLinePrefix := []string{
		"<meta name=\"original_source\" content=\"",
		"doc-id=\"",
	}

	lines := strings.Split(content, "\n")
	processedContent := []string{}
	for _, l := range lines {
		trimmed := strings.TrimLeft(l, " ")
		toBeIgnored := false
		for _, ignored := range ignoredLinePrefix {
			if strings.HasPrefix(trimmed, ignored) {
				toBeIgnored = true
				break
			}
		}

		if !toBeIgnored {
			processedContent = append(processedContent, l)
		}
	}

	return strings.Join(processedContent, "\n")
}
<commit_msg>Re-add last-updated as an ignored prefix in export_test<commit_after>// Copyright 2016-2019 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package cmd_test ensures the end to end works as intended.
package cmd_test

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/googlecodelabs/tools/claat/cmd"
)

func TestExportCodelabMemory(t *testing.T) {
	/*
		Test Plan: Ensure ExportCodelabMemory and ExportCodelab can generate identical
		artifact on valid cases with a few difference e.g. removal of "source"
		metadata field.
	*/
	tests := []struct {
		name     string
		filePath string
	}{
		{
			name:     "Multiple Steps",
			filePath: "testdata/simple-2-steps.md",
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			tmp, err := ioutil.TempDir("", "TestExportCodelabMemory-*")
			if err != nil {
				t.Fatal(err)
			}

			defer os.RemoveAll(tmp)

			testFile, err := ioutil.ReadFile(test.filePath)
			if err != nil {
				t.Fatal(err)
			}

			testContent := ioutil.NopCloser(bytes.NewReader(testFile))
			gotBytes := bytes.NewBuffer([]byte{})
			opts := cmd.CmdExportOptions{
				Expenv:   "web",
				Output:   tmp,
				Tmplout:  "devsite",
				GlobalGA: "UA-99999999-99",
			}

			// Given the same markdown input, ExportCodelabMemory should have the same output content as ExportCodelab
			wantMeta, err := cmd.ExportCodelab(test.filePath, nil, opts)
			if err != nil {
				t.Fatal(err)
			}

			generatedFolder := path.Join(tmp, wantMeta.ID)
			files, err := ioutil.ReadDir(generatedFolder)
			if err != nil {
				t.Fatal(err)
			}

			t.Logf("ExportCodelab generated files under %q", generatedFolder)
			for _, f := range files {
				t.Logf("Name: %s, IsDir: %v, Size: %d", f.Name(), f.IsDir(), f.Size())
			}

			wantBytes, err := ioutil.ReadFile(path.Join(tmp, wantMeta.ID, "index.html"))
			if err != nil {
				t.Fatal(err)
			}

			gotMeta, err := cmd.ExportCodelabMemory(testContent, gotBytes, opts)
			if err != nil {
				t.Errorf("ExportCodelabMemory got error %q, want nil", err)
			}

			// Because the In-Memory codelab doesn't have the source, when comparing, we remove Source
			wantMeta.Source = ""
			if !reflect.DeepEqual(wantMeta, gotMeta) {
				t.Errorf("ExportCodelabMemory returns metadata:\n%+v\nwant:\n%+v\n", gotMeta, wantMeta)
			}

			wantContent := filterIgnoredLinePrefix(string(wantBytes))
			gotContent := filterIgnoredLinePrefix(string(gotBytes.Bytes()))
			if diff := cmp.Diff(wantContent, gotContent); diff != "" {
				t.Errorf("ExportCodelabMemory returns diff: %s", diff)
			}
		})
	}
}

func filterIgnoredLinePrefix(content string) string {
	// ignoredLinePrefix is used because
	// 1. InMemory Export method doesn't have a file to begin with
	// 2. Some expected bugs to be resolved.
	ignoredLinePrefix := []string{
		"<meta name=\"original_source\" content=\"",
		"doc-id=\"",
		"last-updated=\"", // https://github.com/googlecodelabs/tools/issues/395
	}

	lines := strings.Split(content, "\n")
	processedContent := []string{}
	for _, l := range lines {
		trimmed := strings.TrimLeft(l, " ")
		toBeIgnored := false
		for _, ignored := range ignoredLinePrefix {
			if strings.HasPrefix(trimmed, ignored) {
				toBeIgnored = true
				break
			}
		}

		if !toBeIgnored {
			processedContent = append(processedContent, l)
		}
	}

	return strings.Join(processedContent, "\n")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/couchbaselabs/logg"
	ocrworker "github.com/tleyden/open-ocr"
)

// This assumes that there is a rabbit mq running
// To test it, fire up a webserver and send it a curl request

func init() {
	logg.LogKeys["OCR"] = true
	logg.LogKeys["OCR_CLIENT"] = true
	logg.LogKeys["OCR_WORKER"] = true
	logg.LogKeys["PREPROCESSOR_WORKER"] = true
	logg.LogKeys["OCR_HTTP"] = true
	logg.LogKeys["OCR_TESSERACT"] = true
}

func main() {

	var preprocessor string
	flagFunc := func() {
		flag.StringVar(
			&preprocessor,
			"preprocessor",
			"identity",
			"The preprocessor to use, eg, stroke-width-transform",
		)

	}

	rabbitConfig := ocrworker.DefaultConfigFlagsOverride(flagFunc)

	// inifinite loop, since sometimes worker <-> rabbitmq connection
	// gets broken.  see https://github.com/tleyden/open-ocr/issues/4
	for {
		logg.LogTo("PREPROCESSOR_WORKER", "Creating new Preprocessor Worker")
		preprocessorWorker, err := ocrworker.NewPreprocessorRpcWorker(
			rabbitConfig,
			preprocessor,
		)
		if err != nil {
			logg.LogPanic("Could not create rpc worker: %v", err)
		}
		preprocessorWorker.Run()

		// this happens when connection is closed
		err = <-preprocessorWorker.Done
		logg.LogError(fmt.Errorf("Preprocessor Worker failed with error: %v", err))
	}

}
<commit_msg>Change github dependency<commit_after>package main

import (
	"flag"
	"fmt"

	"github.com/couchbaselabs/logg"
	ocrworker "github.com/maiduchuy/open-ocr"
)

// This assumes that there is a rabbit mq running
// To test it, fire up a webserver and send it a curl request

func init() {
	logg.LogKeys["OCR"] = true
	logg.LogKeys["OCR_CLIENT"] = true
	logg.LogKeys["OCR_WORKER"] = true
	logg.LogKeys["PREPROCESSOR_WORKER"] = true
	logg.LogKeys["OCR_HTTP"] = true
	logg.LogKeys["OCR_TESSERACT"] = true
}

func main() {

	var preprocessor string
	flagFunc := func() {
		flag.StringVar(
			&preprocessor,
			"preprocessor",
			"identity",
			"The preprocessor to use, eg, stroke-width-transform",
		)

	}

	rabbitConfig := ocrworker.DefaultConfigFlagsOverride(flagFunc)

	// inifinite loop, since sometimes worker <-> rabbitmq connection
	// gets broken.  see https://github.com/tleyden/open-ocr/issues/4
	for {
		logg.LogTo("PREPROCESSOR_WORKER", "Creating new Preprocessor Worker")
		preprocessorWorker, err := ocrworker.NewPreprocessorRpcWorker(
			rabbitConfig,
			preprocessor,
		)
		if err != nil {
			logg.LogPanic("Could not create rpc worker: %v", err)
		}
		preprocessorWorker.Run()

		// this happens when connection is closed
		err = <-preprocessorWorker.Done
		logg.LogError(fmt.Errorf("Preprocessor Worker failed with error: %v", err))
	}

}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"os"
	"os/user"
	"strings"

	"github.com/iamthemuffinman/overseer/config"
	"github.com/iamthemuffinman/overseer/pkg/buildspec"
	"github.com/iamthemuffinman/overseer/pkg/hammer"
	"github.com/iamthemuffinman/overseer/pkg/hostspec"
	"github.com/iamthemuffinman/overseer/pkg/workerpool"

	"github.com/iamthemuffinman/cli"
	log "github.com/iamthemuffinman/logsip"
	"github.com/mitchellh/go-homedir"
	flag "github.com/ogier/pflag"
)

type ProvisionVirtualCommand struct {
	Ui         cli.Ui
	FlagSet    *flag.FlagSet
	ShutdownCh <-chan struct{}
}

func (c *ProvisionVirtualCommand) Run(args []string) int {
	if len(args) == 0 {
		return cli.RunResultHelp
	}

	for _, arg := range args {
		if arg == "-h" || arg == "-help" || arg == "--help" {
			return cli.RunResultHelp
		}
	}

	// Okay, we're ready to start doing some work at this point.
	// Let's create the pool of workers so they can start listening
	// for jobs that are put into the JobQueue.
	dispatcher := workerpool.NewDispatcher()
	dispatcher.Run()

	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)
		c.FlagSet = flag.NewFlagSet("virtual", flag.ExitOnError)

		specfile := c.FlagSet.StringP("hostspec", "h", "", "Provide a specfile name for your host(s) (i.e. indy.prod.kafka)")

		// Parse everything after 3 arguments (i.e overseer provision virtual STARTHERE)
		c.FlagSet.Parse(os.Args[3:])

		// GTFO if a hostspec wasn't specified
		if *specfile == "" {
			log.Fatal("You must specify a hostspec")
		}

		// Get user's home directory so we can pass it to the config parser
		home, err := homedir.Dir()
		if err != nil {
			// If for some reason the above doesn't work, let's see what the standard library
			// can do for us here. If this doesn't work, something is wrong and we should
			// cut out at this point.
			currentUser, err := user.Current()
			if err != nil {
				log.Fatalf("unable to get the home directory of the user running this process")
			}

			home = currentUser.HomeDir
		}

		// Parse overseer's config file which contains usernames and passwords
		conf, err := config.ParseFile(fmt.Sprintf("%s/.overseer/overseer.conf", home))
		if err != nil {
			log.Fatalf("unable to parse overseer config: %s", err)
		}

		// Here is where we essentially parse the entire hostspecs directory to find
		// the hostspec specified on the command line.
		hspec, err := hostspec.ParseDir("/etc/overseer/hostspecs", *specfile)
		if err != nil {
			log.Fatalf("unable to parse hostspec: %s", err)
		}

		// If there are arguments, then the user has specified a host on the
		// command line rather than using a buildspec
		if len(c.FlagSet.Args()) > 0 {
			log.Errorf("Please use a buildspec instead of specifying hosts on the command line")
			os.Exit(1)
		} else {
			// Parse the buildspec in the current directory to get a list of hosts
			bspec, err := buildspec.ParseFile("./buildspec")
			if err != nil {
				log.Fatalf("couldn't find your buildspec: %s", err)
			}

			// Same thing as above - range over all the hosts in the buildspec
			for _, host := range bspec.Hosts {
				cmd := &hammer.Hammer{
					Username:          conf.Foreman.Username,
					Password:          conf.Foreman.Password,
					Hostname:          host,
					Organization:      hspec.Foreman.Organization,
					Location:          hspec.Foreman.Location,
					Hostgroup:         hspec.Foreman.Hostgroup,
					Environment:       hspec.Foreman.Environment,
					PartitionTableID:  hspec.Foreman.PartitionTableID,
					OperatingSystemID: hspec.Foreman.OperatingSystemID,
					Medium:            hspec.Foreman.Medium,
					ArchitectureID:    hspec.Foreman.ArchitectureID,
					DomainID:          hspec.Foreman.DomainID,
					ComputeProfile:    hspec.Foreman.ComputeProfile,
					ComputeResource:   hspec.Foreman.ComputeResource,
					Host: hammer.Host{
						CPUs:   hspec.Virtual.CPUs,
						Cores:  hspec.Virtual.Cores,
						Memory: hspec.Virtual.Memory,
						Disks:  hspec.Vsphere.Devices.Disks,
					},
				}

				// Execute is a method that will send the command to a job queue
				// to be processed by a goroutine. This way we can build more
				// hosts at the same time by executing hammer in parallel.
				if err := cmd.Execute(); err != nil {
					log.Fatalf("error executing hammer: %s", err)
				}
			}
		}
	}()

	select {
	case <-c.ShutdownCh:
		log.Info("Interrupt received. Gracefully shutting down...")

		// Stop execution here
		// need to either find out or do something here about removing data for all hosts
		// or just the current host

		select {
		case <-c.ShutdownCh:
			log.Warn("Two interrupts received - exiting immediately. Some things may not have finished and no cleanup will be attempted.")
			return 1
		case <-doneCh:
		}
	case <-doneCh:
	}

	return 0
}

func (c *ProvisionVirtualCommand) Help() string {
	return c.helpProvisionVirtual()
}

func (c *ProvisionVirtualCommand) Synopsis() string {
	return "Provision virtual infrastructure"
}

func (c *ProvisionVirtualCommand) helpProvisionVirtual() string {
	helpText := `
Usage: overseer provision virtual [OPTIONS] [HOSTS]
`
	return strings.TrimSpace(helpText)
}
<commit_msg>Quick note for later<commit_after>package cli

import (
	"fmt"
	"os"
	"os/user"
	"strings"

	"github.com/iamthemuffinman/overseer/config"
	"github.com/iamthemuffinman/overseer/pkg/buildspec"
	"github.com/iamthemuffinman/overseer/pkg/hammer"
	"github.com/iamthemuffinman/overseer/pkg/hostspec"
	"github.com/iamthemuffinman/overseer/pkg/workerpool"

	"github.com/iamthemuffinman/cli"
	log "github.com/iamthemuffinman/logsip"
	"github.com/mitchellh/go-homedir"
	flag "github.com/ogier/pflag"
)

type ProvisionVirtualCommand struct {
	Ui         cli.Ui
	FlagSet    *flag.FlagSet
	ShutdownCh <-chan struct{}
}

func (c *ProvisionVirtualCommand) Run(args []string) int {
	if len(args) == 0 {
		return cli.RunResultHelp
	}

	for _, arg := range args {
		if arg == "-h" || arg == "-help" || arg == "--help" {
			return cli.RunResultHelp
		}
	}

	// Okay, we're ready to start doing some work at this point.
	// Let's create the pool of workers so they can start listening
	// for jobs that are put into the JobQueue.
	dispatcher := workerpool.NewDispatcher()
	dispatcher.Run()

	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)
		c.FlagSet = flag.NewFlagSet("virtual", flag.ExitOnError)

		specfile := c.FlagSet.StringP("hostspec", "h", "", "Provide a specfile name for your host(s) (i.e. indy.prod.kafka)")

		// Parse everything after 3 arguments (i.e overseer provision virtual STARTHERE)
		c.FlagSet.Parse(os.Args[3:])

		// GTFO if a hostspec wasn't specified
		if *specfile == "" {
			log.Fatal("You must specify a hostspec")
		}

		// Get user's home directory so we can pass it to the config parser
		home, err := homedir.Dir()
		if err != nil {
			// If for some reason the above doesn't work, let's see what the standard library
			// can do for us here. If this doesn't work, something is wrong and we should
			// cut out at this point.
			currentUser, err := user.Current()
			if err != nil {
				log.Fatalf("unable to get the home directory of the user running this process")
			}

			home = currentUser.HomeDir
		}

		// Parse overseer's config file which contains usernames and passwords
		conf, err := config.ParseFile(fmt.Sprintf("%s/.overseer/overseer.conf", home))
		if err != nil {
			log.Fatalf("unable to parse overseer config: %s", err)
		}

		// Here is where we essentially parse the entire hostspecs directory to find
		// the hostspec specified on the command line.
		hspec, err := hostspec.ParseDir("/etc/overseer/hostspecs", *specfile)
		if err != nil {
			log.Fatalf("unable to parse hostspec: %s", err)
		}

		// If there are arguments, then the user has specified a host on the
		// command line rather than using a buildspec
		if len(c.FlagSet.Args()) > 0 {
			log.Errorf("Please use a buildspec instead of specifying hosts on the command line")
			os.Exit(1)
		} else {
			// Parse the buildspec in the current directory to get a list of hosts
			bspec, err := buildspec.ParseFile("./buildspec")
			if err != nil {
				log.Fatalf("couldn't find your buildspec: %s", err)
			}

			// Same thing as above - range over all the hosts in the buildspec
			for _, host := range bspec.Hosts {
				cmd := &hammer.Hammer{
					Username:          conf.Foreman.Username,
					Password:          conf.Foreman.Password,
					Hostname:          host,
					Organization:      hspec.Foreman.Organization,
					Location:          hspec.Foreman.Location,
					Hostgroup:         hspec.Foreman.Hostgroup,
					Environment:       hspec.Foreman.Environment,
					PartitionTableID:  hspec.Foreman.PartitionTableID,
					OperatingSystemID: hspec.Foreman.OperatingSystemID,
					Medium:            hspec.Foreman.Medium,
					ArchitectureID:    hspec.Foreman.ArchitectureID,
					DomainID:          hspec.Foreman.DomainID,
					ComputeProfile:    hspec.Foreman.ComputeProfile,
					ComputeResource:   hspec.Foreman.ComputeResource,
					Host: hammer.Host{
						CPUs:   hspec.Virtual.CPUs,
						Cores:  hspec.Virtual.Cores,
						Memory: hspec.Virtual.Memory,
						Disks:  hspec.Vsphere.Devices.Disks,
					},
				}

				// Execute is a method that will send the command to a job queue
				// to be processed by a goroutine. This way we can build more
				// hosts at the same time by executing hammer in parallel.
				if err := cmd.Execute(); err != nil {
					log.Fatalf("error executing hammer: %s", err)
				}

				// Run chef/knife stuff here
			}
		}
	}()

	select {
	case <-c.ShutdownCh:
		log.Info("Interrupt received. Gracefully shutting down...")

		// Stop execution here
		// need to either find out or do something here about removing data for all hosts
		// or just the current host

		select {
		case <-c.ShutdownCh:
			log.Warn("Two interrupts received - exiting immediately. Some things may not have finished and no cleanup will be attempted.")
			return 1
		case <-doneCh:
		}
	case <-doneCh:
	}

	return 0
}

func (c *ProvisionVirtualCommand) Help() string {
	return c.helpProvisionVirtual()
}

func (c *ProvisionVirtualCommand) Synopsis() string {
	return "Provision virtual infrastructure"
}

func (c *ProvisionVirtualCommand) helpProvisionVirtual() string {
	helpText := `
Usage: overseer provision virtual [OPTIONS] [HOSTS]
`
	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package apiClient // import "resenje.org/httputils/client/api"

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// DefaultKeyHeader is default HTTP header name to pass API key when
// making a request.
var DefaultKeyHeader = "X-Key"

// Client stores properties that defines communication with a HTTP API service.
type Client struct {
	// Endpoint is an URL of the service. (required)
	Endpoint string
	// Key is a single string that is used in request authorization.
	Key string
	// KeyHeader is HTTP header name used to pass Client.Key value.
	// If it is left blank, DefaultKeyHeader is used.
	KeyHeader string
	// UserAgent is a string that will be passed as a value to User-Agent
	// HTTP header.
	UserAgent string
	// Headers is optional additional headers that will be passed on
	// each request.
	Headers map[string]string
	// ErrorRegistry maps error codes to actual errors. It is used to
	// identify errors from the services and pass them as return values.
	ErrorRegistry ErrorRegistry
	// HTTPClient is net/http.Client to be used for making HTTP requests.
	// If Client is nil, DefaultClient is used.
	HTTPClient *http.Client
}

// New returns a new instance of Client with default values.
func New(endpoint string, errorRegistry ErrorRegistry) *Client {
	return &Client{
		Endpoint:      endpoint,
		ErrorRegistry: errorRegistry,
		KeyHeader:     DefaultKeyHeader,
		HTTPClient:    http.DefaultClient,
	}
}

// Request makes a HTTP request based on Client configuration and
// arguments provided.
func (c Client) Request(method, path string, query url.Values, body io.Reader, accept []string) (resp *http.Response, err error) {
	if !strings.HasPrefix(c.Endpoint, "http://") && !strings.HasPrefix(c.Endpoint, "https://") {
		c.Endpoint = "http://" + c.Endpoint
	}
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return
	}
	u.Path += path

	u.RawQuery = query.Encode()
	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return
	}
	for _, a := range accept {
		req.Header.Add("Accept", a)
	}
	if c.UserAgent != "" {
		req.Header.Set("User-Agent", c.UserAgent)
	}
	for key, value := range c.Headers {
		req.Header.Set(key, value)
	}
	if c.Key != "" {
		keyHeader := c.KeyHeader
		if keyHeader == "" {
			keyHeader = DefaultKeyHeader
		}
		req.Header.Set(keyHeader, c.Key)
	}

	httpClient := c.HTTPClient
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	resp, err = httpClient.Do(req)
	if err != nil {
		return
	}

	if 200 > resp.StatusCode || resp.StatusCode >= 300 {
		defer func() {
			io.Copy(ioutil.Discard, resp.Body)
			resp.Body.Close()
		}()

		message := struct {
			Message *string `json:"message"`
			Code    *int    `json:"code"`
		}{}
		if resp.ContentLength != 0 && strings.Contains(resp.Header.Get("Content-Type"), "application/json") {
			if err = json.NewDecoder(resp.Body).Decode(&message); err != nil {
				if e, ok := err.(*json.SyntaxError); ok {
					err = fmt.Errorf("json: %s, offset: %d", e, e.Offset)
					return
				}
				return
			}
		}
		if message.Code != nil && c.ErrorRegistry != nil {
			if err = c.ErrorRegistry.Error(*message.Code); err != nil {
				return
			}
		}
		var status string
		if message.Message != nil {
			status = *message.Message
		} else {
			status = http.StatusText(resp.StatusCode)
		}
		err = &Error{
			Status: status,
			Code:   resp.StatusCode,
		}
	}

	return
}

// JSON makes a HTTP request that expects application/json response.
// It decodes response body to a `response` argument.
func (c Client) JSON(method, path string, query url.Values, body io.Reader, response interface{}) (err error) {
	resp, err := c.Request(method, path, query, body, []string{"application/json"})
	if err != nil {
		return
	}
	defer func() {
		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
	}()

	if response != nil {
		if resp.ContentLength == 0 {
			return errors.New("empty response body")
		}
		contentType := resp.Header.Get("Content-Type")
		if !strings.Contains(contentType, "application/json") {
			return fmt.Errorf("unsupported content type: %s", contentType)
		}
		if err = json.NewDecoder(resp.Body).Decode(&response); err != nil {
			switch e := err.(type) {
			case *json.SyntaxError:
				return fmt.Errorf("json: %s, offset: %d", e, e.Offset)
			case *json.UnmarshalTypeError:
				return fmt.Errorf("expected json %s value but got %s, offset %d", e.Type, e.Value, e.Offset)
			}
			return
		}
	}

	return
}

// Stream makes a HTTP request and returns request body as io.ReadCloser,
// to be able to read long running responses. Returned io.ReadCloser must be
// closed at the end of read. To reuse HTTP connection, make sure that the
// whole data is read before closing the reader.
func (c Client) Stream(method, path string, query url.Values, body io.Reader, accept []string) (data io.ReadCloser, contentType string, err error) {
	resp, err := c.Request(method, path, query, body, accept)
	if err != nil {
		return
	}

	contentType = resp.Header.Get("Content-Type")
	data = resp.Body
	return
}
<commit_msg>Update error handling for API client<commit_after>// Copyright (c) 2016, Janoš Guljaš <janos@resenje.org>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package apiClient // import "resenje.org/httputils/client/api"

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

// DefaultKeyHeader is default HTTP header name to pass API key when
// making a request.
var DefaultKeyHeader = "X-Key"

// Client stores properties that defines communication with a HTTP API service.
type Client struct {
	// Endpoint is an URL of the service. (required)
	Endpoint string
	// Key is a single string that is used in request authorization.
	Key string
	// KeyHeader is HTTP header name used to pass Client.Key value.
	// If it is left blank, DefaultKeyHeader is used.
	KeyHeader string
	// UserAgent is a string that will be passed as a value to User-Agent
	// HTTP header.
	UserAgent string
	// Headers is optional additional headers that will be passed on
	// each request.
	Headers map[string]string
	// ErrorRegistry maps error codes to actual errors. It is used to
	// identify errors from the services and pass them as return values.
	ErrorRegistry ErrorRegistry
	// HTTPClient is net/http.Client to be used for making HTTP requests.
	// If Client is nil, DefaultClient is used.
	HTTPClient *http.Client
}

// New returns a new instance of Client with default values.
func New(endpoint string, errorRegistry ErrorRegistry) *Client {
	return &Client{
		Endpoint:      endpoint,
		ErrorRegistry: errorRegistry,
		KeyHeader:     DefaultKeyHeader,
		HTTPClient:    http.DefaultClient,
	}
}

// Request makes a HTTP request based on Client configuration and
// arguments provided.
func (c Client) Request(method, path string, query url.Values, body io.Reader, accept []string) (resp *http.Response, err error) {
	if !strings.HasPrefix(c.Endpoint, "http://") && !strings.HasPrefix(c.Endpoint, "https://") {
		c.Endpoint = "http://" + c.Endpoint
	}
	u, err := url.Parse(c.Endpoint)
	if err != nil {
		return
	}
	u.Path += path

	u.RawQuery = query.Encode()
	req, err := http.NewRequest(method, u.String(), body)
	if err != nil {
		return
	}
	for _, a := range accept {
		req.Header.Add("Accept", a)
	}
	if c.UserAgent != "" {
		req.Header.Set("User-Agent", c.UserAgent)
	}
	for key, value := range c.Headers {
		req.Header.Set(key, value)
	}
	if c.Key != "" {
		keyHeader := c.KeyHeader
		if keyHeader == "" {
			keyHeader = DefaultKeyHeader
		}
		req.Header.Set(keyHeader, c.Key)
	}

	httpClient := c.HTTPClient
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	resp, err = httpClient.Do(req)
	if err != nil {
		return
	}

	if 200 > resp.StatusCode || resp.StatusCode >= 300 {
		defer func() {
			io.Copy(ioutil.Discard, resp.Body)
			resp.Body.Close()
		}()

		message := struct {
			Message string `json:"message"`
			Code    *int   `json:"code"`
		}{}
		if resp.ContentLength != 0 && strings.Contains(resp.Header.Get("Content-Type"), "application/json") {
			if err = json.NewDecoder(resp.Body).Decode(&message); err != nil {
				switch e := err.(type) {
				case *json.SyntaxError:
					message.Message = fmt.Sprintf("json %s, offset: %d", e, e.Offset)
				case *json.UnmarshalTypeError:
					// If the type of message is not as expected,
					// continue with http based error reporting.
				default:
					return
				}
			}
		}
		if message.Code != nil && c.ErrorRegistry != nil {
			if err = c.ErrorRegistry.Error(*message.Code); err != nil {
				return
			}
		}
		var status string
		if message.Message != "" {
			status = message.Message
		} else {
			status = strings.ToLower(http.StatusText(resp.StatusCode))
		}
		err = &Error{
			Status: status,
			Code:   resp.StatusCode,
		}
	}

	return
}

// JSON makes a HTTP request that expects application/json response.
// It decodes response body to a `response` argument.
func (c Client) JSON(method, path string, query url.Values, body io.Reader, response interface{}) (err error) {
	resp, err := c.Request(method, path, query, body, []string{"application/json"})
	if err != nil {
		return
	}
	defer func() {
		io.Copy(ioutil.Discard, resp.Body)
		resp.Body.Close()
	}()

	if response != nil {
		if resp.ContentLength == 0 {
			return errors.New("empty response body")
		}
		contentType := resp.Header.Get("Content-Type")
		if !strings.Contains(contentType, "application/json") {
			return fmt.Errorf("unsupported content type: %s", contentType)
		}
		if err = json.NewDecoder(resp.Body).Decode(&response); err != nil {
			switch e := err.(type) {
			case *json.SyntaxError:
				return fmt.Errorf("json %s, offset: %d", e, e.Offset)
			case *json.UnmarshalTypeError:
				return fmt.Errorf("expected json %s value but got %s, offset %d", e.Type, e.Value, e.Offset)
			}
			return
		}
	}

	return
}

// Stream makes a HTTP request and returns request body as io.ReadCloser,
// to be able to read long running responses. Returned io.ReadCloser must be
// closed at the end of read. To reuse HTTP connection, make sure that the
// whole data is read before closing the reader.
func (c Client) Stream(method, path string, query url.Values, body io.Reader, accept []string) (data io.ReadCloser, contentType string, err error) {
	resp, err := c.Request(method, path, query, body, accept)
	if err != nil {
		return
	}

	contentType = resp.Header.Get("Content-Type")
	data = resp.Body
	return
}
<|endoftext|>


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

package apis

import (
	"fmt"
	"os"
	"path"
	"testing"

	"github.com/apigee/apigeecli/apiclient"
)

const proxyName = "test"
const testFolder = "test"

func setup() (err error) {
	org := os.Getenv("APIGEE_ORG")
	if org == "" {
		return fmt.Errorf("APIGEE_ORG not set")
	}
	apiclient.SetApigeeOrg(org)

	token := os.Getenv("APIGEE_TOKEN")
	if token == "" {
		return fmt.Errorf("APIGEE_TOKEN not set")
	}
	apiclient.SetApigeeToken(token)
	return nil
}

func getApigeecliHome() (cliPath string, err error) {
	cliPath = os.Getenv("APIGEECLI_PATH")
	if cliPath == "" {
		return "", fmt.Errorf("APIGEECLI_PATH not set")
	}
	return cliPath, err
}

func TestCreateProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	cliPath, err := getApigeecliHome()
	if err != nil {
		t.Fatalf("%v", err)
	}
	if _, err = CreateProxy(proxyName, path.Join(cliPath, testFolder, "test_proxy.zip")); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestDeleteProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := DeleteProxy(proxyName); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestDeleteProxyRevision(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := DeleteProxyRevision(proxyName, 1); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestFetchProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if err := FetchProxy(proxyName, 1); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestGetProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := GetProxy(proxyName, 1); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := GetProxy(proxyName, -1); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestGetHighestProxyRevision(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := GetHighestProxyRevision(proxyName); err != nil {
		t.Fatalf("%v", err)
	}
}
<commit_msg>fix test for apis<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apis

import (
	"fmt"
	"os"
	"path"
	"testing"

	"github.com/apigee/apigeecli/apiclient"
)

const proxyName = "test"
const testFolder = "test"

func setup() (err error) {
	org := os.Getenv("APIGEE_ORG")
	if org == "" {
		return fmt.Errorf("APIGEE_ORG not set")
	}

	if err = apiclient.SetApigeeOrg(org); err != nil {
		return fmt.Errorf("APIGEE_ORG not set")
	}

	token := os.Getenv("APIGEE_TOKEN")
	if token == "" {
		return fmt.Errorf("APIGEE_TOKEN not set")
	}
	apiclient.SetApigeeToken(token)
	return nil
}

func getApigeecliHome() (cliPath string, err error) {
	cliPath = os.Getenv("APIGEECLI_PATH")
	if cliPath == "" {
		return "", fmt.Errorf("APIGEECLI_PATH not set")
	}
	return cliPath, err
}

func TestCreateProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	cliPath, err := getApigeecliHome()
	if err != nil {
		t.Fatalf("%v", err)
	}
	if _, err = CreateProxy(proxyName, path.Join(cliPath, testFolder, "test_proxy.zip")); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestDeleteProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := DeleteProxy(proxyName); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestDeleteProxyRevision(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := DeleteProxyRevision(proxyName, 1); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestFetchProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if err := FetchProxy(proxyName, 1); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestGetProxy(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := GetProxy(proxyName, 1); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := GetProxy(proxyName, -1); err != nil {
		t.Fatalf("%v", err)
	}
}

func TestGetHighestProxyRevision(t *testing.T) {
	if err := setup(); err != nil {
		t.Fatalf("%v", err)
	}
	if _, err := GetHighestProxyRevision(proxyName); err != nil {
		t.Fatalf("%v", err)
	}
}
<|endoftext|>


<commit_before>package cmd

// Version defines the current Pop version.
const Version = "v4.5.9"
<commit_msg>Setup for development<commit_after>package cmd

// Version defines the current Pop version.
const Version = "development"
<|endoftext|>


<commit_before>// Copyright 2016 The etcd 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 clientv3_test

import (
	"log"
	"time"

	"github.com/coreos/etcd/clientv3"
	"golang.org/x/net/context"
)

var (
	dialTimeout    = 5 * time.Second
	requestTimeout = 1 * time.Second
	endpoints      = []string{"localhost:2379", "localhost:22379", "localhost:32379"}
)

func Example() {
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: dialTimeout,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer cli.Close() // make sure to close the client

	_, err = cli.Put(context.TODO(), "foo", "bar")
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>clientv3: add 'ExampleConfig_withTLS'<commit_after>// Copyright 2016 The etcd 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 clientv3_test

import (
	"log"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/pkg/transport"
	"golang.org/x/net/context"
)

var (
	dialTimeout    = 5 * time.Second
	requestTimeout = 1 * time.Second
	endpoints      = []string{"localhost:2379", "localhost:22379", "localhost:32379"}
)

func Example() {
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: dialTimeout,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer cli.Close() // make sure to close the client

	_, err = cli.Put(context.TODO(), "foo", "bar")
	if err != nil {
		log.Fatal(err)
	}
}

func ExampleConfig_withTLS() {
	tlsInfo := transport.TLSInfo{
		CertFile:      "/tmp/test-certs/test-name-1.pem",
		KeyFile:       "/tmp/test-certs/test-name-1-key.pem",
		TrustedCAFile: "/tmp/test-certs/trusted-ca.pem",
	}
	tlsConfig, err := tlsInfo.ClientConfig()
	if err != nil {
		log.Fatal(err)
	}
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   endpoints,
		DialTimeout: dialTimeout,
		TLS:         tlsConfig,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer cli.Close() // make sure to close the client

	_, err = cli.Put(context.TODO(), "foo", "bar")
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// +build linux darwin

/*
Copyright 2011 Google Inc.

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

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

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

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/cacher"
	"camlistore.org/pkg/client"
	"camlistore.org/pkg/fs"
	"camlistore.org/third_party/code.google.com/p/rsc/fuse"
)

var (
	debug = flag.Bool("debug", false, "print debugging messages.")
	xterm = flag.Bool("xterm", false, "Run an xterm in the mounted directory. Shut down when xterm ends.")
)

func usage() {
	fmt.Fprint(os.Stderr, "usage: cammount [opts] <mountpoint> [<root-blobref>|<share URL>]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	var conn *fuse.Conn

	// Scans the arg list and sets up flags
	client.AddFlags()
	flag.Parse()

	narg := flag.NArg()
	if narg < 1 || narg > 2 {
		usage()
	}

	mountPoint := flag.Arg(0)

	errorf := func(msg string, args ...interface{}) {
		fmt.Fprintf(os.Stderr, msg, args...)
		fmt.Fprint(os.Stderr, "\n")
		usage()
	}

	var (
		cl    *client.Client
		root  blob.Ref // nil if only one arg
		camfs *fs.CamliFileSystem
	)
	if narg == 2 {
		rootArg := flag.Arg(1)
		// not trying very hard since NewFromShareRoot will do it better with a regex
		if strings.HasPrefix(rootArg, "http://") ||
			strings.HasPrefix(rootArg, "https://") {
			if client.ExplicitServer() != "" {
				errorf("Can't use an explicit blobserver with a share URL; the blobserver is implicit from the share URL.")
			}
			var err error
			cl, root, err = client.NewFromShareRoot(rootArg)
			if err != nil {
				log.Fatal(err)
			}
		} else {
			cl = client.NewOrFail() // automatic from flags
			var ok bool
			root, ok = blob.Parse(rootArg)
			if !ok {
				log.Fatalf("Error parsing root blobref: %q\n", rootArg)
			}
			cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
		}
	} else {
		cl = client.NewOrFail() // automatic from flags
		cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
	}

	diskCacheFetcher, err := cacher.NewDiskCache(cl)
	if err != nil {
		log.Fatalf("Error setting up local disk cache: %v", err)
	}
	defer diskCacheFetcher.Clean()
	if root.Valid() {
		var err error
		camfs, err = fs.NewRootedCamliFileSystem(diskCacheFetcher, root)
		if err != nil {
			log.Fatalf("Error creating root with %v: %v", root, err)
		}
	} else {
		camfs = fs.NewCamliFileSystem(cl, diskCacheFetcher)
	}

	if *debug {
		fuse.Debugf = log.Printf
		// TODO: set fs's logger
	}

	// This doesn't appear to work on OS X:
	sigc := make(chan os.Signal, 1)

	conn, err = fuse.Mount(mountPoint)
	if err != nil {
		if err.Error() == "cannot find load_fusefs" && runtime.GOOS == "darwin" {
			log.Fatal("FUSE not available; install from http://osxfuse.github.io/")
		}
		log.Fatalf("Mount: %v", err)
	}

	xtermDone := make(chan bool, 1)
	if *xterm {
		cmd := exec.Command("xterm")
		cmd.Dir = mountPoint
		if err := cmd.Start(); err != nil {
			log.Printf("Error starting xterm: %v", err)
		} else {
			go func() {
				cmd.Wait()
				xtermDone <- true
			}()
			defer cmd.Process.Kill()
		}
	}

	signal.Notify(sigc, syscall.SIGQUIT, syscall.SIGTERM)

	doneServe := make(chan error, 1)
	go func() {
		doneServe <- conn.Serve(camfs)
	}()

	quitKey := make(chan bool, 1)
	go awaitQuitKey(quitKey)

	select {
	case err := <-doneServe:
		log.Printf("conn.Serve returned %v", err)
	case sig := <-sigc:
		log.Printf("Signal %s received, shutting down.", sig)
	case <-quitKey:
		log.Printf("Quit key pressed. Shutting down.")
	case <-xtermDone:
		log.Printf("xterm done")
	}

	time.AfterFunc(2*time.Second, func() {
		os.Exit(1)
	})
	log.Printf("Unmounting...")
	err = fs.Unmount(mountPoint)
	log.Printf("Unmount = %v", err)

	log.Printf("cammount FUSE process ending.")
}

func awaitQuitKey(done chan<- bool) {
	var buf [1]byte
	for {
		_, err := os.Stdin.Read(buf[:])
		if err != nil {
			return
		}
		if buf[0] == 'q' {
			done <- true
			return
		}
	}
}
<commit_msg>cammount: handle SIGINT, so that we unmount too in that case.<commit_after>// +build linux darwin

/*
Copyright 2011 Google Inc.

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

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

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

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"strings"
	"syscall"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/cacher"
	"camlistore.org/pkg/client"
	"camlistore.org/pkg/fs"
	"camlistore.org/third_party/code.google.com/p/rsc/fuse"
)

var (
	debug = flag.Bool("debug", false, "print debugging messages.")
	xterm = flag.Bool("xterm", false, "Run an xterm in the mounted directory. Shut down when xterm ends.")
)

func usage() {
	fmt.Fprint(os.Stderr, "usage: cammount [opts] <mountpoint> [<root-blobref>|<share URL>]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	var conn *fuse.Conn

	// Scans the arg list and sets up flags
	client.AddFlags()
	flag.Parse()

	narg := flag.NArg()
	if narg < 1 || narg > 2 {
		usage()
	}

	mountPoint := flag.Arg(0)

	errorf := func(msg string, args ...interface{}) {
		fmt.Fprintf(os.Stderr, msg, args...)
		fmt.Fprint(os.Stderr, "\n")
		usage()
	}

	var (
		cl    *client.Client
		root  blob.Ref // nil if only one arg
		camfs *fs.CamliFileSystem
	)
	if narg == 2 {
		rootArg := flag.Arg(1)
		// not trying very hard since NewFromShareRoot will do it better with a regex
		if strings.HasPrefix(rootArg, "http://") ||
			strings.HasPrefix(rootArg, "https://") {
			if client.ExplicitServer() != "" {
				errorf("Can't use an explicit blobserver with a share URL; the blobserver is implicit from the share URL.")
			}
			var err error
			cl, root, err = client.NewFromShareRoot(rootArg)
			if err != nil {
				log.Fatal(err)
			}
		} else {
			cl = client.NewOrFail() // automatic from flags
			var ok bool
			root, ok = blob.Parse(rootArg)
			if !ok {
				log.Fatalf("Error parsing root blobref: %q\n", rootArg)
			}
			cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
		}
	} else {
		cl = client.NewOrFail() // automatic from flags
		cl.SetHTTPClient(&http.Client{Transport: cl.TransportForConfig(nil)})
	}

	diskCacheFetcher, err := cacher.NewDiskCache(cl)
	if err != nil {
		log.Fatalf("Error setting up local disk cache: %v", err)
	}
	defer diskCacheFetcher.Clean()
	if root.Valid() {
		var err error
		camfs, err = fs.NewRootedCamliFileSystem(diskCacheFetcher, root)
		if err != nil {
			log.Fatalf("Error creating root with %v: %v", root, err)
		}
	} else {
		camfs = fs.NewCamliFileSystem(cl, diskCacheFetcher)
	}

	if *debug {
		fuse.Debugf = log.Printf
		// TODO: set fs's logger
	}

	// This doesn't appear to work on OS X:
	sigc := make(chan os.Signal, 1)

	conn, err = fuse.Mount(mountPoint)
	if err != nil {
		if err.Error() == "cannot find load_fusefs" && runtime.GOOS == "darwin" {
			log.Fatal("FUSE not available; install from http://osxfuse.github.io/")
		}
		log.Fatalf("Mount: %v", err)
	}

	xtermDone := make(chan bool, 1)
	if *xterm {
		cmd := exec.Command("xterm")
		cmd.Dir = mountPoint
		if err := cmd.Start(); err != nil {
			log.Printf("Error starting xterm: %v", err)
		} else {
			go func() {
				cmd.Wait()
				xtermDone <- true
			}()
			defer cmd.Process.Kill()
		}
	}

	signal.Notify(sigc, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)

	doneServe := make(chan error, 1)
	go func() {
		doneServe <- conn.Serve(camfs)
	}()

	quitKey := make(chan bool, 1)
	go awaitQuitKey(quitKey)

	select {
	case err := <-doneServe:
		log.Printf("conn.Serve returned %v", err)
	case sig := <-sigc:
		log.Printf("Signal %s received, shutting down.", sig)
	case <-quitKey:
		log.Printf("Quit key pressed. Shutting down.")
	case <-xtermDone:
		log.Printf("xterm done")
	}

	time.AfterFunc(2*time.Second, func() {
		os.Exit(1)
	})
	log.Printf("Unmounting...")
	err = fs.Unmount(mountPoint)
	log.Printf("Unmount = %v", err)

	log.Printf("cammount FUSE process ending.")
}

func awaitQuitKey(done chan<- bool) {
	var buf [1]byte
	for {
		_, err := os.Stdin.Read(buf[:])
		if err != nil {
			return
		}
		if buf[0] == 'q' {
			done <- true
			return
		}
	}
}
<|endoftext|>