<commit_before>package cmd

import (
	"fmt"
	"github.com/avarabyeu/releaser/util"
	"github.com/spf13/cobra"
	"gopkg.in/cheggaaa/pb.v1"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"sync"
	"time"
	"html/template"
	"encoding/json"
	"bytes"
	"github.com/juju/errgo/errors"
)

const (
	refreshRate  = time.Millisecond * 10
	btVersionURL = "https://api.bintray.com/packages/{{.org}}/{{.repo}}/{{.package}}/versions"
)

var btVersionTemplate *template.Template

func init() {
	btVersionTemplate, _ = template.New("btVersionTMPL").Parse(btVersionURL)
}

var bintrayCommand = &cobra.Command{
	Use:   "bintray",
	Short: "Uploads to bintray",
	Long:  `Uploads artifacts to bintray`,
	Run: func(cmd *cobra.Command, args []string) {
		err := uploadToBintray(cmd)
		if nil != err {
			log.Fatalf("Cannot upload to bintray. %s", err)
		}
	},
}

func uploadToBintray(cmd *cobra.Command) error {
	wd, err := os.Getwd()
	if nil != err {
		log.Printf("Cannot get work dir! %s", err.Error())
		return err
	}

	err = checkData(config.Bintray)
	if nil != err {
		log.Printf("BintrayConf data isn't provided: %s", err.Error())
		return err
	}

	artifactsFolder := path.Join(wd, cmd.Flag("artifactsFolder").Value.String())
	files, _ := ioutil.ReadDir(artifactsFolder)

	if len(files) > 0 {
		er := createVersion(GetSemver(cmd).Version.String(), config.Bintray)
		if nil != er {
			return er
		}

	}

	// update bars
	pool, err := pb.StartPool()
	if err != nil {
		return err
	}

	wg := new(sync.WaitGroup)
	for _, f := range files {
		wg.Add(1)
		log.Print(f.Name())
		go uploadFile(GetSemver(cmd).Current(), path.Join(artifactsFolder, f.Name()), config.Bintray, wg, pool)
	}

	wg.Wait()
	pool.Stop()
	return nil
}

func createVersion(version string, bd *BintrayConf) error {
	details := map[string]string{"name": version, "desc": "New version"}
	rqBody, err := json.Marshal(details)
	if nil != err {
		return err
	}

	url := new(bytes.Buffer)
	btVersionTemplate.Execute(url, map[string]string{"org": bd.Org, "repo": bd.Repo, "package": bd.Pack})

	log.Println(url.String())
	rq, err := http.NewRequest("POST", url.String(), bytes.NewBuffer(rqBody))
	if err != nil {
		return err
	}
	rq.Header.Set("Authorization", bd.getAuth())
	rq.Header.Set("Content-Type", "application/json")

	rs, err := http.DefaultClient.Do(rq)
	if err != nil {
		return err
	}
	defer rs.Body.Close()

	if rs.StatusCode/100 != 2 {
		body, _ := ioutil.ReadAll(rs.Body)
		fmt.Println("Error:", string(body))
		return errors.Newf("Cannot create new version. Status code: %d", rs.StatusCode)
	}

	return nil
}

func uploadFile(version string, filePath string, bt *BintrayConf, wg *sync.WaitGroup, pool *pb.Pool) {
	defer wg.Done()
	var err error
	var f *os.File
	var fi os.FileInfo

	var bar *pb.ProgressBar

	if f, err = os.Open(filePath); err != nil {
		log.Fatal(err)
	}
	if fi, err = f.Stat(); err != nil {
		log.Fatal(err)
	}
	bar = pb.New64(fi.Size()).Prefix(util.SubstrAfterLast(f.Name(), "/")).SetUnits(pb.U_BYTES).SetRefreshRate(refreshRate)
	pool.Add(bar)

	r, w := io.Pipe()
	mpw := multipart.NewWriter(w)
	go func() {
		var part io.Writer
		defer w.Close()
		defer f.Close()

		if part, err = mpw.CreateFormFile("file", fi.Name()); err != nil {
			log.Fatal(err)
		}
		part = io.MultiWriter(part, bar)
		if _, err = io.Copy(part, f); err != nil {
			log.Fatal(err)
		}
		if err = mpw.Close(); err != nil {
			log.Fatal(err)
		}
	}()

	rq, err := http.NewRequest("PUT", fmt.Sprintf("https://api.bintray.com/content/%s/%s/%s/%s", bt.User, bt.Repo, version, fi.Name()), r)
	rq.Header.Set("Content-Type", mpw.FormDataContentType())
	rq.Header.Set("X-Bintray-Package", bt.Pack)
	rq.Header.Set("X-Bintray-Version", version)
	rq.Header.Set("X-Bintray-Override", "1")
	rq.Header.Set("X-Bintray-Publish", "1")
	rq.Header.Set("Authorization", bt.getAuth())

	resp, err := http.DefaultClient.Do(rq)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	rs, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	} else {
		log.Println(string(rs))
	}
}

func checkData(btData *BintrayConf) error {
	var errs string

	if "" == btData.Org {
		errs += "BintrayConf organization not set\n"
	}

	if "" == btData.User {
		errs += "BintrayConf user not set\n"
	}

	if "" == btData.Token {
		errs += "BintrayConf token not set\n"
	}

	if "" == btData.Pack {
		errs += "BintrayConf package not set\n"
	}

	if "" == btData.Repo {
		errs += "BintrayConf repo not set\n"
	}

	if "" != errs {
		log.Fatalf("Validation failed: \n%s", errs)
		return errors.New(errs)
	}

	return nil

}
<commit_msg>fix possible issue with bintray upload<commit_after>package cmd

import (
	"fmt"
	"github.com/avarabyeu/releaser/util"
	"github.com/spf13/cobra"
	"gopkg.in/cheggaaa/pb.v1"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"sync"
	"time"
	"html/template"
	"encoding/json"
	"bytes"
	"github.com/juju/errgo/errors"
)

const (
	refreshRate  = time.Millisecond * 10
	btVersionURL = "https://api.bintray.com/packages/{{.org}}/{{.repo}}/{{.package}}/versions"
)

var btVersionTemplate *template.Template

func init() {
	btVersionTemplate, _ = template.New("btVersionTMPL").Parse(btVersionURL)
}

var bintrayCommand = &cobra.Command{
	Use:   "bintray",
	Short: "Uploads to bintray",
	Long:  `Uploads artifacts to bintray`,
	Run: func(cmd *cobra.Command, args []string) {
		err := uploadToBintray(cmd)
		if nil != err {
			log.Fatalf("Cannot upload to bintray. %s", err)
		}
	},
}

func uploadToBintray(cmd *cobra.Command) error {
	wd, err := os.Getwd()
	if nil != err {
		log.Printf("Cannot get work dir! %s", err.Error())
		return err
	}

	err = checkData(config.Bintray)
	if nil != err {
		log.Printf("BintrayConf data isn't provided: %s", err.Error())
		return err
	}

	artifactsFolder := path.Join(wd, cmd.Flag("artifactsFolder").Value.String())
	files, _ := ioutil.ReadDir(artifactsFolder)

	if len(files) > 0 {
		er := createVersion(GetSemver(cmd).Version.String(), config.Bintray)
		if nil != er {
			return er
		}

	}

	// update bars
	pool, err := pb.StartPool()
	if err != nil {
		return err
	}

	wg := new(sync.WaitGroup)
	for _, f := range files {
		wg.Add(1)
		log.Print(f.Name())
		go uploadFile(GetSemver(cmd).Current(), path.Join(artifactsFolder, f.Name()), config.Bintray, wg, pool)
	}

	wg.Wait()
	pool.Stop()
	return nil
}

func createVersion(version string, bd *BintrayConf) error {
	details := map[string]string{"name": version, "desc": "New version"}
	rqBody, err := json.Marshal(details)
	if nil != err {
		return err
	}

	url := new(bytes.Buffer)
	btVersionTemplate.Execute(url, map[string]string{"org": bd.Org, "repo": bd.Repo, "package": bd.Pack})

	log.Println(url.String())
	rq, err := http.NewRequest("POST", url.String(), bytes.NewBuffer(rqBody))
	if err != nil {
		return err
	}
	rq.Header.Set("Authorization", bd.getAuth())
	rq.Header.Set("Content-Type", "application/json")

	rs, err := http.DefaultClient.Do(rq)
	if err != nil {
		return err
	}
	defer rs.Body.Close()

	if rs.StatusCode/100 != 2 {
		body, _ := ioutil.ReadAll(rs.Body)
		fmt.Println("Error:", string(body))
		return errors.Newf("Cannot create new version. Status code: %d", rs.StatusCode)
	}

	return nil
}

func uploadFile(version string, filePath string, bt *BintrayConf, wg *sync.WaitGroup, pool *pb.Pool) {
	defer wg.Done()
	var err error
	var f *os.File
	var fi os.FileInfo

	var bar *pb.ProgressBar

	if f, err = os.Open(filePath); err != nil {
		log.Fatal(err)
	}
	if fi, err = f.Stat(); err != nil {
		log.Fatal(err)
	}
	bar = pb.New64(fi.Size()).Prefix(util.SubstrAfterLast(f.Name(), "/")).SetUnits(pb.U_BYTES).SetRefreshRate(refreshRate)
	pool.Add(bar)

	r, w := io.Pipe()
	mpw := multipart.NewWriter(w)
	go func() {
		var part io.Writer
		defer w.Close()
		defer f.Close()

		if part, err = mpw.CreateFormFile("file", fi.Name()); err != nil {
			log.Fatal(err)
		}
		part = io.MultiWriter(part, bar)
		if _, err = io.Copy(part, f); err != nil {
			log.Fatal(err)
		}
		if err = mpw.Close(); err != nil {
			log.Fatal(err)
		}
	}()

	rq, err := http.NewRequest("PUT", fmt.Sprintf("https://api.bintray.com/content/%s/%s/%s/%s", bt.Org, bt.Repo, version, fi.Name()), r)
	rq.Header.Set("Content-Type", mpw.FormDataContentType())
	rq.Header.Set("X-Bintray-Package", bt.Pack)
	rq.Header.Set("X-Bintray-Version", version)
	rq.Header.Set("X-Bintray-Override", "1")
	rq.Header.Set("X-Bintray-Publish", "1")
	rq.Header.Set("Authorization", bt.getAuth())

	resp, err := http.DefaultClient.Do(rq)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	rs, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	} else {
		log.Println(string(rs))
	}
}

func checkData(btData *BintrayConf) error {
	var errs string

	if "" == btData.Org {
		errs += "BintrayConf organization not set\n"
	}

	if "" == btData.User {
		errs += "BintrayConf user not set\n"
	}

	if "" == btData.Token {
		errs += "BintrayConf token not set\n"
	}

	if "" == btData.Pack {
		errs += "BintrayConf package not set\n"
	}

	if "" == btData.Repo {
		errs += "BintrayConf repo not set\n"
	}

	if "" != errs {
		log.Fatalf("Validation failed: \n%s", errs)
		return errors.New(errs)
	}

	return nil

}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

import (
	"strings"

	"github.com/kubernetes-incubator/kompose/pkg/app"
	"github.com/kubernetes-incubator/kompose/pkg/kobject"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// TODO: comment
var (
	ConvertSource                string
	ConvertOut                   string
	ConvertBuildRepo             string
	ConvertBuildBranch           string
	ConvertChart                 bool
	ConvertDeployment            bool
	ConvertDaemonSet             bool
	ConvertReplicationController bool
	ConvertYaml                  bool
	ConvertJSON                  bool
	ConvertStdout                bool
	ConvertEmptyVols             bool
	ConvertDeploymentConfig      bool
	ConvertBuildConfig           bool
	ConvertReplicas              int
	ConvertOpt                   kobject.ConvertOptions
)

// ConvertProvider TODO: comment
var ConvertProvider = GlobalProvider

var convertCmd = &cobra.Command{
	Use:   "convert [file]",
	Short: "Convert a Docker Compose file",
	PreRun: func(cmd *cobra.Command, args []string) {

		// Create the Convert Options.
		ConvertOpt = kobject.ConvertOptions{
			ToStdout:                    ConvertStdout,
			CreateChart:                 ConvertChart,
			GenerateYaml:                ConvertYaml,
			GenerateJSON:                ConvertJSON,
			Replicas:                    ConvertReplicas,
			InputFiles:                  GlobalFiles,
			OutFile:                     ConvertOut,
			Provider:                    strings.ToLower(GlobalProvider),
			CreateD:                     ConvertDeployment,
			CreateDS:                    ConvertDaemonSet,
			CreateRC:                    ConvertReplicationController,
			BuildRepo:                   ConvertBuildRepo,
			BuildBranch:                 ConvertBuildBranch,
			CreateDeploymentConfig:      ConvertDeploymentConfig,
			EmptyVols:                   ConvertEmptyVols,
			IsDeploymentFlag:            cmd.Flags().Lookup("deployment").Changed,
			IsDaemonSetFlag:             cmd.Flags().Lookup("daemon-set").Changed,
			IsReplicationControllerFlag: cmd.Flags().Lookup("replication-controller").Changed,
			IsDeploymentConfigFlag:      cmd.Flags().Lookup("deployment-config").Changed,
		}

		// Validate before doing anything else. Use "bundle" if passed in.
		app.ValidateFlags(GlobalBundle, args, cmd, &ConvertOpt)
		app.ValidateComposeFile(cmd, &ConvertOpt)
	},
	Run: func(cmd *cobra.Command, args []string) {

		app.Convert(ConvertOpt)
	},
}

func init() {

	// Automatically grab environment variables
	viper.AutomaticEnv()

	// Kubernetes only
	convertCmd.Flags().BoolVarP(&ConvertChart, "chart", "c", false, "Create a Helm chart for converted objects")
	convertCmd.Flags().BoolVar(&ConvertDaemonSet, "daemon-set", false, "Generate a Kubernetes daemonset object")
	convertCmd.Flags().BoolVarP(&ConvertDeployment, "deployment", "d", false, "Generate a Kubernetes deployment object")
	convertCmd.Flags().BoolVar(&ConvertReplicationController, "replication-controller", false, "Generate a Kubernetes replication controller object")
	convertCmd.Flags().MarkHidden("chart")
	convertCmd.Flags().MarkHidden("daemon-set")
	convertCmd.Flags().MarkHidden("replication-controller")
	convertCmd.Flags().MarkHidden("deployment")

	// OpenShift only
	convertCmd.Flags().BoolVar(&ConvertDeploymentConfig, "deployment-config", true, "Generate an OpenShift deploymentconfig object")
	convertCmd.Flags().MarkHidden("deployment-config")
	convertCmd.Flags().StringVar(&ConvertBuildRepo, "build-repo", "", "Specify source repository for buildconfig (default remote origin)")
	convertCmd.Flags().MarkHidden("build-repo")
	convertCmd.Flags().StringVar(&ConvertBuildBranch, "build-branch", "", "Specify repository branch to use for buildconfig (default master)")
	convertCmd.Flags().MarkHidden("build-branch")

	// Standard between the two
	convertCmd.Flags().BoolVarP(&ConvertYaml, "yaml", "y", false, "Generate resource files into YAML format")
	convertCmd.Flags().MarkDeprecated("yaml", "YAML is the default format now.")
	convertCmd.Flags().MarkShorthandDeprecated("y", "YAML is the default format now.")
	convertCmd.Flags().BoolVarP(&ConvertJSON, "json", "j", false, "Generate resource files into JSON format")
	convertCmd.Flags().BoolVar(&ConvertStdout, "stdout", false, "Print converted objects to stdout")
	convertCmd.Flags().BoolVar(&ConvertEmptyVols, "emptyvols", false, "Use Empty Volumes. Do not generate PVCs")
	convertCmd.Flags().StringVarP(&ConvertOut, "out", "o", "", "Specify a file name to save objects to")
	convertCmd.Flags().IntVar(&ConvertReplicas, "replicas", 1, "Specify the number of repliaces in the generate resource spec")

	// In order to 'separate' both OpenShift and Kubernetes only flags. A custom help page is created
	customHelp := `Usage:{{if .Runnable}}
  {{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
  {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}

Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}

Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}

Resource Flags:
      --build-branch             Specify repository branch to use for buildconfig (default is current branch name)
      --build-repo               Specify source repository for buildconfig (default is current branch's remote url
  -c, --chart                    Create a Helm chart for converted objects
      --daemon-set               Generate a Kubernetes daemonset object
  -d, --deployment               Generate a Kubernetes deployment object
      --deployment-config        Generate an OpenShift deployment config object
      --replication-controller   Generate a Kubernetes replication controller object

Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
	// Set the help template + add the command to root
	convertCmd.SetHelpTemplate(customHelp)

	RootCmd.AddCommand(convertCmd)
}
<commit_msg>Removing unused variable from convert.go<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package cmd

import (
	"strings"

	"github.com/kubernetes-incubator/kompose/pkg/app"
	"github.com/kubernetes-incubator/kompose/pkg/kobject"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// TODO: comment
var (
	ConvertOut                   string
	ConvertBuildRepo             string
	ConvertBuildBranch           string
	ConvertChart                 bool
	ConvertDeployment            bool
	ConvertDaemonSet             bool
	ConvertReplicationController bool
	ConvertYaml                  bool
	ConvertJSON                  bool
	ConvertStdout                bool
	ConvertEmptyVols             bool
	ConvertDeploymentConfig      bool
	ConvertReplicas              int
	ConvertOpt                   kobject.ConvertOptions
)

// ConvertProvider TODO: comment
var ConvertProvider = GlobalProvider

var convertCmd = &cobra.Command{
	Use:   "convert [file]",
	Short: "Convert a Docker Compose file",
	PreRun: func(cmd *cobra.Command, args []string) {

		// Create the Convert Options.
		ConvertOpt = kobject.ConvertOptions{
			ToStdout:                    ConvertStdout,
			CreateChart:                 ConvertChart,
			GenerateYaml:                ConvertYaml,
			GenerateJSON:                ConvertJSON,
			Replicas:                    ConvertReplicas,
			InputFiles:                  GlobalFiles,
			OutFile:                     ConvertOut,
			Provider:                    strings.ToLower(GlobalProvider),
			CreateD:                     ConvertDeployment,
			CreateDS:                    ConvertDaemonSet,
			CreateRC:                    ConvertReplicationController,
			BuildRepo:                   ConvertBuildRepo,
			BuildBranch:                 ConvertBuildBranch,
			CreateDeploymentConfig:      ConvertDeploymentConfig,
			EmptyVols:                   ConvertEmptyVols,
			IsDeploymentFlag:            cmd.Flags().Lookup("deployment").Changed,
			IsDaemonSetFlag:             cmd.Flags().Lookup("daemon-set").Changed,
			IsReplicationControllerFlag: cmd.Flags().Lookup("replication-controller").Changed,
			IsDeploymentConfigFlag:      cmd.Flags().Lookup("deployment-config").Changed,
		}

		// Validate before doing anything else. Use "bundle" if passed in.
		app.ValidateFlags(GlobalBundle, args, cmd, &ConvertOpt)
		app.ValidateComposeFile(cmd, &ConvertOpt)
	},
	Run: func(cmd *cobra.Command, args []string) {

		app.Convert(ConvertOpt)
	},
}

func init() {

	// Automatically grab environment variables
	viper.AutomaticEnv()

	// Kubernetes only
	convertCmd.Flags().BoolVarP(&ConvertChart, "chart", "c", false, "Create a Helm chart for converted objects")
	convertCmd.Flags().BoolVar(&ConvertDaemonSet, "daemon-set", false, "Generate a Kubernetes daemonset object")
	convertCmd.Flags().BoolVarP(&ConvertDeployment, "deployment", "d", false, "Generate a Kubernetes deployment object")
	convertCmd.Flags().BoolVar(&ConvertReplicationController, "replication-controller", false, "Generate a Kubernetes replication controller object")
	convertCmd.Flags().MarkHidden("chart")
	convertCmd.Flags().MarkHidden("daemon-set")
	convertCmd.Flags().MarkHidden("replication-controller")
	convertCmd.Flags().MarkHidden("deployment")

	// OpenShift only
	convertCmd.Flags().BoolVar(&ConvertDeploymentConfig, "deployment-config", true, "Generate an OpenShift deploymentconfig object")
	convertCmd.Flags().MarkHidden("deployment-config")
	convertCmd.Flags().StringVar(&ConvertBuildRepo, "build-repo", "", "Specify source repository for buildconfig (default remote origin)")
	convertCmd.Flags().MarkHidden("build-repo")
	convertCmd.Flags().StringVar(&ConvertBuildBranch, "build-branch", "", "Specify repository branch to use for buildconfig (default master)")
	convertCmd.Flags().MarkHidden("build-branch")

	// Standard between the two
	convertCmd.Flags().BoolVarP(&ConvertYaml, "yaml", "y", false, "Generate resource files into YAML format")
	convertCmd.Flags().MarkDeprecated("yaml", "YAML is the default format now.")
	convertCmd.Flags().MarkShorthandDeprecated("y", "YAML is the default format now.")
	convertCmd.Flags().BoolVarP(&ConvertJSON, "json", "j", false, "Generate resource files into JSON format")
	convertCmd.Flags().BoolVar(&ConvertStdout, "stdout", false, "Print converted objects to stdout")
	convertCmd.Flags().BoolVar(&ConvertEmptyVols, "emptyvols", false, "Use Empty Volumes. Do not generate PVCs")
	convertCmd.Flags().StringVarP(&ConvertOut, "out", "o", "", "Specify a file name to save objects to")
	convertCmd.Flags().IntVar(&ConvertReplicas, "replicas", 1, "Specify the number of repliaces in the generate resource spec")

	// In order to 'separate' both OpenShift and Kubernetes only flags. A custom help page is created
	customHelp := `Usage:{{if .Runnable}}
  {{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
  {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}

Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}

Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}

Resource Flags:
      --build-branch             Specify repository branch to use for buildconfig (default is current branch name)
      --build-repo               Specify source repository for buildconfig (default is current branch's remote url
  -c, --chart                    Create a Helm chart for converted objects
      --daemon-set               Generate a Kubernetes daemonset object
  -d, --deployment               Generate a Kubernetes deployment object
      --deployment-config        Generate an OpenShift deployment config object
      --replication-controller   Generate a Kubernetes replication controller object

Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
	// Set the help template + add the command to root
	convertCmd.SetHelpTemplate(customHelp)

	RootCmd.AddCommand(convertCmd)
}
<|endoftext|>


<commit_before>package gen

import (
	"encoding/json"

	"github.com/gernest/valeria/gen"
	"github.com/gernest/valeria/vm"
	"github.com/spf13/afero"
)

const (
	scriptsDir = "_scripts"
	initDri    = "init"
	planDir    = "plan"
	configFile = "config.json"
)

type BuildError struct {
	Stage   string `json:"stage"`
	Message string `json:"msg"`
}

func (b BuildError) Error() string {
	o, err := json.Marshal(b)
	if err != nil {
		return err.Error()
	}
	return string(o)
}

type Generator struct {
	vm  *vm.VM
	sys *gen.System
	fs  afero.Fs
}

func (g *Generator) Build() error {
	return evaluate(g.init, g.config, g.plan, g.exec, g.down)
}
func (g *Generator) init() error {
	return nil
}
func (g *Generator) config() error {
	return nil
}
func (g *Generator) plan() error {
	return nil
}
func (g *Generator) exec() error {
	return nil
}
func (g *Generator) down() error {
	return nil
}

func evaluate(fn ...func() error) error {
	var err error
	for _, f := range fn {
		err = f()
		if err != nil {
			return err
		}
	}
	return err
}
<commit_msg>Make *BuldError implement error interface<commit_after>package gen

import (
	"encoding/json"

	"github.com/gernest/valeria/gen"
	"github.com/gernest/valeria/vm"
	"github.com/spf13/afero"
)

const (
	scriptsDir = "_scripts"
	initDri    = "init"
	planDir    = "plan"
	configFile = "config.json"
)

type BuildError struct {
	Stage   string `json:"stage"`
	Message string `json:"msg"`
}

func (b *BuildError) Error() string {
	o, err := json.Marshal(b)
	if err != nil {
		return err.Error()
	}
	return string(o)
}

type Generator struct {
	vm  *vm.VM
	sys *gen.System
	fs  afero.Fs
}

func (g *Generator) Build() error {
	return evaluate(g.init, g.config, g.plan, g.exec, g.down)
}
func (g *Generator) init() error {
	return nil
}
func (g *Generator) config() error {
	return nil
}
func (g *Generator) plan() error {
	return nil
}
func (g *Generator) exec() error {
	return nil
}
func (g *Generator) down() error {
	return nil
}

func evaluate(fn ...func() error) error {
	var err error
	for _, f := range fn {
		err = f()
		if err != nil {
			return err
		}
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. 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 main

import (
	"flag"
	"fmt"

	ui "github.com/gizak/termui"
)

var (
	target = flag.String("target", "http://localhost:6060", "the target process to profile; it has to enable pprof debug server")

	prompt  *ui.Par
	ls      *ui.List
	sp      *ui.Sparklines
	display *ui.Par

	cpuProfile     = &Report{name: "profile", secs: 30}
	heapProfile    = &Report{name: "heap"}
	currentProfile = heapProfile

	reportItems []string
	promptMsg   string
	filter      string
)

func main() {
	flag.Parse()
	if err := ui.Init(); err != nil {
		panic(err)
	}
	defer ui.Close()
	draw()
	ui.Handle("/sys/kbd", func(e ui.Event) {
		ev := e.Data.(ui.EvtKbd)
		switch ev.KeyStr {
		case ":":
			promptMsg = ":"
		case "C-8":
			if l := len(promptMsg); l != 0 {
				promptMsg = promptMsg[:l-1]
			}
		case "<enter>":
			// todo
			promptMsg = ""
		case "<escape>":
			promptMsg = ""
		default:
			promptMsg += ev.KeyStr
		}
		refresh()
	})
	ui.Handle("/sys/kbd/C-c", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Handle("/timer/1s", func(ui.Event) {
		loadProfile(false)
		loadStats()
		refresh()
	})
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ui.Body.Width = ui.TermWidth()
		refresh()
	})

	ui.Body.Align()
	ui.Render(ui.Body)
	ui.Loop()
}

func draw() {
	display = ui.NewPar("")
	display.Height = 2
	display.Border = false

	prompt = ui.NewPar(promptMsg)
	prompt.Height = 1
	prompt.Border = false

	help := ui.NewPar(`:c, :h for profiles; :f to filter; :0 to paginate`)
	help.Height = 1
	help.Border = false
	help.TextBgColor = ui.ColorBlue
	help.Bg = ui.ColorBlue
	help.TextFgColor = ui.ColorWhite

	gs := ui.Sparkline{}
	gs.Title = "goroutines"
	gs.Height = 4
	gs.LineColor = ui.ColorCyan

	ts := ui.Sparkline{}
	ts.Title = "threads"
	ts.Height = 4
	ts.LineColor = ui.ColorCyan

	sp = ui.NewSparklines(gs, ts)
	sp.Height = 10
	sp.Border = false

	g := make([]*ui.Gauge, 10)
	for i := range g {
		g[i] = ui.NewGauge()
		g[i].LabelAlign = ui.AlignLeft
		g[i].Height = 2
		g[i].Border = false
		g[i].Percent = 100 - i*10
		g[i].PaddingBottom = 1
		g[i].BarColor = ui.ColorRed
	}

	ls = ui.NewList()
	ls.Border = false
	// ls.Items = []string{
	// 	" 0 0% 0% 0.01s 100% 00000000000105a7 runtime.notesleep",
	// 	"",
	// 	" [2] Downloading File 2",
	// 	"",
	// 	" [3] Uploading File 3",
	// 	"",
	// 	" [3] Uploading File 3",
	// 	"",
	// 	" [3] Uploading File 3",
	// }
	// ls.Height = 5
	ui.Body.AddRows(
		ui.NewRow(ui.NewCol(4, 0, prompt), ui.NewCol(8, 0, help)),
		ui.NewRow(ui.NewCol(12, 0, sp)),
		ui.NewRow(ui.NewCol(12, 0, display)),
		ui.NewRow(
			ui.NewCol(3, 0, g[0], g[1], g[2], g[3], g[4], g[5]),
			ui.NewCol(9, 0, ls)),
	)
}

func loadStats() {
	var max = ui.TermWidth()
	s, err := fetchStats()
	if err != nil {
		displayMsg(fmt.Sprintf("error fetching stats: %v", err))
		return
	}
	var cnts = []struct {
		cnt      int
		titleFmt string
	}{
		{s.Goroutine, "goroutines (%d)"},
		{s.Thread, "threads (%d)"},
	}
	for i, v := range cnts {
		if n := len(sp.Lines[i].Data); n > max {
			sp.Lines[i].Data = sp.Lines[i].Data[n-max : n]
		}
		sp.Lines[i].Title = fmt.Sprintf(v.titleFmt, v.cnt)
		sp.Lines[i].Data = append(sp.Lines[i].Data, v.cnt)
	}
}

func loadProfile(force bool) {
	if err := currentProfile.Fetch(0); err != nil {
		displayMsg(err.Error())
		return
	}
	reportItems = currentProfile.Filter(true, nil)
	fmt.Println(reportItems)
}

func refresh() {
	prompt.Text = promptMsg
	ls.Items = reportItems
	ui.Body.Align()
	ui.Render(ui.Body)
}

func displayMsg(msg string) {
	// TODO(jbd): hide after n secs.
	display.Text = msg
	ui.Render(display)
}
<commit_msg>display profile data<commit_after>// Copyright 2014 Google Inc. 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 main

import (
	"flag"
	"fmt"

	ui "github.com/gizak/termui"
)

var (
	target = flag.String("target", "http://localhost:6060", "the target process to profile; it has to enable pprof debug server")

	prompt  *ui.Par
	ls      *ui.List
	sp      *ui.Sparklines
	display *ui.Par

	cpuProfile     = &Report{name: "profile", secs: 30}
	heapProfile    = &Report{name: "heap"}
	currentProfile = heapProfile

	reportItems []string
	promptMsg   string
	filter      string
)

func main() {
	flag.Parse()
	if err := ui.Init(); err != nil {
		panic(err)
	}
	defer ui.Close()
	draw()
	ui.Handle("/sys/kbd", func(e ui.Event) {
		ev := e.Data.(ui.EvtKbd)
		switch ev.KeyStr {
		case ":":
			promptMsg = ":"
		case "C-8":
			if l := len(promptMsg); l != 0 {
				promptMsg = promptMsg[:l-1]
			}
		case "<enter>":
			// todo
			promptMsg = ""
		case "<escape>":
			promptMsg = ""
		default:
			promptMsg += ev.KeyStr
		}
		refresh()
	})
	ui.Handle("/sys/kbd/C-c", func(ui.Event) {
		ui.StopLoop()
	})
	ui.Handle("/timer/1s", func(ui.Event) {
		loadProfile(false)
		loadStats()
		refresh()
	})
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ui.Body.Width = ui.TermWidth()
		refresh()
	})

	ui.Body.Align()
	ui.Render(ui.Body)
	ui.Loop()
}

func draw() {
	display = ui.NewPar("")
	display.Height = 2
	display.Border = false

	prompt = ui.NewPar(promptMsg)
	prompt.Height = 1
	prompt.Border = false

	help := ui.NewPar(`:c, :h for profiles; :f to filter; :0 to paginate`)
	help.Height = 1
	help.Border = false
	help.TextBgColor = ui.ColorBlue
	help.Bg = ui.ColorBlue
	help.TextFgColor = ui.ColorWhite

	gs := ui.Sparkline{}
	gs.Title = "goroutines"
	gs.Height = 4
	gs.LineColor = ui.ColorCyan

	ts := ui.Sparkline{}
	ts.Title = "threads"
	ts.Height = 4
	ts.LineColor = ui.ColorCyan

	sp = ui.NewSparklines(gs, ts)
	sp.Height = 10
	sp.Border = false

	ls = ui.NewList()
	ls.Border = false
	ui.Body.AddRows(
		ui.NewRow(ui.NewCol(4, 0, prompt), ui.NewCol(8, 0, help)),
		ui.NewRow(ui.NewCol(12, 0, sp)),
		ui.NewRow(ui.NewCol(12, 0, display)),
		ui.NewRow(ui.NewCol(12, 0, ls)),
	)
}

func loadStats() {
	var max = ui.TermWidth()
	s, err := fetchStats()
	if err != nil {
		displayMsg(fmt.Sprintf("error fetching stats: %v", err))
		return
	}
	var cnts = []struct {
		cnt      int
		titleFmt string
	}{
		{s.Goroutine, "goroutines (%d)"},
		{s.Thread, "threads (%d)"},
	}
	for i, v := range cnts {
		if n := len(sp.Lines[i].Data); n > max {
			sp.Lines[i].Data = sp.Lines[i].Data[n-max : n]
		}
		sp.Lines[i].Title = fmt.Sprintf(v.titleFmt, v.cnt)
		sp.Lines[i].Data = append(sp.Lines[i].Data, v.cnt)
	}
}

func loadProfile(force bool) {
	if err := currentProfile.Fetch(0); err != nil {
		displayMsg(err.Error())
		return
	}
	reportItems = currentProfile.Filter(true, nil)
}

func refresh() {
	ls.Height = ui.TermHeight() - 13

	prompt.Text = promptMsg
	ls.Items = reportItems[:ls.Height]
	ui.Body.Align()
	ui.Render(ui.Body)
}

func displayMsg(msg string) {
	// TODO(jbd): hide after n secs.
	display.Text = msg
	ui.Render(display)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"

	"github.com/pkg/errors"
	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/config"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var (
	mrLabels       []string
	mrState        string
	mrTargetBranch string
	mrMilestone    string
	mrNumRet       int
	mrAll          bool
	mrMine         bool
	mrDraft        bool
	mrReady        bool
	mrConflicts    bool
	mrNoConflicts  bool
	mrExactMatch   bool
	assigneeID     *int
	mrAssignee     string
	order          string
	sortedBy       string
)

// listCmd represents the list command
var listCmd = &cobra.Command{
	Use:     "list [remote] [search]",
	Aliases: []string{"ls", "search"},
	Short:   "List merge requests",
	Long:    ``,
	Args:    cobra.MaximumNArgs(2),
	Example: `lab mr list
lab mr list "search terms"         # search merge requests for "search terms"
lab mr search "search terms"       # same as above
lab mr list remote "search terms"  # search "remote" for merge requests with "search terms"`,
	PersistentPreRun: LabPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		mrs, err := mrList(args)
		if err != nil {
			log.Print(err)
			config.UserConfigError()
		}

		pager := NewPager(cmd.Flags())
		defer pager.Close()

		for _, mr := range mrs {
			fmt.Printf("!%d %s\n", mr.IID, mr.Title)
		}
	},
}

func mrList(args []string) ([]*gitlab.MergeRequest, error) {
	rn, search, err := parseArgsRemoteAndProject(args)
	if err != nil {
		return nil, err
	}

	labels, err := MapLabels(rn, mrLabels)
	if err != nil {
		return nil, err
	}

	num := mrNumRet
	if mrAll {
		num = -1
	}

	if mrAssignee != "" {
		_assigneeID, err := lab.UserIDByUserName(mrAssignee)
		if err != nil {
			log.Fatal(err)
		}
		assigneeID = &_assigneeID
	} else if mrMine {
		_assigneeID, err := lab.UserID()
		if err != nil {
			log.Fatal(err)
		}
		assigneeID = &_assigneeID
	}

	if mrMilestone != "" {
		milestone, err := lab.MilestoneGet(rn, mrMilestone)
		if err != nil {
			log.Fatal(err)
		}
		mrMilestone = milestone.Title
	}

	orderBy := gitlab.String(order)

	sort := gitlab.String(sortedBy)

	// if none of the flags are set, return every single MR
	mrCheckConflicts := (mrConflicts || mrNoConflicts)

	opts := gitlab.ListProjectMergeRequestsOptions{
		ListOptions: gitlab.ListOptions{
			PerPage: mrNumRet,
		},
		Labels:                 labels,
		State:                  &mrState,
		TargetBranch:           &mrTargetBranch,
		Milestone:              &mrMilestone,
		OrderBy:                orderBy,
		Sort:                   sort,
		AssigneeID:             assigneeID,
		WithMergeStatusRecheck: gitlab.Bool(mrCheckConflicts),
	}

	if mrDraft && !mrReady {
		opts.WIP = gitlab.String("yes")
	} else if mrReady && !mrDraft {
		opts.WIP = gitlab.String("no")
	}

	if mrExactMatch {
		if search == "" {
			return nil, errors.New("Exact match requested, but no search terms provided")
		}
		search = "\"" + search + "\""
	}

	if search != "" {
		opts.Search = &search
	}

	mrs, err := lab.MRList(rn, opts, num)
	if err != nil {
		return mrs, err
	}

	// only return MRs that matches the Conflicts requirement
	if mrCheckConflicts {
		var newMrList []*gitlab.MergeRequest
		for _, mr := range mrs {
			if mr.HasConflicts && mrConflicts {
				newMrList = append(newMrList, mr)
			} else if !mr.HasConflicts && mrNoConflicts {
				newMrList = append(newMrList, mr)
			}
		}
		mrs = newMrList
	}

	return mrs, nil
}

func init() {
	listCmd.Flags().StringSliceVarP(
		&mrLabels, "label", "l", []string{}, "filter merge requests by label")
	listCmd.Flags().StringVarP(
		&mrState, "state", "s", "opened",
		"filter merge requests by state (opened/closed/merged)")
	listCmd.Flags().IntVarP(
		&mrNumRet, "number", "n", 10,
		"number of merge requests to return")
	listCmd.Flags().StringVarP(
		&mrTargetBranch, "target-branch", "t", "",
		"filter merge requests by target branch")
	listCmd.Flags().StringVar(
		&mrMilestone, "milestone", "", "list only MRs for the given milestone")
	listCmd.Flags().BoolVarP(&mrAll, "all", "a", false, "list all MRs on the project")
	listCmd.Flags().BoolVarP(&mrMine, "mine", "m", false, "list only MRs assigned to me")
	listCmd.Flags().StringVar(
		&mrAssignee, "assignee", "", "list only MRs assigned to $username")
	listCmd.Flags().StringVar(&order, "order", "updated_at", "display order (updated_at/created_at)")
	listCmd.Flags().StringVar(&sortedBy, "sort", "desc", "sort order (desc/asc)")
	listCmd.Flags().BoolVarP(&mrDraft, "draft", "", false, "list MRs marked as draft")
	listCmd.Flags().BoolVarP(&mrReady, "ready", "", false, "list MRs not marked as draft")
	listCmd.Flags().SortFlags = false
	listCmd.Flags().BoolVar(&mrNoConflicts, "no-conflicts", false, "list only MRs that can be merged")
	listCmd.Flags().BoolVar(&mrConflicts, "conflicts", false, "list only MRs that cannot be merged")
	listCmd.Flags().BoolVarP(&mrExactMatch, "exact-match", "x", false, "match on the exact (case-insensitive) search terms")

	mrCmd.AddCommand(listCmd)
	carapace.Gen(listCmd).FlagCompletion(carapace.ActionMap{
		"state": carapace.ActionValues("opened", "closed", "merged"),
	})

	carapace.Gen(listCmd).PositionalCompletion(
		action.Remotes(),
	)
}
<commit_msg>mr_list: remove config error message<commit_after>package cmd

import (
	"fmt"
	"log"

	"github.com/pkg/errors"
	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var (
	mrLabels       []string
	mrState        string
	mrTargetBranch string
	mrMilestone    string
	mrNumRet       int
	mrAll          bool
	mrMine         bool
	mrDraft        bool
	mrReady        bool
	mrConflicts    bool
	mrNoConflicts  bool
	mrExactMatch   bool
	assigneeID     *int
	mrAssignee     string
	order          string
	sortedBy       string
)

// listCmd represents the list command
var listCmd = &cobra.Command{
	Use:     "list [remote] [search]",
	Aliases: []string{"ls", "search"},
	Short:   "List merge requests",
	Long:    ``,
	Args:    cobra.MaximumNArgs(2),
	Example: `lab mr list
lab mr list "search terms"         # search merge requests for "search terms"
lab mr search "search terms"       # same as above
lab mr list remote "search terms"  # search "remote" for merge requests with "search terms"`,
	PersistentPreRun: LabPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		mrs, err := mrList(args)
		if err != nil {
			log.Fatal(err)
		}

		pager := NewPager(cmd.Flags())
		defer pager.Close()

		for _, mr := range mrs {
			fmt.Printf("!%d %s\n", mr.IID, mr.Title)
		}
	},
}

func mrList(args []string) ([]*gitlab.MergeRequest, error) {
	rn, search, err := parseArgsRemoteAndProject(args)
	if err != nil {
		return nil, err
	}

	labels, err := MapLabels(rn, mrLabels)
	if err != nil {
		return nil, err
	}

	num := mrNumRet
	if mrAll {
		num = -1
	}

	if mrAssignee != "" {
		_assigneeID, err := lab.UserIDByUserName(mrAssignee)
		if err != nil {
			log.Fatal(err)
		}
		assigneeID = &_assigneeID
	} else if mrMine {
		_assigneeID, err := lab.UserID()
		if err != nil {
			log.Fatal(err)
		}
		assigneeID = &_assigneeID
	}

	if mrMilestone != "" {
		milestone, err := lab.MilestoneGet(rn, mrMilestone)
		if err != nil {
			log.Fatal(err)
		}
		mrMilestone = milestone.Title
	}

	orderBy := gitlab.String(order)

	sort := gitlab.String(sortedBy)

	// if none of the flags are set, return every single MR
	mrCheckConflicts := (mrConflicts || mrNoConflicts)

	opts := gitlab.ListProjectMergeRequestsOptions{
		ListOptions: gitlab.ListOptions{
			PerPage: mrNumRet,
		},
		Labels:                 labels,
		State:                  &mrState,
		TargetBranch:           &mrTargetBranch,
		Milestone:              &mrMilestone,
		OrderBy:                orderBy,
		Sort:                   sort,
		AssigneeID:             assigneeID,
		WithMergeStatusRecheck: gitlab.Bool(mrCheckConflicts),
	}

	if mrDraft && !mrReady {
		opts.WIP = gitlab.String("yes")
	} else if mrReady && !mrDraft {
		opts.WIP = gitlab.String("no")
	}

	if mrExactMatch {
		if search == "" {
			return nil, errors.New("Exact match requested, but no search terms provided")
		}
		search = "\"" + search + "\""
	}

	if search != "" {
		opts.Search = &search
	}

	mrs, err := lab.MRList(rn, opts, num)
	if err != nil {
		return mrs, err
	}

	// only return MRs that matches the Conflicts requirement
	if mrCheckConflicts {
		var newMrList []*gitlab.MergeRequest
		for _, mr := range mrs {
			if mr.HasConflicts && mrConflicts {
				newMrList = append(newMrList, mr)
			} else if !mr.HasConflicts && mrNoConflicts {
				newMrList = append(newMrList, mr)
			}
		}
		mrs = newMrList
	}

	return mrs, nil
}

func init() {
	listCmd.Flags().StringSliceVarP(
		&mrLabels, "label", "l", []string{}, "filter merge requests by label")
	listCmd.Flags().StringVarP(
		&mrState, "state", "s", "opened",
		"filter merge requests by state (opened/closed/merged)")
	listCmd.Flags().IntVarP(
		&mrNumRet, "number", "n", 10,
		"number of merge requests to return")
	listCmd.Flags().StringVarP(
		&mrTargetBranch, "target-branch", "t", "",
		"filter merge requests by target branch")
	listCmd.Flags().StringVar(
		&mrMilestone, "milestone", "", "list only MRs for the given milestone")
	listCmd.Flags().BoolVarP(&mrAll, "all", "a", false, "list all MRs on the project")
	listCmd.Flags().BoolVarP(&mrMine, "mine", "m", false, "list only MRs assigned to me")
	listCmd.Flags().StringVar(
		&mrAssignee, "assignee", "", "list only MRs assigned to $username")
	listCmd.Flags().StringVar(&order, "order", "updated_at", "display order (updated_at/created_at)")
	listCmd.Flags().StringVar(&sortedBy, "sort", "desc", "sort order (desc/asc)")
	listCmd.Flags().BoolVarP(&mrDraft, "draft", "", false, "list MRs marked as draft")
	listCmd.Flags().BoolVarP(&mrReady, "ready", "", false, "list MRs not marked as draft")
	listCmd.Flags().SortFlags = false
	listCmd.Flags().BoolVar(&mrNoConflicts, "no-conflicts", false, "list only MRs that can be merged")
	listCmd.Flags().BoolVar(&mrConflicts, "conflicts", false, "list only MRs that cannot be merged")
	listCmd.Flags().BoolVarP(&mrExactMatch, "exact-match", "x", false, "match on the exact (case-insensitive) search terms")

	mrCmd.AddCommand(listCmd)
	carapace.Gen(listCmd).FlagCompletion(carapace.ActionMap{
		"state": carapace.ActionValues("opened", "closed", "merged"),
	})

	carapace.Gen(listCmd).PositionalCompletion(
		action.Remotes(),
	)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"strings"

	"github.com/charmbracelet/glamour"
	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/git"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var (
	mrShowPatch        bool
	mrShowPatchReverse bool
	mrShowNoColorDiff  bool
)

var mrShowCmd = &cobra.Command{
	Use:              "show [remote] <id>",
	Aliases:          []string{"get"},
	ArgAliases:       []string{"s"},
	Short:            "Describe a merge request",
	Long:             ``,
	PersistentPreRun: LabPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {

		rn, mrNum, err := parseArgsWithGitBranchMR(args)
		if err != nil {
			log.Fatal(err)
		}

		mr, err := lab.MRGet(rn, int(mrNum))
		if err != nil {
			log.Fatal(err)
		}

		renderMarkdown := false
		if isOutputTerminal() {
			noMarkdown, _ := cmd.Flags().GetBool("no-markdown")
			if err != nil {
				log.Fatal(err)
			}
			renderMarkdown = !noMarkdown
		}

		if mrShowPatch {
			var remote string

			if len(args) == 1 {
				remote = findLocalRemote(mr.TargetProjectID)
			} else if len(args) == 2 {
				remote = args[0]
			} else {
				log.Fatal("Too many arguments.")
			}

			err := git.Fetch(remote, mr.SHA)
			if err != nil {
				log.Fatal(err)
			}
			git.Show(remote+"/"+mr.TargetBranch, mr.SHA, mrShowPatchReverse)
		} else {
			printMR(mr, rn, renderMarkdown)
		}

		showComments, _ := cmd.Flags().GetBool("comments")
		if showComments {
			discussions, err := lab.MRListDiscussions(rn, int(mrNum))
			if err != nil {
				log.Fatal(err)
			}

			since, err := cmd.Flags().GetString("since")
			if err != nil {
				log.Fatal(err)
			}

			PrintDiscussions(discussions, since, "mr", int(mrNum))
		}
	},
}

func findLocalRemote(ProjectID int) string {
	var remote string

	project, err := lab.GetProject(ProjectID)
	if err != nil {
		log.Fatal(err)
	}
	remotesStr, err := git.GetLocalRemotes()
	if err != nil {
		log.Fatal(err)
	}
	remotes := strings.Split(remotesStr, "\n")

	// find the matching local remote for this project
	for r := range remotes {
		// The fetch and push entries can be different for a remote.
		// Only the fetch entry is useful.
		if strings.Contains(remotes[r], project.SSHURLToRepo+" (fetch)") ||
			strings.Contains(remotes[r], project.HTTPURLToRepo+" (fetch)") {
			found := strings.Split(remotes[r], "\t")
			remote = found[0]
			break
		}
	}

	if remote == "" {
		log.Fatal("remote for ", project.NameWithNamespace, " not found in local remotes")
	}
	return remote
}

func printMR(mr *gitlab.MergeRequest, project string, renderMarkdown bool) {
	assignee := "None"
	milestone := "None"
	labels := "None"
	approvedByUsers := "None"
	state := map[string]string{
		"opened": "Open",
		"closed": "Closed",
		"merged": "Merged",
	}[mr.State]

	if mr.Assignee != nil && mr.Assignee.Username != "" {
		assignee = mr.Assignee.Username
	}
	if mr.Milestone != nil {
		milestone = mr.Milestone.Title
	}
	if len(mr.Labels) > 0 {
		labels = strings.Join(mr.Labels, ", ")
	}

	if renderMarkdown {
		r, _ := glamour.NewTermRenderer(
			glamour.WithStandardStyle("auto"),
		)

		mr.Description, _ = r.Render(mr.Description)
	}

	closingIssues, err := lab.ListIssuesClosedOnMerge(project, mr.IID)
	if err != nil {
		log.Fatal(err)
	}

	approvedBys, err := lab.GetMRApprovedBys(project, mr.IID)
	if err != nil {
		log.Fatal(err)
	}
	if len(approvedBys) > 0 {
		approvedByUsers = strings.Join(approvedBys, ", ")
	}

	fmt.Printf(`
!%d %s
===================================
%s
-----------------------------------
Project: %s
Branches: %s->%s
Status: %s
Assignee: %s
Author: %s
Approved By: %s
Milestone: %s
Labels: %s
Issues Closed by this MR: %s
WebURL: %s
`,
		mr.IID, mr.Title, mr.Description, project, mr.SourceBranch,
		mr.TargetBranch, state, assignee,
		mr.Author.Username, approvedByUsers, milestone, labels,
		strings.Trim(strings.Replace(fmt.Sprint(closingIssues), " ", ",", -1), "[]"),
		mr.WebURL,
	)
}

func init() {
	mrShowCmd.Flags().BoolP("no-markdown", "M", false, "don't use markdown renderer to print the issue description")
	mrShowCmd.Flags().BoolP("comments", "c", false, "show comments for the merge request")
	mrShowCmd.Flags().StringP("since", "s", "", "show comments since specified date (format: 2020-08-21 14:57:46.808 +0000 UTC)")
	mrShowCmd.Flags().BoolVarP(&mrShowPatch, "patch", "p", false, "show MR patches")
	mrShowCmd.Flags().BoolVarP(&mrShowPatchReverse, "reverse", "", false, "reverse order when showing MR patches (chronological instead of anti-chronological)")
	mrShowCmd.Flags().BoolVarP(&mrShowNoColorDiff, "no-color-diff", "", false, "show color diffs in comments")
	mrCmd.AddCommand(mrShowCmd)
	carapace.Gen(mrShowCmd).PositionalCompletion(
		action.Remotes(),
		action.MergeRequests(mrList),
	)
}
<commit_msg>mr_show: Fix --patch option with no arguments<commit_after>package cmd

import (
	"fmt"
	"log"
	"strings"

	"github.com/charmbracelet/glamour"
	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/git"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

var (
	mrShowPatch        bool
	mrShowPatchReverse bool
	mrShowNoColorDiff  bool
)

var mrShowCmd = &cobra.Command{
	Use:              "show [remote] <id>",
	Aliases:          []string{"get"},
	ArgAliases:       []string{"s"},
	Short:            "Describe a merge request",
	Long:             ``,
	PersistentPreRun: LabPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {

		rn, mrNum, err := parseArgsWithGitBranchMR(args)
		if err != nil {
			log.Fatal(err)
		}

		mr, err := lab.MRGet(rn, int(mrNum))
		if err != nil {
			log.Fatal(err)
		}

		renderMarkdown := false
		if isOutputTerminal() {
			noMarkdown, _ := cmd.Flags().GetBool("no-markdown")
			if err != nil {
				log.Fatal(err)
			}
			renderMarkdown = !noMarkdown
		}

		if mrShowPatch {
			var remote string

			if len(args) < 2 {
				remote = findLocalRemote(mr.TargetProjectID)
			} else if len(args) == 2 {
				remote = args[0]
			} else {
				log.Fatal("Too many arguments.")
			}

			err := git.Fetch(remote, mr.SHA)
			if err != nil {
				log.Fatal(err)
			}
			git.Show(remote+"/"+mr.TargetBranch, mr.SHA, mrShowPatchReverse)
		} else {
			printMR(mr, rn, renderMarkdown)
		}

		showComments, _ := cmd.Flags().GetBool("comments")
		if showComments {
			discussions, err := lab.MRListDiscussions(rn, int(mrNum))
			if err != nil {
				log.Fatal(err)
			}

			since, err := cmd.Flags().GetString("since")
			if err != nil {
				log.Fatal(err)
			}

			PrintDiscussions(discussions, since, "mr", int(mrNum))
		}
	},
}

func findLocalRemote(ProjectID int) string {
	var remote string

	project, err := lab.GetProject(ProjectID)
	if err != nil {
		log.Fatal(err)
	}
	remotesStr, err := git.GetLocalRemotes()
	if err != nil {
		log.Fatal(err)
	}
	remotes := strings.Split(remotesStr, "\n")

	// find the matching local remote for this project
	for r := range remotes {
		// The fetch and push entries can be different for a remote.
		// Only the fetch entry is useful.
		if strings.Contains(remotes[r], project.SSHURLToRepo+" (fetch)") ||
			strings.Contains(remotes[r], project.HTTPURLToRepo+" (fetch)") {
			found := strings.Split(remotes[r], "\t")
			remote = found[0]
			break
		}
	}

	if remote == "" {
		log.Fatal("remote for ", project.NameWithNamespace, " not found in local remotes")
	}
	return remote
}

func printMR(mr *gitlab.MergeRequest, project string, renderMarkdown bool) {
	assignee := "None"
	milestone := "None"
	labels := "None"
	approvedByUsers := "None"
	state := map[string]string{
		"opened": "Open",
		"closed": "Closed",
		"merged": "Merged",
	}[mr.State]

	if mr.Assignee != nil && mr.Assignee.Username != "" {
		assignee = mr.Assignee.Username
	}
	if mr.Milestone != nil {
		milestone = mr.Milestone.Title
	}
	if len(mr.Labels) > 0 {
		labels = strings.Join(mr.Labels, ", ")
	}

	if renderMarkdown {
		r, _ := glamour.NewTermRenderer(
			glamour.WithStandardStyle("auto"),
		)

		mr.Description, _ = r.Render(mr.Description)
	}

	closingIssues, err := lab.ListIssuesClosedOnMerge(project, mr.IID)
	if err != nil {
		log.Fatal(err)
	}

	approvedBys, err := lab.GetMRApprovedBys(project, mr.IID)
	if err != nil {
		log.Fatal(err)
	}
	if len(approvedBys) > 0 {
		approvedByUsers = strings.Join(approvedBys, ", ")
	}

	fmt.Printf(`
!%d %s
===================================
%s
-----------------------------------
Project: %s
Branches: %s->%s
Status: %s
Assignee: %s
Author: %s
Approved By: %s
Milestone: %s
Labels: %s
Issues Closed by this MR: %s
WebURL: %s
`,
		mr.IID, mr.Title, mr.Description, project, mr.SourceBranch,
		mr.TargetBranch, state, assignee,
		mr.Author.Username, approvedByUsers, milestone, labels,
		strings.Trim(strings.Replace(fmt.Sprint(closingIssues), " ", ",", -1), "[]"),
		mr.WebURL,
	)
}

func init() {
	mrShowCmd.Flags().BoolP("no-markdown", "M", false, "don't use markdown renderer to print the issue description")
	mrShowCmd.Flags().BoolP("comments", "c", false, "show comments for the merge request")
	mrShowCmd.Flags().StringP("since", "s", "", "show comments since specified date (format: 2020-08-21 14:57:46.808 +0000 UTC)")
	mrShowCmd.Flags().BoolVarP(&mrShowPatch, "patch", "p", false, "show MR patches")
	mrShowCmd.Flags().BoolVarP(&mrShowPatchReverse, "reverse", "", false, "reverse order when showing MR patches (chronological instead of anti-chronological)")
	mrShowCmd.Flags().BoolVarP(&mrShowNoColorDiff, "no-color-diff", "", false, "show color diffs in comments")
	mrCmd.AddCommand(mrShowCmd)
	carapace.Gen(mrShowCmd).PositionalCompletion(
		action.Remotes(),
		action.MergeRequests(mrList),
	)
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/apache/cloudstack-cloudmonkey/config"
)

func findSessionCookie(cookies []*http.Cookie) *http.Cookie {
	if cookies == nil {
		return nil
	}
	for _, cookie := range cookies {
		if cookie.Name == "sessionkey" {
			return cookie
		}
	}
	return nil
}

// Login logs in a user based on provided request and returns http client and session key
func Login(r *Request) (string, error) {
	params := make(url.Values)
	params.Add("command", "login")
	params.Add("username", r.Config.ActiveProfile.Username)
	params.Add("password", r.Config.ActiveProfile.Password)
	params.Add("domain", r.Config.ActiveProfile.Domain)
	params.Add("response", "json")

	msURL, _ := url.Parse(r.Config.ActiveProfile.URL)
	if sessionCookie := findSessionCookie(r.Client().Jar.Cookies(msURL)); sessionCookie != nil {
		return sessionCookie.Value, nil
	}

	config.Debug("Login POST URL:", msURL, params)
	spinner := r.Config.StartSpinner("trying to log in...")
	resp, err := r.Client().PostForm(msURL.String(), params)
	r.Config.StopSpinner(spinner)

	if err != nil {
		return "", errors.New("failed to authenticate with the CloudStack server, please check the settings: " + err.Error())
	}

	config.Debug("Login POST response status code:", resp.StatusCode)
	if resp.StatusCode != http.StatusOK {
		e := errors.New("failed to authenticate, please check the credentials")
		if err != nil {
			e = errors.New("failed to authenticate due to " + err.Error())
		}
		return "", e
	}

	var sessionKey string
	curTime := time.Now()
	expiryDuration := 15 * time.Minute
	for _, cookie := range resp.Cookies() {
		if cookie.Expires.After(curTime) {
			expiryDuration = cookie.Expires.Sub(curTime)
		}
		if cookie.Name == "sessionkey" {
			sessionKey = cookie.Value
		}
	}
	go func() {
		time.Sleep(expiryDuration)
		r.Client().Jar, _ = cookiejar.New(nil)
	}()

	config.Debug("Login sessionkey:", sessionKey)
	return sessionKey, nil
}

func encodeRequestParams(params url.Values) string {
	if params == nil {
		return ""
	}

	keys := make([]string, 0, len(params))
	for key := range params {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	var buf bytes.Buffer
	for _, key := range keys {
		value := params.Get(key)
		if buf.Len() > 0 {
			buf.WriteByte('&')
		}
		buf.WriteString(key)
		buf.WriteString("=")
		buf.WriteString(url.QueryEscape(value))
	}
	return buf.String()
}

func getResponseData(data map[string]interface{}) map[string]interface{} {
	for k := range data {
		if strings.HasSuffix(k, "response") {
			return data[k].(map[string]interface{})
		}
	}
	return nil
}

func pollAsyncJob(r *Request, jobID string) (map[string]interface{}, error) {
	for timeout := float64(r.Config.Core.Timeout); timeout > 0.0; {
		startTime := time.Now()
		spinner := r.Config.StartSpinner("polling for async API result")
		queryResult, queryError := NewAPIRequest(r, "queryAsyncJobResult", []string{"jobid=" + jobID}, false)
		diff := time.Duration(1*time.Second).Nanoseconds() - time.Now().Sub(startTime).Nanoseconds()
		if diff > 0 {
			time.Sleep(time.Duration(diff) * time.Nanosecond)
		}
		timeout = timeout - time.Now().Sub(startTime).Seconds()
		r.Config.StopSpinner(spinner)
		if queryError != nil {
			return queryResult, queryError
		}
		jobStatus := queryResult["jobstatus"].(float64)
		if jobStatus == 0 {
			continue
		}
		if jobStatus == 1 {
			return queryResult["jobresult"].(map[string]interface{}), nil

		}
		if jobStatus == 2 {
			return queryResult, errors.New("async API failed for job " + jobID)
		}
	}
	return nil, errors.New("async API job query timed out")
}

// NewAPIRequest makes an API request to configured management server
func NewAPIRequest(r *Request, api string, args []string, isAsync bool) (map[string]interface{}, error) {
	params := make(url.Values)
	params.Add("command", api)
	for _, arg := range args {
		parts := strings.SplitN(arg, "=", 2)
		if len(parts) == 2 {
			params.Add(parts[0], parts[1])
		}
	}
	params.Add("response", "json")

	var encodedParams string
	var err error

	if len(r.Config.ActiveProfile.APIKey) > 0 && len(r.Config.ActiveProfile.SecretKey) > 0 {
		apiKey := r.Config.ActiveProfile.APIKey
		secretKey := r.Config.ActiveProfile.SecretKey

		if len(apiKey) > 0 {
			params.Add("apiKey", apiKey)
		}
		encodedParams = encodeRequestParams(params)

		mac := hmac.New(sha1.New, []byte(secretKey))
		mac.Write([]byte(strings.Replace(strings.ToLower(encodedParams), "+", "%20", -1)))
		signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
		encodedParams = encodedParams + fmt.Sprintf("&signature=%s", url.QueryEscape(signature))
	} else if len(r.Config.ActiveProfile.Username) > 0 && len(r.Config.ActiveProfile.Password) > 0 {
		sessionKey, err := Login(r)
		if err != nil {
			return nil, err
		}
		params.Add("sessionkey", sessionKey)
		encodedParams = encodeRequestParams(params)
	} else {
		fmt.Println("Please provide either apikey/secretkey or username/password to make an API call")
		return nil, errors.New("failed to authenticate to make API call")
	}

	requestURL := fmt.Sprintf("%s?%s", r.Config.ActiveProfile.URL, encodedParams)
	config.Debug("NewAPIRequest API request URL:", requestURL)

	response, err := r.Client().Get(requestURL)
	if err != nil {
		return nil, err
	}
	config.Debug("NewAPIRequest response status code:", response.StatusCode)

	if response != nil && response.StatusCode == http.StatusUnauthorized {
		r.Client().Jar, _ = cookiejar.New(nil)
		sessionKey, err := Login(r)
		if err != nil {
			return nil, err
		}
		params.Del("sessionkey")
		params.Add("sessionkey", sessionKey)
		requestURL = fmt.Sprintf("%s?%s", r.Config.ActiveProfile.URL, encodeRequestParams(params))
		config.Debug("NewAPIRequest API request URL:", requestURL)
		response, err = r.Client().Get(requestURL)
		if err != nil {
			return nil, err
		}
	}

	body, _ := ioutil.ReadAll(response.Body)
	config.Debug("NewAPIRequest response body:", string(body))

	var data map[string]interface{}
	_ = json.Unmarshal([]byte(body), &data)

	if isAsync && r.Config.Core.AsyncBlock {
		if jobResponse := getResponseData(data); jobResponse != nil && jobResponse["jobid"] != nil {
			jobID := jobResponse["jobid"].(string)
			return pollAsyncJob(r, jobID)
		}
	}

	if apiResponse := getResponseData(data); apiResponse != nil {
		if _, ok := apiResponse["errorcode"]; ok {
			return nil, fmt.Errorf("(HTTP %v, error code %v) %v", apiResponse["errorcode"], apiResponse["cserrorcode"], apiResponse["errortext"])
		}
		return apiResponse, nil
	}

	return nil, errors.New("failed to decode response")
}
<commit_msg>network: remove nested quotes in parameters<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/apache/cloudstack-cloudmonkey/config"
)

func findSessionCookie(cookies []*http.Cookie) *http.Cookie {
	if cookies == nil {
		return nil
	}
	for _, cookie := range cookies {
		if cookie.Name == "sessionkey" {
			return cookie
		}
	}
	return nil
}

// Login logs in a user based on provided request and returns http client and session key
func Login(r *Request) (string, error) {
	params := make(url.Values)
	params.Add("command", "login")
	params.Add("username", r.Config.ActiveProfile.Username)
	params.Add("password", r.Config.ActiveProfile.Password)
	params.Add("domain", r.Config.ActiveProfile.Domain)
	params.Add("response", "json")

	msURL, _ := url.Parse(r.Config.ActiveProfile.URL)
	if sessionCookie := findSessionCookie(r.Client().Jar.Cookies(msURL)); sessionCookie != nil {
		return sessionCookie.Value, nil
	}

	config.Debug("Login POST URL:", msURL, params)
	spinner := r.Config.StartSpinner("trying to log in...")
	resp, err := r.Client().PostForm(msURL.String(), params)
	r.Config.StopSpinner(spinner)

	if err != nil {
		return "", errors.New("failed to authenticate with the CloudStack server, please check the settings: " + err.Error())
	}

	config.Debug("Login POST response status code:", resp.StatusCode)
	if resp.StatusCode != http.StatusOK {
		e := errors.New("failed to authenticate, please check the credentials")
		if err != nil {
			e = errors.New("failed to authenticate due to " + err.Error())
		}
		return "", e
	}

	var sessionKey string
	curTime := time.Now()
	expiryDuration := 15 * time.Minute
	for _, cookie := range resp.Cookies() {
		if cookie.Expires.After(curTime) {
			expiryDuration = cookie.Expires.Sub(curTime)
		}
		if cookie.Name == "sessionkey" {
			sessionKey = cookie.Value
		}
	}
	go func() {
		time.Sleep(expiryDuration)
		r.Client().Jar, _ = cookiejar.New(nil)
	}()

	config.Debug("Login sessionkey:", sessionKey)
	return sessionKey, nil
}

func encodeRequestParams(params url.Values) string {
	if params == nil {
		return ""
	}

	keys := make([]string, 0, len(params))
	for key := range params {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	var buf bytes.Buffer
	for _, key := range keys {
		value := params.Get(key)
		if buf.Len() > 0 {
			buf.WriteByte('&')
		}
		buf.WriteString(key)
		buf.WriteString("=")
		buf.WriteString(url.QueryEscape(value))
	}
	return buf.String()
}

func getResponseData(data map[string]interface{}) map[string]interface{} {
	for k := range data {
		if strings.HasSuffix(k, "response") {
			return data[k].(map[string]interface{})
		}
	}
	return nil
}

func pollAsyncJob(r *Request, jobID string) (map[string]interface{}, error) {
	for timeout := float64(r.Config.Core.Timeout); timeout > 0.0; {
		startTime := time.Now()
		spinner := r.Config.StartSpinner("polling for async API result")
		queryResult, queryError := NewAPIRequest(r, "queryAsyncJobResult", []string{"jobid=" + jobID}, false)
		diff := time.Duration(1*time.Second).Nanoseconds() - time.Now().Sub(startTime).Nanoseconds()
		if diff > 0 {
			time.Sleep(time.Duration(diff) * time.Nanosecond)
		}
		timeout = timeout - time.Now().Sub(startTime).Seconds()
		r.Config.StopSpinner(spinner)
		if queryError != nil {
			return queryResult, queryError
		}
		jobStatus := queryResult["jobstatus"].(float64)
		if jobStatus == 0 {
			continue
		}
		if jobStatus == 1 {
			return queryResult["jobresult"].(map[string]interface{}), nil

		}
		if jobStatus == 2 {
			return queryResult, errors.New("async API failed for job " + jobID)
		}
	}
	return nil, errors.New("async API job query timed out")
}

// NewAPIRequest makes an API request to configured management server
func NewAPIRequest(r *Request, api string, args []string, isAsync bool) (map[string]interface{}, error) {
	params := make(url.Values)
	params.Add("command", api)
	for _, arg := range args {
		parts := strings.SplitN(arg, "=", 2)
		if len(parts) == 2 {
			key := parts[0]
			value := parts[1]
			if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
				value = value[1 : len(value)-1]
			}
			params.Add(key, value)
		}
	}
	params.Add("response", "json")

	var encodedParams string
	var err error

	if len(r.Config.ActiveProfile.APIKey) > 0 && len(r.Config.ActiveProfile.SecretKey) > 0 {
		apiKey := r.Config.ActiveProfile.APIKey
		secretKey := r.Config.ActiveProfile.SecretKey

		if len(apiKey) > 0 {
			params.Add("apiKey", apiKey)
		}
		encodedParams = encodeRequestParams(params)

		mac := hmac.New(sha1.New, []byte(secretKey))
		mac.Write([]byte(strings.Replace(strings.ToLower(encodedParams), "+", "%20", -1)))
		signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
		encodedParams = encodedParams + fmt.Sprintf("&signature=%s", url.QueryEscape(signature))
	} else if len(r.Config.ActiveProfile.Username) > 0 && len(r.Config.ActiveProfile.Password) > 0 {
		sessionKey, err := Login(r)
		if err != nil {
			return nil, err
		}
		params.Add("sessionkey", sessionKey)
		encodedParams = encodeRequestParams(params)
	} else {
		fmt.Println("Please provide either apikey/secretkey or username/password to make an API call")
		return nil, errors.New("failed to authenticate to make API call")
	}

	requestURL := fmt.Sprintf("%s?%s", r.Config.ActiveProfile.URL, encodedParams)
	config.Debug("NewAPIRequest API request URL:", requestURL)

	response, err := r.Client().Get(requestURL)
	if err != nil {
		return nil, err
	}
	config.Debug("NewAPIRequest response status code:", response.StatusCode)

	if response != nil && response.StatusCode == http.StatusUnauthorized {
		r.Client().Jar, _ = cookiejar.New(nil)
		sessionKey, err := Login(r)
		if err != nil {
			return nil, err
		}
		params.Del("sessionkey")
		params.Add("sessionkey", sessionKey)
		requestURL = fmt.Sprintf("%s?%s", r.Config.ActiveProfile.URL, encodeRequestParams(params))
		config.Debug("NewAPIRequest API request URL:", requestURL)
		response, err = r.Client().Get(requestURL)
		if err != nil {
			return nil, err
		}
	}

	body, _ := ioutil.ReadAll(response.Body)
	config.Debug("NewAPIRequest response body:", string(body))

	var data map[string]interface{}
	_ = json.Unmarshal([]byte(body), &data)

	if isAsync && r.Config.Core.AsyncBlock {
		if jobResponse := getResponseData(data); jobResponse != nil && jobResponse["jobid"] != nil {
			jobID := jobResponse["jobid"].(string)
			return pollAsyncJob(r, jobID)
		}
	}

	if apiResponse := getResponseData(data); apiResponse != nil {
		if _, ok := apiResponse["errorcode"]; ok {
			return nil, fmt.Errorf("(HTTP %v, error code %v) %v", apiResponse["errorcode"], apiResponse["cserrorcode"], apiResponse["errortext"])
		}
		return apiResponse, nil
	}

	return nil, errors.New("failed to decode response")
}
<|endoftext|>


<commit_before>//go:build !windows
// +build !windows

package graphdriver

import (
	"errors"
	"fmt"
	"os"
	"syscall"

	"github.com/containers/storage/pkg/idtools"
	"github.com/containers/storage/pkg/system"
)

type platformChowner struct {
}

func newLChowner() *platformChowner {
	return &platformChowner{}
}

func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error {
	st, ok := info.Sys().(*syscall.Stat_t)
	if !ok {
		return nil
	}
	// Map an on-disk UID/GID pair from host to container
	// using the first map, then back to the host using the
	// second map.  Skip that first step if they're 0, to
	// compensate for cases where a parent layer should
	// have had a mapped value, but didn't.
	uid, gid := int(st.Uid), int(st.Gid)
	if toContainer != nil {
		pair := idtools.IDPair{
			UID: uid,
			GID: gid,
		}
		mappedUID, mappedGID, err := toContainer.ToContainer(pair)
		if err != nil {
			if (uid != 0) || (gid != 0) {
				return fmt.Errorf("error mapping host ID pair %#v for %q to container: %v", pair, path, err)
			}
			mappedUID, mappedGID = uid, gid
		}
		uid, gid = mappedUID, mappedGID
	}
	if toHost != nil {
		pair := idtools.IDPair{
			UID: uid,
			GID: gid,
		}
		mappedPair, err := toHost.ToHost(pair)
		if err != nil {
			return fmt.Errorf("error mapping container ID pair %#v for %q to host: %v", pair, path, err)
		}
		uid, gid = mappedPair.UID, mappedPair.GID
	}
	if uid != int(st.Uid) || gid != int(st.Gid) {
		cap, err := system.Lgetxattr(path, "security.capability")
		if err != nil && !errors.Is(err, system.EOPNOTSUPP) && err != system.ErrNotSupportedPlatform {
			return fmt.Errorf("%s: %v", os.Args[0], err)
		}

		// Make the change.
		if err := system.Lchown(path, uid, gid); err != nil {
			return fmt.Errorf("%s: %v", os.Args[0], err)
		}
		// Restore the SUID and SGID bits if they were originally set.
		if (info.Mode()&os.ModeSymlink == 0) && info.Mode()&(os.ModeSetuid|os.ModeSetgid) != 0 {
			if err := system.Chmod(path, info.Mode()); err != nil {
				return fmt.Errorf("%s: %v", os.Args[0], err)
			}
		}
		if cap != nil {
			if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil {
				return fmt.Errorf("%s: %v", os.Args[0], err)
			}
		}

	}
	return nil
}
<commit_msg>drivers, chown: support chown of hard links<commit_after>//go:build !windows
// +build !windows

package graphdriver

import (
	"errors"
	"fmt"
	"os"
	"sync"
	"syscall"

	"github.com/containers/storage/pkg/idtools"
	"github.com/containers/storage/pkg/system"
)

type inode struct {
	Dev uint64
	Ino uint64
}

type platformChowner struct {
	mutex  sync.Mutex
	inodes map[inode]bool
}

func newLChowner() *platformChowner {
	return &platformChowner{
		inodes: make(map[inode]bool),
	}
}

func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error {
	st, ok := info.Sys().(*syscall.Stat_t)
	if !ok {
		return nil
	}

	i := inode{
		Dev: uint64(st.Dev),
		Ino: uint64(st.Ino),
	}
	c.mutex.Lock()
	_, found := c.inodes[i]
	if !found {
		c.inodes[i] = true
	}
	c.mutex.Unlock()

	if found {
		return nil
	}

	// Map an on-disk UID/GID pair from host to container
	// using the first map, then back to the host using the
	// second map.  Skip that first step if they're 0, to
	// compensate for cases where a parent layer should
	// have had a mapped value, but didn't.
	uid, gid := int(st.Uid), int(st.Gid)
	if toContainer != nil {
		pair := idtools.IDPair{
			UID: uid,
			GID: gid,
		}
		mappedUID, mappedGID, err := toContainer.ToContainer(pair)
		if err != nil {
			if (uid != 0) || (gid != 0) {
				return fmt.Errorf("error mapping host ID pair %#v for %q to container: %v", pair, path, err)
			}
			mappedUID, mappedGID = uid, gid
		}
		uid, gid = mappedUID, mappedGID
	}
	if toHost != nil {
		pair := idtools.IDPair{
			UID: uid,
			GID: gid,
		}
		mappedPair, err := toHost.ToHost(pair)
		if err != nil {
			return fmt.Errorf("error mapping container ID pair %#v for %q to host: %v", pair, path, err)
		}
		uid, gid = mappedPair.UID, mappedPair.GID
	}
	if uid != int(st.Uid) || gid != int(st.Gid) {
		cap, err := system.Lgetxattr(path, "security.capability")
		if err != nil && !errors.Is(err, system.EOPNOTSUPP) && err != system.ErrNotSupportedPlatform {
			return fmt.Errorf("%s: %v", os.Args[0], err)
		}

		// Make the change.
		if err := system.Lchown(path, uid, gid); err != nil {
			return fmt.Errorf("%s: %v", os.Args[0], err)
		}
		// Restore the SUID and SGID bits if they were originally set.
		if (info.Mode()&os.ModeSymlink == 0) && info.Mode()&(os.ModeSetuid|os.ModeSetgid) != 0 {
			if err := system.Chmod(path, info.Mode()); err != nil {
				return fmt.Errorf("%s: %v", os.Args[0], err)
			}
		}
		if cap != nil {
			if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil {
				return fmt.Errorf("%s: %v", os.Args[0], err)
			}
		}

	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"

	"github.com/go-clix/cli"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"golang.org/x/term"

	"github.com/grafana/tanka/pkg/tanka"
)

var interactive = term.IsTerminal(int(os.Stdout.Fd()))

func main() {
	rootCmd := &cli.Command{
		Use:     "tk",
		Short:   "tanka <3 jsonnet",
		Version: tanka.CurrentVersion,
	}

	addCommandsWithLogLevel := func(cmds ...*cli.Command) {
		for _, cmd := range cmds {
			levels := []string{zerolog.Disabled.String(), zerolog.FatalLevel.String(), zerolog.ErrorLevel.String(), zerolog.WarnLevel.String(), zerolog.InfoLevel.String(), zerolog.DebugLevel.String(), zerolog.TraceLevel.String()}
			cmd.Flags().String("log-level", zerolog.InfoLevel.String(), "possible values: "+strings.Join(levels, ", "))

			cmdRun := cmd.Run
			cmd.Run = func(cmd *cli.Command, args []string) error {
				level, err := zerolog.ParseLevel(cmd.Flags().Lookup("log-level").Value.String())
				if err != nil {
					return err
				}
				zerolog.SetGlobalLevel(level)

				return cmdRun(cmd, args)
			}
			rootCmd.AddCommand(cmd)
		}
	}

	// workflow commands
	addCommandsWithLogLevel(
		applyCmd(),
		showCmd(),
		diffCmd(),
		pruneCmd(),
		deleteCmd(),
	)

	addCommandsWithLogLevel(
		envCmd(),
		statusCmd(),
		exportCmd(),
	)

	// jsonnet commands
	addCommandsWithLogLevel(
		fmtCmd(),
		lintCmd(),
		evalCmd(),
		initCmd(),
		toolCmd(),
	)

	// external commands prefixed with "tk-"
	addCommandsWithLogLevel(
		prefixCommands("tk-")...,
	)

	// Run!
	if err := rootCmd.Execute(); err != nil {
		log.Fatal().Msgf("Error: %s", err)
	}
}
<commit_msg>Revert to fmt.Print for error logging (#777)<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/go-clix/cli"
	"github.com/rs/zerolog"
	"golang.org/x/term"

	"github.com/grafana/tanka/pkg/tanka"
)

var interactive = term.IsTerminal(int(os.Stdout.Fd()))

func main() {
	rootCmd := &cli.Command{
		Use:     "tk",
		Short:   "tanka <3 jsonnet",
		Version: tanka.CurrentVersion,
	}

	addCommandsWithLogLevel := func(cmds ...*cli.Command) {
		for _, cmd := range cmds {
			levels := []string{zerolog.Disabled.String(), zerolog.FatalLevel.String(), zerolog.ErrorLevel.String(), zerolog.WarnLevel.String(), zerolog.InfoLevel.String(), zerolog.DebugLevel.String(), zerolog.TraceLevel.String()}
			cmd.Flags().String("log-level", zerolog.InfoLevel.String(), "possible values: "+strings.Join(levels, ", "))

			cmdRun := cmd.Run
			cmd.Run = func(cmd *cli.Command, args []string) error {
				level, err := zerolog.ParseLevel(cmd.Flags().Lookup("log-level").Value.String())
				if err != nil {
					return err
				}
				zerolog.SetGlobalLevel(level)

				return cmdRun(cmd, args)
			}
			rootCmd.AddCommand(cmd)
		}
	}

	// workflow commands
	addCommandsWithLogLevel(
		applyCmd(),
		showCmd(),
		diffCmd(),
		pruneCmd(),
		deleteCmd(),
	)

	addCommandsWithLogLevel(
		envCmd(),
		statusCmd(),
		exportCmd(),
	)

	// jsonnet commands
	addCommandsWithLogLevel(
		fmtCmd(),
		lintCmd(),
		evalCmd(),
		initCmd(),
		toolCmd(),
	)

	// external commands prefixed with "tk-"
	addCommandsWithLogLevel(
		prefixCommands("tk-")...,
	)

	// Run!
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintln(os.Stderr, "Error:", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

var (
	version = "1.3.0"
	githash = "HEAD"
)

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("haproxy-exporter %s (%s)\n", version, githash)
	},
}
<commit_msg>chore: bump to 1.3.1<commit_after>package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

var (
	version = "1.3.1"
	githash = "HEAD"
)

func init() {
	RootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the version number",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("haproxy-exporter %s (%s)\n", version, githash)
	},
}
<|endoftext|>


<commit_before>package termbox

import (
	"testing"

	"github.com/kylelemons/godebug/pretty"
	"github.com/mum4k/termdash/terminalapi"
)

func TestNewTerminal(t *testing.T) {
	tests := []struct {
		desc string
		opts []Option
		want *Terminal
	}{
		{
			desc: "default options",
			want: &Terminal{
				colorMode: terminalapi.ColorMode256,
			},
		},
		{
			desc: "sets color mode",
			opts: []Option{
				ColorMode(terminalapi.ColorModeNormal),
			},
			want: &Terminal{
				colorMode: terminalapi.ColorModeNormal,
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.desc, func(t *testing.T) {
			got := newTerminal(tc.opts...)

			// Ignore these fields.
			got.events = nil
			got.done = nil

			if diff := pretty.Compare(tc.want, got); diff != "" {
				t.Errorf("newTerminal => unexpected diff (-want, +got):\n%s", diff)
			}
		})
	}
}
<commit_msg>Adding licences.<commit_after>// Copyright 2019 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 termbox

import (
	"testing"

	"github.com/kylelemons/godebug/pretty"
	"github.com/mum4k/termdash/terminalapi"
)

func TestNewTerminal(t *testing.T) {
	tests := []struct {
		desc string
		opts []Option
		want *Terminal
	}{
		{
			desc: "default options",
			want: &Terminal{
				colorMode: terminalapi.ColorMode256,
			},
		},
		{
			desc: "sets color mode",
			opts: []Option{
				ColorMode(terminalapi.ColorModeNormal),
			},
			want: &Terminal{
				colorMode: terminalapi.ColorModeNormal,
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.desc, func(t *testing.T) {
			got := newTerminal(tc.opts...)

			// Ignore these fields.
			got.events = nil
			got.done = nil

			if diff := pretty.Compare(tc.want, got); diff != "" {
				t.Errorf("newTerminal => unexpected diff (-want, +got):\n%s", diff)
			}
		})
	}
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"
	"reflect"
)

// NodeModuleRemoved represents a module that is no longer in the
// config.
type NodeModuleRemoved struct {
	PathValue []string
}

func (n *NodeModuleRemoved) Name() string {
	return fmt.Sprintf("%s (removed)", modulePrefixStr(n.PathValue))
}

// GraphNodeSubPath
func (n *NodeModuleRemoved) Path() []string {
	return n.PathValue
}

// GraphNodeEvalable
func (n *NodeModuleRemoved) EvalTree() EvalNode {
	return &EvalOpFilter{
		Ops: []walkOperation{walkRefresh, walkApply, walkDestroy},
		Node: &EvalDeleteModule{
			PathValue: n.PathValue,
		},
	}
}

// EvalDeleteModule is an EvalNode implementation that removes an empty module
// entry from the state.
type EvalDeleteModule struct {
	PathValue []string
}

func (n *EvalDeleteModule) Eval(ctx EvalContext) (interface{}, error) {
	state, lock := ctx.State()
	if state == nil {
		return nil, nil
	}

	// Get a write lock so we can access this instance
	lock.Lock()
	defer lock.Unlock()

	// Make sure we have a clean state
	// Destroyed resources aren't deleted, they're written with an ID of "".
	state.prune()

	// find the module and delete it
	for i, m := range state.Modules {
		if reflect.DeepEqual(m.Path, n.PathValue) {
			if !m.Empty() {
				// a targeted apply may leave module resources even without a config,
				// so just log this and return.
				log.Printf("[DEBUG] cannot remove module %s, not empty", modulePrefixStr(n.PathValue))
				break
			}
			tail := len(state.Modules) - 1
			state.Modules[i] = state.Modules[tail]
			state.Modules = state.Modules[:tail]
			break
		}
	}

	return nil, nil
}
<commit_msg>preserve order when removing module from state<commit_after>package terraform

import (
	"fmt"
	"log"
	"reflect"
)

// NodeModuleRemoved represents a module that is no longer in the
// config.
type NodeModuleRemoved struct {
	PathValue []string
}

func (n *NodeModuleRemoved) Name() string {
	return fmt.Sprintf("%s (removed)", modulePrefixStr(n.PathValue))
}

// GraphNodeSubPath
func (n *NodeModuleRemoved) Path() []string {
	return n.PathValue
}

// GraphNodeEvalable
func (n *NodeModuleRemoved) EvalTree() EvalNode {
	return &EvalOpFilter{
		Ops: []walkOperation{walkRefresh, walkApply, walkDestroy},
		Node: &EvalDeleteModule{
			PathValue: n.PathValue,
		},
	}
}

// EvalDeleteModule is an EvalNode implementation that removes an empty module
// entry from the state.
type EvalDeleteModule struct {
	PathValue []string
}

func (n *EvalDeleteModule) Eval(ctx EvalContext) (interface{}, error) {
	state, lock := ctx.State()
	if state == nil {
		return nil, nil
	}

	// Get a write lock so we can access this instance
	lock.Lock()
	defer lock.Unlock()

	// Make sure we have a clean state
	// Destroyed resources aren't deleted, they're written with an ID of "".
	state.prune()

	// find the module and delete it
	for i, m := range state.Modules {
		if reflect.DeepEqual(m.Path, n.PathValue) {
			if !m.Empty() {
				// a targeted apply may leave module resources even without a config,
				// so just log this and return.
				log.Printf("[DEBUG] cannot remove module %s, not empty", modulePrefixStr(n.PathValue))
				break
			}
			state.Modules = append(state.Modules[:i], state.Modules[i+1:]...)
			break
		}
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package vfs

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

	"github.com/containers/storage/drivers"
	"github.com/containers/storage/pkg/idtools"
	"github.com/containers/storage/pkg/ostree"
	"github.com/containers/storage/pkg/system"
	"github.com/opencontainers/selinux/go-selinux/label"
)

var (
	// CopyDir defines the copy method to use.
	CopyDir = dirCopy
)

func init() {
	graphdriver.Register("vfs", Init)
}

// Init returns a new VFS driver.
// This sets the home directory for the driver and returns NaiveDiffDriver.
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
	d := &Driver{
		homes:      []string{home},
		idMappings: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
	}
	rootIDs := d.idMappings.RootPair()
	if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
		return nil, err
	}
	for _, option := range options {
		if strings.HasPrefix(option, "vfs.imagestore=") {
			d.homes = append(d.homes, strings.Split(option[15:], ",")...)
			continue
		}
		if strings.HasPrefix(option, ".imagestore=") {
			d.homes = append(d.homes, strings.Split(option[12:], ",")...)
			continue
		}
		if strings.HasPrefix(option, "vfs.ostree_repo=") {
			if !ostree.OstreeSupport() {
				return nil, fmt.Errorf("vfs: ostree_repo specified but support for ostree is missing")
			}
			d.ostreeRepo = option[16:]
		}
		if strings.HasPrefix(option, ".ostree_repo=") {
			if !ostree.OstreeSupport() {
				return nil, fmt.Errorf("vfs: ostree_repo specified but support for ostree is missing")
			}
			d.ostreeRepo = option[13:]
		}
		if strings.HasPrefix(option, "vfs.mountopt=") {
			return nil, fmt.Errorf("vfs driver does not support mount options")
		}
	}
	if d.ostreeRepo != "" {
		rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
		if err != nil {
			return nil, err
		}
		if err := ostree.CreateOSTreeRepository(d.ostreeRepo, rootUID, rootGID); err != nil {
			return nil, err
		}
	}
	return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil
}

// Driver holds information about the driver, home directory of the driver.
// Driver implements graphdriver.ProtoDriver. It uses only basic vfs operations.
// In order to support layering, files are copied from the parent layer into the new layer. There is no copy-on-write support.
// Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
type Driver struct {
	homes      []string
	idMappings *idtools.IDMappings
	ostreeRepo string
}

func (d *Driver) String() string {
	return "vfs"
}

// Status is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any status information.
func (d *Driver) Status() [][2]string {
	return nil
}

// Metadata is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any meta data.
func (d *Driver) Metadata(id string) (map[string]string, error) {
	return nil, nil
}

// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
func (d *Driver) Cleanup() error {
	return nil
}

// CreateFromTemplate creates a layer with the same contents and parent as another layer.
func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
	if readWrite {
		return d.CreateReadWrite(id, template, opts)
	}
	return d.Create(id, template, opts)
}

// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
	return d.create(id, parent, opts, false)
}

// Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent.
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
	return d.create(id, parent, opts, true)
}

func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, ro bool) error {
	if opts != nil && len(opts.StorageOpt) != 0 {
		return fmt.Errorf("--storage-opt is not supported for vfs")
	}

	dir := d.dir(id)
	rootIDs := d.idMappings.RootPair()
	if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
		return err
	}
	if parent != "" {
		st, err := system.Stat(d.dir(parent))
		if err != nil {
			return err
		}
		rootIDs.UID = int(st.UID())
		rootIDs.GID = int(st.GID())
	}
	if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil {
		return err
	}
	labelOpts := []string{"level:s0"}
	if _, mountLabel, err := label.InitLabels(labelOpts); err == nil {
		label.SetFileLabel(dir, mountLabel)
	}
	if parent != "" {
		parentDir, err := d.Get(parent, graphdriver.MountOpts{})
		if err != nil {
			return fmt.Errorf("%s: %s", parent, err)
		}
		if err := dirCopy(parentDir, dir); err != nil {
			return err
		}
	}

	if ro && d.ostreeRepo != "" {
		if err := ostree.ConvertToOSTree(d.ostreeRepo, dir, id); err != nil {
			return err
		}
	}
	return nil

}

func (d *Driver) dir(id string) string {
	for i, home := range d.homes {
		if i > 0 {
			home = filepath.Join(home, d.String())
		}
		candidate := filepath.Join(home, "dir", filepath.Base(id))
		fi, err := os.Stat(candidate)
		if err == nil && fi.IsDir() {
			return candidate
		}
	}
	return filepath.Join(d.homes[0], "dir", filepath.Base(id))
}

// Remove deletes the content from the directory for a given id.
func (d *Driver) Remove(id string) error {
	if d.ostreeRepo != "" {
		// Ignore errors, we don't want to fail if the ostree branch doesn't exist,
		ostree.DeleteOSTree(d.ostreeRepo, id)
	}
	return system.EnsureRemoveAll(d.dir(id))
}

// Get returns the directory for the given id.
func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) {
	dir := d.dir(id)
	if len(options.Options) > 0 {
		return "", fmt.Errorf("vfs driver does not support mount options")
	}
	if st, err := os.Stat(dir); err != nil {
		return "", err
	} else if !st.IsDir() {
		return "", fmt.Errorf("%s: not a directory", dir)
	}
	return dir, nil
}

// Put is a noop for vfs that return nil for the error, since this driver has no runtime resources to clean up.
func (d *Driver) Put(id string) error {
	// The vfs driver has no runtime resources (e.g. mounts)
	// to clean up, so we don't need anything here
	return nil
}

// Exists checks to see if the directory exists for the given id.
func (d *Driver) Exists(id string) bool {
	_, err := os.Stat(d.dir(id))
	return err == nil
}

// AdditionalImageStores returns additional image stores supported by the driver
func (d *Driver) AdditionalImageStores() []string {
	if len(d.homes) > 1 {
		return d.homes[1:]
	}
	return nil
}
<commit_msg>vfs: prefer layer custom idmappings<commit_after>package vfs

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

	"github.com/containers/storage/drivers"
	"github.com/containers/storage/pkg/idtools"
	"github.com/containers/storage/pkg/ostree"
	"github.com/containers/storage/pkg/system"
	"github.com/opencontainers/selinux/go-selinux/label"
)

var (
	// CopyDir defines the copy method to use.
	CopyDir = dirCopy
)

func init() {
	graphdriver.Register("vfs", Init)
}

// Init returns a new VFS driver.
// This sets the home directory for the driver and returns NaiveDiffDriver.
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
	d := &Driver{
		homes:      []string{home},
		idMappings: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps),
	}
	rootIDs := d.idMappings.RootPair()
	if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil {
		return nil, err
	}
	for _, option := range options {
		if strings.HasPrefix(option, "vfs.imagestore=") {
			d.homes = append(d.homes, strings.Split(option[15:], ",")...)
			continue
		}
		if strings.HasPrefix(option, ".imagestore=") {
			d.homes = append(d.homes, strings.Split(option[12:], ",")...)
			continue
		}
		if strings.HasPrefix(option, "vfs.ostree_repo=") {
			if !ostree.OstreeSupport() {
				return nil, fmt.Errorf("vfs: ostree_repo specified but support for ostree is missing")
			}
			d.ostreeRepo = option[16:]
		}
		if strings.HasPrefix(option, ".ostree_repo=") {
			if !ostree.OstreeSupport() {
				return nil, fmt.Errorf("vfs: ostree_repo specified but support for ostree is missing")
			}
			d.ostreeRepo = option[13:]
		}
		if strings.HasPrefix(option, "vfs.mountopt=") {
			return nil, fmt.Errorf("vfs driver does not support mount options")
		}
	}
	if d.ostreeRepo != "" {
		rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
		if err != nil {
			return nil, err
		}
		if err := ostree.CreateOSTreeRepository(d.ostreeRepo, rootUID, rootGID); err != nil {
			return nil, err
		}
	}
	return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil
}

// Driver holds information about the driver, home directory of the driver.
// Driver implements graphdriver.ProtoDriver. It uses only basic vfs operations.
// In order to support layering, files are copied from the parent layer into the new layer. There is no copy-on-write support.
// Driver must be wrapped in NaiveDiffDriver to be used as a graphdriver.Driver
type Driver struct {
	homes      []string
	idMappings *idtools.IDMappings
	ostreeRepo string
}

func (d *Driver) String() string {
	return "vfs"
}

// Status is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any status information.
func (d *Driver) Status() [][2]string {
	return nil
}

// Metadata is used for implementing the graphdriver.ProtoDriver interface. VFS does not currently have any meta data.
func (d *Driver) Metadata(id string) (map[string]string, error) {
	return nil, nil
}

// Cleanup is used to implement graphdriver.ProtoDriver. There is no cleanup required for this driver.
func (d *Driver) Cleanup() error {
	return nil
}

// CreateFromTemplate creates a layer with the same contents and parent as another layer.
func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
	if readWrite {
		return d.CreateReadWrite(id, template, opts)
	}
	return d.Create(id, template, opts)
}

// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
	return d.create(id, parent, opts, false)
}

// Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent.
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
	return d.create(id, parent, opts, true)
}

func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, ro bool) error {
	if opts != nil && len(opts.StorageOpt) != 0 {
		return fmt.Errorf("--storage-opt is not supported for vfs")
	}

	idMappings := opts.IDMappings()
	if idMappings == nil {
		idMappings = d.idMappings
	}

	dir := d.dir(id)
	rootIDs := idMappings.RootPair()
	if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil {
		return err
	}
	if parent != "" {
		st, err := system.Stat(d.dir(parent))
		if err != nil {
			return err
		}
		rootIDs.UID = int(st.UID())
		rootIDs.GID = int(st.GID())
	}
	if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil {
		return err
	}
	labelOpts := []string{"level:s0"}
	if _, mountLabel, err := label.InitLabels(labelOpts); err == nil {
		label.SetFileLabel(dir, mountLabel)
	}
	if parent != "" {
		parentDir, err := d.Get(parent, graphdriver.MountOpts{})
		if err != nil {
			return fmt.Errorf("%s: %s", parent, err)
		}
		if err := dirCopy(parentDir, dir); err != nil {
			return err
		}
	}

	if ro && d.ostreeRepo != "" {
		if err := ostree.ConvertToOSTree(d.ostreeRepo, dir, id); err != nil {
			return err
		}
	}
	return nil

}

func (d *Driver) dir(id string) string {
	for i, home := range d.homes {
		if i > 0 {
			home = filepath.Join(home, d.String())
		}
		candidate := filepath.Join(home, "dir", filepath.Base(id))
		fi, err := os.Stat(candidate)
		if err == nil && fi.IsDir() {
			return candidate
		}
	}
	return filepath.Join(d.homes[0], "dir", filepath.Base(id))
}

// Remove deletes the content from the directory for a given id.
func (d *Driver) Remove(id string) error {
	if d.ostreeRepo != "" {
		// Ignore errors, we don't want to fail if the ostree branch doesn't exist,
		ostree.DeleteOSTree(d.ostreeRepo, id)
	}
	return system.EnsureRemoveAll(d.dir(id))
}

// Get returns the directory for the given id.
func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) {
	dir := d.dir(id)
	if len(options.Options) > 0 {
		return "", fmt.Errorf("vfs driver does not support mount options")
	}
	if st, err := os.Stat(dir); err != nil {
		return "", err
	} else if !st.IsDir() {
		return "", fmt.Errorf("%s: not a directory", dir)
	}
	return dir, nil
}

// Put is a noop for vfs that return nil for the error, since this driver has no runtime resources to clean up.
func (d *Driver) Put(id string) error {
	// The vfs driver has no runtime resources (e.g. mounts)
	// to clean up, so we don't need anything here
	return nil
}

// Exists checks to see if the directory exists for the given id.
func (d *Driver) Exists(id string) bool {
	_, err := os.Stat(d.dir(id))
	return err == nil
}

// AdditionalImageStores returns additional image stores supported by the driver
func (d *Driver) AdditionalImageStores() []string {
	if len(d.homes) > 1 {
		return d.homes[1:]
	}
	return nil
}
<|endoftext|>


<commit_before>package codec

type Interface interface {
	Marshal(v interface{}) ([]byte, error)
	Unmarshal(b []byte, v interface{}) error
}
<commit_msg>Comment on codec.Interface<commit_after>package codec

// Interface is implemented by bolster codecs.
//
// A bolster codec can marshal a struct to bytes and unmarshal bytes back into
// a struct of the same type.
type Interface interface {
	Marshal(v interface{}) ([]byte, error)
	Unmarshal(b []byte, v interface{}) error
}
<|endoftext|>