<commit_before>// Copyright 2015 Matthew Holt and The Caddy 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 caddycmd

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/caddyserver/caddy/v2"
	"github.com/mholt/certmagic"
	"github.com/mitchellh/go-ps"
)

func cmdStart() (int, error) {
	startCmd := flag.NewFlagSet("start", flag.ExitOnError)
	startCmdConfigFlag := startCmd.String("config", "", "Configuration file")
	startCmd.Parse(os.Args[2:])

	// open a listener to which the child process will connect when
	// it is ready to confirm that it has successfully started
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("opening listener for success confirmation: %v", err)
	}
	defer ln.Close()

	// craft the command with a pingback address and with a
	// pipe for its stdin, so we can tell it our confirmation
	// code that we expect so that some random port scan at
	// the most unfortunate time won't fool us into thinking
	// the child succeeded (i.e. the alternative is to just
	// wait for any connection on our listener, but better to
	// ensure it's the process we're expecting - we can be
	// sure by giving it some random bytes and having it echo
	// them back to us)
	cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
	if *startCmdConfigFlag != "" {
		cmd.Args = append(cmd.Args, "--config", *startCmdConfigFlag)
	}
	stdinpipe, err := cmd.StdinPipe()
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("creating stdin pipe: %v", err)
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// generate the random bytes we'll send to the child process
	expect := make([]byte, 32)
	_, err = rand.Read(expect)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("generating random confirmation bytes: %v", err)
	}

	// begin writing the confirmation bytes to the child's
	// stdin; use a goroutine since the child hasn't been
	// started yet, and writing sychronously would result
	// in a deadlock
	go func() {
		stdinpipe.Write(expect)
		stdinpipe.Close()
	}()

	// start the process
	err = cmd.Start()
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("starting caddy process: %v", err)
	}

	// there are two ways we know we're done: either
	// the process will connect to our listener, or
	// it will exit with an error
	success, exit := make(chan struct{}), make(chan error)

	// in one goroutine, we await the success of the child process
	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				if !strings.Contains(err.Error(), "use of closed network connection") {
					log.Println(err)
				}
				break
			}
			err = handlePingbackConn(conn, expect)
			if err == nil {
				close(success)
				break
			}
			log.Println(err)
		}
	}()

	// in another goroutine, we await the failure of the child process
	go func() {
		err := cmd.Wait() // don't send on this line! Wait blocks, but send starts before it unblocks
		exit <- err       // sending on separate line ensures select won't trigger until after Wait unblocks
	}()

	// when one of the goroutines unblocks, we're done and can exit
	select {
	case <-success:
		fmt.Println("Successfully started Caddy")
	case err := <-exit:
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("caddy process exited with error: %v", err)
	}

	return caddy.ExitCodeSuccess, nil
}

func cmdRun() (int, error) {
	runCmd := flag.NewFlagSet("run", flag.ExitOnError)
	runCmdConfigFlag := runCmd.String("config", "", "Configuration file")
	runCmdPingbackFlag := runCmd.String("pingback", "", "Echo confirmation bytes to this address on success")
	runCmd.Parse(os.Args[2:])

	// if a config file was specified for bootstrapping
	// the server instance, load it now
	var config []byte
	if *runCmdConfigFlag != "" {
		var err error
		config, err = ioutil.ReadFile(*runCmdConfigFlag)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("reading config file: %v", err)
		}
	}

	// set a fitting User-Agent for ACME requests
	goModule := caddy.GoModule()
	cleanModVersion := strings.TrimPrefix(goModule.Version, "v")
	certmagic.UserAgent = "Caddy/" + cleanModVersion

	// start the admin endpoint along with any initial config
	err := caddy.StartAdmin(config)
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("starting caddy administration endpoint: %v", err)
	}
	defer caddy.StopAdmin()

	// if we are to report to another process the successful start
	// of the server, do so now by echoing back contents of stdin
	if *runCmdPingbackFlag != "" {
		confirmationBytes, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("reading confirmation bytes from stdin: %v", err)
		}
		conn, err := net.Dial("tcp", *runCmdPingbackFlag)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("dialing confirmation address: %v", err)
		}
		defer conn.Close()
		_, err = conn.Write(confirmationBytes)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("writing confirmation bytes to %s: %v", *runCmdPingbackFlag, err)
		}
	}

	select {}
}

func cmdStop() (int, error) {
	processList, err := ps.Processes()
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("listing processes: %v", err)
	}
	thisProcName := filepath.Base(os.Args[0])
	var found bool
	for _, p := range processList {
		// the process we're looking for should have the same name but different PID
		if p.Executable() == thisProcName && p.Pid() != os.Getpid() {
			found = true
			fmt.Printf("pid=%d\n", p.Pid())
			fmt.Printf("Graceful stop...")
			if err := gracefullyStopProcess(p.Pid()); err != nil {
				return caddy.ExitCodeFailedStartup, err
			}
		}
	}
	if !found {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("Caddy is not running")
	}
	fmt.Println(" success")
	return caddy.ExitCodeSuccess, nil
}

func cmdReload() (int, error) {
	reloadCmd := flag.NewFlagSet("load", flag.ExitOnError)
	reloadCmdConfigFlag := reloadCmd.String("config", "", "Configuration file")
	reloadCmdAddrFlag := reloadCmd.String("address", "", "Address of the administration listener, if different from config")
	reloadCmd.Parse(os.Args[2:])

	// a configuration is required
	if *reloadCmdConfigFlag == "" {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("no configuration to load (use --config)")
	}

	// load the configuration file
	config, err := ioutil.ReadFile(*reloadCmdConfigFlag)
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("reading config file: %v", err)
	}

	// get the address of the admin listener and craft endpoint URL
	adminAddr := *reloadCmdAddrFlag
	if adminAddr == "" {
		var tmpStruct struct {
			Admin caddy.AdminConfig `json:"admin"`
		}
		err = json.Unmarshal(config, &tmpStruct)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("unmarshaling admin listener address from config: %v", err)
		}
		adminAddr = tmpStruct.Admin.Listen
	}
	if adminAddr == "" {
		adminAddr = caddy.DefaultAdminListen
	}
	adminEndpoint := fmt.Sprintf("http://%s/load", adminAddr)

	// send the configuration to the instance
	resp, err := http.Post(adminEndpoint, "application/json", bytes.NewReader(config))
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("sending configuration to instance: %v", err)
	}
	defer resp.Body.Close()

	// if it didn't work, let the user know
	if resp.StatusCode >= 400 {
		respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*10))
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err)
		}
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("caddy responded with error: HTTP %d: %s", resp.StatusCode, respBody)
	}

	return caddy.ExitCodeSuccess, nil
}

func cmdVersion() (int, error) {
	goModule := caddy.GoModule()
	if goModule.Sum != "" {
		// a build with a known version will also have a checksum
		fmt.Printf("%s %s\n", goModule.Version, goModule.Sum)
	} else {
		fmt.Println(goModule.Version)
	}
	return caddy.ExitCodeSuccess, nil
}

func cmdListModules() (int, error) {
	for _, m := range caddy.Modules() {
		fmt.Println(m)
	}
	return caddy.ExitCodeSuccess, nil
}

func cmdEnviron() (int, error) {
	for _, v := range os.Environ() {
		fmt.Println(v)
	}
	return caddy.ExitCodeSuccess, nil
}
<commit_msg>cmd: Add print-env flag to run command<commit_after>// Copyright 2015 Matthew Holt and The Caddy 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 caddycmd

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/caddyserver/caddy/v2"
	"github.com/mholt/certmagic"
	"github.com/mitchellh/go-ps"
)

func cmdStart() (int, error) {
	startCmd := flag.NewFlagSet("start", flag.ExitOnError)
	startCmdConfigFlag := startCmd.String("config", "", "Configuration file")
	startCmd.Parse(os.Args[2:])

	// open a listener to which the child process will connect when
	// it is ready to confirm that it has successfully started
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("opening listener for success confirmation: %v", err)
	}
	defer ln.Close()

	// craft the command with a pingback address and with a
	// pipe for its stdin, so we can tell it our confirmation
	// code that we expect so that some random port scan at
	// the most unfortunate time won't fool us into thinking
	// the child succeeded (i.e. the alternative is to just
	// wait for any connection on our listener, but better to
	// ensure it's the process we're expecting - we can be
	// sure by giving it some random bytes and having it echo
	// them back to us)
	cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
	if *startCmdConfigFlag != "" {
		cmd.Args = append(cmd.Args, "--config", *startCmdConfigFlag)
	}
	stdinpipe, err := cmd.StdinPipe()
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("creating stdin pipe: %v", err)
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// generate the random bytes we'll send to the child process
	expect := make([]byte, 32)
	_, err = rand.Read(expect)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("generating random confirmation bytes: %v", err)
	}

	// begin writing the confirmation bytes to the child's
	// stdin; use a goroutine since the child hasn't been
	// started yet, and writing sychronously would result
	// in a deadlock
	go func() {
		stdinpipe.Write(expect)
		stdinpipe.Close()
	}()

	// start the process
	err = cmd.Start()
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("starting caddy process: %v", err)
	}

	// there are two ways we know we're done: either
	// the process will connect to our listener, or
	// it will exit with an error
	success, exit := make(chan struct{}), make(chan error)

	// in one goroutine, we await the success of the child process
	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				if !strings.Contains(err.Error(), "use of closed network connection") {
					log.Println(err)
				}
				break
			}
			err = handlePingbackConn(conn, expect)
			if err == nil {
				close(success)
				break
			}
			log.Println(err)
		}
	}()

	// in another goroutine, we await the failure of the child process
	go func() {
		err := cmd.Wait() // don't send on this line! Wait blocks, but send starts before it unblocks
		exit <- err       // sending on separate line ensures select won't trigger until after Wait unblocks
	}()

	// when one of the goroutines unblocks, we're done and can exit
	select {
	case <-success:
		fmt.Println("Successfully started Caddy")
	case err := <-exit:
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("caddy process exited with error: %v", err)
	}

	return caddy.ExitCodeSuccess, nil
}

func cmdRun() (int, error) {
	runCmd := flag.NewFlagSet("run", flag.ExitOnError)
	runCmdConfigFlag := runCmd.String("config", "", "Configuration file")
	runCmdPrintEnvFlag := runCmd.Bool("print-env", false, "Print environment (useful for debugging)")
	runCmdPingbackFlag := runCmd.String("pingback", "", "Echo confirmation bytes to this address on success")
	runCmd.Parse(os.Args[2:])

	// if we are supposed to print the environment, do that first
	if *runCmdPrintEnvFlag {
		exitCode, err := cmdEnviron()
		if err != nil {
			return exitCode, err
		}
	}

	// if a config file was specified for bootstrapping
	// the server instance, load it now
	var config []byte
	if *runCmdConfigFlag != "" {
		var err error
		config, err = ioutil.ReadFile(*runCmdConfigFlag)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("reading config file: %v", err)
		}
	}

	// set a fitting User-Agent for ACME requests
	goModule := caddy.GoModule()
	cleanModVersion := strings.TrimPrefix(goModule.Version, "v")
	certmagic.UserAgent = "Caddy/" + cleanModVersion

	// start the admin endpoint along with any initial config
	err := caddy.StartAdmin(config)
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("starting caddy administration endpoint: %v", err)
	}
	defer caddy.StopAdmin()

	// if we are to report to another process the successful start
	// of the server, do so now by echoing back contents of stdin
	if *runCmdPingbackFlag != "" {
		confirmationBytes, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("reading confirmation bytes from stdin: %v", err)
		}
		conn, err := net.Dial("tcp", *runCmdPingbackFlag)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("dialing confirmation address: %v", err)
		}
		defer conn.Close()
		_, err = conn.Write(confirmationBytes)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("writing confirmation bytes to %s: %v", *runCmdPingbackFlag, err)
		}
	}

	select {}
}

func cmdStop() (int, error) {
	processList, err := ps.Processes()
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("listing processes: %v", err)
	}
	thisProcName := filepath.Base(os.Args[0])
	var found bool
	for _, p := range processList {
		// the process we're looking for should have the same name but different PID
		if p.Executable() == thisProcName && p.Pid() != os.Getpid() {
			found = true
			fmt.Printf("pid=%d\n", p.Pid())
			fmt.Printf("Graceful stop...")
			if err := gracefullyStopProcess(p.Pid()); err != nil {
				return caddy.ExitCodeFailedStartup, err
			}
		}
	}
	if !found {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("Caddy is not running")
	}
	fmt.Println(" success")
	return caddy.ExitCodeSuccess, nil
}

func cmdReload() (int, error) {
	reloadCmd := flag.NewFlagSet("load", flag.ExitOnError)
	reloadCmdConfigFlag := reloadCmd.String("config", "", "Configuration file")
	reloadCmdAddrFlag := reloadCmd.String("address", "", "Address of the administration listener, if different from config")
	reloadCmd.Parse(os.Args[2:])

	// a configuration is required
	if *reloadCmdConfigFlag == "" {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("no configuration to load (use --config)")
	}

	// load the configuration file
	config, err := ioutil.ReadFile(*reloadCmdConfigFlag)
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("reading config file: %v", err)
	}

	// get the address of the admin listener and craft endpoint URL
	adminAddr := *reloadCmdAddrFlag
	if adminAddr == "" {
		var tmpStruct struct {
			Admin caddy.AdminConfig `json:"admin"`
		}
		err = json.Unmarshal(config, &tmpStruct)
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("unmarshaling admin listener address from config: %v", err)
		}
		adminAddr = tmpStruct.Admin.Listen
	}
	if adminAddr == "" {
		adminAddr = caddy.DefaultAdminListen
	}
	adminEndpoint := fmt.Sprintf("http://%s/load", adminAddr)

	// send the configuration to the instance
	resp, err := http.Post(adminEndpoint, "application/json", bytes.NewReader(config))
	if err != nil {
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("sending configuration to instance: %v", err)
	}
	defer resp.Body.Close()

	// if it didn't work, let the user know
	if resp.StatusCode >= 400 {
		respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*10))
		if err != nil {
			return caddy.ExitCodeFailedStartup,
				fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err)
		}
		return caddy.ExitCodeFailedStartup,
			fmt.Errorf("caddy responded with error: HTTP %d: %s", resp.StatusCode, respBody)
	}

	return caddy.ExitCodeSuccess, nil
}

func cmdVersion() (int, error) {
	goModule := caddy.GoModule()
	if goModule.Sum != "" {
		// a build with a known version will also have a checksum
		fmt.Printf("%s %s\n", goModule.Version, goModule.Sum)
	} else {
		fmt.Println(goModule.Version)
	}
	return caddy.ExitCodeSuccess, nil
}

func cmdListModules() (int, error) {
	for _, m := range caddy.Modules() {
		fmt.Println(m)
	}
	return caddy.ExitCodeSuccess, nil
}

func cmdEnviron() (int, error) {
	for _, v := range os.Environ() {
		fmt.Println(v)
	}
	return caddy.ExitCodeSuccess, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"errors"
	"fmt"

	"github.com/buddyspike/mbt/lib"
	"github.com/spf13/cobra"
)

func init() {
	DescribePrCmd.Flags().StringVar(&src, "src", "", "source branch")
	DescribePrCmd.Flags().StringVar(&dst, "dst", "", "destination branch")

	DescribeCmd.AddCommand(DescribeCommitCmd)
	DescribeCmd.AddCommand(DescribeBranchCmd)
	DescribeCmd.AddCommand(DescribePrCmd)
	RootCmd.AddCommand(DescribeCmd)
}

var DescribeCmd = &cobra.Command{
	Use:   "describe",
	Short: "Describes the manifest of a repo",
}

var DescribeBranchCmd = &cobra.Command{
	Use:   "branch <branch>",
	Short: "Describes the manifest for the given branch",
	RunE: func(cmd *cobra.Command, args []string) error {
		branch := "master"
		if len(args) > 0 {
			branch = args[0]
		}
		m, err := lib.ManifestByBranch(in, branch)
		if err != nil {
			return err
		}

		output(m)
		return nil
	},
}

var DescribePrCmd = &cobra.Command{
	Use:   "pr --src <branch> --dst <branch>",
	Short: "Describes the manifest for a given pr",
	RunE: func(cmd *cobra.Command, args []string) error {
		if src == "" {
			return errors.New("requires source")
		}

		if dst == "" {
			return errors.New("requires dest")
		}

		m, err := lib.ManifestByPr(in, src, dst)
		if err != nil {
			return err
		}

		output(m)

		return nil
	},
}

var DescribeCommitCmd = &cobra.Command{
	Use:   "commit <sha>",
	Short: "Describes the manifest for a given commit",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("requires the commit sha")
		}

		commit := args[0]

		m, err := lib.ManifestBySha(in, commit)
		if err != nil {
			return err
		}

		output(m)

		return nil
	},
}

func output(m *lib.Manifest) {
	for _, a := range m.Applications {
		fmt.Printf("%s %s\n", a.Name, a.Version)
	}
}
<commit_msg>improved output format.<commit_after>package cmd

import (
	"errors"
	"fmt"
	"strings"

	"github.com/buddyspike/mbt/lib"
	"github.com/spf13/cobra"
)

func init() {
	DescribePrCmd.Flags().StringVar(&src, "src", "", "source branch")
	DescribePrCmd.Flags().StringVar(&dst, "dst", "", "destination branch")

	DescribeCmd.AddCommand(DescribeCommitCmd)
	DescribeCmd.AddCommand(DescribeBranchCmd)
	DescribeCmd.AddCommand(DescribePrCmd)
	RootCmd.AddCommand(DescribeCmd)
}

var DescribeCmd = &cobra.Command{
	Use:   "describe",
	Short: "Describes the manifest of a repo",
}

var DescribeBranchCmd = &cobra.Command{
	Use:   "branch <branch>",
	Short: "Describes the manifest for the given branch",
	RunE: func(cmd *cobra.Command, args []string) error {
		branch := "master"
		if len(args) > 0 {
			branch = args[0]
		}
		m, err := lib.ManifestByBranch(in, branch)
		if err != nil {
			return err
		}

		output(m)
		return nil
	},
}

var DescribePrCmd = &cobra.Command{
	Use:   "pr --src <branch> --dst <branch>",
	Short: "Describes the manifest for a given pr",
	RunE: func(cmd *cobra.Command, args []string) error {
		if src == "" {
			return errors.New("requires source")
		}

		if dst == "" {
			return errors.New("requires dest")
		}

		m, err := lib.ManifestByPr(in, src, dst)
		if err != nil {
			return err
		}

		output(m)

		return nil
	},
}

var DescribeCommitCmd = &cobra.Command{
	Use:   "commit <sha>",
	Short: "Describes the manifest for a given commit",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("requires the commit sha")
		}

		commit := args[0]

		m, err := lib.ManifestBySha(in, commit)
		if err != nil {
			return err
		}

		output(m)

		return nil
	},
}

const COLUMN_WIDTH = 30

func formatRow(args ...interface{}) string {
	padded := make([]interface{}, len(args))
	for i, a := range args {
		requiredPadding := COLUMN_WIDTH - len(a.(string))
		if requiredPadding > 0 {
			padded[i] = fmt.Sprintf("%s%s", a, strings.Join(make([]string, requiredPadding), " "))
		} else {
			padded[i] = a
		}
	}
	return fmt.Sprintf("%s\t\t%s\t\t%s\n", padded...)
}

func output(m *lib.Manifest) {
	fmt.Print(formatRow("Name", "Path", "Version"))
	for _, a := range m.Applications {
		fmt.Printf(formatRow(a.Name, a.Path, a.Version))
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	netURL "net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// downloadCmd represents the download command
var downloadCmd = &cobra.Command{
	Use:     "download",
	Aliases: []string{"d"},
	Short:   "Download an exercise.",
	Long: `Download an exercise.

You may download an exercise to work on. If you've already
started working on it, the command will also download your
latest solution.

Download other people's solutions by providing the UUID.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfig()

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("user")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()
		cfg.UserViperConfig = v

		return runDownload(cfg, cmd.Flags(), args)
	},
}

func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig
	if err := validateUserConfig(usrCfg); err != nil {
		return err
	}

	uuid, err := flags.GetString("uuid")
	if err != nil {
		return err
	}
	slug, err := flags.GetString("exercise")
	if err != nil {
		return err
	}
	if uuid != "" && slug != "" || uuid == slug {
		return errors.New("need an --exercise name or a solution --uuid")
	}

	track, err := flags.GetString("track")
	if err != nil {
		return err
	}

	team, err := flags.GetString("team")
	if err != nil {
		return err
	}

	param := "latest"
	if uuid != "" {
		param = uuid
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), param)

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}

	req, err := client.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	if uuid == "" {
		q := req.URL.Query()
		q.Add("exercise_id", slug)
		if track != "" {
			q.Add("track_id", track)
		}
		if team != "" {
			q.Add("team_id", team)
		}
		req.URL.RawQuery = q.Encode()
	}

	res, err := client.Do(req)
	if err != nil {
		return err
	}

	var payload downloadPayload
	defer res.Body.Close()
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return fmt.Errorf("unable to parse API response - %s", err)
	}

	if res.StatusCode == http.StatusUnauthorized {
		siteURL := config.InferSiteURL(usrCfg.GetString("apibaseurl"))
		return fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
	}

	if res.StatusCode != http.StatusOK {
		switch payload.Error.Type {
		case "track_ambiguous":
			return fmt.Errorf("%s: %s", payload.Error.Message, strings.Join(payload.Error.PossibleTrackIDs, ", "))
		default:
			return errors.New(payload.Error.Message)
		}
	}

	metadata := workspace.ExerciseMetadata{
		AutoApprove:  payload.Solution.Exercise.AutoApprove,
		Track:        payload.Solution.Exercise.Track.ID,
		Team:         payload.Solution.Team.Slug,
		ExerciseSlug: payload.Solution.Exercise.ID,
		ID:           payload.Solution.ID,
		URL:          payload.Solution.URL,
		Handle:       payload.Solution.User.Handle,
		IsRequester:  payload.Solution.User.IsRequester,
	}

	root := usrCfg.GetString("workspace")
	if metadata.Team != "" {
		root = filepath.Join(root, "teams", metadata.Team)
	}
	if !metadata.IsRequester {
		root = filepath.Join(root, "users", metadata.Handle)
	}

	exercise := workspace.Exercise{
		Root:  root,
		Track: metadata.Track,
		Slug:  metadata.ExerciseSlug,
	}

	dir := exercise.MetadataDir()

	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
		return err
	}

	err = metadata.Write(dir)
	if err != nil {
		return err
	}

	for _, file := range payload.Solution.Files {
		unparsedURL := fmt.Sprintf("%s%s", payload.Solution.FileDownloadBaseURL, file)
		parsedURL, err := netURL.ParseRequestURI(unparsedURL)

		if err != nil {
			return err
		}

		url := parsedURL.String()

		req, err := client.NewRequest("GET", url, nil)
		if err != nil {
			return err
		}

		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()

		if res.StatusCode != http.StatusOK {
			// TODO: deal with it
			continue
		}
		// Don't bother with empty files.
		if res.Header.Get("Content-Length") == "0" {
			continue
		}

		// TODO: if there's a collision, interactively resolve (show diff, ask if overwrite).
		// TODO: handle --force flag to overwrite without asking.

		// Work around a path bug due to an early design decision (later reversed) to
		// allow numeric suffixes for exercise directories, allowing people to have
		// multiple parallel versions of an exercise.
		pattern := fmt.Sprintf(`\A.*[/\\]%s-\d*/`, metadata.ExerciseSlug)
		rgxNumericSuffix := regexp.MustCompile(pattern)
		if rgxNumericSuffix.MatchString(file) {
			file = string(rgxNumericSuffix.ReplaceAll([]byte(file), []byte("")))
		}

		// Rewrite paths submitted with an older, buggy client where the Windows path is being treated as part of the filename.
		file = strings.Replace(file, "\\", "/", -1)

		relativePath := filepath.FromSlash(file)
		dir := filepath.Join(metadata.Dir, filepath.Dir(relativePath))
		os.MkdirAll(dir, os.FileMode(0755))

		f, err := os.Create(filepath.Join(metadata.Dir, relativePath))
		if err != nil {
			return err
		}
		defer f.Close()
		_, err = io.Copy(f, res.Body)
		if err != nil {
			return err
		}
	}
	fmt.Fprintf(Err, "\nDownloaded to\n")
	fmt.Fprintf(Out, "%s\n", metadata.Dir)
	return nil
}

type downloadPayload struct {
	Solution struct {
		ID   string `json:"id"`
		URL  string `json:"url"`
		Team struct {
			Name string `json:"name"`
			Slug string `json:"slug"`
		} `json:"team"`
		User struct {
			Handle      string `json:"handle"`
			IsRequester bool   `json:"is_requester"`
		} `json:"user"`
		Exercise struct {
			ID              string `json:"id"`
			InstructionsURL string `json:"instructions_url"`
			AutoApprove     bool   `json:"auto_approve"`
			Track           struct {
				ID       string `json:"id"`
				Language string `json:"language"`
			} `json:"track"`
		} `json:"exercise"`
		FileDownloadBaseURL string   `json:"file_download_base_url"`
		Files               []string `json:"files"`
		Iteration           struct {
			SubmittedAt *string `json:"submitted_at"`
		}
	} `json:"solution"`
	Error struct {
		Type             string   `json:"type"`
		Message          string   `json:"message"`
		PossibleTrackIDs []string `json:"possible_track_ids"`
	} `json:"error,omitempty"`
}

func setupDownloadFlags(flags *pflag.FlagSet) {
	flags.StringP("uuid", "u", "", "the solution UUID")
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise slug")
	flags.StringP("team", "T", "", "the team slug")
}

func init() {
	RootCmd.AddCommand(downloadCmd)
	setupDownloadFlags(downloadCmd.Flags())
}
<commit_msg>Enhance downloadPayload with helper to return exercise metadata<commit_after>package cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	netURL "net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// downloadCmd represents the download command
var downloadCmd = &cobra.Command{
	Use:     "download",
	Aliases: []string{"d"},
	Short:   "Download an exercise.",
	Long: `Download an exercise.

You may download an exercise to work on. If you've already
started working on it, the command will also download your
latest solution.

Download other people's solutions by providing the UUID.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfig()

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("user")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()
		cfg.UserViperConfig = v

		return runDownload(cfg, cmd.Flags(), args)
	},
}

func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig
	if err := validateUserConfig(usrCfg); err != nil {
		return err
	}

	uuid, err := flags.GetString("uuid")
	if err != nil {
		return err
	}
	slug, err := flags.GetString("exercise")
	if err != nil {
		return err
	}
	if uuid != "" && slug != "" || uuid == slug {
		return errors.New("need an --exercise name or a solution --uuid")
	}

	track, err := flags.GetString("track")
	if err != nil {
		return err
	}

	team, err := flags.GetString("team")
	if err != nil {
		return err
	}

	param := "latest"
	if uuid != "" {
		param = uuid
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), param)

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}

	req, err := client.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	if uuid == "" {
		q := req.URL.Query()
		q.Add("exercise_id", slug)
		if track != "" {
			q.Add("track_id", track)
		}
		if team != "" {
			q.Add("team_id", team)
		}
		req.URL.RawQuery = q.Encode()
	}

	res, err := client.Do(req)
	if err != nil {
		return err
	}

	var payload downloadPayload
	defer res.Body.Close()
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return fmt.Errorf("unable to parse API response - %s", err)
	}

	if res.StatusCode == http.StatusUnauthorized {
		siteURL := config.InferSiteURL(usrCfg.GetString("apibaseurl"))
		return fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
	}

	if res.StatusCode != http.StatusOK {
		switch payload.Error.Type {
		case "track_ambiguous":
			return fmt.Errorf("%s: %s", payload.Error.Message, strings.Join(payload.Error.PossibleTrackIDs, ", "))
		default:
			return errors.New(payload.Error.Message)
		}
	}

	metadata := payload.metadata()

	root := usrCfg.GetString("workspace")
	if metadata.Team != "" {
		root = filepath.Join(root, "teams", metadata.Team)
	}
	if !metadata.IsRequester {
		root = filepath.Join(root, "users", metadata.Handle)
	}

	exercise := workspace.Exercise{
		Root:  root,
		Track: metadata.Track,
		Slug:  metadata.ExerciseSlug,
	}

	dir := exercise.MetadataDir()

	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
		return err
	}

	err = metadata.Write(dir)
	if err != nil {
		return err
	}

	for _, file := range payload.Solution.Files {
		unparsedURL := fmt.Sprintf("%s%s", payload.Solution.FileDownloadBaseURL, file)
		parsedURL, err := netURL.ParseRequestURI(unparsedURL)

		if err != nil {
			return err
		}

		url := parsedURL.String()

		req, err := client.NewRequest("GET", url, nil)
		if err != nil {
			return err
		}

		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()

		if res.StatusCode != http.StatusOK {
			// TODO: deal with it
			continue
		}
		// Don't bother with empty files.
		if res.Header.Get("Content-Length") == "0" {
			continue
		}

		// TODO: if there's a collision, interactively resolve (show diff, ask if overwrite).
		// TODO: handle --force flag to overwrite without asking.

		// Work around a path bug due to an early design decision (later reversed) to
		// allow numeric suffixes for exercise directories, allowing people to have
		// multiple parallel versions of an exercise.
		pattern := fmt.Sprintf(`\A.*[/\\]%s-\d*/`, metadata.ExerciseSlug)
		rgxNumericSuffix := regexp.MustCompile(pattern)
		if rgxNumericSuffix.MatchString(file) {
			file = string(rgxNumericSuffix.ReplaceAll([]byte(file), []byte("")))
		}

		// Rewrite paths submitted with an older, buggy client where the Windows path is being treated as part of the filename.
		file = strings.Replace(file, "\\", "/", -1)

		relativePath := filepath.FromSlash(file)
		dir := filepath.Join(metadata.Dir, filepath.Dir(relativePath))
		os.MkdirAll(dir, os.FileMode(0755))

		f, err := os.Create(filepath.Join(metadata.Dir, relativePath))
		if err != nil {
			return err
		}
		defer f.Close()
		_, err = io.Copy(f, res.Body)
		if err != nil {
			return err
		}
	}
	fmt.Fprintf(Err, "\nDownloaded to\n")
	fmt.Fprintf(Out, "%s\n", metadata.Dir)
	return nil
}

type downloadPayload struct {
	Solution struct {
		ID   string `json:"id"`
		URL  string `json:"url"`
		Team struct {
			Name string `json:"name"`
			Slug string `json:"slug"`
		} `json:"team"`
		User struct {
			Handle      string `json:"handle"`
			IsRequester bool   `json:"is_requester"`
		} `json:"user"`
		Exercise struct {
			ID              string `json:"id"`
			InstructionsURL string `json:"instructions_url"`
			AutoApprove     bool   `json:"auto_approve"`
			Track           struct {
				ID       string `json:"id"`
				Language string `json:"language"`
			} `json:"track"`
		} `json:"exercise"`
		FileDownloadBaseURL string   `json:"file_download_base_url"`
		Files               []string `json:"files"`
		Iteration           struct {
			SubmittedAt *string `json:"submitted_at"`
		}
	} `json:"solution"`
	Error struct {
		Type             string   `json:"type"`
		Message          string   `json:"message"`
		PossibleTrackIDs []string `json:"possible_track_ids"`
	} `json:"error,omitempty"`
}

func (dp downloadPayload) metadata() workspace.ExerciseMetadata {
	return workspace.ExerciseMetadata{
		AutoApprove:  dp.Solution.Exercise.AutoApprove,
		Track:        dp.Solution.Exercise.Track.ID,
		Team:         dp.Solution.Team.Slug,
		ExerciseSlug: dp.Solution.Exercise.ID,
		ID:           dp.Solution.ID,
		URL:          dp.Solution.URL,
		Handle:       dp.Solution.User.Handle,
		IsRequester:  dp.Solution.User.IsRequester,
	}
}

func setupDownloadFlags(flags *pflag.FlagSet) {
	flags.StringP("uuid", "u", "", "the solution UUID")
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise slug")
	flags.StringP("team", "T", "", "the team slug")
}

func init() {
	RootCmd.AddCommand(downloadCmd)
	setupDownloadFlags(downloadCmd.Flags())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/cloudfoundry-incubator/auction/communication/http/auction_http_handlers"
	auctionroutes "github.com/cloudfoundry-incubator/auction/communication/http/routes"
	"github.com/cloudfoundry-incubator/cf-debug-server"
	"github.com/cloudfoundry-incubator/cf-lager"
	"github.com/cloudfoundry-incubator/executor"
	executorclient "github.com/cloudfoundry-incubator/executor/http/client"
	"github.com/cloudfoundry-incubator/rep/auction_cell_rep"
	"github.com/cloudfoundry-incubator/rep/generator"
	"github.com/cloudfoundry-incubator/rep/harmonizer"
	repserver "github.com/cloudfoundry-incubator/rep/http_server"
	"github.com/cloudfoundry-incubator/rep/lrp_stopper"
	"github.com/cloudfoundry-incubator/rep/maintain"
	Bbs "github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/lock_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	bbsroutes "github.com/cloudfoundry-incubator/runtime-schema/routes"
	"github.com/cloudfoundry/dropsonde"
	"github.com/cloudfoundry/gunk/timeprovider"
	"github.com/cloudfoundry/gunk/workpool"
	"github.com/cloudfoundry/storeadapter/etcdstoreadapter"
	"github.com/nu7hatch/gouuid"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/localip"
	"github.com/pivotal-golang/operationq"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/tedsuo/rata"
)

var etcdCluster = flag.String(
	"etcdCluster",
	"http://127.0.0.1:4001",
	"comma-separated list of etcd addresses (http://ip:port)",
)

var heartbeatInterval = flag.Duration(
	"heartbeatInterval",
	lock_bbs.HEARTBEAT_INTERVAL,
	"the interval between heartbeats for maintaining presence",
)

var executorURL = flag.String(
	"executorURL",
	"http://127.0.0.1:1700",
	"location of executor to represent",
)

var listenAddr = flag.String(
	"listenAddr",
	"0.0.0.0:1800",
	"host:port to serve auction and LRP stop requests on",
)

var stack = flag.String(
	"stack",
	"",
	"the rep stack - must be specified",
)

var cellID = flag.String(
	"cellID",
	"",
	"the ID used by the rep to identify itself to external systems - must be specified",
)

var zone = flag.String(
	"zone",
	"",
	"the availability zone associated with the rep",
)

var pollingInterval = flag.Duration(
	"pollingInterval",
	30*time.Second,
	"the interval on which to scan the executor",
)

const (
	dropsondeDestination = "localhost:3457"
	dropsondeOrigin      = "rep"
)

var logger = cf_lager.New("rep")

func init() {
	cf_debug_server.AddFlags(flag.CommandLine)
	initializeDropsonde(logger)
	flag.Parse()
}

func main() {
	if *cellID == "" {
		log.Fatalf("-cellID must be specified")
	}

	if *stack == "" {
		log.Fatalf("-stack must be specified")
	}

	bbs := initializeRepBBS(logger)
	executorClient := executorclient.New(http.DefaultClient, *executorURL)
	server, address := initializeServer(bbs, executorClient, logger)
	opGenerator := generator.New(*cellID, bbs, executorClient)

	// only one outstanding operation per container is necessary
	queue := operationq.NewSlidingQueue(1)

	members := grouper.Members{
		{"server", server},
		{"heartbeater", initializeCellHeartbeat(address, bbs, executorClient, logger)},
		{"bulker", harmonizer.NewBulker(logger, *pollingInterval, timeprovider.NewTimeProvider(), opGenerator, queue)},
		{"event-consumer", harmonizer.NewEventConsumer(logger, opGenerator, queue)},
	}

	if dbgAddr := cf_debug_server.DebugAddress(flag.CommandLine); dbgAddr != "" {
		members = append(grouper.Members{
			{"debug-server", cf_debug_server.Runner(dbgAddr)},
		}, members...)
	}

	group := grouper.NewOrdered(os.Interrupt, members)

	monitor := ifrit.Invoke(sigmon.New(group))

	logger.Info("started", lager.Data{"cell-id": *cellID})

	<-monitor.Wait()
	logger.Info("shutting-down")
}

func initializeDropsonde(logger lager.Logger) {
	err := dropsonde.Initialize(dropsondeDestination, dropsondeOrigin)
	if err != nil {
		logger.Error("failed to initialize dropsonde: %v", err)
	}
}

func initializeCellHeartbeat(address string, bbs Bbs.RepBBS, executorClient executor.Client, logger lager.Logger) ifrit.Runner {
	cellPresence := models.CellPresence{
		CellID:     *cellID,
		RepAddress: address,
		Stack:      *stack,
		Zone:       *zone,
	}

	heartbeat := bbs.NewCellHeartbeat(cellPresence, *heartbeatInterval)
	return maintain.New(executorClient, heartbeat, logger, *heartbeatInterval, timeprovider.NewTimeProvider())
}

func initializeRepBBS(logger lager.Logger) Bbs.RepBBS {
	etcdAdapter := etcdstoreadapter.NewETCDStoreAdapter(
		strings.Split(*etcdCluster, ","),
		workpool.NewWorkPool(10),
	)

	err := etcdAdapter.Connect()
	if err != nil {
		logger.Fatal("failed-to-connect-to-etcd", err)
	}

	return Bbs.NewRepBBS(etcdAdapter, timeprovider.NewTimeProvider(), logger)
}

func initializeLRPStopper(guid string, executorClient executor.Client, logger lager.Logger) lrp_stopper.LRPStopper {
	return lrp_stopper.New(guid, executorClient, logger)
}

func initializeServer(
	bbs Bbs.RepBBS,
	executorClient executor.Client,
	logger lager.Logger,
) (ifrit.Runner, string) {
	lrpStopper := initializeLRPStopper(*cellID, executorClient, logger)

	auctionCellRep := auction_cell_rep.New(*cellID, *stack, *zone, generateGuid, bbs, executorClient, logger)
	handlers := auction_http_handlers.New(auctionCellRep, logger)

	handlers[bbsroutes.StopLRPInstance] = repserver.NewStopLRPInstanceHandler(logger, lrpStopper)
	routes := append(auctionroutes.Routes, bbsroutes.StopLRPRoutes...)

	router, err := rata.NewRouter(routes, handlers)
	if err != nil {
		logger.Fatal("failed-to-construct-router", err)
	}

	ip, err := localip.LocalIP()
	if err != nil {
		logger.Fatal("failed-to-fetch-ip", err)
	}

	port := strings.Split(*listenAddr, ":")[1]
	address := fmt.Sprintf("http://%s:%s", ip, port)

	return http_server.New(*listenAddr, router), address
}

func generateGuid() (string, error) {
	guid, err := uuid.NewV4()
	if err != nil {
		return "", err
	}
	return guid.String(), nil
}
<commit_msg>Move flag parsing to main<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/cloudfoundry-incubator/auction/communication/http/auction_http_handlers"
	auctionroutes "github.com/cloudfoundry-incubator/auction/communication/http/routes"
	"github.com/cloudfoundry-incubator/cf-debug-server"
	"github.com/cloudfoundry-incubator/cf-lager"
	"github.com/cloudfoundry-incubator/executor"
	executorclient "github.com/cloudfoundry-incubator/executor/http/client"
	"github.com/cloudfoundry-incubator/rep/auction_cell_rep"
	"github.com/cloudfoundry-incubator/rep/generator"
	"github.com/cloudfoundry-incubator/rep/harmonizer"
	repserver "github.com/cloudfoundry-incubator/rep/http_server"
	"github.com/cloudfoundry-incubator/rep/lrp_stopper"
	"github.com/cloudfoundry-incubator/rep/maintain"
	Bbs "github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs/lock_bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	bbsroutes "github.com/cloudfoundry-incubator/runtime-schema/routes"
	"github.com/cloudfoundry/dropsonde"
	"github.com/cloudfoundry/gunk/timeprovider"
	"github.com/cloudfoundry/gunk/workpool"
	"github.com/cloudfoundry/storeadapter/etcdstoreadapter"
	"github.com/nu7hatch/gouuid"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/localip"
	"github.com/pivotal-golang/operationq"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/grouper"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/tedsuo/rata"
)

var etcdCluster = flag.String(
	"etcdCluster",
	"http://127.0.0.1:4001",
	"comma-separated list of etcd addresses (http://ip:port)",
)

var heartbeatInterval = flag.Duration(
	"heartbeatInterval",
	lock_bbs.HEARTBEAT_INTERVAL,
	"the interval between heartbeats for maintaining presence",
)

var executorURL = flag.String(
	"executorURL",
	"http://127.0.0.1:1700",
	"location of executor to represent",
)

var listenAddr = flag.String(
	"listenAddr",
	"0.0.0.0:1800",
	"host:port to serve auction and LRP stop requests on",
)

var stack = flag.String(
	"stack",
	"",
	"the rep stack - must be specified",
)

var cellID = flag.String(
	"cellID",
	"",
	"the ID used by the rep to identify itself to external systems - must be specified",
)

var zone = flag.String(
	"zone",
	"",
	"the availability zone associated with the rep",
)

var pollingInterval = flag.Duration(
	"pollingInterval",
	30*time.Second,
	"the interval on which to scan the executor",
)

const (
	dropsondeDestination = "localhost:3457"
	dropsondeOrigin      = "rep"
)

func main() {
	cf_debug_server.AddFlags(flag.CommandLine)
	flag.Parse()

	logger := cf_lager.New("rep")
	initializeDropsonde(logger)

	if *cellID == "" {
		log.Fatalf("-cellID must be specified")
	}

	if *stack == "" {
		log.Fatalf("-stack must be specified")
	}

	bbs := initializeRepBBS(logger)
	executorClient := executorclient.New(http.DefaultClient, *executorURL)
	server, address := initializeServer(bbs, executorClient, logger)
	opGenerator := generator.New(*cellID, bbs, executorClient)

	// only one outstanding operation per container is necessary
	queue := operationq.NewSlidingQueue(1)

	members := grouper.Members{
		{"server", server},
		{"heartbeater", initializeCellHeartbeat(address, bbs, executorClient, logger)},
		{"bulker", harmonizer.NewBulker(logger, *pollingInterval, timeprovider.NewTimeProvider(), opGenerator, queue)},
		{"event-consumer", harmonizer.NewEventConsumer(logger, opGenerator, queue)},
	}

	if dbgAddr := cf_debug_server.DebugAddress(flag.CommandLine); dbgAddr != "" {
		members = append(grouper.Members{
			{"debug-server", cf_debug_server.Runner(dbgAddr)},
		}, members...)
	}

	group := grouper.NewOrdered(os.Interrupt, members)

	monitor := ifrit.Invoke(sigmon.New(group))

	logger.Info("started", lager.Data{"cell-id": *cellID})

	<-monitor.Wait()
	logger.Info("shutting-down")
}

func initializeDropsonde(logger lager.Logger) {
	err := dropsonde.Initialize(dropsondeDestination, dropsondeOrigin)
	if err != nil {
		logger.Error("failed to initialize dropsonde: %v", err)
	}
}

func initializeCellHeartbeat(address string, bbs Bbs.RepBBS, executorClient executor.Client, logger lager.Logger) ifrit.Runner {
	cellPresence := models.CellPresence{
		CellID:     *cellID,
		RepAddress: address,
		Stack:      *stack,
		Zone:       *zone,
	}

	heartbeat := bbs.NewCellHeartbeat(cellPresence, *heartbeatInterval)
	return maintain.New(executorClient, heartbeat, logger, *heartbeatInterval, timeprovider.NewTimeProvider())
}

func initializeRepBBS(logger lager.Logger) Bbs.RepBBS {
	etcdAdapter := etcdstoreadapter.NewETCDStoreAdapter(
		strings.Split(*etcdCluster, ","),
		workpool.NewWorkPool(10),
	)

	err := etcdAdapter.Connect()
	if err != nil {
		logger.Fatal("failed-to-connect-to-etcd", err)
	}

	return Bbs.NewRepBBS(etcdAdapter, timeprovider.NewTimeProvider(), logger)
}

func initializeLRPStopper(guid string, executorClient executor.Client, logger lager.Logger) lrp_stopper.LRPStopper {
	return lrp_stopper.New(guid, executorClient, logger)
}

func initializeServer(
	bbs Bbs.RepBBS,
	executorClient executor.Client,
	logger lager.Logger,
) (ifrit.Runner, string) {
	lrpStopper := initializeLRPStopper(*cellID, executorClient, logger)

	auctionCellRep := auction_cell_rep.New(*cellID, *stack, *zone, generateGuid, bbs, executorClient, logger)
	handlers := auction_http_handlers.New(auctionCellRep, logger)

	handlers[bbsroutes.StopLRPInstance] = repserver.NewStopLRPInstanceHandler(logger, lrpStopper)
	routes := append(auctionroutes.Routes, bbsroutes.StopLRPRoutes...)

	router, err := rata.NewRouter(routes, handlers)
	if err != nil {
		logger.Fatal("failed-to-construct-router", err)
	}

	ip, err := localip.LocalIP()
	if err != nil {
		logger.Fatal("failed-to-fetch-ip", err)
	}

	port := strings.Split(*listenAddr, ":")[1]
	address := fmt.Sprintf("http://%s:%s", ip, port)

	return http_server.New(*listenAddr, router), address
}

func generateGuid() (string, error) {
	guid, err := uuid.NewV4()
	if err != nil {
		return "", err
	}
	return guid.String(), nil
}
<|endoftext|>


<commit_before>package goutil

import (
	"time"
	"errors"
	"log"
	"strconv"
	"strings"
)

//日期类型

const (
	YYYY_MM_DD__HI_MM_SS = "2006-01-02 15:04:05"
	YYYY_MM_DD = "2006-01-02"
	YYYY_MM = "2006-01"
	YYYY = "2006"
	YYYYMMDDHIMMSS = "20060102150405"
	YYYYMMDD = "20060102"
	YYYYMM = "200601"
	YYYYMMDDHIMMSSsss = "20060102150405.9999"
)

// GetCurrentDate 获取当前月份,
// 返回格式为:YYYY-MM
// 示例:2016-06
func GetCurrentMonth() string {
	return time.Now().Format(YYYY_MM)
}

// GetCurrentDate 获取当前日期,
// 返回格式为:YYYY-MM-DD
// 示例:2016-06-05
func GetCurrentDate() string {
	return time.Now().Format(YYYY_MM_DD)
}
//获取当前时间
func GetFormatTime(format string) (string) {
	return strings.Replace(time.Now().Format(format), ".", "", -1)
}

// GetCurrentTime 获取当前时间,
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetCurrentTime() string {
	return time.Now().Format(YYYY_MM_DD__HI_MM_SS)
}

// GetPreDayTime 获取当前时间之前的N天的时间,
// 参数为int64位整型,
// 当参数小于0时,代表获取当前时间之后的N天时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreDayTime(day int64) string {
	sec := time.Now().Unix() - day * 24 * 60 * 60
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}

// GetPreHourTime 获取当前时间之前的N小时的时间,
// 参数为int64位整型,
// 当参数小于0时,获取当前时间之后的N小时的时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreHourTime(hour int64) string {
	sec := time.Now().Unix() - hour * 60 * 60
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}

// GetPreMinuteTime 获取当前时间之前的N分钟时间,
// 参数为int64位整型,
// 当参数小于0时,代表获取当前时间之后的N分钟时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreMinuteTime(minute int64) string {
	sec := time.Now().Unix() - minute * 60
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}

// GetPreSecondTime 获取当前时间之前的N秒钟时间,
// 参数为int64位整型,
// 当参数小于0时,代表获取当前时间之后的N秒钟时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreSecondTime(second int64) string {
	sec := time.Now().Unix() - second
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}
// UnixTimeToStr 将Unix时间转换为字符串格式(YYYY-MM-DD HI:mm:ss),
// 参数为int64位整型,
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func UnixTimeToStr(format string, t1 int64) (timeStr string, err error) {
	if format == YYYY || format == YYYY_MM_DD__HI_MM_SS || format == YYYY_MM_DD ||
	format == YYYY_MM || format == YYYYMM || format == YYYYMMDDHIMMSS || format == YYYYMMDD {
		tm := time.Unix(t1, 0)
		return tm.Format(format), nil
	}
	return YYYY_MM_DD__HI_MM_SS, errors.New("暂时不支持的日期格式")

}

// StrToUnixTime 将Unix时间转换为字符串格式(format),
func StrToUnixTime(format, t1 string) (unixTime int64, err error) {
	if format == YYYY || format == YYYY_MM_DD__HI_MM_SS || format == YYYY_MM_DD ||
	format == YYYY_MM || format == YYYYMM || format == YYYYMMDDHIMMSS || format == YYYYMMDD {
		tm, err := time.ParseInLocation(format, t1, time.Local)
		if err != nil {
			return 0, err
		}
		return tm.Unix(), nil
	}
	return 0, errors.New("暂时不支持的日期格式")

}

//获取纳秒级时间差
func GetCurrentNano() int64 {
	return time.Now().UnixNano()
}

//获取指定时间前几天时间
func GetBeforeDayTime(format, nowTime string, day int64) string {

	uT, err := StrToUnixTime(format, nowTime)
	if err != nil {
		log.Println(err.Error())
		return ""
	}
	preUt := uT - day * 24 * 60 * 60
	sT, err := UnixTimeToStr(format, preUt)

	if err != nil {
		log.Println(err.Error())
		return ""
	}
	return sT
}

func GetBeforeSecondTime(format, nowTime string, second int64) string {

	uT, err := StrToUnixTime(format, nowTime)
	if err != nil {
		log.Println(err.Error())
		return ""
	}
	preUt := uT - second
	sT, err := UnixTimeToStr(format, preUt)

	if err != nil {
		log.Println(err.Error())
		return ""
	}
	return sT
}

//获取当月的开始时间,例:2016-06-08 00:00:00
func GetFirstDayTimeInMonth() string {
	year, mon, _ := time.Now().Date()
	forMon := ""
	if mon < 10 {
		forMon = "0" + strconv.Itoa(int(mon))
	} else {
		forMon = strconv.Itoa(int(mon))
	}
	strTime := strconv.Itoa(year) + "-" + forMon + "-01 00:00:00"
	return strTime
}

//取当月的结束时间,如: 2016-06-30 23:59:59
func GetLastDayTimeInMonth() string {
	firstTime := ""
	year, mon, _ := time.Now().Date()
	nextM := int(mon)
	if nextM < 12 {
		nextM = nextM + 1
	} else {
		nextM = 1
		year = year + 1
	}
	forMon := ""
	if nextM < 10 {
		forMon = "0" + strconv.Itoa(nextM)
	} else {
		forMon = strconv.Itoa(nextM)
	}
	firstTime = strconv.Itoa(year) + "-" + forMon + "-01 00:00:00"
	return GetBeforeSecondTime(YYYY_MM_DD__HI_MM_SS, firstTime, 1)
}
<commit_msg>add the method to get pre month<commit_after>package goutil

import (
	"time"
	"errors"
	"log"
	"strconv"
	"strings"
)

//日期类型

const (
	YYYY_MM_DD__HI_MM_SS = "2006-01-02 15:04:05"
	YYYY_MM_DD = "2006-01-02"
	YYYY_MM = "2006-01"
	YYYY = "2006"
	YYYYMMDDHIMMSS = "20060102150405"
	YYYYMMDD = "20060102"
	YYYYMM = "200601"
	YYYYMMDDHIMMSSsss = "20060102150405.9999"
)

// GetCurrentDate 获取当前月份,
// 返回格式为:YYYY-MM
// 示例:2016-06
func GetCurrentMonth() string {
	return time.Now().Format(YYYY_MM)
}

// GetCurrentDate 获取当前日期,
// 返回格式为:YYYY-MM-DD
// 示例:2016-06-05
func GetCurrentDate() string {
	return time.Now().Format(YYYY_MM_DD)
}
//获取当前时间
func GetFormatTime(format string) (string) {
	return strings.Replace(time.Now().Format(format), ".", "", -1)
}

// GetCurrentTime 获取当前时间,
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetCurrentTime() string {
	return time.Now().Format(YYYY_MM_DD__HI_MM_SS)
}

// GetPreDayTime 获取当前时间之前的N天的时间,
// 参数为int64位整型,
// 当参数小于0时,代表获取当前时间之后的N天时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreDayTime(day int64) string {
	sec := time.Now().Unix() - day * 24 * 60 * 60
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}

// GetPreHourTime 获取当前时间之前的N小时的时间,
// 参数为int64位整型,
// 当参数小于0时,获取当前时间之后的N小时的时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreHourTime(hour int64) string {
	sec := time.Now().Unix() - hour * 60 * 60
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}

// GetPreMinuteTime 获取当前时间之前的N分钟时间,
// 参数为int64位整型,
// 当参数小于0时,代表获取当前时间之后的N分钟时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreMinuteTime(minute int64) string {
	sec := time.Now().Unix() - minute * 60
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}

// GetPreSecondTime 获取当前时间之前的N秒钟时间,
// 参数为int64位整型,
// 当参数小于0时,代表获取当前时间之后的N秒钟时间
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func GetPreSecondTime(second int64) string {
	sec := time.Now().Unix() - second
	timeStr, _ := UnixTimeToStr(YYYY_MM_DD__HI_MM_SS, sec)
	return timeStr
}
// UnixTimeToStr 将Unix时间转换为字符串格式(YYYY-MM-DD HI:mm:ss),
// 参数为int64位整型,
// 返回格式为:YYYY-MM-DD HI:MI:SS,
// 示例:2016-06-05 12:22:33
func UnixTimeToStr(format string, t1 int64) (timeStr string, err error) {
	if format == YYYY || format == YYYY_MM_DD__HI_MM_SS || format == YYYY_MM_DD ||
	format == YYYY_MM || format == YYYYMM || format == YYYYMMDDHIMMSS || format == YYYYMMDD {
		tm := time.Unix(t1, 0)
		return tm.Format(format), nil
	}
	return YYYY_MM_DD__HI_MM_SS, errors.New("暂时不支持的日期格式")

}

// StrToUnixTime 将Unix时间转换为字符串格式(format),
func StrToUnixTime(format, t1 string) (unixTime int64, err error) {
	if format == YYYY || format == YYYY_MM_DD__HI_MM_SS || format == YYYY_MM_DD ||
	format == YYYY_MM || format == YYYYMM || format == YYYYMMDDHIMMSS || format == YYYYMMDD {
		tm, err := time.ParseInLocation(format, t1, time.Local)
		if err != nil {
			return 0, err
		}
		return tm.Unix(), nil
	}
	return 0, errors.New("暂时不支持的日期格式")

}

//获取纳秒级时间差
func GetCurrentNano() int64 {
	return time.Now().UnixNano()
}

//获取指定时间前几天时间
func GetBeforeDayTime(format, nowTime string, day int64) string {

	uT, err := StrToUnixTime(format, nowTime)
	if err != nil {
		log.Println(err.Error())
		return ""
	}
	preUt := uT - day * 24 * 60 * 60
	sT, err := UnixTimeToStr(format, preUt)

	if err != nil {
		log.Println(err.Error())
		return ""
	}
	return sT
}

func GetBeforeSecondTime(format, nowTime string, second int64) string {

	uT, err := StrToUnixTime(format, nowTime)
	if err != nil {
		log.Println(err.Error())
		return ""
	}
	preUt := uT - second
	sT, err := UnixTimeToStr(format, preUt)

	if err != nil {
		log.Println(err.Error())
		return ""
	}
	return sT
}

//获取当月的开始时间,例:2016-06-08 00:00:00
func GetFirstDayTimeInMonth() string {
	year, mon, _ := time.Now().Date()
	forMon := ""
	if mon < 10 {
		forMon = "0" + strconv.Itoa(int(mon))
	} else {
		forMon = strconv.Itoa(int(mon))
	}
	strTime := strconv.Itoa(year) + "-" + forMon + "-01 00:00:00"
	return strTime
}

func GetPreMonth() string {
	year, mon, _ := time.Now().Date()
	forMon := ""
	if mon == 1 {
		forMon = "12"
		year = year - 1
	} else if mon <= 10 {
		forMon = "0" + strconv.Itoa(int(mon - 1))
	} else {
		forMon = strconv.Itoa(int(mon))
	}
	strTime := strconv.Itoa(year) + "-" + forMon
	return strTime
}
//取当月的结束时间,如: 2016-06-30 23:59:59
func GetLastDayTimeInMonth() string {
	firstTime := ""
	year, mon, _ := time.Now().Date()
	nextM := int(mon)
	if nextM < 12 {
		nextM = nextM + 1
	} else {
		nextM = 1
		year = year + 1
	}
	forMon := ""
	if nextM < 10 {
		forMon = "0" + strconv.Itoa(nextM)
	} else {
		forMon = strconv.Itoa(nextM)
	}
	firstTime = strconv.Itoa(year) + "-" + forMon + "-01 00:00:00"
	return GetBeforeSecondTime(YYYY_MM_DD__HI_MM_SS, firstTime, 1)
}
<|endoftext|>


<commit_before>package elements

import (
	"encoding/json"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/erichs/cloudsysfs"
)

type Config struct {
	Directory string
	Listen    string
	Path      string
}

type Elements struct {
	Config   Config
	Elements map[string]interface{}
	mu       sync.Mutex
}

func (e *Elements) Add(key string, value interface{}) {
	e.mu.Lock()
	defer e.mu.Unlock()

	if e.Elements == nil {
		e.Elements = make(map[string]interface{})
	}

	e.Elements[key] = value
}

func (e *Elements) Get() (interface{}, error) {
	systemPathRE := regexp.MustCompile("^system")
	externalPathRE := regexp.MustCompile("^external")
	cloudPathRE := regexp.MustCompile("^cloud")

	cloud := cloudsysfs.Detect()

	switch {
	case systemPathRE.MatchString(e.Config.Path):
		elements, err := e.GetSystemElements()
		if err != nil {
			return nil, err
		}
		e.Add("system", elements)
	case externalPathRE.MatchString(e.Config.Path):
		externalElements, err := e.GetExternalElements()
		if err != nil {
			return nil, err
		}
		e.Add("external", externalElements)
	case cloudPathRE.MatchString(e.Config.Path):
		if cloud != "" {
			cloudElements, err := e.GetCloudElements(cloud)
			if err != nil {
				return nil, err
			}
			e.Add("cloud", cloudElements)
		}
	default:
		elements, err := e.GetSystemElements()
		if err != nil {
			return nil, err
		}
		e.Add("system", elements)

		externalElements, err := e.GetExternalElements()
		if err != nil {
			return nil, err
		}
		e.Add("external", externalElements)

		if cloud != "" {
			cloudElements, err := e.GetCloudElements(cloud)
			if err != nil {
				return nil, err
			}
			e.Add("cloud", cloudElements)
		}
	}

	return e.ElementsAtPath()
}

func (e *Elements) ElementsAtPath() (interface{}, error) {
	var data interface{}
	var value interface{}
	path_pieces := strings.Split(e.Config.Path, ".")

	// Convert the Element structure into a generic interface{}
	// by first converting it to JSON and then decoding it.
	j, err := json.Marshal(e.Elements)
	if err != nil {
		return nil, err
	}

	d := json.NewDecoder(strings.NewReader(string(j)))
	d.UseNumber()
	if err := d.Decode(&data); err != nil {
		return nil, err
	}

	// Walk through the given path.
	// If there's a result, print it.
	if len(path_pieces) > 1 {
		for _, p := range path_pieces {
			i, err := strconv.Atoi(p)
			if err != nil {
				if _, ok := data.(map[string]interface{}); ok {
					value = data.(map[string]interface{})[p]
				} else {
					value = nil
				}
			} else {
				if _, ok := data.([]interface{}); ok {
					if len(data.([]interface{})) > i {
						value = data.([]interface{})[i]
					} else {
						value = nil
					}
				}
			}
			data = value
		}
	}

	return data, nil
}
<commit_msg>Ignore cloud elements for unsupported providers<commit_after>package elements

import (
	"encoding/json"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/erichs/cloudsysfs"
)

type Config struct {
	Directory string
	Listen    string
	Path      string
}

type Elements struct {
	Config   Config
	Elements map[string]interface{}
	mu       sync.Mutex
}

func (e *Elements) Add(key string, value interface{}) {
	e.mu.Lock()
	defer e.mu.Unlock()

	if e.Elements == nil {
		e.Elements = make(map[string]interface{})
	}

	e.Elements[key] = value
}

func (e *Elements) Get() (interface{}, error) {
	systemPathRE := regexp.MustCompile("^system")
	externalPathRE := regexp.MustCompile("^external")
	cloudPathRE := regexp.MustCompile("^cloud")

	cloud := cloudsysfs.Detect()

	switch {
	case systemPathRE.MatchString(e.Config.Path):
		elements, err := e.GetSystemElements()
		if err != nil {
			return nil, err
		}
		e.Add("system", elements)
	case externalPathRE.MatchString(e.Config.Path):
		externalElements, err := e.GetExternalElements()
		if err != nil {
			return nil, err
		}
		e.Add("external", externalElements)
	case cloudPathRE.MatchString(e.Config.Path):
		if cloud != "" {
			cloudElements, err := e.GetCloudElements(cloud)
			if err != nil {
				return nil, err
			}
			e.Add("cloud", cloudElements)
		}
	default:
		elements, err := e.GetSystemElements()
		if err != nil {
			return nil, err
		}
		e.Add("system", elements)

		externalElements, err := e.GetExternalElements()
		if err != nil {
			return nil, err
		}
		e.Add("external", externalElements)

		if cloud != "" {
			if cloudElements, err := e.GetCloudElements(cloud); err == nil {
				e.Add("cloud", cloudElements)
			}
		}
	}

	return e.ElementsAtPath()
}

func (e *Elements) ElementsAtPath() (interface{}, error) {
	var data interface{}
	var value interface{}
	path_pieces := strings.Split(e.Config.Path, ".")

	// Convert the Element structure into a generic interface{}
	// by first converting it to JSON and then decoding it.
	j, err := json.Marshal(e.Elements)
	if err != nil {
		return nil, err
	}

	d := json.NewDecoder(strings.NewReader(string(j)))
	d.UseNumber()
	if err := d.Decode(&data); err != nil {
		return nil, err
	}

	// Walk through the given path.
	// If there's a result, print it.
	if len(path_pieces) > 1 {
		for _, p := range path_pieces {
			i, err := strconv.Atoi(p)
			if err != nil {
				if _, ok := data.(map[string]interface{}); ok {
					value = data.(map[string]interface{})[p]
				} else {
					value = nil
				}
			} else {
				if _, ok := data.([]interface{}); ok {
					if len(data.([]interface{})) > i {
						value = data.([]interface{})[i]
					} else {
						value = nil
					}
				}
			}
			data = value
		}
	}

	return data, nil
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package loader

import (
	"bytes"
	"encoding/xml"
	"github.com/pkg/errors"
	"github.com/bboozzoo/q3stats/util"
	"io"
	"io/ioutil"
	"log"
	"os"
)

type RawStat struct {
	Name  string `xml:"name,attr"`
	Value int    `xml:"value,attr"`
}

type RawItem struct {
	Name    string `xml:"name,attr"`
	Pickups int    `xml:"pickups,attr"`
	Time    int    `xml:"time,attr"`
}

type RawWeapon struct {
	Name  string `xml:"name,attr"`
	Hits  int    `xml:"hits,attr"`
	Shots int    `xml:"shots,attr"`
	Kills int    `xml:"kills,attr"`
}

type RawPlayer struct {
	Name     string      `xml:"name,attr"`
	Stats    []RawStat   `xml:"stat"`
	Items    []RawItem   `xml:"items>item"`
	Weapons  []RawWeapon `xml:"weapons>weapon"`
	Powerups []RawItem   `xml:"powerups>item"`
}

type RawMatch struct {
	Datetime string      `xml:"datetime,attr"`
	Map      string      `xml:"map,attr"`
	Type     string      `xml:"type,attr"`
	Duration uint         `xml:"duration,attr"`
	Players  []RawPlayer `xml:"player"`
	DataHash string
}

func LoadMatch(src io.Reader) (*RawMatch, error) {
	raw, err := ioutil.ReadAll(src)
	if err != nil {
		return nil, errors.Wrap(err, "failed to load match data")
	}

	var match RawMatch
	err = xml.Unmarshal(raw, &match)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshal failed")
	}

	match.DataHash = util.DataHash(raw)

	log.Printf("match: %+v", match)
	return &match, nil
}

func LoadMatchFile(path string) (*RawMatch, error) {

	in, err := os.Open(path)
	if err != nil {
		return nil, errors.Wrap(err, "failed to open match file")
	}
	defer in.Close()

	return LoadMatch(in)
}

func LoadMatchData(data []byte) (*RawMatch, error) {
	buf := bytes.NewBuffer(data)
	return LoadMatch(buf)
}
<commit_msg>matchloader: gofmt<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package loader

import (
	"bytes"
	"encoding/xml"
	"github.com/bboozzoo/q3stats/util"
	"github.com/pkg/errors"
	"io"
	"io/ioutil"
	"log"
	"os"
)

type RawStat struct {
	Name  string `xml:"name,attr"`
	Value int    `xml:"value,attr"`
}

type RawItem struct {
	Name    string `xml:"name,attr"`
	Pickups int    `xml:"pickups,attr"`
	Time    int    `xml:"time,attr"`
}

type RawWeapon struct {
	Name  string `xml:"name,attr"`
	Hits  int    `xml:"hits,attr"`
	Shots int    `xml:"shots,attr"`
	Kills int    `xml:"kills,attr"`
}

type RawPlayer struct {
	Name     string      `xml:"name,attr"`
	Stats    []RawStat   `xml:"stat"`
	Items    []RawItem   `xml:"items>item"`
	Weapons  []RawWeapon `xml:"weapons>weapon"`
	Powerups []RawItem   `xml:"powerups>item"`
}

type RawMatch struct {
	Datetime string      `xml:"datetime,attr"`
	Map      string      `xml:"map,attr"`
	Type     string      `xml:"type,attr"`
	Duration uint        `xml:"duration,attr"`
	Players  []RawPlayer `xml:"player"`
	DataHash string
}

func LoadMatch(src io.Reader) (*RawMatch, error) {
	raw, err := ioutil.ReadAll(src)
	if err != nil {
		return nil, errors.Wrap(err, "failed to load match data")
	}

	var match RawMatch
	err = xml.Unmarshal(raw, &match)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshal failed")
	}

	match.DataHash = util.DataHash(raw)

	log.Printf("match: %+v", match)
	return &match, nil
}

func LoadMatchFile(path string) (*RawMatch, error) {

	in, err := os.Open(path)
	if err != nil {
		return nil, errors.Wrap(err, "failed to open match file")
	}
	defer in.Close()

	return LoadMatch(in)
}

func LoadMatchData(data []byte) (*RawMatch, error) {
	buf := bytes.NewBuffer(data)
	return LoadMatch(buf)
}
<|endoftext|>


<commit_before>package ui

import (
	"image"

	"github.com/fogleman/nes/nes"
	"github.com/go-gl/gl/v2.1/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

const padding = 0

type GameView struct {
	director *Director
	console  *nes.Console
	title    string
	texture  uint32
	record   bool
	frames   []image.Image
}

func NewGameView(director *Director, console *nes.Console, title string) View {
	texture := createTexture()
	return &GameView{director, console, title, texture, false, nil}
}

func (view *GameView) Enter() {
	gl.ClearColor(0, 0, 0, 1)
	view.director.SetTitle(view.title)
	view.console.SetAudioChannel(view.director.audio.channel)
	view.director.window.SetKeyCallback(view.onKey)
}

func (view *GameView) Exit() {
	view.director.window.SetKeyCallback(nil)
	view.console.SetAudioChannel(nil)
}

func (view *GameView) Update(t, dt float64) {
	window := view.director.window
	console := view.console
	if joystickReset(glfw.Joystick1) {
		view.director.ShowMenu()
	}
	if joystickReset(glfw.Joystick2) {
		view.director.ShowMenu()
	}
	if readKey(window, glfw.KeyEscape) {
		view.director.ShowMenu()
	}
	updateControllers(window, console)
	console.StepSeconds(dt)
	gl.BindTexture(gl.TEXTURE_2D, view.texture)
	setTexture(console.Buffer())
	drawBuffer(view.director.window)
	gl.BindTexture(gl.TEXTURE_2D, 0)
	if view.record {
		view.frames = append(view.frames, copyImage(console.Buffer()))
	}
}

func (view *GameView) onKey(window *glfw.Window,
	key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if action == glfw.Press {
		switch key {
		case glfw.KeySpace:
			screenshot(view.console.Buffer())
		case glfw.KeyR:
			view.console.Reset()
		case glfw.KeyTab:
			if view.record {
				view.record = false
				animation(view.frames)
				view.frames = nil
			} else {
				view.record = true
			}
		}
	}
}

func drawBuffer(window *glfw.Window) {
	w, h := window.GetFramebufferSize()
	s1 := float32(w) / float32(width)
	s2 := float32(h) / float32(height)
	f := float32(1 - padding)
	var x, y float32
	if s1 >= s2 {
		x = f * s2 / s1
		y = f
	} else {
		x = f
		y = f * s1 / s2
	}
	gl.Begin(gl.QUADS)
	gl.TexCoord2f(0, 1)
	gl.Vertex2f(-x, -y)
	gl.TexCoord2f(1, 1)
	gl.Vertex2f(x, -y)
	gl.TexCoord2f(1, 0)
	gl.Vertex2f(x, y)
	gl.TexCoord2f(0, 0)
	gl.Vertex2f(-x, y)
	gl.End()
}

func updateControllers(window *glfw.Window, console *nes.Console) {
	turbo := console.PPU.Frame%6 < 3
	k1 := readKeys(window, turbo)
	j1 := readJoystick(glfw.Joystick1, turbo)
	j2 := readJoystick(glfw.Joystick2, turbo)
	console.SetButtons1(combineButtons(k1, j1))
	console.SetButtons2(j2)
}
<commit_msg>clear large dt for after saving gif<commit_after>package ui

import (
	"image"

	"github.com/fogleman/nes/nes"
	"github.com/go-gl/gl/v2.1/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

const padding = 0

type GameView struct {
	director *Director
	console  *nes.Console
	title    string
	texture  uint32
	record   bool
	frames   []image.Image
}

func NewGameView(director *Director, console *nes.Console, title string) View {
	texture := createTexture()
	return &GameView{director, console, title, texture, false, nil}
}

func (view *GameView) Enter() {
	gl.ClearColor(0, 0, 0, 1)
	view.director.SetTitle(view.title)
	view.console.SetAudioChannel(view.director.audio.channel)
	view.director.window.SetKeyCallback(view.onKey)
}

func (view *GameView) Exit() {
	view.director.window.SetKeyCallback(nil)
	view.console.SetAudioChannel(nil)
}

func (view *GameView) Update(t, dt float64) {
	if dt > 1 {
		dt = 0
	}
	window := view.director.window
	console := view.console
	if joystickReset(glfw.Joystick1) {
		view.director.ShowMenu()
	}
	if joystickReset(glfw.Joystick2) {
		view.director.ShowMenu()
	}
	if readKey(window, glfw.KeyEscape) {
		view.director.ShowMenu()
	}
	updateControllers(window, console)
	console.StepSeconds(dt)
	gl.BindTexture(gl.TEXTURE_2D, view.texture)
	setTexture(console.Buffer())
	drawBuffer(view.director.window)
	gl.BindTexture(gl.TEXTURE_2D, 0)
	if view.record {
		view.frames = append(view.frames, copyImage(console.Buffer()))
	}
}

func (view *GameView) onKey(window *glfw.Window,
	key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if action == glfw.Press {
		switch key {
		case glfw.KeySpace:
			screenshot(view.console.Buffer())
		case glfw.KeyR:
			view.console.Reset()
		case glfw.KeyTab:
			if view.record {
				view.record = false
				animation(view.frames)
				view.frames = nil
			} else {
				view.record = true
			}
		}
	}
}

func drawBuffer(window *glfw.Window) {
	w, h := window.GetFramebufferSize()
	s1 := float32(w) / float32(width)
	s2 := float32(h) / float32(height)
	f := float32(1 - padding)
	var x, y float32
	if s1 >= s2 {
		x = f * s2 / s1
		y = f
	} else {
		x = f
		y = f * s1 / s2
	}
	gl.Begin(gl.QUADS)
	gl.TexCoord2f(0, 1)
	gl.Vertex2f(-x, -y)
	gl.TexCoord2f(1, 1)
	gl.Vertex2f(x, -y)
	gl.TexCoord2f(1, 0)
	gl.Vertex2f(x, y)
	gl.TexCoord2f(0, 0)
	gl.Vertex2f(-x, y)
	gl.End()
}

func updateControllers(window *glfw.Window, console *nes.Console) {
	turbo := console.PPU.Frame%6 < 3
	k1 := readKeys(window, turbo)
	j1 := readJoystick(glfw.Joystick1, turbo)
	j2 := readJoystick(glfw.Joystick2, turbo)
	console.SetButtons1(combineButtons(k1, j1))
	console.SetButtons2(j2)
}
<|endoftext|>


<commit_before>package apparmor

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/cgroup"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
)

// Internal copy of the instance interface.
type instance interface {
	Project() string
	Name() string
	ExpandedConfig() map[string]string
	Type() instancetype.Type
	LogPath() string
	Path() string
	DevPaths() []string
}

// InstanceProfileName returns the instance's AppArmor profile name.
func InstanceProfileName(inst instance) string {
	path := shared.VarPath("")
	name := fmt.Sprintf("%s_<%s>", project.Instance(inst.Project(), inst.Name()), path)
	return profileName("", name)
}

// InstanceNamespaceName returns the instance's AppArmor namespace.
func InstanceNamespaceName(inst instance) string {
	// Unlike in profile names, / isn't an allowed character so replace with a -.
	path := strings.Replace(strings.Trim(shared.VarPath(""), "/"), "/", "-", -1)
	name := fmt.Sprintf("%s_<%s>", project.Instance(inst.Project(), inst.Name()), path)
	return profileName("", name)
}

// instanceProfileFilename returns the name of the on-disk profile name.
func instanceProfileFilename(inst instance) string {
	name := project.Instance(inst.Project(), inst.Name())
	return profileName("", name)
}

// InstanceLoad ensures that the instances's policy is loaded into the kernel so the it can boot.
func InstanceLoad(state *state.State, inst instance) error {
	if inst.Type() == instancetype.Container {
		err := createNamespace(state, InstanceNamespaceName(inst))
		if err != nil {
			return err
		}
	}

	/* In order to avoid forcing a profile parse (potentially slow) on
	 * every container start, let's use AppArmor's binary policy cache,
	 * which checks mtime of the files to figure out if the policy needs to
	 * be regenerated.
	 *
	 * Since it uses mtimes, we shouldn't just always write out our local
	 * AppArmor template; instead we should check to see whether the
	 * template is the same as ours. If it isn't we should write our
	 * version out so that the new changes are reflected and we definitely
	 * force a recompile.
	 */
	profile := filepath.Join(aaPath, "profiles", instanceProfileFilename(inst))
	content, err := ioutil.ReadFile(profile)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	updated, err := instanceProfile(state, inst)
	if err != nil {
		return err
	}

	if string(content) != string(updated) {
		err = ioutil.WriteFile(profile, []byte(updated), 0600)
		if err != nil {
			return err
		}
	}

	err = loadProfile(state, instanceProfileFilename(inst))
	if err != nil {
		return err
	}

	return nil
}

// InstanceUnload ensures that the instances's policy namespace is unloaded to free kernel memory.
// This does not delete the policy from disk or cache.
func InstanceUnload(state *state.State, inst instance) error {
	if inst.Type() == instancetype.Container {
		err := deleteNamespace(state, InstanceNamespaceName(inst))
		if err != nil {
			return err
		}
	}

	err := unloadProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst))
	if err != nil {
		return err
	}

	return nil
}

// InstanceParse validates the instance profile.
func InstanceParse(state *state.State, inst instance) error {
	return parseProfile(state, instanceProfileFilename(inst))
}

// InstanceDelete removes the policy from cache/disk.
func InstanceDelete(state *state.State, inst instance) error {
	return deleteProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst))
}

// instanceProfile generates the AppArmor profile template from the given instance.
func instanceProfile(state *state.State, inst instance) (string, error) {
	// Prepare raw.apparmor.
	rawContent := ""
	rawApparmor, ok := inst.ExpandedConfig()["raw.apparmor"]
	if ok {
		for _, line := range strings.Split(strings.Trim(rawApparmor, "\n"), "\n") {
			rawContent += fmt.Sprintf("  %s\n", line)
		}
	}

	// Check for features.
	unixSupported, err := parserSupports(state, "unix")
	if err != nil {
		return "", err
	}

	// Render the profile.
	var sb *strings.Builder = &strings.Builder{}
	if inst.Type() == instancetype.Container {
		err = lxcProfileTpl.Execute(sb, map[string]interface{}{
			"feature_cgns":     state.OS.CGInfo.Namespacing,
			"feature_cgroup2":  state.OS.CGInfo.Layout == cgroup.CgroupsUnified || state.OS.CGInfo.Layout == cgroup.CgroupsHybrid,
			"feature_stacking": state.OS.AppArmorStacking && !state.OS.AppArmorStacked,
			"feature_unix":     unixSupported,
			"name":             InstanceProfileName(inst),
			"namespace":        InstanceNamespaceName(inst),
			"nesting":          shared.IsTrue(inst.ExpandedConfig()["security.nesting"]),
			"raw":              rawContent,
			"unprivileged":     !shared.IsTrue(inst.ExpandedConfig()["security.privileged"]) || state.OS.RunningInUserNS,
		})
		if err != nil {
			return "", err
		}
	} else {
		rootPath := ""
		if shared.InSnap() {
			rootPath = "/var/lib/snapd/hostfs"
		}

		// AppArmor requires deref of all paths.
		path, err := filepath.EvalSymlinks(inst.Path())
		if err != nil {
			return "", err
		}

		devPaths := inst.DevPaths()
		for i := range devPaths {
			devPaths[i], err = filepath.EvalSymlinks(devPaths[i])
			if err != nil {
				return "", err
			}
		}

		ovmfPath := "/usr/share/OVMF"
		if os.Getenv("LXD_OVMF_PATH") != "" {
			ovmfPath = os.Getenv("LXD_OVMF_PATH")
		}

		err = qemuProfileTpl.Execute(sb, map[string]interface{}{
			"devPaths":    devPaths,
			"exePath":     util.GetExecPath(),
			"libraryPath": strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":"),
			"logPath":     inst.LogPath(),
			"name":        InstanceProfileName(inst),
			"path":        path,
			"raw":         rawContent,
			"rootPath":    rootPath,
			"snap":        shared.InSnap(),
			"ovmfPath":    ovmfPath,
		})
		if err != nil {
			return "", err
		}
	}

	return sb.String(), nil
}
<commit_msg>lxd/apparmor/instance: Deref OVMF path<commit_after>package apparmor

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/cgroup"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
)

// Internal copy of the instance interface.
type instance interface {
	Project() string
	Name() string
	ExpandedConfig() map[string]string
	Type() instancetype.Type
	LogPath() string
	Path() string
	DevPaths() []string
}

// InstanceProfileName returns the instance's AppArmor profile name.
func InstanceProfileName(inst instance) string {
	path := shared.VarPath("")
	name := fmt.Sprintf("%s_<%s>", project.Instance(inst.Project(), inst.Name()), path)
	return profileName("", name)
}

// InstanceNamespaceName returns the instance's AppArmor namespace.
func InstanceNamespaceName(inst instance) string {
	// Unlike in profile names, / isn't an allowed character so replace with a -.
	path := strings.Replace(strings.Trim(shared.VarPath(""), "/"), "/", "-", -1)
	name := fmt.Sprintf("%s_<%s>", project.Instance(inst.Project(), inst.Name()), path)
	return profileName("", name)
}

// instanceProfileFilename returns the name of the on-disk profile name.
func instanceProfileFilename(inst instance) string {
	name := project.Instance(inst.Project(), inst.Name())
	return profileName("", name)
}

// InstanceLoad ensures that the instances's policy is loaded into the kernel so the it can boot.
func InstanceLoad(state *state.State, inst instance) error {
	if inst.Type() == instancetype.Container {
		err := createNamespace(state, InstanceNamespaceName(inst))
		if err != nil {
			return err
		}
	}

	/* In order to avoid forcing a profile parse (potentially slow) on
	 * every container start, let's use AppArmor's binary policy cache,
	 * which checks mtime of the files to figure out if the policy needs to
	 * be regenerated.
	 *
	 * Since it uses mtimes, we shouldn't just always write out our local
	 * AppArmor template; instead we should check to see whether the
	 * template is the same as ours. If it isn't we should write our
	 * version out so that the new changes are reflected and we definitely
	 * force a recompile.
	 */
	profile := filepath.Join(aaPath, "profiles", instanceProfileFilename(inst))
	content, err := ioutil.ReadFile(profile)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	updated, err := instanceProfile(state, inst)
	if err != nil {
		return err
	}

	if string(content) != string(updated) {
		err = ioutil.WriteFile(profile, []byte(updated), 0600)
		if err != nil {
			return err
		}
	}

	err = loadProfile(state, instanceProfileFilename(inst))
	if err != nil {
		return err
	}

	return nil
}

// InstanceUnload ensures that the instances's policy namespace is unloaded to free kernel memory.
// This does not delete the policy from disk or cache.
func InstanceUnload(state *state.State, inst instance) error {
	if inst.Type() == instancetype.Container {
		err := deleteNamespace(state, InstanceNamespaceName(inst))
		if err != nil {
			return err
		}
	}

	err := unloadProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst))
	if err != nil {
		return err
	}

	return nil
}

// InstanceParse validates the instance profile.
func InstanceParse(state *state.State, inst instance) error {
	return parseProfile(state, instanceProfileFilename(inst))
}

// InstanceDelete removes the policy from cache/disk.
func InstanceDelete(state *state.State, inst instance) error {
	return deleteProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst))
}

// instanceProfile generates the AppArmor profile template from the given instance.
func instanceProfile(state *state.State, inst instance) (string, error) {
	// Prepare raw.apparmor.
	rawContent := ""
	rawApparmor, ok := inst.ExpandedConfig()["raw.apparmor"]
	if ok {
		for _, line := range strings.Split(strings.Trim(rawApparmor, "\n"), "\n") {
			rawContent += fmt.Sprintf("  %s\n", line)
		}
	}

	// Check for features.
	unixSupported, err := parserSupports(state, "unix")
	if err != nil {
		return "", err
	}

	// Render the profile.
	var sb *strings.Builder = &strings.Builder{}
	if inst.Type() == instancetype.Container {
		err = lxcProfileTpl.Execute(sb, map[string]interface{}{
			"feature_cgns":     state.OS.CGInfo.Namespacing,
			"feature_cgroup2":  state.OS.CGInfo.Layout == cgroup.CgroupsUnified || state.OS.CGInfo.Layout == cgroup.CgroupsHybrid,
			"feature_stacking": state.OS.AppArmorStacking && !state.OS.AppArmorStacked,
			"feature_unix":     unixSupported,
			"name":             InstanceProfileName(inst),
			"namespace":        InstanceNamespaceName(inst),
			"nesting":          shared.IsTrue(inst.ExpandedConfig()["security.nesting"]),
			"raw":              rawContent,
			"unprivileged":     !shared.IsTrue(inst.ExpandedConfig()["security.privileged"]) || state.OS.RunningInUserNS,
		})
		if err != nil {
			return "", err
		}
	} else {
		rootPath := ""
		if shared.InSnap() {
			rootPath = "/var/lib/snapd/hostfs"
		}

		// AppArmor requires deref of all paths.
		path, err := filepath.EvalSymlinks(inst.Path())
		if err != nil {
			return "", err
		}

		devPaths := inst.DevPaths()
		for i := range devPaths {
			devPaths[i], err = filepath.EvalSymlinks(devPaths[i])
			if err != nil {
				return "", err
			}
		}

		ovmfPath := "/usr/share/OVMF"
		if os.Getenv("LXD_OVMF_PATH") != "" {
			ovmfPath = os.Getenv("LXD_OVMF_PATH")
		}

		ovmfPath, err = filepath.EvalSymlinks(ovmfPath)
		if err != nil {
			return "", err
		}

		err = qemuProfileTpl.Execute(sb, map[string]interface{}{
			"devPaths":    devPaths,
			"exePath":     util.GetExecPath(),
			"libraryPath": strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":"),
			"logPath":     inst.LogPath(),
			"name":        InstanceProfileName(inst),
			"path":        path,
			"raw":         rawContent,
			"rootPath":    rootPath,
			"snap":        shared.InSnap(),
			"ovmfPath":    ovmfPath,
		})
		if err != nil {
			return "", err
		}
	}

	return sb.String(), nil
}
<|endoftext|>