<commit_before>package teams

import (
	"fmt"
	"sort"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

type statusList struct {
	Teams  []keybase1.MemberInfo `json:"teams"`
	Status libkb.AppStatus       `json:"status"`
}

func (r *statusList) GetAppStatus() *libkb.AppStatus {
	return &r.Status
}

func getTeamsListFromServer(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID, all bool) ([]keybase1.MemberInfo, error) {
	var endpoint string
	if all {
		endpoint = "team/teammates_for_user"
	} else {
		endpoint = "team/for_user"
	}
	a := libkb.NewAPIArg(endpoint)
	if uid.Exists() {
		a.Args = libkb.HTTPArgs{
			"uid": libkb.S{Val: uid.String()},
		}
	}
	a.NetContext = ctx
	a.SessionType = libkb.APISessionTypeREQUIRED
	var list statusList
	if err := g.API.GetDecode(a, &list); err != nil {
		return nil, err
	}
	return list.Teams, nil
}

func List(ctx context.Context, g *libkb.GlobalContext, arg keybase1.TeamListArg) (*keybase1.AnnotatedTeamList, error) {
	var uid keybase1.UID
	if arg.UserAssertion != "" {
		res := g.Resolver.ResolveFullExpression(ctx, arg.UserAssertion)
		if res.GetError() != nil {
			return nil, res.GetError()
		}
		uid = res.GetUID()
	}

	meUID := g.ActiveDevice.UID()

	teams, err := getTeamsListFromServer(ctx, g, uid, arg.All)
	if err != nil {
		return nil, err
	}

	teamNames := make(map[string]bool)
	upakLoader := g.GetUPAKLoader()
	annotatedTeams := make([]keybase1.AnnotatedMemberInfo, len(teams))
	administeredTeams := make(map[string]bool)
	for idx, memberInfo := range teams {
		teamNames[memberInfo.FqName] = true
		if memberInfo.UserID == meUID && (memberInfo.Role.IsAdminOrAbove() || (memberInfo.Implicit != nil && memberInfo.Implicit.Role.IsAdminOrAbove())) {
			administeredTeams[memberInfo.FqName] = true
		}

		memberuid := memberInfo.UserID
		username, err := upakLoader.LookupUsername(context.Background(), memberuid)
		if err != nil {
			return nil, err
		}
		fullName, err := engine.GetFullName(context.Background(), g, memberuid)
		if err != nil {
			return nil, err
		}
		annotatedTeams[idx] = keybase1.AnnotatedMemberInfo{
			TeamID:   memberInfo.TeamID,
			FqName:   memberInfo.FqName,
			UserID:   memberInfo.UserID,
			Role:     memberInfo.Role,
			Implicit: memberInfo.Implicit,
			Username: username.String(),
			FullName: fullName,
		}
	}

	annotatedInvites := make(map[keybase1.TeamInviteID]keybase1.AnnotatedTeamInvite)
	for teamName := range teamNames {
		_, ok := administeredTeams[teamName]
		if ok {
			t, err := GetForTeamManagementByStringName(ctx, g, teamName, true)
			if err != nil {
				g.Log.Warning("Error while getting team (%s): %v", teamName, err)
				continue
			}
			teamAnnotatedInvites, err := AnnotateInvites(ctx, g, t.chain().inner.ActiveInvites, teamName)
			if err != nil {
				return nil, err
			}
			for teamInviteID, annotatedTeamInvite := range teamAnnotatedInvites {
				annotatedInvites[teamInviteID] = annotatedTeamInvite
			}
		}
	}

	tl := keybase1.AnnotatedTeamList{
		Teams: annotatedTeams,
		AnnotatedActiveInvites: annotatedInvites,
	}

	return &tl, nil
}

func AnnotateInvites(ctx context.Context, g *libkb.GlobalContext,
	invites map[keybase1.TeamInviteID]keybase1.TeamInvite, teamName string) (map[keybase1.TeamInviteID]keybase1.AnnotatedTeamInvite, error) {

	annotatedInvites := make(map[keybase1.TeamInviteID]keybase1.AnnotatedTeamInvite, len(invites))
	upakLoader := g.GetUPAKLoader()
	for id, invite := range invites {
		username, err := upakLoader.LookupUsername(context.Background(), invite.Inviter.Uid)
		if err != nil {
			return annotatedInvites, err
		}
		annotatedInvites[id] = keybase1.AnnotatedTeamInvite{
			Role:            invite.Role,
			Id:              invite.Id,
			Type:            invite.Type,
			Name:            invite.Name,
			InviterUsername: username.String(),
			TeamName:        teamName,
		}
	}
	return annotatedInvites, nil
}

func TeamTree(ctx context.Context, g *libkb.GlobalContext, arg keybase1.TeamTreeArg) (res keybase1.TeamTreeResult, err error) {
	if !arg.Name.IsRootTeam() {
		return res, fmt.Errorf("cannot get tree of non-root team")
	}

	serverList, err := getTeamsListFromServer(ctx, g, "", false)
	if err != nil {
		return res, err
	}

	// Map from team name (string) to entry
	entryMap := make(map[string]keybase1.TeamTreeEntry)

	// The server might have omitted some teams, oh well.
	// Trusts the server for role.
	// Load the teams by ID to make sure they are valid and get the validated names.
	for _, info := range serverList {
		serverName, err := info.TeamName()
		if err != nil {
			return res, err
		}
		if !serverName.RootAncestorName().Eq(arg.Name) {
			// Skip those not in this tree.
			continue
		}
		team, err := Load(ctx, g, keybase1.LoadTeamArg{
			ID:          info.TeamID,
			ForceRepoll: true,
		})
		if err != nil {
			return res, err
		}
		var admin bool // true if an admin or implicit admin
		if info.Role.IsAdminOrAbove() {
			admin = true
		}
		if info.Implicit != nil && info.Implicit.Role.IsAdminOrAbove() {
			admin = true
		}
		entryMap[team.Name().String()] = keybase1.TeamTreeEntry{
			Name:  team.Name(),
			Admin: admin,
		}
	}

	// Add all parent names (recursively)
	// So that if only A.B.C is in the list, we add A.B and A as well.
	// Adding map entries while iterating is safe.
	// "If map entries are created during iteration, that entry may be produced during the iteration or may be skipped."
	for _, entry := range entryMap {
		name := entry.Name.DeepCopy()
		for name.Depth() > 0 {
			_, ok := entryMap[name.String()]
			if !ok {
				entryMap[name.String()] = keybase1.TeamTreeEntry{
					Name:  name,
					Admin: false,
				}
			}
			name, err = name.Parent()
			if err != nil {
				break
			}
		}
	}

	for _, entry := range entryMap {
		res.Entries = append(res.Entries, entry)
	}

	if len(res.Entries) == 0 {
		return res, fmt.Errorf("team not found: %v", arg.Name)
	}

	// Order into a tree order. Which happens to be alphabetical ordering.
	// Example: [a, a.b, a.b.c, a.b.d, a.e.f, a.e.g]
	sort.Slice(res.Entries, func(i, j int) bool {
		return res.Entries[i].Name.String() < res.Entries[j].Name.String()
	})

	return res, nil
}
<commit_msg>Lookup usernames for keybase invites<commit_after>package teams

import (
	"fmt"
	"sort"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

type statusList struct {
	Teams  []keybase1.MemberInfo `json:"teams"`
	Status libkb.AppStatus       `json:"status"`
}

func (r *statusList) GetAppStatus() *libkb.AppStatus {
	return &r.Status
}

func getTeamsListFromServer(ctx context.Context, g *libkb.GlobalContext, uid keybase1.UID, all bool) ([]keybase1.MemberInfo, error) {
	var endpoint string
	if all {
		endpoint = "team/teammates_for_user"
	} else {
		endpoint = "team/for_user"
	}
	a := libkb.NewAPIArg(endpoint)
	if uid.Exists() {
		a.Args = libkb.HTTPArgs{
			"uid": libkb.S{Val: uid.String()},
		}
	}
	a.NetContext = ctx
	a.SessionType = libkb.APISessionTypeREQUIRED
	var list statusList
	if err := g.API.GetDecode(a, &list); err != nil {
		return nil, err
	}
	return list.Teams, nil
}

func List(ctx context.Context, g *libkb.GlobalContext, arg keybase1.TeamListArg) (*keybase1.AnnotatedTeamList, error) {
	var uid keybase1.UID
	if arg.UserAssertion != "" {
		res := g.Resolver.ResolveFullExpression(ctx, arg.UserAssertion)
		if res.GetError() != nil {
			return nil, res.GetError()
		}
		uid = res.GetUID()
	}

	meUID := g.ActiveDevice.UID()

	teams, err := getTeamsListFromServer(ctx, g, uid, arg.All)
	if err != nil {
		return nil, err
	}

	teamNames := make(map[string]bool)
	upakLoader := g.GetUPAKLoader()
	annotatedTeams := make([]keybase1.AnnotatedMemberInfo, len(teams))
	administeredTeams := make(map[string]bool)
	for idx, memberInfo := range teams {
		teamNames[memberInfo.FqName] = true
		if memberInfo.UserID == meUID && (memberInfo.Role.IsAdminOrAbove() || (memberInfo.Implicit != nil && memberInfo.Implicit.Role.IsAdminOrAbove())) {
			administeredTeams[memberInfo.FqName] = true
		}

		memberuid := memberInfo.UserID
		username, err := upakLoader.LookupUsername(context.Background(), memberuid)
		if err != nil {
			return nil, err
		}
		fullName, err := engine.GetFullName(context.Background(), g, memberuid)
		if err != nil {
			return nil, err
		}
		annotatedTeams[idx] = keybase1.AnnotatedMemberInfo{
			TeamID:   memberInfo.TeamID,
			FqName:   memberInfo.FqName,
			UserID:   memberInfo.UserID,
			Role:     memberInfo.Role,
			Implicit: memberInfo.Implicit,
			Username: username.String(),
			FullName: fullName,
		}
	}

	annotatedInvites := make(map[keybase1.TeamInviteID]keybase1.AnnotatedTeamInvite)
	for teamName := range teamNames {
		_, ok := administeredTeams[teamName]
		if ok {
			t, err := GetForTeamManagementByStringName(ctx, g, teamName, true)
			if err != nil {
				g.Log.Warning("Error while getting team (%s): %v", teamName, err)
				continue
			}
			teamAnnotatedInvites, err := AnnotateInvites(ctx, g, t.chain().inner.ActiveInvites, teamName)
			if err != nil {
				return nil, err
			}
			for teamInviteID, annotatedTeamInvite := range teamAnnotatedInvites {
				annotatedInvites[teamInviteID] = annotatedTeamInvite
			}
		}
	}

	tl := keybase1.AnnotatedTeamList{
		Teams: annotatedTeams,
		AnnotatedActiveInvites: annotatedInvites,
	}

	return &tl, nil
}

func AnnotateInvites(ctx context.Context, g *libkb.GlobalContext, invites map[keybase1.TeamInviteID]keybase1.TeamInvite, teamName string) (map[keybase1.TeamInviteID]keybase1.AnnotatedTeamInvite, error) {

	annotatedInvites := make(map[keybase1.TeamInviteID]keybase1.AnnotatedTeamInvite, len(invites))
	upakLoader := g.GetUPAKLoader()
	for id, invite := range invites {
		username, err := upakLoader.LookupUsername(context.Background(), invite.Inviter.Uid)
		if err != nil {
			return annotatedInvites, err
		}

		name := invite.Name
		category, err := invite.Type.C()
		if err != nil {
			return nil, err
		}
		if category == keybase1.TeamInviteCategory_KEYBASE {
			// "keybase" invites (i.e. pukless users) have uid for name
			uid, err := keybase1.UIDFromString(string(invite.Name))
			if err != nil {
				return nil, err
			}
			normalized, err := upakLoader.LookupUsername(context.Background(), uid)
			if err != nil {
				return nil, err
			}
			name = keybase1.TeamInviteName(normalized.String())
		}
		annotatedInvites[id] = keybase1.AnnotatedTeamInvite{
			Role:            invite.Role,
			Id:              invite.Id,
			Type:            invite.Type,
			Name:            name,
			InviterUsername: username.String(),
			TeamName:        teamName,
		}
	}
	return annotatedInvites, nil
}

func TeamTree(ctx context.Context, g *libkb.GlobalContext, arg keybase1.TeamTreeArg) (res keybase1.TeamTreeResult, err error) {
	if !arg.Name.IsRootTeam() {
		return res, fmt.Errorf("cannot get tree of non-root team")
	}

	serverList, err := getTeamsListFromServer(ctx, g, "", false)
	if err != nil {
		return res, err
	}

	// Map from team name (string) to entry
	entryMap := make(map[string]keybase1.TeamTreeEntry)

	// The server might have omitted some teams, oh well.
	// Trusts the server for role.
	// Load the teams by ID to make sure they are valid and get the validated names.
	for _, info := range serverList {
		serverName, err := info.TeamName()
		if err != nil {
			return res, err
		}
		if !serverName.RootAncestorName().Eq(arg.Name) {
			// Skip those not in this tree.
			continue
		}
		team, err := Load(ctx, g, keybase1.LoadTeamArg{
			ID:          info.TeamID,
			ForceRepoll: true,
		})
		if err != nil {
			return res, err
		}
		var admin bool // true if an admin or implicit admin
		if info.Role.IsAdminOrAbove() {
			admin = true
		}
		if info.Implicit != nil && info.Implicit.Role.IsAdminOrAbove() {
			admin = true
		}
		entryMap[team.Name().String()] = keybase1.TeamTreeEntry{
			Name:  team.Name(),
			Admin: admin,
		}
	}

	// Add all parent names (recursively)
	// So that if only A.B.C is in the list, we add A.B and A as well.
	// Adding map entries while iterating is safe.
	// "If map entries are created during iteration, that entry may be produced during the iteration or may be skipped."
	for _, entry := range entryMap {
		name := entry.Name.DeepCopy()
		for name.Depth() > 0 {
			_, ok := entryMap[name.String()]
			if !ok {
				entryMap[name.String()] = keybase1.TeamTreeEntry{
					Name:  name,
					Admin: false,
				}
			}
			name, err = name.Parent()
			if err != nil {
				break
			}
		}
	}

	for _, entry := range entryMap {
		res.Entries = append(res.Entries, entry)
	}

	if len(res.Entries) == 0 {
		return res, fmt.Errorf("team not found: %v", arg.Name)
	}

	// Order into a tree order. Which happens to be alphabetical ordering.
	// Example: [a, a.b, a.b.c, a.b.d, a.e.f, a.e.g]
	sort.Slice(res.Entries, func(i, j int) bool {
		return res.Entries[i].Name.String() < res.Entries[j].Name.String()
	})

	return res, nil
}
<|endoftext|>


<commit_before>package util

import (
	"bufio"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"sync"
)

// ShellRunner An interface to abstract shell command structs
type ShellRunner interface {
	SetPrefix(string)
	Run(string, ...string) (string, error)
}

// ShellExecutor Shell Command wrapper
type ShellExecutor struct {
	prefix string
}

// SetPrefix Sets the prefix that will be used for console output
func (e *ShellExecutor) SetPrefix(prefix string) {
	e.prefix = prefix
}

// Run executes a command with arguments.  Both stdout and stderr are captured
// from the command and logged using the prefix defined in the shellPrefix parameter.
// The command output and any error is also returned.  This function blocks until
// the shell command is complete.
func (e ShellExecutor) Run(command string, args ...string) (out string, err error) {

	var cmdout []string
	var errout []string

	var cmdReader io.ReadCloser
	var errReader io.ReadCloser

	cmd := exec.Command(command, args...)

	cmdReader, err = cmd.StdoutPipe()
	if err != nil {
		LogFatal(1, "Error creating StdoutPipe for Cmd", err)
	}

	errReader, err = cmd.StderrPipe()

	if err != nil {
		LogFatal(1, "Error creating StdoutError for Cmd", err)
	}

	var wg sync.WaitGroup
	wg.Add(2)

	scanner := bufio.NewScanner(cmdReader)
	go func() {
		defer wg.Done()
		for scanner.Scan() {
			text := scanner.Text()
			if e.prefix != "" {
				LogInfo(e.prefix + ": " + text)
			} else {
				LogInfo(text)
			}
			cmdout = append(cmdout, text)
		}
	}()

	errScanner := bufio.NewScanner(errReader)
	go func() {
		defer wg.Done()
		for errScanner.Scan() {
			text := errScanner.Text()
			if e.prefix != "" {
				LogInfo(e.prefix + ": " + text)
			} else {
				LogInfo(text)
			}
			errout = append(errout, text)
		}
	}()

	err = cmd.Start()
	if err != nil {
		LogErrorf("Error starting for Cmd: [%s] Error: [%s]", command, strings.Join(errout, "\n"))
		LogFatal(1, err)
	}

	err = cmd.Wait()
	if err != nil {
		LogErrorf("Error waiting for Cmd: [%s] Error: [%s]", command, strings.Join(errout, "\n"))
		LogFatal(1, err)
	}

	wg.Wait()

	return strings.Join(cmdout, "\n"), err

}

// Lambda Callback type to allow simulated execution of shell commands
type Lambda func(string, []string) error

// ExpectedCommand Defines a mock command input and output
type ExpectedCommand struct {
	Cmd       string
	Args      []string
	Triggered bool
	result    string
	err       error
	lambda    Lambda
}

// String String representation of the mock command
func (c ExpectedCommand) String() string {
	return fmt.Sprintf("%s %s", c.Cmd, strings.Join(c.Args, " "))
}

// Compare Check if the input command matches this mock command
func (c ExpectedCommand) Compare(cmd string, args ...string) error {
	if cmd != c.Cmd {
		return fmt.Errorf("ExpectedCommand Mismatch. Expected Command: [%s] Detected: [%s]", c.Cmd, cmd)
	}

	if len(c.Args) != len(args) {
		return fmt.Errorf("ExpectedCommand Mismatch. Command: [%s]: Expected [%d] args.  Received: [%d]", c.String(), len(c.Args), len(args))
	}

	for i, arg := range args {
		if arg != c.Args[i] {
			return fmt.Errorf("ExpectedCommand Mismatch. Command: [%s]: argument in position %d expected [%s].  Received: [%s]", c.String(), i, c.Args[i], arg)
		}
	}
	return nil
}

// GetResult Return the command result with the correct prefix
func (c ExpectedCommand) GetResult(prefix string) string {
	if len(prefix) > 0 {
		lines := strings.Split(c.result, "\n")
		joiner := fmt.Sprintf("\n%s: ", prefix)
		LogInfof("%s: %s", prefix, strings.Join(lines, joiner))
	}
	return c.result
}

// Lambda Execute simulated function
func (c ExpectedCommand) Lambda() error {
	if c.lambda != nil {
		return c.lambda(c.Cmd, c.Args)
	}
	return nil
}

// MockShellExecutor A mock command executor that fulfills the Runner interface
// modelled after DATA-DOG/go-sqlmock
type MockShellExecutor struct {
	prefix       string
	expectations []ExpectedCommand
	next         *ExpectedCommand
}

func (e MockShellExecutor) Count() int {
	return len(e.expectations)
}

// ExpectExec Create a expected command
func (e *MockShellExecutor) ExpectExec(cmd string, args []string, output string, err error) {
	e.expectations = append(e.expectations, ExpectedCommand{
		Cmd:    cmd,
		Args:   args,
		result: output,
		err:    err,
	})
}

// ExpectExecWithLambda Create a expected command
func (e *MockShellExecutor) ExpectExecWithLambda(cmd string, args []string, output string, err error, lb Lambda) {
	e.expectations = append(e.expectations, ExpectedCommand{
		Cmd:    cmd,
		Args:   args,
		result: output,
		err:    err,
		lambda: lb,
	})
}

// SetPrefix Set a prefix on the command output
func (e *MockShellExecutor) SetPrefix(prefix string) {
	e.prefix = prefix
}

// Run Execute a command.  This function checks all expected commands for a match in order of
// creation and returns the expected output or error if found.
func (e *MockShellExecutor) Run(cmd string, args ...string) (out string, err error) {
	var expected *ExpectedCommand
	var fulfilled int

	for i, next := range e.expectations {
		if next.Triggered {
			fulfilled++
			continue
		}
		expected = &e.expectations[i]
		break
	}
	if expected == nil {
		msg := "ExpectedCommand: [%s] with Args [%+v], was not expected"
		if fulfilled == len(e.expectations) {
			msg = " all expectations were already fulfilled, " + msg
		}
		return "", fmt.Errorf(msg, cmd, args)
	}

	if err = expected.Compare(cmd, args...); err != nil {
		return "", err
	}

	expected.Triggered = true
	LogAlert("Mock ExpectedCommand >> DETECTED: ###> [" + expected.String() + "]\n\n")

	// Trigger lambda
	err = expected.Lambda()
	if err != nil {
		return "", err
	}

	if expected.err != nil {
		return "", expected.err // mocked to return error
	}

	return expected.GetResult(e.prefix), err
}

func (e *MockShellExecutor) ExpectationsWereMet() error {
	for _, ex := range e.expectations {
		if !ex.Triggered {
			return fmt.Errorf("there is a remaining expectation which was not matched: %s", ex)
		}
	}
	return nil
}
<commit_msg>Reworded some unclear error messages<commit_after>package util

import (
	"bufio"
	"fmt"
	"io"
	"os/exec"
	"strings"
	"sync"
)

// ShellRunner An interface to abstract shell command structs
type ShellRunner interface {
	SetPrefix(string)
	Run(string, ...string) (string, error)
}

// ShellExecutor Shell Command wrapper
type ShellExecutor struct {
	prefix string
}

// SetPrefix Sets the prefix that will be used for console output
func (e *ShellExecutor) SetPrefix(prefix string) {
	e.prefix = prefix
}

// Run executes a command with arguments.  Both stdout and stderr are captured
// from the command and logged using the prefix defined in the shellPrefix parameter.
// The command output and any error is also returned.  This function blocks until
// the shell command is complete.
func (e ShellExecutor) Run(command string, args ...string) (out string, err error) {

	var cmdout []string
	var errout []string

	var cmdReader io.ReadCloser
	var errReader io.ReadCloser

	cmd := exec.Command(command, args...)

	cmdReader, err = cmd.StdoutPipe()
	if err != nil {
		LogFatal(1, "Error creating StdoutPipe for Cmd", err)
	}

	errReader, err = cmd.StderrPipe()

	if err != nil {
		LogFatal(1, "Error creating StdoutError for Cmd", err)
	}

	var wg sync.WaitGroup
	wg.Add(2)

	scanner := bufio.NewScanner(cmdReader)
	go func() {
		defer wg.Done()
		for scanner.Scan() {
			text := scanner.Text()
			if e.prefix != "" {
				LogInfo(e.prefix + ": " + text)
			} else {
				LogInfo(text)
			}
			cmdout = append(cmdout, text)
		}
	}()

	errScanner := bufio.NewScanner(errReader)
	go func() {
		defer wg.Done()
		for errScanner.Scan() {
			text := errScanner.Text()
			if e.prefix != "" {
				LogInfo(e.prefix + ": " + text)
			} else {
				LogInfo(text)
			}
			errout = append(errout, text)
		}
	}()

	err = cmd.Start()
	if err != nil {
		LogErrorf("Error starting for Cmd: [%s] Error: [%s]", command, strings.Join(errout, "\n"))
		LogFatal(1, err)
	}

	err = cmd.Wait()
	if err != nil {
		LogErrorf("Error waiting for Cmd: [%s] Error: [%s]", command, strings.Join(errout, "\n"))
		LogFatal(1, err)
	}

	wg.Wait()

	return strings.Join(cmdout, "\n"), err

}

// Lambda Callback type to allow simulated execution of shell commands
type Lambda func(string, []string) error

// ExpectedCommand Defines a mock command input and output
type ExpectedCommand struct {
	Cmd       string
	Args      []string
	Triggered bool
	result    string
	err       error
	lambda    Lambda
}

// String String representation of the mock command
func (c ExpectedCommand) String() string {
	return fmt.Sprintf("%s %s", c.Cmd, strings.Join(c.Args, " "))
}

// Compare Check if the input command matches this mock command
func (c ExpectedCommand) Compare(cmd string, args ...string) error {
	if cmd != c.Cmd {
		return fmt.Errorf("ExpectedCommand Mismatch. Expected Command: [%s] Detected: [%s]", c.Cmd, cmd)
	}

	if len(c.Args) != len(args) {
		return fmt.Errorf("ExpectedCommand Mismatch. Command: [%s]: Expected [%d] args.  Received: [%d]", c.String(), len(c.Args), len(args))
	}

	for i, arg := range args {
		if arg != c.Args[i] {
			return fmt.Errorf("ExpectedCommand Mismatch. Command: [%s]: argument in position %d expected [%s].  Received: [%s]", c.String(), i, c.Args[i], arg)
		}
	}
	return nil
}

// GetResult Return the command result with the correct prefix
func (c ExpectedCommand) GetResult(prefix string) string {
	if len(prefix) > 0 {
		lines := strings.Split(c.result, "\n")
		joiner := fmt.Sprintf("\n%s: ", prefix)
		LogInfof("%s: %s", prefix, strings.Join(lines, joiner))
	}
	return c.result
}

// Lambda Execute simulated function
func (c ExpectedCommand) Lambda() error {
	if c.lambda != nil {
		return c.lambda(c.Cmd, c.Args)
	}
	return nil
}

// MockShellExecutor A mock command executor that fulfills the Runner interface
// modelled after DATA-DOG/go-sqlmock
type MockShellExecutor struct {
	prefix       string
	expectations []ExpectedCommand
	next         *ExpectedCommand
}

func (e MockShellExecutor) Count() int {
	return len(e.expectations)
}

// ExpectExec Create a expected command
func (e *MockShellExecutor) ExpectExec(cmd string, args []string, output string, err error) {
	e.expectations = append(e.expectations, ExpectedCommand{
		Cmd:    cmd,
		Args:   args,
		result: output,
		err:    err,
	})
}

// ExpectExecWithLambda Create a expected command
func (e *MockShellExecutor) ExpectExecWithLambda(cmd string, args []string, output string, err error, lb Lambda) {
	e.expectations = append(e.expectations, ExpectedCommand{
		Cmd:    cmd,
		Args:   args,
		result: output,
		err:    err,
		lambda: lb,
	})
}

// SetPrefix Set a prefix on the command output
func (e *MockShellExecutor) SetPrefix(prefix string) {
	e.prefix = prefix
}

// Run Execute a command.  This function checks all expected commands for a match in order of
// creation and returns the expected output or error if found.
func (e *MockShellExecutor) Run(cmd string, args ...string) (out string, err error) {
	var expected *ExpectedCommand
	var fulfilled int

	for i, next := range e.expectations {
		if next.Triggered {
			fulfilled++
			continue
		}
		expected = &e.expectations[i]
		break
	}
	if expected == nil {
		msg := "Command: [%s] with Args [%+v], was not expected"
		if fulfilled == len(e.expectations) {
			msg = " All expectations were already fulfilled, " + msg
		}
		return "", fmt.Errorf(msg, cmd, args)
	}

	if err = expected.Compare(cmd, args...); err != nil {
		return "", err
	}

	expected.Triggered = true
	LogAlert("Mock ExpectedCommand >> DETECTED: ###> [" + expected.String() + "]\n\n")

	// Trigger lambda
	err = expected.Lambda()
	if err != nil {
		return "", err
	}

	if expected.err != nil {
		return "", expected.err // mocked to return error
	}

	return expected.GetResult(e.prefix), err
}

func (e *MockShellExecutor) ExpectationsWereMet() error {
	for _, ex := range e.expectations {
		if !ex.Triggered {
			return fmt.Errorf("there is a remaining expectation which was not matched: %s", ex)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package api

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

type RepoConfig struct {
	RealPath string `json:"real_path"`

	SshCloneUrl  string `json:"ssh_clone_url"`
	HttpCloneUrl string `json:"http_clone_url"`
	GitCloneUrl  string `json:"git_clone_url"`

	CustomPreReceivePath  string `json:"custom_pre_receive_path"`
	CustomPostReceivePath string `json:"custom_post_receive_path"`
	CustomUpdatePath      string `json:"custom_update_path"`
}

type InternalApi interface {
	GetRepoConfig(string, string) (*RepoConfig, error)
}

type GitoriousInternalApi struct {
	ApiUrl string
}

func (a *GitoriousInternalApi) GetRepoConfig(repoPath, username string) (*RepoConfig, error) {
	u, err := url.Parse(a.ApiUrl + "/repo-config")
	if err != nil {
		return nil, err
	}

	q := u.Query()
	q.Set("repo_path", repoPath)
	q.Set("username", username)
	u.RawQuery = q.Encode()

	var repoConfig RepoConfig

	if err := a.getJson(u, &repoConfig); err != nil {
		return nil, err
	}

	return &repoConfig, nil
}

func (a *GitoriousInternalApi) getJson(u *url.URL, target interface{}) error {
	request, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	request.Header.Add("Accept", "application/json")

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return errors.New(fmt.Sprintf("got status %v from %v", response.StatusCode, u))
	}

	decoder := json.NewDecoder(response.Body)
	err = decoder.Decode(target)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Extend InternalApi with AuthenticateUser method<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

type RepoConfig struct {
	RealPath string `json:"real_path"`

	SshCloneUrl  string `json:"ssh_clone_url"`
	HttpCloneUrl string `json:"http_clone_url"`
	GitCloneUrl  string `json:"git_clone_url"`

	CustomPreReceivePath  string `json:"custom_pre_receive_path"`
	CustomPostReceivePath string `json:"custom_post_receive_path"`
	CustomUpdatePath      string `json:"custom_update_path"`
}

type User struct {
	Username string `json:"username"`
}

type InternalApi interface {
	GetRepoConfig(string, string) (*RepoConfig, error)
	AuthenticateUser(string, string) (*User, error)
}

type HttpError struct {
	Url        *url.URL
	StatusCode int
}

func (e *HttpError) Error() string {
	return fmt.Sprintf("got HTTP status %v for %v", e.StatusCode, e.Url)
}

type GitoriousInternalApi struct {
	ApiUrl string
}

func (a *GitoriousInternalApi) GetRepoConfig(repoPath, username string) (*RepoConfig, error) {
	u, err := url.Parse(a.ApiUrl + "/repo-config")
	if err != nil {
		return nil, err
	}

	q := u.Query()
	q.Set("repo_path", repoPath)
	q.Set("username", username)
	u.RawQuery = q.Encode()

	var repoConfig RepoConfig

	if err := a.getJson(u, &repoConfig); err != nil {
		return nil, err
	}

	return &repoConfig, nil
}

func (a *GitoriousInternalApi) AuthenticateUser(username, password string) (*User, error) {
	u, err := url.Parse(a.ApiUrl + "/authenticate")
	if err != nil {
		return nil, err
	}

	q := u.Query()
	q.Set("username", username)
	q.Set("password", password)
	u.RawQuery = q.Encode()

	var user User

	if err := a.getJson(u, &user); err != nil {
		if httpErr, ok := err.(*HttpError); ok {
			if httpErr.StatusCode == 401 {
				return nil, nil
			}
		}

		return nil, err
	}

	return &user, nil
}

func (a *GitoriousInternalApi) getJson(u *url.URL, target interface{}) error {
	request, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	request.Header.Add("Accept", "application/json")

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return &HttpError{u, response.StatusCode}
	}

	decoder := json.NewDecoder(response.Body)
	err = decoder.Decode(target)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

type RepoConfig struct {
	RealPath string `json:"real_path"`

	SshCloneUrl  string `json:"ssh_clone_url"`
	HttpCloneUrl string `json:"http_clone_url"`
	GitCloneUrl  string `json:"git_clone_url"`

	CustomPreReceivePath  string `json:"custom_pre_receive_path"`
	CustomPostReceivePath string `json:"custom_post_receive_path"`
	CustomUpdatePath      string `json:"custom_update_path"`
}

type InternalApi interface {
	GetRepoConfig(string, string) (*RepoConfig, error)
}

type GitoriousInternalApi struct {
	ApiUrl string
}

func (a *GitoriousInternalApi) GetRepoConfig(repoPath, username string) (*RepoConfig, error) {
	path := fmt.Sprintf("/repo-config?repo_path=%v&username=%v", repoPath, username)
	var repoConfig RepoConfig

	if err := a.getJson(path, &repoConfig); err != nil {
		return nil, err
	}

	return &repoConfig, nil
}

func (a *GitoriousInternalApi) getJson(path string, target interface{}) error {
	url := a.ApiUrl + path

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

	request.Header.Add("Accept", "application/json")

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return errors.New(fmt.Sprintf("got status %v from %v", response.StatusCode, url))
	}

	decoder := json.NewDecoder(response.Body)
	err = decoder.Decode(target)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>URL-encode params sent to internal repo config API<commit_after>package api

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

type RepoConfig struct {
	RealPath string `json:"real_path"`

	SshCloneUrl  string `json:"ssh_clone_url"`
	HttpCloneUrl string `json:"http_clone_url"`
	GitCloneUrl  string `json:"git_clone_url"`

	CustomPreReceivePath  string `json:"custom_pre_receive_path"`
	CustomPostReceivePath string `json:"custom_post_receive_path"`
	CustomUpdatePath      string `json:"custom_update_path"`
}

type InternalApi interface {
	GetRepoConfig(string, string) (*RepoConfig, error)
}

type GitoriousInternalApi struct {
	ApiUrl string
}

func (a *GitoriousInternalApi) GetRepoConfig(repoPath, username string) (*RepoConfig, error) {
	u, err := url.Parse(a.ApiUrl + "/repo-config")
	if err != nil {
		return nil, err
	}

	q := u.Query()
	q.Set("repo_path", repoPath)
	q.Set("username", username)
	u.RawQuery = q.Encode()

	var repoConfig RepoConfig

	if err := a.getJson(u, &repoConfig); err != nil {
		return nil, err
	}

	return &repoConfig, nil
}

func (a *GitoriousInternalApi) getJson(u *url.URL, target interface{}) error {
	request, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return err
	}

	request.Header.Add("Accept", "application/json")

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return errors.New(fmt.Sprintf("got status %v from %v", response.StatusCode, u))
	}

	decoder := json.NewDecoder(response.Body)
	err = decoder.Decode(target)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/golang/glog"
	"github.com/imdario/mergo"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

type Probe struct {
	Path             string
	InitialDelay     int `yaml:"initialDelay"`
	PeriodSeconds    int `yaml:"periodSeconds"`
	FailureThreshold int `yaml:"failureThreshold"`
	Timeout          int `yaml:"timeout"`
}

type Healthcheck struct {
	Liveness  Probe
	Readiness Probe
}

type ResourceList struct {
	Cpu    string
	Memory string
}

type ResourceRequirements struct {
	Limits   ResourceList
	Requests ResourceList
}

type PrometheusConfig struct {
	Enabled bool
	Port    string
	Path    string
}

type IstioConfig struct {
	Enabled bool
}

type NaisManifest struct {
	Team            string
	Image           string
	Port            int
	Healthcheck     Healthcheck
	PreStopHookPath string         `yaml:"preStopHookPath"`
	Prometheus      PrometheusConfig
	Istio           IstioConfig
	Replicas        Replicas
	Ingress         Ingress
	Resources       ResourceRequirements
	FasitResources  FasitResources `yaml:"fasitResources"`
	LeaderElection  bool           `yaml:"leaderElection"`
	Redis           bool           `yaml:"redis"`
	Alerts          []PrometheusAlertRule
}

type Ingress struct {
	Disabled bool
}

type Replicas struct {
	Min                    int
	Max                    int
	CpuThresholdPercentage int `yaml:"cpuThresholdPercentage"`
}

type FasitResources struct {
	Used    []UsedResource
	Exposed []ExposedResource
}

type UsedResource struct {
	Alias        string
	ResourceType string            `yaml:"resourceType"`
	PropertyMap  map[string]string `yaml:"propertyMap"`
}

type ExposedResource struct {
	Alias          string
	ResourceType   string `yaml:"resourceType"`
	Path           string
	Description    string
	WsdlGroupId    string `yaml:"wsdlGroupId"`
	WsdlArtifactId string `yaml:"wsdlArtifactId"`
	WsdlVersion    string `yaml:"wsdlVersion"`
	SecurityToken  string `yaml:"securityToken"`
	AllZones       bool   `yaml:"allZones"`
}

type ValidationErrors struct {
	Errors []ValidationError
}

type ValidationError struct {
	ErrorMessage string
	Fields       map[string]string
}

type Field struct {
	Name  string
	Value string
}

func GenerateManifest(deploymentRequest NaisDeploymentRequest) (naisManifest NaisManifest, err error) {

	manifest, err := downloadManifest(deploymentRequest)

	if err != nil {
		glog.Errorf("could not download manifest", err)
		return NaisManifest{}, err
	}

	if err := AddDefaultManifestValues(&manifest, deploymentRequest.Application); err != nil {
		glog.Errorf("Could not merge manifest %s", err)
		return NaisManifest{}, err
	}

	validationErrors := ValidateManifest(manifest)
	if len(validationErrors.Errors) != 0 {
		glog.Error("Invalid manifest: ", validationErrors.Error())
		return NaisManifest{}, validationErrors
	}

	return manifest, nil
}

func downloadManifest(deploymentRequest NaisDeploymentRequest) (naisManifest NaisManifest, err error) {
	// manifest url is provided in deployment request
	manifestUrl := deploymentRequest.ManifestUrl
	if len(manifestUrl) > 0 {
		manifest, err := fetchManifest(manifestUrl)
		if err != nil {
			return NaisManifest{}, err
		} else {
			return manifest, nil
		}
	}

	// not provided, using defaults
	urls := createManifestUrl(deploymentRequest.Application, deploymentRequest.Version)
	for _, url := range urls {
		manifest, err := fetchManifest(url)
		if err == nil {
			return manifest, nil
		}
	}

	return NaisManifest{}, fmt.Errorf("No manifest found on the URLs %s, or the url %s\n", urls, manifestUrl)
}

func createManifestUrl(application, version string) []string {
	return []string{
		fmt.Sprintf("https://repo.adeo.no/repository/raw/nais/%s/%s/nais.yaml", application, version),
		fmt.Sprintf("http://nexus.adeo.no/nexus/service/local/repositories/m2internal/content/nais/%s/%s/nais.yaml", application, version),
		fmt.Sprintf("http://nexus.adeo.no/nexus/service/local/repositories/m2internal/content/nais/%s/%s/%s.yaml", application, version, application+"-"+version),
	}
}

func AddDefaultManifestValues(manifest *NaisManifest, application string) error {
	return mergo.Merge(manifest, GetDefaultManifest(application))
}
func fetchManifest(url string) (NaisManifest, error) {
	glog.Infof("Fetching manifest from URL %s\n", url)
	response, err := http.Get(url)
	if err != nil {
		glog.Errorf("Could not fetch %s", err)
		return NaisManifest{}, fmt.Errorf("HTTP GET failed for url: %s. %s", url, err.Error())
	}

	defer response.Body.Close()

	if response.StatusCode > 299 {
		return NaisManifest{}, fmt.Errorf("got HTTP status code %d fetching manifest from URL: %s", response.StatusCode, url)
	}

	if body, err := ioutil.ReadAll(response.Body); err != nil {
		return NaisManifest{}, err
	} else {
		var manifest NaisManifest
		if err := yaml.Unmarshal(body, &manifest); err != nil {
			glog.Errorf("Could not unmarshal yaml %s", err)
			return NaisManifest{}, fmt.Errorf("unable to unmarshal yaml: %s", err.Error())
		}
		glog.Infof("Got manifest %s", manifest)
		return manifest, err
	}
}

func ValidateManifest(manifest NaisManifest) ValidationErrors {
	validations := []func(NaisManifest) *ValidationError{
		validateImage,
		validateReplicasMax,
		validateReplicasMin,
		validateMinIsSmallerThanMax,
		validateCpuThreshold,
		validateResources,
		validateAlertRules,
	}

	var validationErrors ValidationErrors
	for _, valfunc := range validations {
		if valError := valfunc(manifest); valError != nil {
			validationErrors.Errors = append(validationErrors.Errors, *valError)
		}
	}

	return validationErrors
}

func validateResources(manifest NaisManifest) *ValidationError {
	for _, resource := range manifest.FasitResources.Exposed {
		if resource.ResourceType == "" || resource.Alias == "" {
			return &ValidationError{
				"Alias and ResourceType must be specified",
				map[string]string{"Alias": resource.Alias},
			}
		}
	}
	for _, resource := range manifest.FasitResources.Used {
		if resource.ResourceType == "" || resource.Alias == "" {
			return &ValidationError{
				"Alias and ResourceType must be specified",
				map[string]string{"Alias": resource.Alias},
			}
		}
	}
	return nil
}
func validateImage(manifest NaisManifest) *ValidationError {
	if strings.LastIndex(manifest.Image, ":") > strings.LastIndex(manifest.Image, "/") {
		return &ValidationError{
			"Image cannot contain tag",
			map[string]string{"Image": manifest.Image},
		}
	}
	return nil
}

func validateCpuThreshold(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.CpuThresholdPercentage < 10 || manifest.Replicas.CpuThresholdPercentage > 90 {
		err := new(ValidationError)
		err.ErrorMessage = "CpuThreshold must be between 10 and 90."
		err.Fields = make(map[string]string)
		err.Fields["Replicas.CpuThreshold"] = strconv.Itoa(manifest.Replicas.CpuThresholdPercentage)
		return err

	}
	return nil

}
func validateMinIsSmallerThanMax(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.Min > manifest.Replicas.Max {
		validationError := new(ValidationError)
		validationError.ErrorMessage = "Replicas.Min is larger than Replicas.Max."
		validationError.Fields = make(map[string]string)
		validationError.Fields["Replicas.Max"] = strconv.Itoa(manifest.Replicas.Max)
		validationError.Fields["Replicas.Min"] = strconv.Itoa(manifest.Replicas.Min)
		return validationError
	}
	return nil

}
func validateReplicasMin(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.Min == 0 {
		validationError := new(ValidationError)
		validationError.ErrorMessage = "Replicas.Min is not set"
		validationError.Fields = make(map[string]string)
		validationError.Fields["Replicas.Min"] = strconv.Itoa(manifest.Replicas.Min)
		return validationError

	}
	return nil

}
func validateReplicasMax(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.Max == 0 {
		validationError := new(ValidationError)
		validationError.ErrorMessage = "Replicas.Max is not set"
		validationError.Fields = make(map[string]string)
		validationError.Fields["Replicas.Max"] = strconv.Itoa(manifest.Replicas.Max)
		return validationError
	}
	return nil

}

func (errors ValidationErrors) Error() (s string) {
	for _, validationError := range errors.Errors {
		s += validationError.ErrorMessage + "\n"
		for k, v := range validationError.Fields {
			s += " - " + k + ": " + v + ".\n"
		}
	}
	return s
}
<commit_msg>Improve error messages when unable to fetch or marshal nais.yaml<commit_after>package api

import (
	"fmt"
	"github.com/golang/glog"
	"github.com/imdario/mergo"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
)

type Probe struct {
	Path             string
	InitialDelay     int `yaml:"initialDelay"`
	PeriodSeconds    int `yaml:"periodSeconds"`
	FailureThreshold int `yaml:"failureThreshold"`
	Timeout          int `yaml:"timeout"`
}

type Healthcheck struct {
	Liveness  Probe
	Readiness Probe
}

type ResourceList struct {
	Cpu    string
	Memory string
}

type ResourceRequirements struct {
	Limits   ResourceList
	Requests ResourceList
}

type PrometheusConfig struct {
	Enabled bool
	Port    string
	Path    string
}

type IstioConfig struct {
	Enabled bool
}

type NaisManifest struct {
	Team            string
	Image           string
	Port            int
	Healthcheck     Healthcheck
	PreStopHookPath string         `yaml:"preStopHookPath"`
	Prometheus      PrometheusConfig
	Istio           IstioConfig
	Replicas        Replicas
	Ingress         Ingress
	Resources       ResourceRequirements
	FasitResources  FasitResources `yaml:"fasitResources"`
	LeaderElection  bool           `yaml:"leaderElection"`
	Redis           bool           `yaml:"redis"`
	Alerts          []PrometheusAlertRule
}

type Ingress struct {
	Disabled bool
}

type Replicas struct {
	Min                    int
	Max                    int
	CpuThresholdPercentage int `yaml:"cpuThresholdPercentage"`
}

type FasitResources struct {
	Used    []UsedResource
	Exposed []ExposedResource
}

type UsedResource struct {
	Alias        string
	ResourceType string            `yaml:"resourceType"`
	PropertyMap  map[string]string `yaml:"propertyMap"`
}

type ExposedResource struct {
	Alias          string
	ResourceType   string `yaml:"resourceType"`
	Path           string
	Description    string
	WsdlGroupId    string `yaml:"wsdlGroupId"`
	WsdlArtifactId string `yaml:"wsdlArtifactId"`
	WsdlVersion    string `yaml:"wsdlVersion"`
	SecurityToken  string `yaml:"securityToken"`
	AllZones       bool   `yaml:"allZones"`
}

type ValidationErrors struct {
	Errors []ValidationError
}

type ValidationError struct {
	ErrorMessage string
	Fields       map[string]string
}

type Field struct {
	Name  string
	Value string
}

func GenerateManifest(deploymentRequest NaisDeploymentRequest) (naisManifest NaisManifest, err error) {

	manifest, err := downloadManifest(deploymentRequest)

	if err != nil {
		glog.Errorf("could not download manifest", err)
		return NaisManifest{}, err
	}

	if err := AddDefaultManifestValues(&manifest, deploymentRequest.Application); err != nil {
		glog.Errorf("Could not merge manifest %s", err)
		return NaisManifest{}, err
	}

	validationErrors := ValidateManifest(manifest)
	if len(validationErrors.Errors) != 0 {
		glog.Error("Invalid manifest: ", validationErrors.Error())
		return NaisManifest{}, validationErrors
	}

	return manifest, nil
}

func downloadManifest(deploymentRequest NaisDeploymentRequest) (naisManifest NaisManifest, err error) {

	var urls []string

	if len(deploymentRequest.ManifestUrl) > 0 {
		urls = []string{deploymentRequest.ManifestUrl}
	} else {
		glog.Info("No manifest url specified. Using defaults")
		urls = createManifestUrl(deploymentRequest.Application, deploymentRequest.Version)
	}

	for _, url := range urls {
		manifest, err := fetchManifest(url)
		if err == nil {
			return manifest, nil
		}
	}

	return NaisManifest{}, fmt.Errorf("Error fetching or parsing manifests from URL(s):  %s", urls)
}

func createManifestUrl(application, version string) []string {
	return []string{
		fmt.Sprintf("https://repo.adeo.no/repository/raw/nais/%s/%s/nais.yaml", application, version),
		fmt.Sprintf("http://nexus.adeo.no/nexus/service/local/repositories/m2internal/content/nais/%s/%s/nais.yaml", application, version),
		fmt.Sprintf("http://nexus.adeo.no/nexus/service/local/repositories/m2internal/content/nais/%s/%s/%s.yaml", application, version, application+"-"+version),
	}
}

func AddDefaultManifestValues(manifest *NaisManifest, application string) error {
	return mergo.Merge(manifest, GetDefaultManifest(application))
}
func fetchManifest(url string) (NaisManifest, error) {
	glog.Infof("Fetching manifest from URL %s\n", url)
	response, err := http.Get(url)
	if err != nil {
		glog.Errorf("Could not fetch %s", err)
		return NaisManifest{}, fmt.Errorf("HTTP GET failed for url: %s. %s", url, err.Error())
	}

	defer response.Body.Close()

	if response.StatusCode > 299 {
		glog.Errorf("got HTTP status code %d fetching manifest from URL: %s", response.StatusCode, url)
		return NaisManifest{}, fmt.Errorf("got HTTP status code %d fetching manifest from URL: %s", response.StatusCode, url)
	}

	if body, err := ioutil.ReadAll(response.Body); err != nil {
		return NaisManifest{}, err
	} else {
		var manifest NaisManifest
		if err := yaml.Unmarshal(body, &manifest); err != nil {
			glog.Errorf("Could not unmarshal yaml %s", err)
			return NaisManifest{}, fmt.Errorf("unable to unmarshal yaml: %s", err.Error())
		}
		glog.Infof("Got manifest %s", manifest)
		return manifest, err
	}
}

func ValidateManifest(manifest NaisManifest) ValidationErrors {
	validations := []func(NaisManifest) *ValidationError{
		validateImage,
		validateReplicasMax,
		validateReplicasMin,
		validateMinIsSmallerThanMax,
		validateCpuThreshold,
		validateResources,
		validateAlertRules,
	}

	var validationErrors ValidationErrors
	for _, valfunc := range validations {
		if valError := valfunc(manifest); valError != nil {
			validationErrors.Errors = append(validationErrors.Errors, *valError)
		}
	}

	return validationErrors
}

func validateResources(manifest NaisManifest) *ValidationError {
	for _, resource := range manifest.FasitResources.Exposed {
		if resource.ResourceType == "" || resource.Alias == "" {
			return &ValidationError{
				"Alias and ResourceType must be specified",
				map[string]string{"Alias": resource.Alias},
			}
		}
	}
	for _, resource := range manifest.FasitResources.Used {
		if resource.ResourceType == "" || resource.Alias == "" {
			return &ValidationError{
				"Alias and ResourceType must be specified",
				map[string]string{"Alias": resource.Alias},
			}
		}
	}
	return nil
}
func validateImage(manifest NaisManifest) *ValidationError {
	if strings.LastIndex(manifest.Image, ":") > strings.LastIndex(manifest.Image, "/") {
		return &ValidationError{
			"Image cannot contain tag",
			map[string]string{"Image": manifest.Image},
		}
	}
	return nil
}

func validateCpuThreshold(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.CpuThresholdPercentage < 10 || manifest.Replicas.CpuThresholdPercentage > 90 {
		err := new(ValidationError)
		err.ErrorMessage = "CpuThreshold must be between 10 and 90."
		err.Fields = make(map[string]string)
		err.Fields["Replicas.CpuThreshold"] = strconv.Itoa(manifest.Replicas.CpuThresholdPercentage)
		return err

	}
	return nil

}
func validateMinIsSmallerThanMax(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.Min > manifest.Replicas.Max {
		validationError := new(ValidationError)
		validationError.ErrorMessage = "Replicas.Min is larger than Replicas.Max."
		validationError.Fields = make(map[string]string)
		validationError.Fields["Replicas.Max"] = strconv.Itoa(manifest.Replicas.Max)
		validationError.Fields["Replicas.Min"] = strconv.Itoa(manifest.Replicas.Min)
		return validationError
	}
	return nil

}
func validateReplicasMin(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.Min == 0 {
		validationError := new(ValidationError)
		validationError.ErrorMessage = "Replicas.Min is not set"
		validationError.Fields = make(map[string]string)
		validationError.Fields["Replicas.Min"] = strconv.Itoa(manifest.Replicas.Min)
		return validationError

	}
	return nil

}
func validateReplicasMax(manifest NaisManifest) *ValidationError {
	if manifest.Replicas.Max == 0 {
		validationError := new(ValidationError)
		validationError.ErrorMessage = "Replicas.Max is not set"
		validationError.Fields = make(map[string]string)
		validationError.Fields["Replicas.Max"] = strconv.Itoa(manifest.Replicas.Max)
		return validationError
	}
	return nil

}

func (errors ValidationErrors) Error() (s string) {
	for _, validationError := range errors.Errors {
		s += validationError.ErrorMessage + "\n"
		for k, v := range validationError.Fields {
			s += " - " + k + ": " + v + ".\n"
		}
	}
	return s
}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/tsuru/tsuru/auth"
	tErrors "github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/event"
	"github.com/tsuru/tsuru/io"
	"github.com/tsuru/tsuru/permission"
	"github.com/tsuru/tsuru/servicemanager"
	appTypes "github.com/tsuru/tsuru/types/app"
)

// title: add platform
// path: /platforms
// method: POST
// consume: multipart/form-data
// produce: application/x-json-stream
// responses:
//   200: Platform created
//   400: Invalid data
//   401: Unauthorized
func platformAdd(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	name := r.FormValue("name")
	file, _, err := r.FormFile("dockerfile_content")
	if err != nil {
		return &tErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	defer file.Close()
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}
	if len(data) == 0 {
		return &tErrors.HTTP{Code: http.StatusBadRequest, Message: appTypes.ErrMissingFileContent.Error()}
	}
	args := make(map[string]string)
	for key, values := range r.Form {
		args[key] = values[0]
	}
	canCreatePlatform := permission.Check(t, permission.PermPlatformCreate)
	if !canCreatePlatform {
		return permission.ErrUnauthorized
	}
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePlatform, Value: name},
		Kind:       permission.PermPlatformCreate,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPlatformReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	ctx, cancel := evt.CancelableContext(context.Background())
	err = servicemanager.Platform.Create(appTypes.PlatformOptions{
		Name:   name,
		Args:   args,
		Data:   data,
		Output: writer,
		Ctx:    ctx,
	})
	cancel()
	if err != nil {
		return err
	}
	writer.Write([]byte("Platform successfully added!\n"))
	return nil
}

// title: update platform
// path: /platforms/{name}
// method: PUT
// produce: application/x-json-stream
// responses:
//   200: Platform updated
//   401: Unauthorized
//   404: Not found
func platformUpdate(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	name := r.URL.Query().Get(":name")
	file, _, _ := r.FormFile("dockerfile_content")
	if file != nil {
		defer file.Close()
	}
	args := make(map[string]string)
	for key, values := range r.Form {
		args[key] = values[0]
	}
	canUpdatePlatform := permission.Check(t, permission.PermPlatformUpdate)
	if !canUpdatePlatform {
		return permission.ErrUnauthorized
	}
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePlatform, Value: name},
		Kind:       permission.PermPlatformUpdate,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPlatformReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	ctx, cancel := evt.CancelableContext(context.Background())
	err = servicemanager.Platform.Update(appTypes.PlatformOptions{
		Name:   name,
		Args:   args,
		Input:  file,
		Output: writer,
		Ctx:    ctx,
	})
	cancel()
	if err == appTypes.ErrPlatformNotFound {
		return &tErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	if err != nil {
		return err
	}
	writer.Write([]byte("Platform successfully updated!\n"))
	return nil
}

// title: remove platform
// path: /platforms/{name}
// method: DELETE
// responses:
//   200: Platform removed
//   401: Unauthorized
//   404: Not found
func platformRemove(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	canDeletePlatform := permission.Check(t, permission.PermPlatformDelete)
	if !canDeletePlatform {
		return permission.ErrUnauthorized
	}
	name := r.URL.Query().Get(":name")
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePlatform, Value: name},
		Kind:       permission.PermPlatformDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPlatformReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	err = servicemanager.Platform.Remove(name)
	if err == appTypes.ErrPlatformNotFound {
		return &tErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	return err
}

// title: platform list
// path: /platforms
// method: GET
// produce: application/json
// responses:
//   200: List platforms
//   204: No content
//   401: Unauthorized
func platformList(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	canUsePlat := permission.Check(t, permission.PermPlatformUpdate) ||
		permission.Check(t, permission.PermPlatformCreate)
	platforms, err := servicemanager.Platform.List(!canUsePlat)
	if err != nil {
		return err
	}
	if len(platforms) == 0 {
		w.WriteHeader(http.StatusNoContent)
		return nil
	}
	w.Header().Set("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(platforms)
}
<commit_msg>api/platform: use event as output<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/tsuru/tsuru/auth"
	tErrors "github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/event"
	"github.com/tsuru/tsuru/io"
	"github.com/tsuru/tsuru/permission"
	"github.com/tsuru/tsuru/servicemanager"
	appTypes "github.com/tsuru/tsuru/types/app"
)

// title: add platform
// path: /platforms
// method: POST
// consume: multipart/form-data
// produce: application/x-json-stream
// responses:
//   200: Platform created
//   400: Invalid data
//   401: Unauthorized
func platformAdd(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	name := r.FormValue("name")
	file, _, err := r.FormFile("dockerfile_content")
	if err != nil {
		return &tErrors.HTTP{Code: http.StatusBadRequest, Message: err.Error()}
	}
	defer file.Close()
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}
	if len(data) == 0 {
		return &tErrors.HTTP{Code: http.StatusBadRequest, Message: appTypes.ErrMissingFileContent.Error()}
	}
	args := make(map[string]string)
	for key, values := range r.Form {
		args[key] = values[0]
	}
	canCreatePlatform := permission.Check(t, permission.PermPlatformCreate)
	if !canCreatePlatform {
		return permission.ErrUnauthorized
	}
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePlatform, Value: name},
		Kind:       permission.PermPlatformCreate,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPlatformReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	evt.SetLogWriter(writer)
	ctx, cancel := evt.CancelableContext(context.Background())
	err = servicemanager.Platform.Create(appTypes.PlatformOptions{
		Name:   name,
		Args:   args,
		Data:   data,
		Output: evt,
		Ctx:    ctx,
	})
	cancel()
	if err != nil {
		return err
	}
	writer.Write([]byte("Platform successfully added!\n"))
	return nil
}

// title: update platform
// path: /platforms/{name}
// method: PUT
// produce: application/x-json-stream
// responses:
//   200: Platform updated
//   401: Unauthorized
//   404: Not found
func platformUpdate(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	name := r.URL.Query().Get(":name")
	file, _, _ := r.FormFile("dockerfile_content")
	if file != nil {
		defer file.Close()
	}
	args := make(map[string]string)
	for key, values := range r.Form {
		args[key] = values[0]
	}
	canUpdatePlatform := permission.Check(t, permission.PermPlatformUpdate)
	if !canUpdatePlatform {
		return permission.ErrUnauthorized
	}
	w.Header().Set("Content-Type", "application/x-json-stream")
	keepAliveWriter := io.NewKeepAliveWriter(w, 30*time.Second, "")
	defer keepAliveWriter.Stop()
	writer := &io.SimpleJsonMessageEncoderWriter{Encoder: json.NewEncoder(keepAliveWriter)}
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePlatform, Value: name},
		Kind:       permission.PermPlatformUpdate,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPlatformReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	evt.SetLogWriter(writer)
	ctx, cancel := evt.CancelableContext(context.Background())
	err = servicemanager.Platform.Update(appTypes.PlatformOptions{
		Name:   name,
		Args:   args,
		Input:  file,
		Output: evt,
		Ctx:    ctx,
	})
	cancel()
	if err == appTypes.ErrPlatformNotFound {
		return &tErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	if err != nil {
		return err
	}
	writer.Write([]byte("Platform successfully updated!\n"))
	return nil
}

// title: remove platform
// path: /platforms/{name}
// method: DELETE
// responses:
//   200: Platform removed
//   401: Unauthorized
//   404: Not found
func platformRemove(w http.ResponseWriter, r *http.Request, t auth.Token) (err error) {
	r.ParseForm()
	canDeletePlatform := permission.Check(t, permission.PermPlatformDelete)
	if !canDeletePlatform {
		return permission.ErrUnauthorized
	}
	name := r.URL.Query().Get(":name")
	evt, err := event.New(&event.Opts{
		Target:     event.Target{Type: event.TargetTypePlatform, Value: name},
		Kind:       permission.PermPlatformDelete,
		Owner:      t,
		CustomData: event.FormToCustomData(r.Form),
		Allowed:    event.Allowed(permission.PermPlatformReadEvents),
	})
	if err != nil {
		return err
	}
	defer func() { evt.Done(err) }()
	err = servicemanager.Platform.Remove(name)
	if err == appTypes.ErrPlatformNotFound {
		return &tErrors.HTTP{Code: http.StatusNotFound, Message: err.Error()}
	}
	return err
}

// title: platform list
// path: /platforms
// method: GET
// produce: application/json
// responses:
//   200: List platforms
//   204: No content
//   401: Unauthorized
func platformList(w http.ResponseWriter, r *http.Request, t auth.Token) error {
	canUsePlat := permission.Check(t, permission.PermPlatformUpdate) ||
		permission.Check(t, permission.PermPlatformCreate)
	platforms, err := servicemanager.Platform.List(!canUsePlat)
	if err != nil {
		return err
	}
	if len(platforms) == 0 {
		w.WriteHeader(http.StatusNoContent)
		return nil
	}
	w.Header().Set("Content-Type", "application/json")
	return json.NewEncoder(w).Encode(platforms)
}
<|endoftext|>


<commit_before>package utils

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestRepeat(t *testing.T) {
	closing := make(chan bool)
	count := 0
	defer close(closing)

	Repeat(func() { count++ }, 1*time.Millisecond, closing)
	time.Sleep(10 * time.Millisecond)
	assert.True(t, count > 0)
}
<commit_msg>test is still flaky<commit_after>package utils

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestRepeat(t *testing.T) {
	closing := make(chan bool)
	count := 0
	defer close(closing)

	Repeat(func() { count++ }, 1*time.Microsecond, closing)
	time.Sleep(50 * time.Millisecond)
	assert.True(t, count > 0)
}
<|endoftext|>


<commit_before>package gocd

import (
	"context"
	"fmt"
)

// PipelinesService describes the HAL _link resource for the api response object for a pipelineconfig
type PipelinesService service

// PipelineRequest describes a pipeline request object
type PipelineRequest struct {
	Group    string    `json:"group"`
	Pipeline *Pipeline `json:"pipeline"`
}

// Pipeline describes a pipeline object
type Pipeline struct {
	Group                 string                 `json:"group,omitempty"`
	Links                 *HALLinks              `json:"_links,omitempty"`
	Name                  string                 `json:"name"`
	LabelTemplate         string                 `json:"label_template,omitempty"`
	EnablePipelineLocking bool                   `json:"enable_pipeline_locking,omitempty"`
	Template              string                 `json:"template,omitempty"`
	Origin                *PipelineConfigOrigin  `json:"origin,omitempty"`
	Parameters            []*Parameter           `json:"parameters,omitempty"`
	EnvironmentVariables  []*EnvironmentVariable `json:"environment_variables,omitempty"`
	Materials             []Material             `json:"materials,omitempty"`
	Label                 string                 `json:"label,omitempty"`
	Stages                []*Stage               `json:"stages,omitempty"`
	Version               string                 `json:"version,omitempty"`
	//TrackingTool          string                 `json:"tracking_tool"`
	//Timer                 string                 `json:"timer"`
}

// Parameter represents a key/value
type Parameter struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

// PipelineConfigOrigin describes where a pipeline config is being loaded from
type PipelineConfigOrigin struct {
	Type string `json:"type"`
	File string `json:"file"`
}

// Material describes an artifact dependency for a pipeline object.
type Material struct {
	Type        string             `json:"type"`
	Fingerprint string             `json:"fingerprint,omitempty"`
	Description string             `json:"description,omitempty"`
	Attributes  MaterialAttributes `json:"attributes"`
}

// MaterialAttributes describes a material type
type MaterialAttributes struct {
	URL             string          `json:"url,omitempty"`
	Destination     string          `json:"destination,omitempty"`
	Filter          *MaterialFilter `json:"filter,omitempty"`
	InvertFilter    bool            `json:"invert_filter"`
	Name            string          `json:"name,omitempty"`
	AutoUpdate      bool            `json:"auto_update,omitempty"`
	Branch          string          `json:"branch,omitempty"`
	SubmoduleFolder string          `json:"submodule_folder,omitempty"`
	ShallowClone    bool            `json:"shallow_clone,omitempty"`
	Pipeline        string          `json:"pipeline,omitempty"`
	Stage           string          `json:"stage,omitempty"`
}

// MaterialFilter describes which globs to ignore
type MaterialFilter struct {
	Ignore []string `json:"ignore"`
}

// PipelineHistory describes the history of runs for a pipeline
type PipelineHistory struct {
	Pipelines []*PipelineInstance `json:"pipelines"`
}

// PipelineInstance describes a single pipeline run
type PipelineInstance struct {
	BuildCause   BuildCause `json:"build_cause"`
	CanRun       bool       `json:"can_run"`
	Name         string     `json:"name"`
	NaturalOrder int        `json:"natural_order"`
	Comment      string     `json:"comment"`
	Stages       []*Stage   `json:"stages"`
}

// BuildCause describes the triggers which caused the build to start.
type BuildCause struct {
	Approver          string             `json:"approver,omitempty"`
	MaterialRevisions []MaterialRevision `json:"material_revisions"`
	TriggerForced     bool               `json:"trigger_forced"`
	TriggerMessage    string             `json:"trigger_message"`
}

// MaterialRevision describes the uniquely identifiable version for the material which was pulled for this build
type MaterialRevision struct {
	Modifications []Modification `json:"modifications"`
	Material      struct {
		Description string `json:"description"`
		Fingerprint string `json:"fingerprint"`
		Type        string `json:"type"`
		ID          int    `json:"id"`
	} `json:"material"`
	Changed bool `json:"changed"`
}

// Modification describes the commit/revision for the material which kicked off the build.
type Modification struct {
	EmailAddress string `json:"email_address"`
	ID           int    `json:"id"`
	ModifiedTime int    `json:"modified_time"`
	UserName     string `json:"user_name"`
	Comment      string `json:"comment"`
	Revision     string `json:"revision"`
}

// PipelineStatus describes whether a pipeline can be run or scheduled.
type PipelineStatus struct {
	Locked      bool `json:"locked"`
	Paused      bool `json:"paused"`
	Schedulable bool `json:"schedulable"`
}

// GetStatus returns a list of pipeline instanves describing the pipeline history.
func (pgs *PipelinesService) GetStatus(ctx context.Context, name string, offset int) (*PipelineStatus, *APIResponse, error) {
	ps := PipelineStatus{}
	_, resp, err := pgs.client.getAction(ctx, &APIClientRequest{
		Path:         fmt.Sprintf("pipelines/%s/status", name),
		ResponseBody: &ps,
	})

	return &ps, resp, err
}

// Pause allows a pipeline to handle new build events
func (pgs *PipelinesService) Pause(ctx context.Context, name string) (bool, *APIResponse, error) {
	return pgs.pipelineAction(ctx, name, "pause")
}

// Unpause allows a pipeline to handle new build events
func (pgs *PipelinesService) Unpause(ctx context.Context, name string) (bool, *APIResponse, error) {
	return pgs.pipelineAction(ctx, name, "unpause")
}

// ReleaseLock frees a pipeline to handle new build events
func (pgs *PipelinesService) ReleaseLock(ctx context.Context, name string) (bool, *APIResponse, error) {
	return pgs.pipelineAction(ctx, name, "releaseLock")
}

// GetInstance of a pipeline run.
func (pgs *PipelinesService) GetInstance(ctx context.Context, name string, offset int) (*PipelineInstance, *APIResponse, error) {
	stub := pgs.buildPaginatedStub("admin/pipelines/%s/instance", name, offset)

	pt := PipelineInstance{}
	_, resp, err := pgs.client.getAction(ctx, &APIClientRequest{
		Path:         stub,
		ResponseBody: &pt,
	})

	return &pt, resp, err
}

// GetHistory returns a list of pipeline instances describing the pipeline history.
func (pgs *PipelinesService) GetHistory(ctx context.Context, name string, offset int) (*PipelineHistory, *APIResponse, error) {
	stub := pgs.buildPaginatedStub("pipelines/%s/history", name, offset)

	pt := PipelineHistory{}
	_, resp, err := pgs.client.getAction(ctx, &APIClientRequest{
		Path:         stub,
		ResponseBody: &pt,
	})

	return &pt, resp, err
}

func (pgs *PipelinesService) pipelineAction(ctx context.Context, name string, action string) (bool, *APIResponse, error) {

	_, resp, err := pgs.client.postAction(ctx, &APIClientRequest{
		Path: fmt.Sprintf("pipelines/%s/%s", name, action),
		Headers: map[string]string{
			"Confirm": "true",
		},
	})

	return resp.HTTP.StatusCode == 200, resp, err
}

func (pgs *PipelinesService) buildPaginatedStub(format string, name string, offset int) string {
	stub := fmt.Sprintf(format, name)
	if offset > 0 {
		stub = fmt.Sprintf("%s/%d", stub, offset)
	}
	return stub
}
<commit_msg>Added ref for package materials<commit_after>package gocd

import (
	"context"
	"fmt"
)

// PipelinesService describes the HAL _link resource for the api response object for a pipelineconfig
type PipelinesService service

// PipelineRequest describes a pipeline request object
type PipelineRequest struct {
	Group    string    `json:"group"`
	Pipeline *Pipeline `json:"pipeline"`
}

// Pipeline describes a pipeline object
type Pipeline struct {
	Group                 string                 `json:"group,omitempty"`
	Links                 *HALLinks              `json:"_links,omitempty"`
	Name                  string                 `json:"name"`
	LabelTemplate         string                 `json:"label_template,omitempty"`
	EnablePipelineLocking bool                   `json:"enable_pipeline_locking,omitempty"`
	Template              string                 `json:"template,omitempty"`
	Origin                *PipelineConfigOrigin  `json:"origin,omitempty"`
	Parameters            []*Parameter           `json:"parameters,omitempty"`
	EnvironmentVariables  []*EnvironmentVariable `json:"environment_variables,omitempty"`
	Materials             []Material             `json:"materials,omitempty"`
	Label                 string                 `json:"label,omitempty"`
	Stages                []*Stage               `json:"stages,omitempty"`
	Version               string                 `json:"version,omitempty"`
	//TrackingTool          string                 `json:"tracking_tool"`
	//Timer                 string                 `json:"timer"`
}

// Parameter represents a key/value
type Parameter struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

// PipelineConfigOrigin describes where a pipeline config is being loaded from
type PipelineConfigOrigin struct {
	Type string `json:"type"`
	File string `json:"file"`
}

// Material describes an artifact dependency for a pipeline object.
type Material struct {
	Type        string             `json:"type"`
	Fingerprint string             `json:"fingerprint,omitempty"`
	Description string             `json:"description,omitempty"`
	Attributes  MaterialAttributes `json:"attributes"`
}

// MaterialAttributes describes a material type
type MaterialAttributes struct {
	URL             string          `json:"url,omitempty"`
	Destination     string          `json:"destination,omitempty"`
	Filter          *MaterialFilter `json:"filter,omitempty"`
	InvertFilter    bool            `json:"invert_filter"`
	Name            string          `json:"name,omitempty"`
	AutoUpdate      bool            `json:"auto_update,omitempty"`
	Branch          string          `json:"branch,omitempty"`
	SubmoduleFolder string          `json:"submodule_folder,omitempty"`
	ShallowClone    bool            `json:"shallow_clone,omitempty"`
	Pipeline        string          `json:"pipeline,omitempty"`
	Stage           string          `json:"stage,omitempty"`
	Ref string `json:"ref"`
}

// MaterialFilter describes which globs to ignore
type MaterialFilter struct {
	Ignore []string `json:"ignore"`
}

// PipelineHistory describes the history of runs for a pipeline
type PipelineHistory struct {
	Pipelines []*PipelineInstance `json:"pipelines"`
}

// PipelineInstance describes a single pipeline run
type PipelineInstance struct {
	BuildCause   BuildCause `json:"build_cause"`
	CanRun       bool       `json:"can_run"`
	Name         string     `json:"name"`
	NaturalOrder int        `json:"natural_order"`
	Comment      string     `json:"comment"`
	Stages       []*Stage   `json:"stages"`
}

// BuildCause describes the triggers which caused the build to start.
type BuildCause struct {
	Approver          string             `json:"approver,omitempty"`
	MaterialRevisions []MaterialRevision `json:"material_revisions"`
	TriggerForced     bool               `json:"trigger_forced"`
	TriggerMessage    string             `json:"trigger_message"`
}

// MaterialRevision describes the uniquely identifiable version for the material which was pulled for this build
type MaterialRevision struct {
	Modifications []Modification `json:"modifications"`
	Material      struct {
		Description string `json:"description"`
		Fingerprint string `json:"fingerprint"`
		Type        string `json:"type"`
		ID          int    `json:"id"`
	} `json:"material"`
	Changed bool `json:"changed"`
}

// Modification describes the commit/revision for the material which kicked off the build.
type Modification struct {
	EmailAddress string `json:"email_address"`
	ID           int    `json:"id"`
	ModifiedTime int    `json:"modified_time"`
	UserName     string `json:"user_name"`
	Comment      string `json:"comment"`
	Revision     string `json:"revision"`
}

// PipelineStatus describes whether a pipeline can be run or scheduled.
type PipelineStatus struct {
	Locked      bool `json:"locked"`
	Paused      bool `json:"paused"`
	Schedulable bool `json:"schedulable"`
}

// GetStatus returns a list of pipeline instanves describing the pipeline history.
func (pgs *PipelinesService) GetStatus(ctx context.Context, name string, offset int) (*PipelineStatus, *APIResponse, error) {
	ps := PipelineStatus{}
	_, resp, err := pgs.client.getAction(ctx, &APIClientRequest{
		Path:         fmt.Sprintf("pipelines/%s/status", name),
		ResponseBody: &ps,
	})

	return &ps, resp, err
}

// Pause allows a pipeline to handle new build events
func (pgs *PipelinesService) Pause(ctx context.Context, name string) (bool, *APIResponse, error) {
	return pgs.pipelineAction(ctx, name, "pause")
}

// Unpause allows a pipeline to handle new build events
func (pgs *PipelinesService) Unpause(ctx context.Context, name string) (bool, *APIResponse, error) {
	return pgs.pipelineAction(ctx, name, "unpause")
}

// ReleaseLock frees a pipeline to handle new build events
func (pgs *PipelinesService) ReleaseLock(ctx context.Context, name string) (bool, *APIResponse, error) {
	return pgs.pipelineAction(ctx, name, "releaseLock")
}

// GetInstance of a pipeline run.
func (pgs *PipelinesService) GetInstance(ctx context.Context, name string, offset int) (*PipelineInstance, *APIResponse, error) {
	stub := pgs.buildPaginatedStub("admin/pipelines/%s/instance", name, offset)

	pt := PipelineInstance{}
	_, resp, err := pgs.client.getAction(ctx, &APIClientRequest{
		Path:         stub,
		ResponseBody: &pt,
	})

	return &pt, resp, err
}

// GetHistory returns a list of pipeline instances describing the pipeline history.
func (pgs *PipelinesService) GetHistory(ctx context.Context, name string, offset int) (*PipelineHistory, *APIResponse, error) {
	stub := pgs.buildPaginatedStub("pipelines/%s/history", name, offset)

	pt := PipelineHistory{}
	_, resp, err := pgs.client.getAction(ctx, &APIClientRequest{
		Path:         stub,
		ResponseBody: &pt,
	})

	return &pt, resp, err
}

func (pgs *PipelinesService) pipelineAction(ctx context.Context, name string, action string) (bool, *APIResponse, error) {

	_, resp, err := pgs.client.postAction(ctx, &APIClientRequest{
		Path: fmt.Sprintf("pipelines/%s/%s", name, action),
		Headers: map[string]string{
			"Confirm": "true",
		},
	})

	return resp.HTTP.StatusCode == 200, resp, err
}

func (pgs *PipelinesService) buildPaginatedStub(format string, name string, offset int) string {
	stub := fmt.Sprintf(format, name)
	if offset > 0 {
		stub = fmt.Sprintf("%s/%d", stub, offset)
	}
	return stub
}
<|endoftext|>


<commit_before>package skydns

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"log"
	"path"
	"runtime"

	"github.com/k8sp/auto-install/bootstrapper/cmd"
	"github.com/k8sp/auto-install/config"
	"github.com/topicai/candy"
)

func serviceUnit(dist string, tmpl string, c *config.Cluster) string {
	t := template.New("")

	if len(tmpl) > 0 {
		t = template.Must(t.Parse(tmpl))
	} else {
		if dist == "centos" {
			t = template.Must(t.Parse(systemdDefaultTemplate))
		} else if dist == "ubuntu" {
			t = template.Must(t.Parse(upstartDefaultTemplate))
		}
	}

	var buf bytes.Buffer
	candy.Must(t.Execute(&buf, c))
	return buf.String()
}

const (
	systemdDefaultTemplate = `
[Unit]
Description=SkyDNS
After=network.target
Requires=network.target

[Service]
Type=simple
ExecStart=/usr/bin/skydns -machines={{.GetEtcdMachines}} -addr=0.0.0.0:53 -nameservers=8.8.8.8:53,8.8.4.4:53 -domain={{.DomainName}}.

[Install]
WantedBy=multi-user.target
`
	upstartDefaultTemplate = `
description "SkyDNS service"

start on runlevel [2345]
stop on runlevel [^2345]

respawn
respawn limit 20 3

script
echo $$ > /var/run/skydns.pid
exec /usr/bin/skydns -machines={{.GetEtcdMachines}} -addr=0.0.0.0:53 -nameservers=8.8.8.8:53,8.8.4.4:53 -domain={{.DomainName}}.
end script

pre-start script
end script

pre-stop script
    rm /var/run/skydns.pid
end script
`
)

func build() {
	installGo("")

	// Be careful, need antiGFW to download
	cmd.RunWithEnv(map[string]string{"GOPATH": "/tmp"},
		"/usr/local/go/bin/go", "get", "-u", "github.com/skynetservices/skydns")

	cmd.Run("/bin/cp", "-f", "/tmp/bin/skydns", "/usr/bin/")
}

func installGo(version string) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		log.Panicf("InstallGo must work with linux/amd64, but not %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	if len(version) == 0 {
		version = "1.6.3"
	}
	cmd.Run("curl", "-s", "-o", "/tmp/go.tar.gz",
		fmt.Sprintf("https://storage.googleapis.com/golang/go%s.linux-amd64.tar.gz", version))

	cmd.Run("tar", "-C", "/usr/local", "-xzf", "/tmp/go.tar.gz")
}

// Download SkyDNS bianary file from github.
// requires that curl have been installed.
func getSkyDNSFile() {
	skydnsfile := path.Join("/usr/bin", "skydns")
	cmd.Run("curl", "-o", skydnsfile, "https://raw.githubusercontent.com/pineking/skydns-binary/master/skydns")
	cmd.Run("chmod", "755", skydnsfile)
}

// Install downloads and builds SkyDNS into /usr/bin/skydns.  It then
// creates a systemd service unit for CentOS.
func Install(tmpl string, c *config.Cluster) {
	build()

	switch dist := config.LinuxDistro(); dist {
	case "centos":
		candy.WithCreated("/etc/systemd/system/skydns.service", func(w io.Writer) {
			_, e := fmt.Fprint(w, serviceUnit(dist, tmpl, c))
			candy.Must(e)
		})

		cmd.Run("systemctl", "enable", "skydns")
		// Due to a bug of CentOS, systemctl cannot run in
		// Docker containers.  Discussions and the explanation
		// of this bug is at
		// https://github.com/docker/docker/issues/7459.  The
		// current fix
		// https://github.com/docker-library/docs/tree/master/centos#systemd-integration
		// is too complex that I don't want to implement.  So
		// I call Try here.
		cmd.Try("systemctl", "restart", "skydns")
	case "ubuntu":
		candy.WithCreated("/etc/init/skydns.conf", func(w io.Writer) {
			_, e := fmt.Fprint(w, serviceUnit(dist, tmpl, c))
			candy.Must(e)
		})

		cmd.Run("service", "skydns", "restart")
	default:
		log.Panicf("Unsupported OS: %s", dist)
	}
}
<commit_msg>Add systemctl daemon-reload<commit_after>package skydns

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"log"
	"path"
	"runtime"

	"github.com/k8sp/auto-install/bootstrapper/cmd"
	"github.com/k8sp/auto-install/config"
	"github.com/topicai/candy"
)

func serviceUnit(dist string, tmpl string, c *config.Cluster) string {
	t := template.New("")

	if len(tmpl) > 0 {
		t = template.Must(t.Parse(tmpl))
	} else {
		if dist == "centos" {
			t = template.Must(t.Parse(systemdDefaultTemplate))
		} else if dist == "ubuntu" {
			t = template.Must(t.Parse(upstartDefaultTemplate))
		}
	}

	var buf bytes.Buffer
	candy.Must(t.Execute(&buf, c))
	return buf.String()
}

const (
	systemdDefaultTemplate = `
[Unit]
Description=SkyDNS
After=network.target
Requires=network.target

[Service]
Type=simple
ExecStart=/usr/bin/skydns -machines={{.GetEtcdMachines}} -addr=0.0.0.0:53 -nameservers=8.8.8.8:53,8.8.4.4:53 -domain={{.DomainName}}.

[Install]
WantedBy=multi-user.target
`
	upstartDefaultTemplate = `
description "SkyDNS service"

start on runlevel [2345]
stop on runlevel [^2345]

respawn
respawn limit 20 3

script
echo $$ > /var/run/skydns.pid
exec /usr/bin/skydns -machines={{.GetEtcdMachines}} -addr=0.0.0.0:53 -nameservers=8.8.8.8:53,8.8.4.4:53 -domain={{.DomainName}}.
end script

pre-start script
end script

pre-stop script
    rm /var/run/skydns.pid
end script
`
)

func build() {
	installGo("")

	// Be careful, need antiGFW to download
	cmd.RunWithEnv(map[string]string{"GOPATH": "/tmp"},
		"/usr/local/go/bin/go", "get", "-u", "github.com/skynetservices/skydns")

	cmd.Run("/bin/cp", "-f", "/tmp/bin/skydns", "/usr/bin/")
}

func installGo(version string) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		log.Panicf("InstallGo must work with linux/amd64, but not %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	if len(version) == 0 {
		version = "1.6.3"
	}
	cmd.Run("curl", "-s", "-o", "/tmp/go.tar.gz",
		fmt.Sprintf("https://storage.googleapis.com/golang/go%s.linux-amd64.tar.gz", version))

	cmd.Run("tar", "-C", "/usr/local", "-xzf", "/tmp/go.tar.gz")
}

// Download SkyDNS bianary file from github.
// requires that curl have been installed.
func getSkyDNSFile() {
	skydnsfile := path.Join("/usr/bin", "skydns")
	cmd.Run("curl", "-o", skydnsfile, "https://raw.githubusercontent.com/pineking/skydns-binary/master/skydns")
	cmd.Run("chmod", "755", skydnsfile)
}

// Install downloads and builds SkyDNS into /usr/bin/skydns.  It then
// creates a systemd service unit for CentOS.
func Install(tmpl string, c *config.Cluster) {
	build()

	switch dist := config.LinuxDistro(); dist {
	case "centos":
		candy.WithCreated("/etc/systemd/system/skydns.service", func(w io.Writer) {
			_, e := fmt.Fprint(w, serviceUnit(dist, tmpl, c))
			candy.Must(e)
		})

		cmd.Run("systemctl", "enable", "skydns")
		cmd.Run("systemctl", "daemon-reload")
		// Due to a bug of CentOS, systemctl cannot run in
		// Docker containers.  Discussions and the explanation
		// of this bug is at
		// https://github.com/docker/docker/issues/7459.  The
		// current fix
		// https://github.com/docker-library/docs/tree/master/centos#systemd-integration
		// is too complex that I don't want to implement.  So
		// I call Try here.
		cmd.Try("systemctl", "restart", "skydns")
	case "ubuntu":
		candy.WithCreated("/etc/init/skydns.conf", func(w io.Writer) {
			_, e := fmt.Fprint(w, serviceUnit(dist, tmpl, c))
			candy.Must(e)
		})

		cmd.Run("service", "skydns", "restart")
	default:
		log.Panicf("Unsupported OS: %s", dist)
	}
}
<|endoftext|>


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

// Package google provides support for making
// OAuth2 authorized and authenticated HTTP requests
// to Google APIs. It supports Web server, client-side,
// service accounts, Google Compute Engine service accounts,
// and Google App Engine service accounts authorization
// and authentications flows:
//
// For more information, please read
// https://developers.google.com/accounts/docs/OAuth2.
package google // import "golang.org/x/oauth2/google"

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/jwt"
)

// TODO(bradfitz,jbd): import "google.golang.org/cloud/compute/metadata" instead of
// the metaClient and metadata.google.internal stuff below.

// Endpoint is Google's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
	AuthURL:  "https://accounts.google.com/o/oauth2/auth",
	TokenURL: "https://accounts.google.com/o/oauth2/token",
}

// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"

// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
// the credentials that authorize and authenticate the requests.
// Create a service account on "Credentials" page under "APIs & Auth" for your
// project at https://console.developers.google.com to download a JSON key file.
func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*jwt.Config, error) {
	var key struct {
		Email      string `json:"client_email"`
		PrivateKey string `json:"private_key"`
	}
	if err := json.Unmarshal(jsonKey, &key); err != nil {
		return nil, err
	}
	return &jwt.Config{
		Email:      key.Email,
		PrivateKey: []byte(key.PrivateKey),
		Scopes:     scope,
		TokenURL:   JWTTokenURL,
	}, nil
}

type metaTokenRespBody struct {
	AccessToken string        `json:"access_token"`
	ExpiresIn   time.Duration `json:"expires_in"`
	TokenType   string        `json:"token_type"`
}

// ComputeTokenSource returns a token source that fetches access tokens
// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
// this token source if your program is running on a GCE instance.
// If no account is specified, "default" is used.
// Further information about retrieving access tokens from the GCE metadata
// server can be found at https://cloud.google.com/compute/docs/authentication.
func ComputeTokenSource(account string) oauth2.TokenSource {
	return oauth2.ReuseTokenSource(nil, &computeSource{account: account})
}

type computeSource struct {
	account string
}

var metaClient = &http.Client{
	Transport: &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   750 * time.Millisecond,
			KeepAlive: 30 * time.Second,
		}).Dial,
		ResponseHeaderTimeout: 750 * time.Millisecond,
	},
}

func (cs *computeSource) Token() (*oauth2.Token, error) {
	acct := cs.account
	if acct == "" {
		acct = "default"
	}
	u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + acct + "/token"
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Google-Metadata-Request", "True")
	resp, err := metaClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, fmt.Errorf("oauth2: can't retrieve a token from metadata server, status code: %d", resp.StatusCode)
	}
	var tokenResp metaTokenRespBody
	err = json.NewDecoder(resp.Body).Decode(&tokenResp)
	if err != nil {
		return nil, err
	}
	return &oauth2.Token{
		AccessToken: tokenResp.AccessToken,
		TokenType:   tokenResp.TokenType,
		Expiry:      time.Now().Add(tokenResp.ExpiresIn * time.Second),
	}, nil
}
<commit_msg>oauth2/google: use the metadata package, cleanups<commit_after>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package google provides support for making
// OAuth2 authorized and authenticated HTTP requests
// to Google APIs. It supports Web server, client-side,
// service accounts, Google Compute Engine service accounts,
// and Google App Engine service accounts authorization
// and authentications flows:
//
// For more information, please read
// https://developers.google.com/accounts/docs/OAuth2.
package google // import "golang.org/x/oauth2/google"

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/cloud/compute/metadata"
)

// TODO(bradfitz,jbd): import "google.golang.org/cloud/compute/metadata" instead of
// the metaClient and metadata.google.internal stuff below.

// Endpoint is Google's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
	AuthURL:  "https://accounts.google.com/o/oauth2/auth",
	TokenURL: "https://accounts.google.com/o/oauth2/token",
}

// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"

// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
// the credentials that authorize and authenticate the requests.
// Create a service account on "Credentials" page under "APIs & Auth" for your
// project at https://console.developers.google.com to download a JSON key file.
func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*jwt.Config, error) {
	var key struct {
		Email      string `json:"client_email"`
		PrivateKey string `json:"private_key"`
	}
	if err := json.Unmarshal(jsonKey, &key); err != nil {
		return nil, err
	}
	return &jwt.Config{
		Email:      key.Email,
		PrivateKey: []byte(key.PrivateKey),
		Scopes:     scope,
		TokenURL:   JWTTokenURL,
	}, nil
}

// ComputeTokenSource returns a token source that fetches access tokens
// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
// this token source if your program is running on a GCE instance.
// If no account is specified, "default" is used.
// Further information about retrieving access tokens from the GCE metadata
// server can be found at https://cloud.google.com/compute/docs/authentication.
func ComputeTokenSource(account string) oauth2.TokenSource {
	return oauth2.ReuseTokenSource(nil, computeSource{account: account})
}

type computeSource struct {
	account string
}

func (cs computeSource) Token() (*oauth2.Token, error) {
	if !metadata.OnGCE() {
		return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
	}
	acct := cs.account
	if acct == "" {
		acct = "default"
	}
	tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token")
	if err != nil {
		return nil, err
	}
	var res struct {
		AccessToken  string `json:"access_token"`
		ExpiresInSec int    `json:"expires_in"`
		TokenType    string `json:"token_type"`
	}
	err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
	if err != nil {
		return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
	}
	if res.ExpiresInSec == 0 || res.AccessToken == "" {
		return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
	}
	return &oauth2.Token{
		AccessToken: res.AccessToken,
		TokenType:   res.TokenType,
		Expiry:      time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
	}, nil
}
<|endoftext|>


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

// Package storageclient defines a wrapper around different ways to download files from GCS.
package storageclient

import (
	"context"
	"fmt"
	"io/ioutil"

	"cloud.google.com/go/storage" /* copybara-comment: storage */
)

// ThirdPartyClient is a client to communicate with GCS from outside of google3.
type ThirdPartyClient struct {
}

func (c *ThirdPartyClient) ReadBytes(ctx context.Context, bucket string, filename string) ([]byte, error) {
	client, err := storage.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("unable to create GCS storage client: %v", err)
	}
	defer client.Close()
	reader, err := client.Bucket(bucket).Object(filename).NewReader(ctx)
	if err != nil {
		return nil, fmt.Errorf("unable to read the file %s/%s from GCS: %v", bucket, filename, err)
	}
	defer reader.Close()
	raw, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, fmt.Errorf("unable to download the file %s/%s from GCS: %v", bucket, filename, err)
	}
	return raw, nil
}
<commit_msg>internal change<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package storageclient defines a wrapper around different ways to download files from GCS.
package storageclient

import (
	"context"
	"fmt"
	"io/ioutil"

	"cloud.google.com/go/storage" /* copybara-comment: storage */
	"google.golang.org/api/option" /* copybara-comment: option */

	goauth2 "golang.org/x/oauth2/google" /* copybara-comment: google */
)

// ThirdPartyClient is a client to communicate with GCS from outside of google3.
type ThirdPartyClient struct {
}

func (c *ThirdPartyClient) ReadBytes(ctx context.Context, bucket string, filename string) ([]byte, error) {
	ts, err := goauth2.DefaultTokenSource(ctx)
	if err != nil {
		return nil, fmt.Errorf("unable to create token source: %v", err)
	}
	client, err := storage.NewClient(ctx, option.WithTokenSource(ts))
	if err != nil {
		return nil, fmt.Errorf("unable to create GCS storage client: %v", err)
	}
	defer client.Close()
	reader, err := client.Bucket(bucket).Object(filename).NewReader(ctx)
	if err != nil {
		return nil, fmt.Errorf("unable to read the file %s/%s from GCS: %v", bucket, filename, err)
	}
	defer reader.Close()
	raw, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, fmt.Errorf("unable to download the file %s/%s from GCS: %v", bucket, filename, err)
	}
	return raw, nil
}
<|endoftext|>