<commit_before>package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/metcalf/loadsim"
	"github.com/montanaflynn/stats"
)

type ResourceMapper struct{}

func (r *ResourceMapper) RequestNeeds(req *http.Request) ([]*loadsim.ResourceNeed, error) {
	if req.Method == "GET" {
		return []*loadsim.ResourceNeed{
			{"time", 500},
			{"CPU", 2500},
		}, nil
	}

	return []*loadsim.ResourceNeed{
		{"time", 1000},
		{"CPU", 500},
	}, nil
}

func buildAgents(clk loadsim.Clock) []loadsim.Agent {
	var agents []loadsim.Agent

	listreq, err := http.NewRequest("GET", "https://qa-api.stripe.com/v1/charges/?limit=100", nil)
	if err != nil {
		log.Fatal(err)
	}
	listreq.SetBasicAuth(os.Getenv("STRIPE_KEY"), "")

	for i := 0; i < 50; i++ {
		agent := &loadsim.RepeatAgent{
			BaseRequest: listreq,
			Clock:       clk,
			ID:          fmt.Sprintf("%s %s", listreq.Method, listreq.URL.String()),
		}
		delay := 20*time.Second + time.Millisecond*time.Duration(i*300)

		agents = append(agents, &loadsim.DelayLimitAgent{
			Agent: agent,
			Delay: delay,
			Limit: 60 * time.Second,
			Clock: clk,
		})
	}

	createreq, err := http.NewRequest("POST", "https://qa-api.stripe.com/v1/charges", nil)
	if err != nil {
		log.Fatal(err)
	}
	createreq.SetBasicAuth(os.Getenv("STRIPE_KEY"), "")

	for i := 0; i < 5; i++ {
		agents = append(agents, &loadsim.RepeatAgent{
			BaseRequest: createreq,
			Clock:       clk,
			ID:          fmt.Sprintf("%s %s", createreq.Method, createreq.URL.String()),
		})
	}

	return agents
}

func main() {
	simRun()
}

func simRun() {
	clk := loadsim.NewSimClock()

	agents := buildAgents(clk)

	var workers []loadsim.Worker
	var allResources []loadsim.Resource

	for host := 0; host < 2; host++ {
		cpu := &loadsim.CPUResource{Count: 4}
		allResources = append(allResources, cpu)

		for proc := 0; proc < 14; proc++ {
			time := &loadsim.TimeResource{}
			allResources = append(allResources, time)

			workers = append(workers, &loadsim.SimWorker{
				ResourceMap: &ResourceMapper{},
				Resources:   []loadsim.Resource{cpu, time},
				Clock:       clk,
			})
		}
	}
	worker := loadsim.WorkerPool{
		Backlog: 200,
		Timeout: 60 * time.Second,
		Workers: workers,
		Clock:   clk,
	}

	stop := make(chan struct{})

	ticker, tickStop := clk.Tick()
	go func() {
		for {
			select {
			case <-stop:
				close(tickStop)
				return
			case <-ticker:
			}

			for _, res := range allResources {
				res.Reset()
			}
		}
	}()

	resultCh := loadsim.Simulate(agents, &worker, clk, 120*time.Second)
	clk.Run(stop)

	results := collect(resultCh)
	close(stop)

	summarize(results, os.Stderr)
	output(results, os.Stdout)
}

func httpRun() {
	var clk loadsim.WallClock
	agents := buildAgents(&clk)

	resultCh := loadsim.Simulate(agents, &loadsim.HTTPWorker{
		Clock: &loadsim.WallClock{},
	}, &loadsim.WallClock{}, 100*time.Second)
	results := collect(resultCh)

	summarize(results, os.Stderr)
	output(results, os.Stdout)
}

func collect(resultCh <-chan loadsim.Result) []loadsim.Result {
	var results []loadsim.Result
	for r := range resultCh {
		results = append(results, r)
	}

	return results
}

func output(results []loadsim.Result, out io.Writer) {
	writer := csv.NewWriter(out)
	writer.Comma = '\t'
	writer.Write([]string{
		"agent", "status_code",
		"request_start", "work_start", "end",
		"total_time", "work_time",
	})

	begin := results[0].Start
	for _, res := range results {
		if res.Start.Before(begin) {
			begin = res.Start
		}
	}

	dtos := func(d time.Duration) string { return strconv.FormatFloat(d.Seconds(), 'f', 6, 64) }

	for _, res := range results {
		writer.Write([]string{
			res.AgentID,
			strconv.Itoa(res.StatusCode),
			dtos(res.Start.Sub(begin)), dtos(res.WorkStart.Sub(begin)), dtos(res.End.Sub(begin)),
			dtos(res.End.Sub(res.Start)), dtos(res.End.Sub(res.WorkStart)),
		})
	}

	writer.Flush()
}

func summarize(results []loadsim.Result, out io.Writer) {
	keyed := make(map[string][]loadsim.Result)
	codeSet := make(map[int]struct{})
	for _, res := range results {
		keyed[res.AgentID] = append(keyed[res.AgentID], res)
		codeSet[res.StatusCode] = struct{}{}
	}

	codes := make([]int, 0, len(codeSet))
	for code := range codeSet {
		codes = append(codes, code)
	}

	io.WriteString(out, "Agent\ttime(p50/p90/p95)\twork time(p50/p90/p95)\tcount")
	for _, code := range codes {
		fmt.Fprintf(out, "\t%d", code)
	}
	io.WriteString(out, "\n")

	for key, data := range keyed {
		codeCounts := make(map[int]int, len(codes))
		times := make([]float64, len(data))
		workTimes := make([]float64, len(data))

		for i, datum := range data {
			times[i] = datum.End.Sub(datum.Start).Seconds() * 1000
			workTimes[i] = datum.End.Sub(datum.WorkStart).Seconds() * 1000
			codeCounts[datum.StatusCode]++
		}

		timeStr, err := statString(times)
		if err != nil {
			panic(err)
		}

		workTimeStr, err := statString(workTimes)
		if err != nil {
			panic(err)
		}

		fmt.Fprintf(out, "%s\t%s\t%s\t%d", key, timeStr, workTimeStr, len(data))
		for _, code := range codes {
			fmt.Fprintf(out, "\t%d", codeCounts[code])
		}
		io.WriteString(out, "\n")
	}
}

func statString(data []float64) (string, error) {
	strs := make([]string, 3)

	for i, pct := range []float64{50, 90, 95} {
		stat, err := stats.Percentile(data, pct)
		if err != nil {
			return "", err
		}
		strs[i] = strconv.FormatFloat(stat, 'f', 2, 64)
	}

	return strings.Join(strs, "/"), nil
}

func newWallClock() loadsim.Clock {
	return &loadsim.WallClock{}
}
<commit_msg>QA contending<commit_after>package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/metcalf/loadsim"
	"github.com/montanaflynn/stats"
)

type ResourceMapper struct{}

func (r *ResourceMapper) RequestNeeds(req *http.Request) ([]*loadsim.ResourceNeed, error) {
	if req.Method == "GET" {
		return []*loadsim.ResourceNeed{
			{"time", 1000},
			{"CPU", 2500},
		}, nil
	}

	return []*loadsim.ResourceNeed{
		{"time", 1000},
		{"CPU", 500},
	}, nil
}

func buildAgents(clk loadsim.Clock) []loadsim.Agent {
	var agents []loadsim.Agent

	listreq, err := http.NewRequest("GET", "https://qa-api.stripe.com/v1/charges/?limit=100", nil)
	if err != nil {
		log.Fatal(err)
	}
	listreq.SetBasicAuth(os.Getenv("STRIPE_KEY"), "")

	for i := 0; i < 60; i++ {
		agent := &loadsim.RepeatAgent{
			BaseRequest: listreq,
			Clock:       clk,
			ID:          fmt.Sprintf("%s %s", listreq.Method, listreq.URL.String()),
		}
		delay := 20*time.Second + time.Millisecond*time.Duration(i*300)

		agents = append(agents, &loadsim.DelayLimitAgent{
			Agent: agent,
			Delay: delay,
			Limit: 60 * time.Second,
			Clock: clk,
		})
	}

	createreq, err := http.NewRequest("POST", "https://qa-api.stripe.com/v1/charges", nil)
	if err != nil {
		log.Fatal(err)
	}
	createreq.SetBasicAuth(os.Getenv("STRIPE_KEY"), "")

	for i := 0; i < 1; i++ {
		agents = append(agents, &loadsim.IntervalAgent{
			BaseRequest: createreq,
			Clock:       clk,
			ID:          fmt.Sprintf("%s %s", createreq.Method, createreq.URL.String()),
			Interval:    300 * time.Millisecond,
		})
	}

	return agents
}

func main() {
	simRun()
}

func simRun() {
	clk := loadsim.NewSimClock()

	agents := buildAgents(clk)

	var workers []loadsim.Worker
	var allResources []loadsim.Resource

	for host := 0; host < 2; host++ {
		cpu := &loadsim.CPUResource{Count: 2}
		allResources = append(allResources, cpu)

		for proc := 0; proc < 14; proc++ {
			time := &loadsim.TimeResource{}
			allResources = append(allResources, time)

			workers = append(workers, &loadsim.SimWorker{
				ResourceMap: &ResourceMapper{},
				Resources:   []loadsim.Resource{cpu, time},
				Clock:       clk,
			})
		}
	}
	worker := loadsim.WorkerPool{
		Backlog: 200,
		Timeout: 30 * time.Second,
		Workers: workers,
		Clock:   clk,
	}

	stop := make(chan struct{})

	ticker, tickStop := clk.Tick()
	go func() {
		for {
			select {
			case <-stop:
				close(tickStop)
				return
			case <-ticker:
			}

			for _, res := range allResources {
				res.Reset()
			}
		}
	}()

	resultCh := loadsim.Simulate(agents, &worker, clk, 120*time.Second)
	go clk.Run(stop)

	results := collect(resultCh)
	close(stop)

	summarize(results, os.Stderr)
	output(results, os.Stdout)
}

func httpRun() {
	var clk loadsim.WallClock
	agents := buildAgents(&clk)

	resultCh := loadsim.Simulate(agents, &loadsim.HTTPWorker{
		Clock: &loadsim.WallClock{},
	}, &loadsim.WallClock{}, 100*time.Second)
	results := collect(resultCh)

	summarize(results, os.Stderr)
	output(results, os.Stdout)
}

func collect(resultCh <-chan loadsim.Result) []loadsim.Result {
	var results []loadsim.Result
	for r := range resultCh {
		results = append(results, r)
	}

	return results
}

func output(results []loadsim.Result, out io.Writer) {
	writer := csv.NewWriter(out)
	writer.Comma = '\t'
	writer.Write([]string{
		"agent", "status_code",
		"request_start", "work_start", "end",
		"total_time", "work_time",
	})

	begin := results[0].Start
	for _, res := range results {
		if res.Start.Before(begin) {
			begin = res.Start
		}
	}

	dtos := func(d time.Duration) string { return strconv.FormatFloat(d.Seconds(), 'f', 6, 64) }

	for _, res := range results {
		writer.Write([]string{
			res.AgentID,
			strconv.Itoa(res.StatusCode),
			dtos(res.Start.Sub(begin)), dtos(res.WorkStart.Sub(begin)), dtos(res.End.Sub(begin)),
			dtos(res.End.Sub(res.Start)), dtos(res.End.Sub(res.WorkStart)),
		})
	}

	writer.Flush()
}

func summarize(results []loadsim.Result, out io.Writer) {
	keyed := make(map[string][]loadsim.Result)
	codeSet := make(map[int]struct{})
	for _, res := range results {
		keyed[res.AgentID] = append(keyed[res.AgentID], res)
		codeSet[res.StatusCode] = struct{}{}
	}

	codes := make([]int, 0, len(codeSet))
	for code := range codeSet {
		codes = append(codes, code)
	}

	io.WriteString(out, "Agent\ttime(p50/p90/p95)\twork time(p50/p90/p95)\tcount")
	for _, code := range codes {
		fmt.Fprintf(out, "\t%d", code)
	}
	io.WriteString(out, "\n")

	for key, data := range keyed {
		codeCounts := make(map[int]int, len(codes))
		times := make([]float64, len(data))
		workTimes := make([]float64, len(data))

		for i, datum := range data {
			times[i] = datum.End.Sub(datum.Start).Seconds() * 1000
			workTimes[i] = datum.End.Sub(datum.WorkStart).Seconds() * 1000
			codeCounts[datum.StatusCode]++
		}

		timeStr, err := statString(times)
		if err != nil {
			panic(err)
		}

		workTimeStr, err := statString(workTimes)
		if err != nil {
			panic(err)
		}

		fmt.Fprintf(out, "%s\t%s\t%s\t%d", key, timeStr, workTimeStr, len(data))
		for _, code := range codes {
			fmt.Fprintf(out, "\t%d", codeCounts[code])
		}
		io.WriteString(out, "\n")
	}
}

func statString(data []float64) (string, error) {
	strs := make([]string, 3)

	for i, pct := range []float64{50, 90, 95} {
		stat, err := stats.Percentile(data, pct)
		if err != nil {
			return "", err
		}
		strs[i] = strconv.FormatFloat(stat, 'f', 2, 64)
	}

	return strings.Join(strs, "/"), nil
}

func newWallClock() loadsim.Clock {
	return &loadsim.WallClock{}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/MakeNowJust/heredoc/v2"
	"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 mrEditCmd = &cobra.Command{
	Use:     "edit [remote] <id>[:<comment_id>]",
	Aliases: []string{"update"},
	Short:   "Edit or update an MR",
	Example: heredoc.Doc(`
		lab mr edit <id>
		lab mr edit <id> -m "new title"
		lab mr edit <id> -m "new title" -m "new desc"
		lab mr edit <id> -l new_label --unlabel old_label
		lab mr edit <id>:<comment_id>
	`),
	PersistentPreRun: labPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		commentNum, branchArgs, err := filterCommentArg(args)
		if err != nil {
			log.Fatal(err)
		}

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

		if mrNum == 0 {
			fmt.Println("Error: Cannot determine MR id.")
			os.Exit(1)
		}

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

		linebreak, err := cmd.Flags().GetBool("force-linebreak")
		if err != nil {
			log.Fatal(err)
		}

		// Edit a comment on the MR
		if commentNum != 0 {
			replyNote(rn, true, mrNum, commentNum, true, true, "", linebreak, false, nil)
			return
		}

		var labelsChanged bool
		// get the labels to add
		addLabelTerms, err := cmd.Flags().GetStringSlice("label")
		if err != nil {
			log.Fatal(err)
		}
		addLabels, err := mapLabels(rn, addLabelTerms)
		if err != nil {
			log.Fatal(err)
		}
		if len(addLabels) > 0 {
			labelsChanged = true
		}

		// get the labels to remove
		rmLabelTerms, err := cmd.Flags().GetStringSlice("unlabel")
		if err != nil {
			log.Fatal(err)
		}
		rmLabels, err := mapLabels(rn, rmLabelTerms)
		if err != nil {
			log.Fatal(err)
		}
		if len(rmLabels) > 0 {
			labelsChanged = true
		}

		// get the assignees to add
		assignees, err := cmd.Flags().GetStringSlice("assign")
		if err != nil {
			log.Fatal(err)
		}

		// get the assignees to remove
		unassignees, err := cmd.Flags().GetStringSlice("unassign")
		if err != nil {
			log.Fatal(err)
		}

		// get the reviewers to add
		reviewers, err := cmd.Flags().GetStringSlice("review")
		if err != nil {
			log.Fatal(err)
		}

		// get the reviewers to remove
		unreviewers, err := cmd.Flags().GetStringSlice("unreview")
		if err != nil {
			log.Fatal(err)
		}

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

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

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

		if draft && ready {
			log.Fatal("--draft and --ready cannot be used together")
		}

		currentAssignees := mrGetCurrentAssignees(mr)
		assigneeIDs, assigneesChanged, err := getUpdateUsers(currentAssignees, assignees, unassignees)
		if err != nil {
			log.Fatal(err)
		}

		currentReviewers := mrGetCurrentReviewers(mr)
		reviewerIDs, reviewersChanged, err := getUpdateUsers(currentReviewers, reviewers, unreviewers)
		if err != nil {
			log.Fatal(err)
		}

		milestoneName, err := cmd.Flags().GetString("milestone")
		if err != nil {
			log.Fatal(err)
		}
		updateMilestone := cmd.Flags().Lookup("milestone").Changed
		milestoneID := -1

		if milestoneName != "" {
			ms, err := lab.MilestoneGet(rn, milestoneName)
			if err != nil {
				log.Fatal(err)
			}
			milestoneID = ms.ID
		}

		targetBranchName, err := cmd.Flags().GetString("target-branch")
		if err != nil {
			log.Fatal(err)
		}

		targetBranchChanged := false
		if targetBranchName != "" {
			targetBranchName, err = getBranchName(rn, targetBranchName)
			if err != nil {
				log.Fatal(err)
			}

			if targetBranchName != mr.TargetBranch {
				targetBranchChanged = true
			}
		}

		// get all of the "message" flags
		msgs, err := cmd.Flags().GetStringSlice("message")
		if err != nil {
			log.Fatal(err)
		}

		var title, body string

		if filename != "" {
			if len(msgs) > 0 {
				log.Fatal("option -F cannot be combined with -m")
			}

			title, body, err = editGetTitleDescFromFile(filename)
			if err != nil {
				log.Fatal(err)
			}
		} else {
			title, body, err = editGetTitleDescription(
				mr.Title, mr.Description, msgs, cmd.Flags().NFlag())
			if err != nil {
				_, f, l, _ := runtime.Caller(0)
				log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
			}
		}

		if title == "" {
			log.Fatal("aborting: empty mr title")
		}

		isWIP := strings.EqualFold(title[0:4], "wip:")
		isDraft := strings.EqualFold(title[0:6], "draft:") ||
			strings.EqualFold(title[0:7], "[draft]") ||
			strings.EqualFold(title[0:7], "(draft)")

		if ready {
			if isWIP {
				title = strings.TrimPrefix(title, title[0:4])
			} else if isDraft {
				if title[0] == '(' || title[0] == '[' {
					title = strings.TrimPrefix(title, title[0:7])
				} else {
					title = strings.TrimPrefix(title, title[0:6])
				}
			}
		}

		if draft {
			if !isWIP && !isDraft {
				title = "Draft: " + title
			}
		}

		abortUpdate := (title == mr.Title && body == mr.Description &&
			!labelsChanged && !assigneesChanged && !updateMilestone &&
			!targetBranchChanged && !reviewersChanged)
		if abortUpdate {
			log.Fatal("aborting: no changes")
		}

		if linebreak {
			body = textToMarkdown(body)
		}

		opts := &gitlab.UpdateMergeRequestOptions{
			Title:       &title,
			Description: &body,
		}

		if labelsChanged {
			// empty arrays are just ignored
			opts.AddLabels = addLabels
			opts.RemoveLabels = rmLabels
		}

		if assigneesChanged {
			opts.AssigneeIDs = assigneeIDs
		}

		if reviewersChanged {
			opts.ReviewerIDs = reviewerIDs
		}

		if updateMilestone {
			opts.MilestoneID = &milestoneID
		}

		if targetBranchChanged {
			opts.TargetBranch = &targetBranchName
		}

		mrURL, err := lab.MRUpdate(rn, int(mrNum), opts)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(mrURL)
	},
}

// mrGetCurrentAssignees returns a string slice of the current assignees'
// usernames
func mrGetCurrentAssignees(mr *gitlab.MergeRequest) []string {
	currentAssignees := make([]string, len(mr.Assignees))
	if len(mr.Assignees) > 0 && mr.Assignees[0].Username != "" {
		for i, a := range mr.Assignees {
			currentAssignees[i] = a.Username
		}
	}
	return currentAssignees
}

// mrGetCurrentReviewers returns a string slice of the current reviewers'
// usernames
func mrGetCurrentReviewers(mr *gitlab.MergeRequest) []string {
	currentReviewers := make([]string, len(mr.Reviewers))
	if len(mr.Reviewers) > 0 && mr.Reviewers[0].Username != "" {
		for i, a := range mr.Reviewers {
			currentReviewers[i] = a.Username
		}
	}
	return currentReviewers
}

// getBranchName considers the possible ambiguity of different branch names
func getBranchName(project, branch string) (string, error) {
	opts := &gitlab.ListBranchesOptions{
		Search: &branch,
	}

	projectBranches, err := lab.BranchList(project, opts)
	if err != nil {
		return "", err
	}

	// Branch API accepts a search parameter, so we may get the answer
	// right away, however, the search term may match as a substring, so we
	// also need to check for multiple branch names and their ambiguity
	var match string

	switch len(projectBranches) {
	case 0:
		return "", errors.Errorf("Branch '%s' not found\n", branch)
	case 1:
		match = projectBranches[0].Name
	default:
		branchNames := make([]string, len(projectBranches))
		for _, branch := range projectBranches {
			branchNames = append(branchNames, branch.Name)
		}

		// Handle term ambiguity for multiple matched branch names
		matches, err := matchTerms([]string{branch}, branchNames)
		if err != nil {
			return "", errors.Errorf("Branch %s\n", err.Error())
		}

		// we only asked for a single term
		match = matches[0]
	}

	return match, nil
}

func init() {
	mrEditCmd.Flags().StringSliceP("message", "m", []string{}, "use the given <msg>; multiple -m are concatenated as separate paragraphs")
	mrEditCmd.Flags().StringSliceP("label", "l", []string{}, "add the given label(s) to the merge request")
	mrEditCmd.Flags().StringSliceP("unlabel", "", []string{}, "remove the given label(s) from the merge request")
	mrEditCmd.Flags().StringSliceP("assign", "a", []string{}, "add an assignee by username")
	mrEditCmd.Flags().StringSliceP("unassign", "", []string{}, "remove an assignee by username")
	mrEditCmd.Flags().String("milestone", "", "set milestone")
	mrEditCmd.Flags().StringP("target-branch", "t", "", "set target branch")
	mrEditCmd.Flags().StringP("file", "F", "", "use the given file as the description")
	mrEditCmd.Flags().Bool("force-linebreak", false, "append 2 spaces to the end of each line to force markdown linebreaks")
	mrEditCmd.Flags().Bool("draft", false, "mark the merge request as draft")
	mrEditCmd.Flags().Bool("ready", false, "mark the merge request as ready")
	mrEditCmd.Flags().StringSliceP("review", "r", []string{}, "add an reviewer by username")
	mrEditCmd.Flags().StringSliceP("unreview", "", []string{}, "remove an reviewer by username")
	mrEditCmd.Flags().SortFlags = false

	mrCmd.AddCommand(mrEditCmd)

	carapace.Gen(mrEditCmd).FlagCompletion(carapace.ActionMap{
		"label": carapace.ActionMultiParts(",", func(c carapace.Context) carapace.Action {
			project, _, err := parseArgsRemoteAndProject(c.Args)
			if err != nil {
				return carapace.ActionMessage(err.Error())
			}
			return action.Labels(project).Invoke(c).Filter(c.Parts).ToA()

		}),
		"milestone": carapace.ActionCallback(func(c carapace.Context) carapace.Action {
			project, _, err := parseArgsRemoteAndProject(c.Args)
			if err != nil {
				return carapace.ActionMessage(err.Error())
			}
			return action.Milestones(project, action.MilestoneOpts{Active: true})
		}),
	})

	carapace.Gen(mrEditCmd).PositionalCompletion(
		action.Remotes(),
		action.MergeRequests(mrList),
	)
}
<commit_msg>cmd/mr_edit: update Draft detection to use hasPrefix()<commit_after>package cmd

import (
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/MakeNowJust/heredoc/v2"
	"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 mrEditCmd = &cobra.Command{
	Use:     "edit [remote] <id>[:<comment_id>]",
	Aliases: []string{"update"},
	Short:   "Edit or update an MR",
	Example: heredoc.Doc(`
		lab mr edit <id>
		lab mr edit <id> -m "new title"
		lab mr edit <id> -m "new title" -m "new desc"
		lab mr edit <id> -l new_label --unlabel old_label
		lab mr edit <id>:<comment_id>
	`),
	PersistentPreRun: labPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		commentNum, branchArgs, err := filterCommentArg(args)
		if err != nil {
			log.Fatal(err)
		}

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

		if mrNum == 0 {
			fmt.Println("Error: Cannot determine MR id.")
			os.Exit(1)
		}

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

		linebreak, err := cmd.Flags().GetBool("force-linebreak")
		if err != nil {
			log.Fatal(err)
		}

		// Edit a comment on the MR
		if commentNum != 0 {
			replyNote(rn, true, mrNum, commentNum, true, true, "", linebreak, false, nil)
			return
		}

		var labelsChanged bool
		// get the labels to add
		addLabelTerms, err := cmd.Flags().GetStringSlice("label")
		if err != nil {
			log.Fatal(err)
		}
		addLabels, err := mapLabels(rn, addLabelTerms)
		if err != nil {
			log.Fatal(err)
		}
		if len(addLabels) > 0 {
			labelsChanged = true
		}

		// get the labels to remove
		rmLabelTerms, err := cmd.Flags().GetStringSlice("unlabel")
		if err != nil {
			log.Fatal(err)
		}
		rmLabels, err := mapLabels(rn, rmLabelTerms)
		if err != nil {
			log.Fatal(err)
		}
		if len(rmLabels) > 0 {
			labelsChanged = true
		}

		// get the assignees to add
		assignees, err := cmd.Flags().GetStringSlice("assign")
		if err != nil {
			log.Fatal(err)
		}

		// get the assignees to remove
		unassignees, err := cmd.Flags().GetStringSlice("unassign")
		if err != nil {
			log.Fatal(err)
		}

		// get the reviewers to add
		reviewers, err := cmd.Flags().GetStringSlice("review")
		if err != nil {
			log.Fatal(err)
		}

		// get the reviewers to remove
		unreviewers, err := cmd.Flags().GetStringSlice("unreview")
		if err != nil {
			log.Fatal(err)
		}

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

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

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

		if draft && ready {
			log.Fatal("--draft and --ready cannot be used together")
		}

		currentAssignees := mrGetCurrentAssignees(mr)
		assigneeIDs, assigneesChanged, err := getUpdateUsers(currentAssignees, assignees, unassignees)
		if err != nil {
			log.Fatal(err)
		}

		currentReviewers := mrGetCurrentReviewers(mr)
		reviewerIDs, reviewersChanged, err := getUpdateUsers(currentReviewers, reviewers, unreviewers)
		if err != nil {
			log.Fatal(err)
		}

		milestoneName, err := cmd.Flags().GetString("milestone")
		if err != nil {
			log.Fatal(err)
		}
		updateMilestone := cmd.Flags().Lookup("milestone").Changed
		milestoneID := -1

		if milestoneName != "" {
			ms, err := lab.MilestoneGet(rn, milestoneName)
			if err != nil {
				log.Fatal(err)
			}
			milestoneID = ms.ID
		}

		targetBranchName, err := cmd.Flags().GetString("target-branch")
		if err != nil {
			log.Fatal(err)
		}

		targetBranchChanged := false
		if targetBranchName != "" {
			targetBranchName, err = getBranchName(rn, targetBranchName)
			if err != nil {
				log.Fatal(err)
			}

			if targetBranchName != mr.TargetBranch {
				targetBranchChanged = true
			}
		}

		// get all of the "message" flags
		msgs, err := cmd.Flags().GetStringSlice("message")
		if err != nil {
			log.Fatal(err)
		}

		var title, body string

		if filename != "" {
			if len(msgs) > 0 {
				log.Fatal("option -F cannot be combined with -m")
			}

			title, body, err = editGetTitleDescFromFile(filename)
			if err != nil {
				log.Fatal(err)
			}
		} else {
			title, body, err = editGetTitleDescription(
				mr.Title, mr.Description, msgs, cmd.Flags().NFlag())
			if err != nil {
				_, f, l, _ := runtime.Caller(0)
				log.Fatal(f+":"+strconv.Itoa(l)+" ", err)
			}
		}

		if title == "" {
			log.Fatal("aborting: empty mr title")
		}

		isWIP := hasPrefix(title, "wip:") ||
			hasPrefix(title, "[wip]")
		isDraft := hasPrefix(title, "draft:") ||
			hasPrefix(title, "[draft]") ||
			hasPrefix(title, "(draft)")

		if ready {
			if isWIP {
				if title[0] == '[' {
					title = strings.TrimPrefix(title, title[0:5])
				} else {
					title = strings.TrimPrefix(title, title[0:4])
				}
			} else if isDraft {
				if title[0] == '(' || title[0] == '[' {
					title = strings.TrimPrefix(title, title[0:7])
				} else {
					title = strings.TrimPrefix(title, title[0:6])
				}
			}
		}

		if draft {
			if !isWIP && !isDraft {
				title = "Draft: " + title
			}
		}

		abortUpdate := (title == mr.Title && body == mr.Description &&
			!labelsChanged && !assigneesChanged && !updateMilestone &&
			!targetBranchChanged && !reviewersChanged)
		if abortUpdate {
			log.Fatal("aborting: no changes")
		}

		if linebreak {
			body = textToMarkdown(body)
		}

		opts := &gitlab.UpdateMergeRequestOptions{
			Title:       &title,
			Description: &body,
		}

		if labelsChanged {
			// empty arrays are just ignored
			opts.AddLabels = addLabels
			opts.RemoveLabels = rmLabels
		}

		if assigneesChanged {
			opts.AssigneeIDs = assigneeIDs
		}

		if reviewersChanged {
			opts.ReviewerIDs = reviewerIDs
		}

		if updateMilestone {
			opts.MilestoneID = &milestoneID
		}

		if targetBranchChanged {
			opts.TargetBranch = &targetBranchName
		}

		mrURL, err := lab.MRUpdate(rn, int(mrNum), opts)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(mrURL)
	},
}

// mrGetCurrentAssignees returns a string slice of the current assignees'
// usernames
func mrGetCurrentAssignees(mr *gitlab.MergeRequest) []string {
	currentAssignees := make([]string, len(mr.Assignees))
	if len(mr.Assignees) > 0 && mr.Assignees[0].Username != "" {
		for i, a := range mr.Assignees {
			currentAssignees[i] = a.Username
		}
	}
	return currentAssignees
}

// mrGetCurrentReviewers returns a string slice of the current reviewers'
// usernames
func mrGetCurrentReviewers(mr *gitlab.MergeRequest) []string {
	currentReviewers := make([]string, len(mr.Reviewers))
	if len(mr.Reviewers) > 0 && mr.Reviewers[0].Username != "" {
		for i, a := range mr.Reviewers {
			currentReviewers[i] = a.Username
		}
	}
	return currentReviewers
}

// getBranchName considers the possible ambiguity of different branch names
func getBranchName(project, branch string) (string, error) {
	opts := &gitlab.ListBranchesOptions{
		Search: &branch,
	}

	projectBranches, err := lab.BranchList(project, opts)
	if err != nil {
		return "", err
	}

	// Branch API accepts a search parameter, so we may get the answer
	// right away, however, the search term may match as a substring, so we
	// also need to check for multiple branch names and their ambiguity
	var match string

	switch len(projectBranches) {
	case 0:
		return "", errors.Errorf("Branch '%s' not found\n", branch)
	case 1:
		match = projectBranches[0].Name
	default:
		branchNames := make([]string, len(projectBranches))
		for _, branch := range projectBranches {
			branchNames = append(branchNames, branch.Name)
		}

		// Handle term ambiguity for multiple matched branch names
		matches, err := matchTerms([]string{branch}, branchNames)
		if err != nil {
			return "", errors.Errorf("Branch %s\n", err.Error())
		}

		// we only asked for a single term
		match = matches[0]
	}

	return match, nil
}

func init() {
	mrEditCmd.Flags().StringSliceP("message", "m", []string{}, "use the given <msg>; multiple -m are concatenated as separate paragraphs")
	mrEditCmd.Flags().StringSliceP("label", "l", []string{}, "add the given label(s) to the merge request")
	mrEditCmd.Flags().StringSliceP("unlabel", "", []string{}, "remove the given label(s) from the merge request")
	mrEditCmd.Flags().StringSliceP("assign", "a", []string{}, "add an assignee by username")
	mrEditCmd.Flags().StringSliceP("unassign", "", []string{}, "remove an assignee by username")
	mrEditCmd.Flags().String("milestone", "", "set milestone")
	mrEditCmd.Flags().StringP("target-branch", "t", "", "set target branch")
	mrEditCmd.Flags().StringP("file", "F", "", "use the given file as the description")
	mrEditCmd.Flags().Bool("force-linebreak", false, "append 2 spaces to the end of each line to force markdown linebreaks")
	mrEditCmd.Flags().Bool("draft", false, "mark the merge request as draft")
	mrEditCmd.Flags().Bool("ready", false, "mark the merge request as ready")
	mrEditCmd.Flags().StringSliceP("review", "r", []string{}, "add an reviewer by username")
	mrEditCmd.Flags().StringSliceP("unreview", "", []string{}, "remove an reviewer by username")
	mrEditCmd.Flags().SortFlags = false

	mrCmd.AddCommand(mrEditCmd)

	carapace.Gen(mrEditCmd).FlagCompletion(carapace.ActionMap{
		"label": carapace.ActionMultiParts(",", func(c carapace.Context) carapace.Action {
			project, _, err := parseArgsRemoteAndProject(c.Args)
			if err != nil {
				return carapace.ActionMessage(err.Error())
			}
			return action.Labels(project).Invoke(c).Filter(c.Parts).ToA()

		}),
		"milestone": carapace.ActionCallback(func(c carapace.Context) carapace.Action {
			project, _, err := parseArgsRemoteAndProject(c.Args)
			if err != nil {
				return carapace.ActionMessage(err.Error())
			}
			return action.Milestones(project, action.MilestoneOpts{Active: true})
		}),
	})

	carapace.Gen(mrEditCmd).PositionalCompletion(
		action.Remotes(),
		action.MergeRequests(mrList),
	)
}
<|endoftext|>


<commit_before>/*
 * MinIO Cloud Storage, (C) 2020 MinIO, 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 cmd

import (
	"context"
	"net/http"
	"os"
	"sync"
	"syscall"

	"github.com/minio/minio/pkg/disk"
	"github.com/minio/minio/pkg/madmin"
	cpuhw "github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/host"
	memhw "github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/process"
)

func getLocalCPUOBDInfo(ctx context.Context) madmin.ServerCPUOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	info, err := cpuhw.InfoWithContext(ctx)
	if err != nil {
		return madmin.ServerCPUOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	time, err := cpuhw.TimesWithContext(ctx, false)
	if err != nil {
		return madmin.ServerCPUOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	return madmin.ServerCPUOBDInfo{
		Addr:     addr,
		CPUStat:  info,
		TimeStat: time,
		Error:    "",
	}

}

func getLocalDrivesOBD(ctx context.Context, parallel bool, endpointZones EndpointZones, r *http.Request) madmin.ServerDrivesOBDInfo {
	var drivesOBDInfo []madmin.DriveOBDInfo
	wg := sync.WaitGroup{}
	for _, ep := range endpointZones {
		for i, endpoint := range ep.Endpoints {
			// Only proceed for local endpoints
			if endpoint.IsLocal {
				if _, err := os.Stat(endpoint.Path); err != nil {
					// Since this drive is not available, add relevant details and proceed
					drivesOBDInfo = append(drivesOBDInfo, madmin.DriveOBDInfo{
						Path:  endpoint.Path,
						Error: err.Error(),
					})
					continue
				}
				measure := func(index int) {
					latency, throughput, err := disk.GetOBDInfo(ctx, pathJoin(endpoint.Path, minioMetaTmpBucket, mustGetUUID()))
					driveOBDInfo := madmin.DriveOBDInfo{
						Path:       endpoint.Path,
						Latency:    latency,
						Throughput: throughput,
					}
					if err != nil {
						driveOBDInfo.Error = err.Error()
					}
					drivesOBDInfo = append(drivesOBDInfo, driveOBDInfo)
					wg.Done()
				}
				wg.Add(1)

				if parallel {
					go measure(i)
				} else {
					measure(i)
				}
			}
		}
	}
	wg.Wait()
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(endpointZones)
	} else {
		addr = "minio"
	}
	if parallel {
		return madmin.ServerDrivesOBDInfo{
			Addr:     addr,
			Parallel: drivesOBDInfo,
		}
	}
	return madmin.ServerDrivesOBDInfo{
		Addr:   addr,
		Serial: drivesOBDInfo,
	}
}

func getLocalMemOBD(ctx context.Context) madmin.ServerMemOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	swap, err := memhw.SwapMemoryWithContext(ctx)
	if err != nil {
		return madmin.ServerMemOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	vm, err := memhw.VirtualMemoryWithContext(ctx)
	if err != nil {
		return madmin.ServerMemOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	return madmin.ServerMemOBDInfo{
		Addr:       addr,
		SwapMem:    swap,
		VirtualMem: vm,
		Error:      "",
	}
}

func getLocalProcOBD(ctx context.Context) madmin.ServerProcOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	errProcInfo := func(err error) madmin.ServerProcOBDInfo {
		return madmin.ServerProcOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	selfPid := int32(syscall.Getpid())
	self, err := process.NewProcess(selfPid)
	if err != nil {
		return errProcInfo(err)
	}

	processes := []*process.Process{self}
	if err != nil {
		return errProcInfo(err)
	}

	sysProcs := []madmin.SysOBDProcess{}
	for _, proc := range processes {
		sysProc := madmin.SysOBDProcess{}
		sysProc.Pid = proc.Pid

		bg, err := proc.BackgroundWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Background = bg

		cpuPercent, err := proc.CPUPercentWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.CPUPercent = cpuPercent

		children, _ := proc.ChildrenWithContext(ctx)

		for _, c := range children {
			sysProc.Children = append(sysProc.Children, c.Pid)
		}
		cmdLine, err := proc.CmdlineWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.CmdLine = cmdLine

		conns, err := proc.ConnectionsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Connections = conns

		createTime, err := proc.CreateTimeWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.CreateTime = createTime

		cwd, err := proc.CwdWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Cwd = cwd

		exe, err := proc.ExeWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Exe = exe

		gids, err := proc.GidsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Gids = gids

		ioCounters, err := proc.IOCountersWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.IOCounters = ioCounters

		isRunning, err := proc.IsRunningWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.IsRunning = isRunning

		memInfo, err := proc.MemoryInfoWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.MemInfo = memInfo

		memMaps, err := proc.MemoryMapsWithContext(ctx, true)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.MemMaps = memMaps

		memPercent, err := proc.MemoryPercentWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.MemPercent = memPercent

		name, err := proc.NameWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Name = name

		netIOCounters, err := proc.NetIOCountersWithContext(ctx, false)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NetIOCounters = netIOCounters

		nice, err := proc.NiceWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Nice = nice

		numCtxSwitches, err := proc.NumCtxSwitchesWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NumCtxSwitches = numCtxSwitches

		numFds, err := proc.NumFDsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NumFds = numFds

		numThreads, err := proc.NumThreadsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NumThreads = numThreads

		openFiles, err := proc.OpenFilesWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.OpenFiles = openFiles

		pageFaults, err := proc.PageFaultsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.PageFaults = pageFaults

		parent, err := proc.ParentWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Parent = parent.Pid

		ppid, err := proc.PpidWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Ppid = ppid

		rlimit, err := proc.RlimitWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Rlimit = rlimit

		status, err := proc.StatusWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Status = status

		tgid, err := proc.Tgid()
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Tgid = tgid

		threads, err := proc.ThreadsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Threads = threads

		times, err := proc.TimesWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Times = times

		uids, err := proc.UidsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Uids = uids

		username, err := proc.UsernameWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Username = username

		sysProcs = append(sysProcs, sysProc)
	}

	return madmin.ServerProcOBDInfo{
		Addr:      addr,
		Processes: sysProcs,
		Error:     "",
	}
}

func getLocalOsInfoOBD(ctx context.Context) madmin.ServerOsOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	info, err := host.InfoWithContext(ctx)
	if err != nil {
		return madmin.ServerOsOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	sensors, err := host.SensorsTemperaturesWithContext(ctx)
	if err != nil {
		return madmin.ServerOsOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	users, err := host.UsersWithContext(ctx)
	if err != nil {
		return madmin.ServerOsOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	return madmin.ServerOsOBDInfo{
		Addr:    addr,
		Info:    info,
		Sensors: sensors,
		Users:   users,
		Error:   "",
	}
}
<commit_msg>fix: drive names in output of parallel obd test (#9312)<commit_after>/*
 * MinIO Cloud Storage, (C) 2020 MinIO, 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 cmd

import (
	"context"
	"net/http"
	"os"
	"sync"
	"syscall"

	"github.com/minio/minio/pkg/disk"
	"github.com/minio/minio/pkg/madmin"
	cpuhw "github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/host"
	memhw "github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/process"
)

func getLocalCPUOBDInfo(ctx context.Context) madmin.ServerCPUOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	info, err := cpuhw.InfoWithContext(ctx)
	if err != nil {
		return madmin.ServerCPUOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	time, err := cpuhw.TimesWithContext(ctx, false)
	if err != nil {
		return madmin.ServerCPUOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	return madmin.ServerCPUOBDInfo{
		Addr:     addr,
		CPUStat:  info,
		TimeStat: time,
		Error:    "",
	}

}

func getLocalDrivesOBD(ctx context.Context, parallel bool, endpointZones EndpointZones, r *http.Request) madmin.ServerDrivesOBDInfo {
	var drivesOBDInfo []madmin.DriveOBDInfo
	wg := sync.WaitGroup{}
	for _, ep := range endpointZones {
		for i, endpoint := range ep.Endpoints {
			// Only proceed for local endpoints
			if endpoint.IsLocal {
				if _, err := os.Stat(endpoint.Path); err != nil {
					// Since this drive is not available, add relevant details and proceed
					drivesOBDInfo = append(drivesOBDInfo, madmin.DriveOBDInfo{
						Path:  endpoint.Path,
						Error: err.Error(),
					})
					continue
				}
				measure := func(index int, path string) {
					latency, throughput, err := disk.GetOBDInfo(ctx, pathJoin(endpoint.Path, minioMetaTmpBucket, mustGetUUID()))
					driveOBDInfo := madmin.DriveOBDInfo{
						Path:       path,
						Latency:    latency,
						Throughput: throughput,
					}
					if err != nil {
						driveOBDInfo.Error = err.Error()
					}
					drivesOBDInfo = append(drivesOBDInfo, driveOBDInfo)
					wg.Done()
				}
				wg.Add(1)

				if parallel {
					go measure(i, endpoint.Path)
				} else {
					measure(i, endpoint.Path)
				}
			}
		}
	}
	wg.Wait()
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(endpointZones)
	} else {
		addr = "minio"
	}
	if parallel {
		return madmin.ServerDrivesOBDInfo{
			Addr:     addr,
			Parallel: drivesOBDInfo,
		}
	}
	return madmin.ServerDrivesOBDInfo{
		Addr:   addr,
		Serial: drivesOBDInfo,
	}
}

func getLocalMemOBD(ctx context.Context) madmin.ServerMemOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	swap, err := memhw.SwapMemoryWithContext(ctx)
	if err != nil {
		return madmin.ServerMemOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	vm, err := memhw.VirtualMemoryWithContext(ctx)
	if err != nil {
		return madmin.ServerMemOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	return madmin.ServerMemOBDInfo{
		Addr:       addr,
		SwapMem:    swap,
		VirtualMem: vm,
		Error:      "",
	}
}

func getLocalProcOBD(ctx context.Context) madmin.ServerProcOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	errProcInfo := func(err error) madmin.ServerProcOBDInfo {
		return madmin.ServerProcOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	selfPid := int32(syscall.Getpid())
	self, err := process.NewProcess(selfPid)
	if err != nil {
		return errProcInfo(err)
	}

	processes := []*process.Process{self}
	if err != nil {
		return errProcInfo(err)
	}

	sysProcs := []madmin.SysOBDProcess{}
	for _, proc := range processes {
		sysProc := madmin.SysOBDProcess{}
		sysProc.Pid = proc.Pid

		bg, err := proc.BackgroundWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Background = bg

		cpuPercent, err := proc.CPUPercentWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.CPUPercent = cpuPercent

		children, _ := proc.ChildrenWithContext(ctx)

		for _, c := range children {
			sysProc.Children = append(sysProc.Children, c.Pid)
		}
		cmdLine, err := proc.CmdlineWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.CmdLine = cmdLine

		conns, err := proc.ConnectionsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Connections = conns

		createTime, err := proc.CreateTimeWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.CreateTime = createTime

		cwd, err := proc.CwdWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Cwd = cwd

		exe, err := proc.ExeWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Exe = exe

		gids, err := proc.GidsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Gids = gids

		ioCounters, err := proc.IOCountersWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.IOCounters = ioCounters

		isRunning, err := proc.IsRunningWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.IsRunning = isRunning

		memInfo, err := proc.MemoryInfoWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.MemInfo = memInfo

		memMaps, err := proc.MemoryMapsWithContext(ctx, true)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.MemMaps = memMaps

		memPercent, err := proc.MemoryPercentWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.MemPercent = memPercent

		name, err := proc.NameWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Name = name

		netIOCounters, err := proc.NetIOCountersWithContext(ctx, false)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NetIOCounters = netIOCounters

		nice, err := proc.NiceWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Nice = nice

		numCtxSwitches, err := proc.NumCtxSwitchesWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NumCtxSwitches = numCtxSwitches

		numFds, err := proc.NumFDsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NumFds = numFds

		numThreads, err := proc.NumThreadsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.NumThreads = numThreads

		openFiles, err := proc.OpenFilesWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.OpenFiles = openFiles

		pageFaults, err := proc.PageFaultsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.PageFaults = pageFaults

		parent, err := proc.ParentWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Parent = parent.Pid

		ppid, err := proc.PpidWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Ppid = ppid

		rlimit, err := proc.RlimitWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Rlimit = rlimit

		status, err := proc.StatusWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Status = status

		tgid, err := proc.Tgid()
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Tgid = tgid

		threads, err := proc.ThreadsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Threads = threads

		times, err := proc.TimesWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Times = times

		uids, err := proc.UidsWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Uids = uids

		username, err := proc.UsernameWithContext(ctx)
		if err != nil {
			return errProcInfo(err)
		}
		sysProc.Username = username

		sysProcs = append(sysProcs, sysProc)
	}

	return madmin.ServerProcOBDInfo{
		Addr:      addr,
		Processes: sysProcs,
		Error:     "",
	}
}

func getLocalOsInfoOBD(ctx context.Context) madmin.ServerOsOBDInfo {
	addr := ""
	if globalIsDistXL {
		addr = GetLocalPeer(globalEndpoints)
	} else {
		addr = "minio"
	}

	info, err := host.InfoWithContext(ctx)
	if err != nil {
		return madmin.ServerOsOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	sensors, err := host.SensorsTemperaturesWithContext(ctx)
	if err != nil {
		return madmin.ServerOsOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	users, err := host.UsersWithContext(ctx)
	if err != nil {
		return madmin.ServerOsOBDInfo{
			Addr:  addr,
			Error: err.Error(),
		}
	}

	return madmin.ServerOsOBDInfo{
		Addr:    addr,
		Info:    info,
		Sensors: sensors,
		Users:   users,
		Error:   "",
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/logs"
	"github.com/qiniu/api.v7/storage"
	"github.com/qiniu/qshell/iqshell"
	"github.com/spf13/cobra"
	"io/ioutil"
	"os"
	"strings"
)

var qUploadCmd = &cobra.Command{
	Use:   "qupload <quploadConfigFile>",
	Short: "Batch upload files to the qiniu bucket",
	Args:  cobra.ExactArgs(1),
	Run:   QiniuUpload,
}

var (
	successFname   string
	failureFname   string
	overwriteFname string
	upthreadCount  int64
	uploadConfig   iqshell.UploadConfig
)

func init() {
	qUploadCmd.Flags().StringVarP(&successFname, "success-list", "s", "", "upload success (all) file list")
	qUploadCmd.Flags().StringVarP(&failureFname, "failure-list", "f", "", "upload failure file list")
	qUploadCmd.Flags().StringVarP(&overwriteFname, "overwrite-list", "w", "", "upload success (overwrite) file list")
	qUploadCmd.Flags().Int64VarP(&upthreadCount, "worker", "c", 1, "worker count")
	qUploadCmd.Flags().StringVarP(&callbackUrls, "callback-urls", "l", "", "upload callback urls, separated by comma")
	qUploadCmd.Flags().StringVarP(&callbackHost, "callback-host", "T", "", "upload callback host")
	RootCmd.AddCommand(qUploadCmd)
}

func parseUploadConfigFile(uploadConfigFile string, uploadConfig *iqshell.UploadConfig) (err error) {
	//read upload config
	if uploadConfigFile == "" {
		err = fmt.Errorf("config filename is empty")
		return
	}
	fp, oErr := os.Open(uploadConfigFile)
	if oErr != nil {
		err = fmt.Errorf("Open upload config file ``%s`: %v\n", uploadConfigFile, oErr)
		return
	}
	defer fp.Close()

	configData, rErr := ioutil.ReadAll(fp)
	if rErr != nil {
		err = fmt.Errorf("Read upload config file `%s`: %v\n", uploadConfigFile, rErr)
		return
	}
	uErr := json.Unmarshal(configData, uploadConfig)
	if uErr != nil {
		err = fmt.Errorf("Parse upload config file `%s`: %v\n", uploadConfigFile, uErr)
		return
	}
	return
}

func QiniuUpload(cmd *cobra.Command, params []string) {

	configFile := params[0]

	pErr := parseUploadConfigFile(configFile, &uploadConfig)
	if pErr != nil {
		logs.Error(fmt.Sprintf("parse config file: %s: %v\n", configFile, pErr))
		os.Exit(iqshell.STATUS_HALT)
	}

	if uploadConfig.FileType != 1 && uploadConfig.FileType != 0 {
		logs.Error("Wrong Filetype, It should be 0 or 1 ")
		os.Exit(iqshell.STATUS_HALT)
	}

	srcFileInfo, err := os.Stat(uploadConfig.SrcDir)
	if err != nil {
		logs.Error("Upload config error for parameter `SrcDir`,", err)
		os.Exit(iqshell.STATUS_HALT)
	}

	if !srcFileInfo.IsDir() {
		logs.Error("Upload src dir should be a directory")
		os.Exit(iqshell.STATUS_HALT)
	}
	policy := storage.PutPolicy{}

	if (callbackUrls == "" && callbackHost != "") || (callbackUrls != "" && callbackHost == "") {
		fmt.Fprintf(os.Stderr, "callbackUrls and callback must exist at the same time\n")
		os.Exit(1)
	}
	if (uploadConfig.CallbackUrls == "" && uploadConfig.CallbackHost != "") || (uploadConfig.CallbackUrls != "" && uploadConfig.CallbackHost == "") {
		fmt.Fprintf(os.Stderr, "callbackUrls and callback must exist at the same time\n")
		os.Exit(1)
	}
	if (callbackHost != "" && callbackUrls != "") || (uploadConfig.CallbackHost != "" && uploadConfig.CallbackUrls != "") {
		callbackUrls = strings.Replace(callbackUrls, ",", ";", -1)
		policy.CallbackHost = callbackHost
		policy.CallbackURL = callbackUrls
		policy.CallbackBody = "key=$(key)&hash=$(etag)"
		policy.CallbackBodyType = "application/x-www-form-urlencoded"
	}
	uploadConfig.PutPolicy = policy

	//upload
	if upthreadCount < iqshell.MIN_UPLOAD_THREAD_COUNT || upthreadCount > iqshell.MAX_UPLOAD_THREAD_COUNT {
		logs.Info("Tip: you can set <ThreadCount> value between %d and %d to improve speed\n",
			iqshell.MIN_UPLOAD_THREAD_COUNT, iqshell.MAX_UPLOAD_THREAD_COUNT)

		if upthreadCount < iqshell.MIN_UPLOAD_THREAD_COUNT {
			upthreadCount = iqshell.MIN_UPLOAD_THREAD_COUNT
		} else if upthreadCount > iqshell.MAX_UPLOAD_THREAD_COUNT {
			upthreadCount = iqshell.MAX_UPLOAD_THREAD_COUNT
		}
	}

	fileExporter, fErr := iqshell.NewFileExporter(successFname, failureFname, overwriteFname)
	if fErr != nil {
		logs.Error("initialize fileExporter: ", fErr)
		os.Exit(iqshell.STATUS_HALT)
	}
	iqshell.QiniuUpload(int(upthreadCount), &uploadConfig, fileExporter)
}
<commit_msg>fix windows utf-8 BOM json decode problem in qupload<commit_after>package cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/logs"
	"github.com/qiniu/api.v7/storage"
	"github.com/qiniu/qshell/iqshell"
	"github.com/spf13/cobra"
	"io/ioutil"
	"os"
	"strings"
)

var qUploadCmd = &cobra.Command{
	Use:   "qupload <quploadConfigFile>",
	Short: "Batch upload files to the qiniu bucket",
	Args:  cobra.ExactArgs(1),
	Run:   QiniuUpload,
}

var (
	successFname   string
	failureFname   string
	overwriteFname string
	upthreadCount  int64
	uploadConfig   iqshell.UploadConfig
)

func init() {
	qUploadCmd.Flags().StringVarP(&successFname, "success-list", "s", "", "upload success (all) file list")
	qUploadCmd.Flags().StringVarP(&failureFname, "failure-list", "f", "", "upload failure file list")
	qUploadCmd.Flags().StringVarP(&overwriteFname, "overwrite-list", "w", "", "upload success (overwrite) file list")
	qUploadCmd.Flags().Int64VarP(&upthreadCount, "worker", "c", 1, "worker count")
	qUploadCmd.Flags().StringVarP(&callbackUrls, "callback-urls", "l", "", "upload callback urls, separated by comma")
	qUploadCmd.Flags().StringVarP(&callbackHost, "callback-host", "T", "", "upload callback host")
	RootCmd.AddCommand(qUploadCmd)
}

func parseUploadConfigFile(uploadConfigFile string, uploadConfig *iqshell.UploadConfig) (err error) {
	//read upload config
	if uploadConfigFile == "" {
		err = fmt.Errorf("config filename is empty")
		return
	}
	fp, oErr := os.Open(uploadConfigFile)
	if oErr != nil {
		err = fmt.Errorf("Open upload config file ``%s`: %v\n", uploadConfigFile, oErr)
		return
	}
	defer fp.Close()

	configData, rErr := ioutil.ReadAll(fp)
	if rErr != nil {
		err = fmt.Errorf("Read upload config file `%s`: %v\n", uploadConfigFile, rErr)
		return
	}
	//remove UTF-8 BOM
	configData = bytes.TrimPrefix(configData, []byte("\xef\xbb\xbf"))
	uErr := json.Unmarshal(configData, uploadConfig)
	if uErr != nil {
		err = fmt.Errorf("Parse upload config file `%s`: %v\n", uploadConfigFile, uErr)
		return
	}
	return
}

func QiniuUpload(cmd *cobra.Command, params []string) {

	configFile := params[0]

	pErr := parseUploadConfigFile(configFile, &uploadConfig)
	if pErr != nil {
		logs.Error(fmt.Sprintf("parse config file: %s: %v\n", configFile, pErr))
		os.Exit(iqshell.STATUS_HALT)
	}

	if uploadConfig.FileType != 1 && uploadConfig.FileType != 0 {
		logs.Error("Wrong Filetype, It should be 0 or 1 ")
		os.Exit(iqshell.STATUS_HALT)
	}

	srcFileInfo, err := os.Stat(uploadConfig.SrcDir)
	if err != nil {
		logs.Error("Upload config error for parameter `SrcDir`,", err)
		os.Exit(iqshell.STATUS_HALT)
	}

	if !srcFileInfo.IsDir() {
		logs.Error("Upload src dir should be a directory")
		os.Exit(iqshell.STATUS_HALT)
	}
	policy := storage.PutPolicy{}

	if (callbackUrls == "" && callbackHost != "") || (callbackUrls != "" && callbackHost == "") {
		fmt.Fprintf(os.Stderr, "callbackUrls and callback must exist at the same time\n")
		os.Exit(1)
	}
	if (uploadConfig.CallbackUrls == "" && uploadConfig.CallbackHost != "") || (uploadConfig.CallbackUrls != "" && uploadConfig.CallbackHost == "") {
		fmt.Fprintf(os.Stderr, "callbackUrls and callback must exist at the same time\n")
		os.Exit(1)
	}
	if (callbackHost != "" && callbackUrls != "") || (uploadConfig.CallbackHost != "" && uploadConfig.CallbackUrls != "") {
		callbackUrls = strings.Replace(callbackUrls, ",", ";", -1)
		policy.CallbackHost = callbackHost
		policy.CallbackURL = callbackUrls
		policy.CallbackBody = "key=$(key)&hash=$(etag)"
		policy.CallbackBodyType = "application/x-www-form-urlencoded"
	}
	uploadConfig.PutPolicy = policy

	//upload
	if upthreadCount < iqshell.MIN_UPLOAD_THREAD_COUNT || upthreadCount > iqshell.MAX_UPLOAD_THREAD_COUNT {
		logs.Info("Tip: you can set <ThreadCount> value between %d and %d to improve speed\n",
			iqshell.MIN_UPLOAD_THREAD_COUNT, iqshell.MAX_UPLOAD_THREAD_COUNT)

		if upthreadCount < iqshell.MIN_UPLOAD_THREAD_COUNT {
			upthreadCount = iqshell.MIN_UPLOAD_THREAD_COUNT
		} else if upthreadCount > iqshell.MAX_UPLOAD_THREAD_COUNT {
			upthreadCount = iqshell.MAX_UPLOAD_THREAD_COUNT
		}
	}

	fileExporter, fErr := iqshell.NewFileExporter(successFname, failureFname, overwriteFname)
	if fErr != nil {
		logs.Error("initialize fileExporter: ", fErr)
		os.Exit(iqshell.STATUS_HALT)
	}
	iqshell.QiniuUpload(int(upthreadCount), &uploadConfig, fileExporter)
}
<|endoftext|>


<commit_before>// Copyright 2016 Palantir Technologies, 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 publish

import (
	"context"
	"fmt"
	"io"
	"mime"
	"net/url"
	"os"
	"path"
	"path/filepath"

	"github.com/google/go-github/github"
	"github.com/jtacoma/uritemplates"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
	"gopkg.in/cheggaaa/pb.v1"

	"github.com/palantir/godel/apps/distgo/params"
)

type GitHubConnectionInfo struct {
	APIURL string
	User   string
	Token  string

	Owner      string
	Repository string
}

func (g *GitHubConnectionInfo) Publish(buildSpec params.ProductBuildSpec, paths ProductPaths, stdout io.Writer) ([]string, error) {
	client := github.NewClient(oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: g.Token},
	)))

	// set base URL (should be of the form "https://api.github.com/")
	apiURL, err := url.Parse(g.APIURL)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse %s as URL for API calls", g.APIURL)
	}
	client.BaseURL = apiURL

	if g.Owner == "" {
		g.Owner = g.User
	}

	fmt.Fprintf(stdout, "Creating GitHub release %s for %s/%s...", buildSpec.ProductVersion, g.Owner, g.Repository)
	releaseRes, _, err := client.Repositories.CreateRelease(context.Background(), g.Owner, g.Repository, &github.RepositoryRelease{
		TagName: github.String(buildSpec.ProductVersion),
	})
	if err != nil {
		// newline to complement "..." output
		fmt.Fprintln(stdout)

		if ghErr, ok := err.(*github.ErrorResponse); ok && len(ghErr.Errors) > 0 {
			if ghErr.Errors[0].Code == "already_exists" {
				return nil, errors.Errorf("GitHub release %s already exists for %s/%s", buildSpec.ProductVersion, g.Owner, g.Repository)
			}
		}
		return nil, errors.Wrapf(err, "failed to create GitHub release %s for %s/%s...", buildSpec.ProductVersion, g.Owner, g.Repository)
	}
	fmt.Fprintln(stdout, "done")

	var uploadURLs []string
	for _, currPath := range paths.artifactPaths {
		uploadURL, err := g.uploadFileAtPath(client, releaseRes, currPath, stdout)
		if err != nil {
			return nil, err
		}
		uploadURLs = append(uploadURLs, uploadURL)
	}
	return uploadURLs, nil
}

func (g *GitHubConnectionInfo) uploadFileAtPath(client *github.Client, release *github.RepositoryRelease, filePath string, stdout io.Writer) (string, error) {
	uploadURI, err := uploadURIForProduct(release.GetUploadURL(), path.Base(filePath))
	if err != nil {
		return "", err
	}

	f, err := os.Open(filePath)
	if err != nil {
		return "", errors.Wrapf(err, "failed to open artifact %s for upload", filePath)
	}
	uploadRes, _, err := githubUploadReleaseAssetWithProgress(context.Background(), client, uploadURI, f, stdout)
	if err != nil {
		return "", errors.Wrapf(err, "failed to upload artifact %s", filePath)
	}
	return uploadRes.GetBrowserDownloadURL(), nil
}

// uploadURIForProduct returns an asset upload URI using the provided upload template from the release creation
// response. See https://developer.github.com/v3/repos/releases/#response for the specifics of the API.
func uploadURIForProduct(githubUploadURLTemplate, name string) (string, error) {
	const nameTemplate = "name"

	t, err := uritemplates.Parse(githubUploadURLTemplate)
	if err != nil {
		return "", errors.Wrapf(err, "failed to parse upload URI template %q", githubUploadURLTemplate)
	}
	uploadURI, err := t.Expand(map[string]interface{}{
		nameTemplate: name,
	})
	if err != nil {
		return "", errors.Wrapf(err, "failed to expand URI template %q with %q = %q", githubUploadURLTemplate, nameTemplate, name)
	}
	return uploadURI, nil
}

// Based on github.Repositories.UploadReleaseAsset. Adds support for progress reporting.
func githubUploadReleaseAssetWithProgress(ctx context.Context, client *github.Client, uploadURI string, file *os.File, stdout io.Writer) (*github.ReleaseAsset, *github.Response, error) {
	stat, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}
	if stat.IsDir() {
		return nil, nil, errors.New("the asset to upload can't be a directory")
	}

	fmt.Fprintf(stdout, "Uploading %s to %s\n", file.Name(), uploadURI)
	bar := pb.New(int(stat.Size())).SetUnits(pb.U_BYTES)
	bar.Output = stdout
	bar.SetMaxWidth(120)
	bar.Start()
	defer bar.Finish()
	reader := bar.NewProxyReader(file)

	mediaType := mime.TypeByExtension(filepath.Ext(file.Name()))
	req, err := client.NewUploadRequest(uploadURI, reader, stat.Size(), mediaType)
	if err != nil {
		return nil, nil, err
	}

	asset := new(github.ReleaseAsset)
	resp, err := client.Do(ctx, req, asset)
	if err != nil {
		return nil, resp, err
	}
	return asset, resp, nil
}
<commit_msg>Append trailing slash to GitHub upload URL if it is missing (#180)<commit_after>// Copyright 2016 Palantir Technologies, 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 publish

import (
	"context"
	"fmt"
	"io"
	"mime"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/google/go-github/github"
	"github.com/jtacoma/uritemplates"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
	"gopkg.in/cheggaaa/pb.v1"

	"github.com/palantir/godel/apps/distgo/params"
)

type GitHubConnectionInfo struct {
	APIURL string
	User   string
	Token  string

	Owner      string
	Repository string
}

func (g *GitHubConnectionInfo) Publish(buildSpec params.ProductBuildSpec, paths ProductPaths, stdout io.Writer) ([]string, error) {
	client := github.NewClient(oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: g.Token},
	)))

	// if base URL does not end in "/", append it (trailing slash is required)
	if !strings.HasSuffix(g.APIURL, "/") {
		g.APIURL += "/"
	}
	// set base URL (should be of the form "https://api.github.com/")
	apiURL, err := url.Parse(g.APIURL)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse %s as URL for API calls", g.APIURL)
	}
	client.BaseURL = apiURL

	if g.Owner == "" {
		g.Owner = g.User
	}

	fmt.Fprintf(stdout, "Creating GitHub release %s for %s/%s...", buildSpec.ProductVersion, g.Owner, g.Repository)
	releaseRes, _, err := client.Repositories.CreateRelease(context.Background(), g.Owner, g.Repository, &github.RepositoryRelease{
		TagName: github.String(buildSpec.ProductVersion),
	})
	if err != nil {
		// newline to complement "..." output
		fmt.Fprintln(stdout)

		if ghErr, ok := err.(*github.ErrorResponse); ok && len(ghErr.Errors) > 0 {
			if ghErr.Errors[0].Code == "already_exists" {
				return nil, errors.Errorf("GitHub release %s already exists for %s/%s", buildSpec.ProductVersion, g.Owner, g.Repository)
			}
		}
		return nil, errors.Wrapf(err, "failed to create GitHub release %s for %s/%s...", buildSpec.ProductVersion, g.Owner, g.Repository)
	}
	fmt.Fprintln(stdout, "done")

	var uploadURLs []string
	for _, currPath := range paths.artifactPaths {
		uploadURL, err := g.uploadFileAtPath(client, releaseRes, currPath, stdout)
		if err != nil {
			return nil, err
		}
		uploadURLs = append(uploadURLs, uploadURL)
	}
	return uploadURLs, nil
}

func (g *GitHubConnectionInfo) uploadFileAtPath(client *github.Client, release *github.RepositoryRelease, filePath string, stdout io.Writer) (string, error) {
	uploadURI, err := uploadURIForProduct(release.GetUploadURL(), path.Base(filePath))
	if err != nil {
		return "", err
	}

	f, err := os.Open(filePath)
	if err != nil {
		return "", errors.Wrapf(err, "failed to open artifact %s for upload", filePath)
	}
	uploadRes, _, err := githubUploadReleaseAssetWithProgress(context.Background(), client, uploadURI, f, stdout)
	if err != nil {
		return "", errors.Wrapf(err, "failed to upload artifact %s", filePath)
	}
	return uploadRes.GetBrowserDownloadURL(), nil
}

// uploadURIForProduct returns an asset upload URI using the provided upload template from the release creation
// response. See https://developer.github.com/v3/repos/releases/#response for the specifics of the API.
func uploadURIForProduct(githubUploadURLTemplate, name string) (string, error) {
	const nameTemplate = "name"

	t, err := uritemplates.Parse(githubUploadURLTemplate)
	if err != nil {
		return "", errors.Wrapf(err, "failed to parse upload URI template %q", githubUploadURLTemplate)
	}
	uploadURI, err := t.Expand(map[string]interface{}{
		nameTemplate: name,
	})
	if err != nil {
		return "", errors.Wrapf(err, "failed to expand URI template %q with %q = %q", githubUploadURLTemplate, nameTemplate, name)
	}
	return uploadURI, nil
}

// Based on github.Repositories.UploadReleaseAsset. Adds support for progress reporting.
func githubUploadReleaseAssetWithProgress(ctx context.Context, client *github.Client, uploadURI string, file *os.File, stdout io.Writer) (*github.ReleaseAsset, *github.Response, error) {
	stat, err := file.Stat()
	if err != nil {
		return nil, nil, err
	}
	if stat.IsDir() {
		return nil, nil, errors.New("the asset to upload can't be a directory")
	}

	fmt.Fprintf(stdout, "Uploading %s to %s\n", file.Name(), uploadURI)
	bar := pb.New(int(stat.Size())).SetUnits(pb.U_BYTES)
	bar.Output = stdout
	bar.SetMaxWidth(120)
	bar.Start()
	defer bar.Finish()
	reader := bar.NewProxyReader(file)

	mediaType := mime.TypeByExtension(filepath.Ext(file.Name()))
	req, err := client.NewUploadRequest(uploadURI, reader, stat.Size(), mediaType)
	if err != nil {
		return nil, nil, err
	}

	asset := new(github.ReleaseAsset)
	resp, err := client.Do(ctx, req, asset)
	if err != nil {
		return nil, resp, err
	}
	return asset, resp, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Brian Hetro <whee@smaertness.net>
// Use of this source code is governed by the ISC
// license which can be found in the LICENSE file.

package main

import (
	"io"
	"os"

	docopt "github.com/docopt/docopt-go"
	"github.com/whee/rp"
)

func main() {
	usage := `Redis Pipe.

Usage:
  rp      -r <name>...
  rp [-p] -w <name>...

Options:
  -r, --read <name>...  Read from the named channel.
  -w, --write <name>...  Write to the named channel.
  -p, --passthrough  Pass written data to standard output.`

	arguments, err := docopt.Parse(usage, nil, true, "Redis Pipe 0.1", false)
	if err != nil {
		panic(err)
	}
	if w, ok := arguments["--write"]; ok {
		names := w.([]string)
		if len(names) > 0 {
			writeTo(names)
			return
		}
	}
	if r, ok := arguments["--read"]; ok {
		names := r.([]string)
		if len(names) > 0 {
			readFrom(names)
			return
		}
	}
}

func writeTo(names []string) (int64, error) {
	t, err := rp.NewWriter(names...)
	if err != nil {
		return 0, err
	}
	defer t.Close()
	return io.Copy(t, os.Stdin)
}

func readFrom(names []string) (int64, error) {
	t, err := rp.NewReader(names...)
	if err != nil {
		return 0, err
	}
	defer t.Close()
	return io.Copy(os.Stdout, t)
}
<commit_msg>Support passthrough mode.<commit_after>// Copyright (c) 2014 Brian Hetro <whee@smaertness.net>
// Use of this source code is governed by the ISC
// license which can be found in the LICENSE file.

package main

import (
	"io"
	"os"
	docopt "github.com/docopt/docopt-go"
	"github.com/whee/rp"
)

func main() {
	usage := `Redis Pipe.

Usage:
  rp      -r <name>...
  rp [-p] -w <name>...

Options:
  -r, --read <name>...  Read from the named channel.
  -w, --write <name>...  Write to the named channel.
  -p, --passthrough  Pass written data to standard output.`

	arguments, err := docopt.Parse(usage, nil, true, "Redis Pipe 0.1", false)
	if err != nil {
		panic(err)
	}
	if w, ok := arguments["--write"]; ok {
		names := w.([]string)
		if len(names) > 0 {
			pt := arguments["--passthrough"].(bool)
			writeTo(names, pt)
			return
		}
	}
	if r, ok := arguments["--read"]; ok {
		names := r.([]string)
		if len(names) > 0 {
			readFrom(names)
			return
		}
	}
}

type ptWriter struct {
	w io.Writer
}

func (w ptWriter) Write(p []byte) (n int, err error) {
	n, err = os.Stdout.Write(p)
	if err != nil {
		return
	}
	return w.w.Write(p)
}

func writeTo(names []string, passthrough bool) (int64, error) {
	t, err := rp.NewWriter(names...)
	if err != nil {
		return 0, err
	}
	defer t.Close()

	var w io.Writer
	if passthrough {
		w = ptWriter{t}
	} else {
		w = t
	}
	return io.Copy(w, os.Stdin)
}

func readFrom(names []string) (int64, error) {
	t, err := rp.NewReader(names...)
	if err != nil {
		return 0, err
	}
	defer t.Close()
	return io.Copy(os.Stdout, t)
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/Azure/acs-engine/pkg/acsengine"
	"github.com/Azure/acs-engine/pkg/api"
	"github.com/Azure/acs-engine/pkg/armhelpers"
	"github.com/Azure/acs-engine/pkg/i18n"
	"github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade"
	"github.com/leonelquinteros/gotext"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

const (
	upgradeName             = "upgrade"
	upgradeShortDescription = "upgrades an existing Kubernetes cluster"
	upgradeLongDescription  = "upgrades an existing Kubernetes cluster, first replacing masters, then nodes"
)

type upgradeCmd struct {
	authArgs

	// user input
	resourceGroupName   string
	deploymentDirectory string
	upgradeVersion      string
	containerService    *api.ContainerService
	apiVersion          string
	location            string
	timeoutInMinutes    int

	// derived
	client              armhelpers.ACSEngineClient
	locale              *gotext.Locale
	nameSuffix          string
	agentPoolsToUpgrade []string
	timeout             *time.Duration
}

// NewUpgradeCmd run a command to upgrade a Kubernetes cluster
func newUpgradeCmd() *cobra.Command {
	uc := upgradeCmd{}

	upgradeCmd := &cobra.Command{
		Use:   upgradeName,
		Short: upgradeShortDescription,
		Long:  upgradeLongDescription,
		RunE: func(cmd *cobra.Command, args []string) error {
			return uc.run(cmd, args)
		},
	}

	f := upgradeCmd.Flags()
	f.StringVar(&uc.location, "location", "", "location the cluster is deployed in")
	f.StringVar(&uc.resourceGroupName, "resource-group", "", "the resource group where the cluster is deployed")
	f.StringVar(&uc.deploymentDirectory, "deployment-dir", "", "the location of the output from `generate`")
	f.StringVar(&uc.upgradeVersion, "upgrade-version", "", "desired kubernetes version")
	f.IntVar(&uc.timeoutInMinutes, "vm-timeout", -1, "how long to wait for each vm to be upgraded in minutes")
	addAuthFlags(&uc.authArgs, f)

	return upgradeCmd
}

func (uc *upgradeCmd) validate(cmd *cobra.Command, args []string) {
	log.Infoln("validating...")

	var err error

	uc.locale, err = i18n.LoadTranslations()
	if err != nil {
		log.Fatalf("error loading translation files: %s", err.Error())
	}

	if uc.resourceGroupName == "" {
		cmd.Usage()
		log.Fatal("--resource-group must be specified")
	}

	if uc.location == "" {
		cmd.Usage()
		log.Fatal("--location must be specified")
	}

	if uc.timeoutInMinutes != -1 {
		timeout := time.Duration(uc.timeoutInMinutes) * time.Minute
		uc.timeout = &timeout
	}

	// TODO(colemick): add in the cmd annotation to help enable autocompletion
	if uc.upgradeVersion == "" {
		cmd.Usage()
		log.Fatal("--upgrade-version must be specified")
	}

	if uc.client, err = uc.authArgs.getClient(); err != nil {
		log.Error("Failed to get client:", err)
	}

	if uc.deploymentDirectory == "" {
		cmd.Usage()
		log.Fatal("--deployment-dir must be specified")
	}

	_, err = uc.client.EnsureResourceGroup(uc.resourceGroupName, uc.location, nil)
	if err != nil {
		log.Fatalln(err)
	}

	// load apimodel from the deployment directory
	apiModelPath := path.Join(uc.deploymentDirectory, "apimodel.json")

	if _, err = os.Stat(apiModelPath); os.IsNotExist(err) {
		log.Fatalf("specified api model does not exist (%s)", apiModelPath)
	}

	apiloader := &api.Apiloader{
		Translator: &i18n.Translator{
			Locale: uc.locale,
		},
	}
	uc.containerService, uc.apiVersion, err = apiloader.LoadContainerServiceFromFile(apiModelPath, true, true, nil)
	if err != nil {
		log.Fatalf("error parsing the api model: %s", err.Error())
	}

	// get available upgrades for container service
	orchestratorInfo, err := api.GetOrchestratorVersionProfile(uc.containerService.Properties.OrchestratorProfile)
	if err != nil {
		log.Fatalf("error getting list of available upgrades: %s", err.Error())
	}
	// add the current version if upgrade has failed
	orchestratorInfo.Upgrades = append(orchestratorInfo.Upgrades, &api.OrchestratorProfile{
		OrchestratorType:    uc.containerService.Properties.OrchestratorProfile.OrchestratorType,
		OrchestratorVersion: uc.containerService.Properties.OrchestratorProfile.OrchestratorVersion})

	// validate desired upgrade version and set goal state
	found := false
	for _, up := range orchestratorInfo.Upgrades {
		if up.OrchestratorVersion == uc.upgradeVersion {
			uc.containerService.Properties.OrchestratorProfile.OrchestratorVersion = uc.upgradeVersion
			found = true
			break
		}
	}
	if !found {
		log.Fatalf("version %s is not supported", uc.upgradeVersion)
	}

	uc.client, err = uc.authArgs.getClient()
	if err != nil {
		log.Fatalf("failed to get client") // TODO: cleanup
	}

	// Read name suffix to identify nodes in the resource group that belong
	// to this cluster.
	// TODO: Also update to read  namesuffix from the parameters file as
	// user could have specified a name suffix instead of using the default
	// value generated by ACS Engine
	templatePath := path.Join(uc.deploymentDirectory, "azuredeploy.json")
	contents, _ := ioutil.ReadFile(templatePath)

	var template interface{}
	json.Unmarshal(contents, &template)

	templateMap := template.(map[string]interface{})
	templateParameters := templateMap["parameters"].(map[string]interface{})

	nameSuffixParam := templateParameters["nameSuffix"].(map[string]interface{})
	uc.nameSuffix = nameSuffixParam["defaultValue"].(string)
	log.Infoln(fmt.Sprintf("Name suffix: %s", uc.nameSuffix))

	uc.agentPoolsToUpgrade = []string{}
	log.Infoln(fmt.Sprintf("Gathering agent pool names..."))
	for _, agentPool := range uc.containerService.Properties.AgentPoolProfiles {
		uc.agentPoolsToUpgrade = append(uc.agentPoolsToUpgrade, agentPool.Name)
	}
}

func (uc *upgradeCmd) run(cmd *cobra.Command, args []string) error {
	uc.validate(cmd, args)

	upgradeCluster := kubernetesupgrade.UpgradeCluster{
		Translator: &i18n.Translator{
			Locale: uc.locale,
		},
		Logger:      log.NewEntry(log.New()),
		Client:      uc.client,
		StepTimeout: uc.timeout,
	}

	kubeConfig, err := acsengine.GenerateKubeConfig(uc.containerService.Properties, uc.location)
	if err != nil {
		log.Fatalf("failed to generate kube config") // TODO: cleanup
	}

	if err = upgradeCluster.UpgradeCluster(uc.authArgs.SubscriptionID, kubeConfig, uc.resourceGroupName,
		uc.containerService, uc.nameSuffix, uc.agentPoolsToUpgrade); err != nil {
		log.Fatalf("Error upgrading cluster: %s \n", err.Error())
	}

	return nil
}
<commit_msg>Save apimodel after upgrade (#2306)<commit_after>package cmd

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/Azure/acs-engine/pkg/acsengine"
	"github.com/Azure/acs-engine/pkg/api"
	"github.com/Azure/acs-engine/pkg/armhelpers"
	"github.com/Azure/acs-engine/pkg/i18n"
	"github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade"
	"github.com/leonelquinteros/gotext"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

const (
	upgradeName             = "upgrade"
	upgradeShortDescription = "upgrades an existing Kubernetes cluster"
	upgradeLongDescription  = "upgrades an existing Kubernetes cluster, first replacing masters, then nodes"
)

type upgradeCmd struct {
	authArgs

	// user input
	resourceGroupName   string
	deploymentDirectory string
	upgradeVersion      string
	containerService    *api.ContainerService
	apiVersion          string
	location            string
	timeoutInMinutes    int

	// derived
	client              armhelpers.ACSEngineClient
	locale              *gotext.Locale
	nameSuffix          string
	agentPoolsToUpgrade []string
	timeout             *time.Duration
}

// NewUpgradeCmd run a command to upgrade a Kubernetes cluster
func newUpgradeCmd() *cobra.Command {
	uc := upgradeCmd{}

	upgradeCmd := &cobra.Command{
		Use:   upgradeName,
		Short: upgradeShortDescription,
		Long:  upgradeLongDescription,
		RunE: func(cmd *cobra.Command, args []string) error {
			return uc.run(cmd, args)
		},
	}

	f := upgradeCmd.Flags()
	f.StringVar(&uc.location, "location", "", "location the cluster is deployed in")
	f.StringVar(&uc.resourceGroupName, "resource-group", "", "the resource group where the cluster is deployed")
	f.StringVar(&uc.deploymentDirectory, "deployment-dir", "", "the location of the output from `generate`")
	f.StringVar(&uc.upgradeVersion, "upgrade-version", "", "desired kubernetes version")
	f.IntVar(&uc.timeoutInMinutes, "vm-timeout", -1, "how long to wait for each vm to be upgraded in minutes")
	addAuthFlags(&uc.authArgs, f)

	return upgradeCmd
}

func (uc *upgradeCmd) validate(cmd *cobra.Command, args []string) {
	log.Infoln("validating...")

	var err error

	uc.locale, err = i18n.LoadTranslations()
	if err != nil {
		log.Fatalf("error loading translation files: %s", err.Error())
	}

	if uc.resourceGroupName == "" {
		cmd.Usage()
		log.Fatal("--resource-group must be specified")
	}

	if uc.location == "" {
		cmd.Usage()
		log.Fatal("--location must be specified")
	}

	if uc.timeoutInMinutes != -1 {
		timeout := time.Duration(uc.timeoutInMinutes) * time.Minute
		uc.timeout = &timeout
	}

	// TODO(colemick): add in the cmd annotation to help enable autocompletion
	if uc.upgradeVersion == "" {
		cmd.Usage()
		log.Fatal("--upgrade-version must be specified")
	}

	if uc.client, err = uc.authArgs.getClient(); err != nil {
		log.Error("Failed to get client:", err)
	}

	if uc.deploymentDirectory == "" {
		cmd.Usage()
		log.Fatal("--deployment-dir must be specified")
	}

	_, err = uc.client.EnsureResourceGroup(uc.resourceGroupName, uc.location, nil)
	if err != nil {
		log.Fatalln(err)
	}

	// load apimodel from the deployment directory
	apiModelPath := path.Join(uc.deploymentDirectory, "apimodel.json")

	if _, err = os.Stat(apiModelPath); os.IsNotExist(err) {
		log.Fatalf("specified api model does not exist (%s)", apiModelPath)
	}

	apiloader := &api.Apiloader{
		Translator: &i18n.Translator{
			Locale: uc.locale,
		},
	}
	uc.containerService, uc.apiVersion, err = apiloader.LoadContainerServiceFromFile(apiModelPath, true, true, nil)
	if err != nil {
		log.Fatalf("error parsing the api model: %s", err.Error())
	}

	// get available upgrades for container service
	orchestratorInfo, err := api.GetOrchestratorVersionProfile(uc.containerService.Properties.OrchestratorProfile)
	if err != nil {
		log.Fatalf("error getting list of available upgrades: %s", err.Error())
	}
	// add the current version if upgrade has failed
	orchestratorInfo.Upgrades = append(orchestratorInfo.Upgrades, &api.OrchestratorProfile{
		OrchestratorType:    uc.containerService.Properties.OrchestratorProfile.OrchestratorType,
		OrchestratorVersion: uc.containerService.Properties.OrchestratorProfile.OrchestratorVersion})

	// validate desired upgrade version and set goal state
	found := false
	for _, up := range orchestratorInfo.Upgrades {
		if up.OrchestratorVersion == uc.upgradeVersion {
			uc.containerService.Properties.OrchestratorProfile.OrchestratorVersion = uc.upgradeVersion
			found = true
			break
		}
	}
	if !found {
		log.Fatalf("version %s is not supported", uc.upgradeVersion)
	}

	uc.client, err = uc.authArgs.getClient()
	if err != nil {
		log.Fatalf("failed to get client") // TODO: cleanup
	}

	// Read name suffix to identify nodes in the resource group that belong
	// to this cluster.
	// TODO: Also update to read  namesuffix from the parameters file as
	// user could have specified a name suffix instead of using the default
	// value generated by ACS Engine
	templatePath := path.Join(uc.deploymentDirectory, "azuredeploy.json")
	contents, _ := ioutil.ReadFile(templatePath)

	var template interface{}
	json.Unmarshal(contents, &template)

	templateMap := template.(map[string]interface{})
	templateParameters := templateMap["parameters"].(map[string]interface{})

	nameSuffixParam := templateParameters["nameSuffix"].(map[string]interface{})
	uc.nameSuffix = nameSuffixParam["defaultValue"].(string)
	log.Infoln(fmt.Sprintf("Name suffix: %s", uc.nameSuffix))

	uc.agentPoolsToUpgrade = []string{}
	log.Infoln(fmt.Sprintf("Gathering agent pool names..."))
	for _, agentPool := range uc.containerService.Properties.AgentPoolProfiles {
		uc.agentPoolsToUpgrade = append(uc.agentPoolsToUpgrade, agentPool.Name)
	}
}

func (uc *upgradeCmd) run(cmd *cobra.Command, args []string) error {
	uc.validate(cmd, args)

	upgradeCluster := kubernetesupgrade.UpgradeCluster{
		Translator: &i18n.Translator{
			Locale: uc.locale,
		},
		Logger:      log.NewEntry(log.New()),
		Client:      uc.client,
		StepTimeout: uc.timeout,
	}

	kubeConfig, err := acsengine.GenerateKubeConfig(uc.containerService.Properties, uc.location)
	if err != nil {
		log.Fatalf("failed to generate kube config") // TODO: cleanup
	}

	if err = upgradeCluster.UpgradeCluster(uc.authArgs.SubscriptionID, kubeConfig, uc.resourceGroupName,
		uc.containerService, uc.nameSuffix, uc.agentPoolsToUpgrade); err != nil {
		log.Fatalf("Error upgrading cluster: %s \n", err.Error())
	}

	apiloader := &api.Apiloader{
		Translator: &i18n.Translator{
			Locale: uc.locale,
		},
	}
	b, e := apiloader.SerializeContainerService(uc.containerService, uc.apiVersion)

	if e != nil {
		return e
	}

	f := acsengine.FileSaver{
		Translator: &i18n.Translator{
			Locale: uc.locale,
		},
	}

	if e = f.SaveFile(uc.deploymentDirectory, "apimodel.json", b); e != nil {
		return e
	}

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"math/rand"
	"testing"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/s3"
)

func TestAccAWSS3Bucket(t *testing.T) {

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSS3BucketDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSS3BucketConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
				),
			},
			resource.TestStep{
				Config: testAccAWSS3BucketWebsiteConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSS3BucketExists("aws_s3_bucket.website"),
				),
			},
		},
	})
}

func testAccCheckAWSS3BucketDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).s3conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_s3_bucket" {
			continue
		}
		_, err := conn.DeleteBucket(&s3.DeleteBucketInput{
			Bucket: aws.String(rs.Primary.ID),
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func testAccCheckAWSS3BucketExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No S3 Bucket ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).s3conn
		_, err := conn.HeadBucket(&s3.HeadBucketInput{
			Bucket: aws.String(rs.Primary.ID),
		})

		if err != nil {
			return fmt.Errorf("S3Bucket error: %v", err)
		}
		return nil
	}
}

// These need a bit of randoness as the name can only be used once globally
// within AWS
var testAccAWSS3BucketConfig = fmt.Sprintf(`
resource "aws_s3_bucket" "bucket" {
	bucket = "tf-test-bucket-%d"
	acl = "public-read"
}
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())

var testAccAWSS3BucketWebsiteConfig = fmt.Sprintf(`
resource "aws_s3_bucket" "website" {
	bucket = "tf-test-bucket-website-%d"
	acl = "public-read"

	website {
		index_document = "index.html"
	}
}
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
<commit_msg>providers/aws: Check that S3 website sets docs<commit_after>package aws

import (
	"fmt"
	"math/rand"
	"testing"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/service/s3"
)

func TestAccAWSS3Bucket(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSS3BucketDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSS3BucketConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
				),
			},
		},
	})
}

func TestAccAWSS3BucketWebsite(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSS3BucketDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSS3BucketWebsiteConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSS3BucketExists("aws_s3_bucket.website"),
					testAccCheckAWSS3BucketWebsite(
						"aws_s3_bucket.website", "index.html", "error.html"),
				),
			},
		},
	})
}

func testAccCheckAWSS3BucketDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).s3conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_s3_bucket" {
			continue
		}
		_, err := conn.DeleteBucket(&s3.DeleteBucketInput{
			Bucket: aws.String(rs.Primary.ID),
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func testAccCheckAWSS3BucketExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No S3 Bucket ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).s3conn
		_, err := conn.HeadBucket(&s3.HeadBucketInput{
			Bucket: aws.String(rs.Primary.ID),
		})

		if err != nil {
			return fmt.Errorf("S3Bucket error: %v", err)
		}
		return nil
	}
}

func testAccCheckAWSS3BucketWebsite(n string, indexDoc string, errorDoc string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, _ := s.RootModule().Resources[n]
		conn := testAccProvider.Meta().(*AWSClient).s3conn

		out, err := conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{
			Bucket: aws.String(rs.Primary.ID),
		})

		if err != nil {
			return fmt.Errorf("S3BucketWebsite error: %v", err)
		}

		if *out.IndexDocument.Suffix != indexDoc {
			return fmt.Errorf("bad: %s", out.IndexDocument)
		}

		if *out.ErrorDocument.Key != errorDoc {
			return fmt.Errorf("bad: %s", out.ErrorDocument)
		}

		return nil
	}
}

// These need a bit of randoness as the name can only be used once globally
// within AWS
var testAccAWSS3BucketConfig = fmt.Sprintf(`
resource "aws_s3_bucket" "bucket" {
	bucket = "tf-test-bucket-%d"
	acl = "public-read"
}
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())

var testAccAWSS3BucketWebsiteConfig = fmt.Sprintf(`
resource "aws_s3_bucket" "website" {
	bucket = "tf-test-bucket-website-%d"
	acl = "public-read"

	website {
		index_document = "index.html"
		error_document = "error.html"
	}
}
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
<|endoftext|>


<commit_before>// Package resolver resolves network ids to addresses
package resolver

type Resolver interface {
	// Resolve returns a list of addresses for an id
	Resolve(id string) ([]*Record, error)
}

// A resoved id
type Record struct {
	Address string `json:"address"`
}
<commit_msg>fix typo<commit_after>// Package resolver resolves network ids to addresses
package resolver

type Resolver interface {
	// Resolve returns a list of addresses for an id
	Resolve(id string) ([]*Record, error)
}

// A resolved record
type Record struct {
	Address string `json:"address"`
}
<|endoftext|>