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

// Package version provides build version information.
package version

import (
	"fmt"
	"runtime"
	"strings"
)

// The following fields are populated at build time using -ldflags -X.
// Note that DATE is omitted for reproducible builds
var (
	buildVersion     = "unknown"
	buildGitRevision = "unknown"
	buildStatus      = "unknown"
	buildTag         = "unknown"
	buildHub         = "unknown"
)

// BuildInfo describes version information about the binary build.
type BuildInfo struct {
	Version       string `json:"version"`
	GitRevision   string `json:"revision"`
	GolangVersion string `json:"golang_version"`
	BuildStatus   string `json:"status"`
	GitTag        string `json:"tag"`
}

// ServerInfo contains the version for a single control plane component
type ServerInfo struct {
	Component string
	Info      BuildInfo
}

// MeshInfo contains the versions for all Istio control plane components
type MeshInfo []ServerInfo

// ProxyInfo contains the version for a single data plane component
type ProxyInfo struct {
	ID           string
	IstioVersion string
}

// DockerBuildInfo contains and exposes Hub: buildHub and Tag: buildVersion
type DockerBuildInfo struct {
	Hub string
	Tag string
}

// NewBuildInfoFromOldString creates a BuildInfo struct based on the output
// of previous Istio components '-- version' output
func NewBuildInfoFromOldString(oldOutput string) (BuildInfo, error) {
	res := BuildInfo{}

	lines := strings.Split(oldOutput, "\n")
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}
		fields := strings.SplitN(line, ":", 2)
		if fields != nil {
			if len(fields) != 2 {
				return BuildInfo{}, fmt.Errorf("invalid BuildInfo input, field '%s' is not valid", fields[0])
			}
			value := strings.TrimSpace(fields[1])
			switch fields[0] {
			case "Version":
				res.Version = value
			case "GitRevision":
				res.GitRevision = value
			case "GolangVersion":
				res.GolangVersion = value
			case "BuildStatus":
				res.BuildStatus = value
			case "GitTag":
				res.GitTag = value
			default:
				// Skip unknown fields, as older versions may report other fields
				continue
			}
		}
	}

	return res, nil
}

var (
	// Info exports the build version information.
	Info       BuildInfo
	DockerInfo DockerBuildInfo
)

// String produces a single-line version info
//
// This looks like:
//
// ```
// u<version>-<git revision>-<build status>
// ```
func (b BuildInfo) String() string {
	return fmt.Sprintf("%v-%v-%v",
		b.Version,
		b.GitRevision,
		b.BuildStatus)
}

// LongForm returns a dump of the Info struct
// This looks like:
func (b BuildInfo) LongForm() string {
	return fmt.Sprintf("%#v", b)
}

func init() {
	Info = BuildInfo{
		Version:       buildVersion,
		GitRevision:   buildGitRevision,
		GolangVersion: runtime.Version(),
		BuildStatus:   buildStatus,
		GitTag:        buildTag,
	}

	DockerInfo = DockerBuildInfo{
		Hub: buildHub,
		Tag: buildVersion,
	}
}
<commit_msg>add build arch to build info (#643)<commit_after>// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package version provides build version information.
package version

import (
	"fmt"
	"runtime"
	"strings"
)

// The following fields are populated at build time using -ldflags -X.
// Note that DATE is omitted for reproducible builds
var (
	buildVersion     = "unknown"
	buildGitRevision = "unknown"
	buildStatus      = "unknown"
	buildTag         = "unknown"
	buildHub         = "unknown"
	buildArch        = "unknown"
)

// BuildInfo describes version information about the binary build.
type BuildInfo struct {
	Version       string `json:"version"`
	GitRevision   string `json:"revision"`
	GolangVersion string `json:"golang_version"`
	BuildStatus   string `json:"status"`
	GitTag        string `json:"tag"`
}

// ServerInfo contains the version for a single control plane component
type ServerInfo struct {
	Component string
	Info      BuildInfo
}

// MeshInfo contains the versions for all Istio control plane components
type MeshInfo []ServerInfo

// ProxyInfo contains the version for a single data plane component
type ProxyInfo struct {
	ID           string
	IstioVersion string
}

// DockerBuildInfo contains and exposes Hub: buildHub, Tag: buildVersion and Arch: buildArch
type DockerBuildInfo struct {
	Hub  string
	Tag  string
	Arch string
}

// NewBuildInfoFromOldString creates a BuildInfo struct based on the output
// of previous Istio components '-- version' output
func NewBuildInfoFromOldString(oldOutput string) (BuildInfo, error) {
	res := BuildInfo{}

	lines := strings.Split(oldOutput, "\n")
	for _, line := range lines {
		if strings.TrimSpace(line) == "" {
			continue
		}
		fields := strings.SplitN(line, ":", 2)
		if fields != nil {
			if len(fields) != 2 {
				return BuildInfo{}, fmt.Errorf("invalid BuildInfo input, field '%s' is not valid", fields[0])
			}
			value := strings.TrimSpace(fields[1])
			switch fields[0] {
			case "Version":
				res.Version = value
			case "GitRevision":
				res.GitRevision = value
			case "GolangVersion":
				res.GolangVersion = value
			case "BuildStatus":
				res.BuildStatus = value
			case "GitTag":
				res.GitTag = value
			default:
				// Skip unknown fields, as older versions may report other fields
				continue
			}
		}
	}

	return res, nil
}

var (
	// Info exports the build version information.
	Info       BuildInfo
	DockerInfo DockerBuildInfo
)

// String produces a single-line version info
//
// This looks like:
//
// ```
// u<version>-<git revision>-<build status>
// ```
func (b BuildInfo) String() string {
	return fmt.Sprintf("%v-%v-%v",
		b.Version,
		b.GitRevision,
		b.BuildStatus)
}

// LongForm returns a dump of the Info struct
// This looks like:
func (b BuildInfo) LongForm() string {
	return fmt.Sprintf("%#v", b)
}

func init() {
	Info = BuildInfo{
		Version:       buildVersion,
		GitRevision:   buildGitRevision,
		GolangVersion: runtime.Version(),
		BuildStatus:   buildStatus,
		GitTag:        buildTag,
	}

	DockerInfo = DockerBuildInfo{
		Hub:  buildHub,
		Tag:  buildVersion,
		Arch: buildArch,
	}
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"
	"strings"
)

var (
	// Version should be updated by hand at each release
	Version = "0.7.0"

	// GitCommit will be overwritten automatically by the build system
	GitCommit = "HEAD"
)

// FullVersion formats the version to be printed
func FullVersion() string {
	return fmt.Sprintf("%s, build %s", Version, GitCommit)
}

// RC checks if the Machine version is a release candidate or not
func RC() bool {
	return strings.Contains(Version, "rc")
}
<commit_msg>Bump version to 0.8.2<commit_after>package version

import (
	"fmt"
	"strings"
)

var (
	// Version should be updated by hand at each release
	Version = "0.8.2"

	// GitCommit will be overwritten automatically by the build system
	GitCommit = "HEAD"
)

// FullVersion formats the version to be printed
func FullVersion() string {
	return fmt.Sprintf("%s, build %s", Version, GitCommit)
}

// RC checks if the Machine version is a release candidate or not
func RC() bool {
	return strings.Contains(Version, "rc")
}
<|endoftext|>


<commit_before>package version

var (
	// VERSION should be updated by hand at each release
	VERSION = "0.3.0"

	// GITCOMMIT will be overwritten automatically by the build system
	GITCOMMIT = "HEAD"
)
<commit_msg>Bump version to 0.4.0<commit_after>package version

var (
	// VERSION should be updated by hand at each release
	VERSION = "0.4.0"

	// GITCOMMIT will be overwritten automatically by the build system
	GITCOMMIT = "HEAD"
)
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
)

// CurrentGaugeVersion represents the current version of Gauge
var CurrentGaugeVersion = &Version{0, 9, 9}

// BuildMetadata represents build information of current release (e.g, nightly build information)
var BuildMetadata = ""
var CommitHash = ""

type Version struct {
	Major int
	Minor int
	Patch int
}

type VersionSupport struct {
	Minimum string
	Maximum string
}

func ParseVersion(versionText string) (*Version, error) {
	splits := strings.Split(versionText, ".")
	if len(splits) != 3 {
		return nil, fmt.Errorf("Incorrect Version format. Version should be in the form 1.5.7")
	}
	Major, err := strconv.Atoi(splits[0])
	if err != nil {
		return nil, VersionError("major", splits[0], err)
	}
	Minor, err := strconv.Atoi(splits[1])
	if err != nil {
		return nil, VersionError("minor", splits[1], err)
	}
	Patch, err := strconv.Atoi(splits[2])
	if err != nil {
		return nil, VersionError("patch", splits[2], err)
	}

	return &Version{Major, Minor, Patch}, nil
}

func VersionError(level, text string, err error) error {
	return fmt.Errorf("Error parsing %s Version %s to integer. %s", level, text, err.Error())
}

func (Version *Version) IsBetween(lower *Version, greater *Version) bool {
	return Version.IsGreaterThanEqualTo(lower) && Version.IsLesserThanEqualTo(greater)
}

func (Version *Version) IsLesserThan(version1 *Version) bool {
	return CompareVersions(Version, version1, LesserThanFunc)
}

func (Version *Version) IsGreaterThan(version1 *Version) bool {
	return CompareVersions(Version, version1, GreaterThanFunc)
}

func (Version *Version) IsLesserThanEqualTo(version1 *Version) bool {
	return Version.IsLesserThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsGreaterThanEqualTo(version1 *Version) bool {
	return Version.IsGreaterThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsEqualTo(version1 *Version) bool {
	return IsEqual(Version.Major, version1.Major) && IsEqual(Version.Minor, version1.Minor) && IsEqual(Version.Patch, version1.Patch)
}

func CompareVersions(first *Version, second *Version, compareFunc func(int, int) bool) bool {
	if compareFunc(first.Major, second.Major) {
		return true
	} else if IsEqual(first.Major, second.Major) {
		if compareFunc(first.Minor, second.Minor) {
			return true
		} else if IsEqual(first.Minor, second.Minor) {
			if compareFunc(first.Patch, second.Patch) {
				return true
			}
			return false
		}
	}
	return false
}

func LesserThanFunc(first, second int) bool {
	return first < second
}

func GreaterThanFunc(first, second int) bool {
	return first > second
}

func IsEqual(first, second int) bool {
	return first == second
}

func (Version *Version) String() string {
	return fmt.Sprintf("%d.%d.%d", Version.Major, Version.Minor, Version.Patch)
}

// FullVersion returns the CurrentGaugeVersion including build metadata.
func FullVersion() string {
	var metadata string
	if BuildMetadata != "" {
		metadata = fmt.Sprintf(".%s", BuildMetadata)
	}
	return fmt.Sprintf("%s%s", CurrentGaugeVersion.String(), metadata)
}

func GetCommitHash() string {
	var commitHash string
	if CommitHash != "" {
		commitHash = fmt.Sprintf("%s", CommitHash)
	}
	return fmt.Sprintf("%s", commitHash)
}

type byDecreasingVersion []*Version

func (a byDecreasingVersion) Len() int      { return len(a) }
func (a byDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDecreasingVersion) Less(i, j int) bool {
	return a[i].IsGreaterThan(a[j])
}

func GetLatestVersion(versions []*Version) *Version {
	sort.Sort(byDecreasingVersion(versions))
	return versions[0]
}

func CheckCompatibility(currentVersion *Version, versionSupport *VersionSupport) error {
	minSupportVersion, err := ParseVersion(versionSupport.Minimum)
	if err != nil {
		return fmt.Errorf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err.Error())
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := ParseVersion(versionSupport.Maximum)
		if err != nil {
			return fmt.Errorf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err.Error())
		}
		if currentVersion.IsBetween(minSupportVersion, maxSupportVersion) {
			return nil
		}
		return fmt.Errorf("Version %s is not between %s and %s", currentVersion, minSupportVersion, maxSupportVersion)
	}

	if minSupportVersion.IsLesserThanEqualTo(currentVersion) {
		return nil
	}
	return fmt.Errorf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, currentVersion)
}
<commit_msg>Bumping version to 1.0.0<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
)

// CurrentGaugeVersion represents the current version of Gauge
var CurrentGaugeVersion = &Version{1, 0, 0}

// BuildMetadata represents build information of current release (e.g, nightly build information)
var BuildMetadata = ""
var CommitHash = ""

type Version struct {
	Major int
	Minor int
	Patch int
}

type VersionSupport struct {
	Minimum string
	Maximum string
}

func ParseVersion(versionText string) (*Version, error) {
	splits := strings.Split(versionText, ".")
	if len(splits) != 3 {
		return nil, fmt.Errorf("Incorrect Version format. Version should be in the form 1.5.7")
	}
	Major, err := strconv.Atoi(splits[0])
	if err != nil {
		return nil, VersionError("major", splits[0], err)
	}
	Minor, err := strconv.Atoi(splits[1])
	if err != nil {
		return nil, VersionError("minor", splits[1], err)
	}
	Patch, err := strconv.Atoi(splits[2])
	if err != nil {
		return nil, VersionError("patch", splits[2], err)
	}

	return &Version{Major, Minor, Patch}, nil
}

func VersionError(level, text string, err error) error {
	return fmt.Errorf("Error parsing %s Version %s to integer. %s", level, text, err.Error())
}

func (Version *Version) IsBetween(lower *Version, greater *Version) bool {
	return Version.IsGreaterThanEqualTo(lower) && Version.IsLesserThanEqualTo(greater)
}

func (Version *Version) IsLesserThan(version1 *Version) bool {
	return CompareVersions(Version, version1, LesserThanFunc)
}

func (Version *Version) IsGreaterThan(version1 *Version) bool {
	return CompareVersions(Version, version1, GreaterThanFunc)
}

func (Version *Version) IsLesserThanEqualTo(version1 *Version) bool {
	return Version.IsLesserThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsGreaterThanEqualTo(version1 *Version) bool {
	return Version.IsGreaterThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsEqualTo(version1 *Version) bool {
	return IsEqual(Version.Major, version1.Major) && IsEqual(Version.Minor, version1.Minor) && IsEqual(Version.Patch, version1.Patch)
}

func CompareVersions(first *Version, second *Version, compareFunc func(int, int) bool) bool {
	if compareFunc(first.Major, second.Major) {
		return true
	} else if IsEqual(first.Major, second.Major) {
		if compareFunc(first.Minor, second.Minor) {
			return true
		} else if IsEqual(first.Minor, second.Minor) {
			if compareFunc(first.Patch, second.Patch) {
				return true
			}
			return false
		}
	}
	return false
}

func LesserThanFunc(first, second int) bool {
	return first < second
}

func GreaterThanFunc(first, second int) bool {
	return first > second
}

func IsEqual(first, second int) bool {
	return first == second
}

func (Version *Version) String() string {
	return fmt.Sprintf("%d.%d.%d", Version.Major, Version.Minor, Version.Patch)
}

// FullVersion returns the CurrentGaugeVersion including build metadata.
func FullVersion() string {
	var metadata string
	if BuildMetadata != "" {
		metadata = fmt.Sprintf(".%s", BuildMetadata)
	}
	return fmt.Sprintf("%s%s", CurrentGaugeVersion.String(), metadata)
}

func GetCommitHash() string {
	var commitHash string
	if CommitHash != "" {
		commitHash = fmt.Sprintf("%s", CommitHash)
	}
	return fmt.Sprintf("%s", commitHash)
}

type byDecreasingVersion []*Version

func (a byDecreasingVersion) Len() int      { return len(a) }
func (a byDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDecreasingVersion) Less(i, j int) bool {
	return a[i].IsGreaterThan(a[j])
}

func GetLatestVersion(versions []*Version) *Version {
	sort.Sort(byDecreasingVersion(versions))
	return versions[0]
}

func CheckCompatibility(currentVersion *Version, versionSupport *VersionSupport) error {
	minSupportVersion, err := ParseVersion(versionSupport.Minimum)
	if err != nil {
		return fmt.Errorf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err.Error())
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := ParseVersion(versionSupport.Maximum)
		if err != nil {
			return fmt.Errorf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err.Error())
		}
		if currentVersion.IsBetween(minSupportVersion, maxSupportVersion) {
			return nil
		}
		return fmt.Errorf("Version %s is not between %s and %s", currentVersion, minSupportVersion, maxSupportVersion)
	}

	if minSupportVersion.IsLesserThanEqualTo(currentVersion) {
		return nil
	}
	return fmt.Errorf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, currentVersion)
}
<|endoftext|>


<commit_before>package version

// Populated by goreleaser.
var (
	Version   = "v0.7.x-dev"
	GitHash   = "dev snapshot"
	BuildDate string
)
<commit_msg>bump version to 0.8<commit_after>package version

// Populated by goreleaser.
var (
	Version   = "v0.8.x-dev"
	GitHash   = "dev snapshot"
	BuildDate string
)
<|endoftext|>


<commit_before>package version

import (
	"bytes"
	"fmt"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string
var GitDescribe string

// The main version number that is being run at the moment.
const Version = "0.3.1"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// VersionInfo
type VersionInfo struct {
	Revision          string
	Version           string
	VersionPrerelease string
}

func GetVersion() *VersionInfo {
	ver := Version
	rel := VersionPrerelease
	if GitDescribe != "" {
		ver = GitDescribe
	}
	if GitDescribe == "" && rel == "" && VersionPrerelease != "" {
		rel = "dev"
	}

	return &VersionInfo{
		Revision:          GitCommit,
		Version:           ver,
		VersionPrerelease: rel,
	}
}

func (c *VersionInfo) String() string {
	var versionString bytes.Buffer

	fmt.Fprintf(&versionString, "Vault v%s", c.Version)
	if c.VersionPrerelease != "" {
		fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease)

		if c.Revision != "" {
			fmt.Fprintf(&versionString, " (%s)", c.Revision)
		}
	}

	return versionString.String()
}
<commit_msg>Bump version to 0.4-dev instead of 0.3.1-dev<commit_after>package version

import (
	"bytes"
	"fmt"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string
var GitDescribe string

// The main version number that is being run at the moment.
const Version = "0.4.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

// VersionInfo
type VersionInfo struct {
	Revision          string
	Version           string
	VersionPrerelease string
}

func GetVersion() *VersionInfo {
	ver := Version
	rel := VersionPrerelease
	if GitDescribe != "" {
		ver = GitDescribe
	}
	if GitDescribe == "" && rel == "" && VersionPrerelease != "" {
		rel = "dev"
	}

	return &VersionInfo{
		Revision:          GitCommit,
		Version:           ver,
		VersionPrerelease: rel,
	}
}

func (c *VersionInfo) String() string {
	var versionString bytes.Buffer

	fmt.Fprintf(&versionString, "Vault v%s", c.Version)
	if c.VersionPrerelease != "" {
		fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease)

		if c.Revision != "" {
			fmt.Fprintf(&versionString, " (%s)", c.Revision)
		}
	}

	return versionString.String()
}
<|endoftext|>


<commit_before>package EthereumAPI

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

var (
	/*
		C++: http://localhost:8545
		Go: http://localhost:8545
		Py: http://localhost:4000
	*/

	server = "localhost:8545"
)

//https://github.com/ethereum/wiki/wiki/JSON-RPC

func Call(method string, params interface{}) (*JSON2Response, error) {
	j := NewJSON2RequestBlank()
	j.Method = method
	j.Params = params
	j.ID = 1

	postGet := "POST"

	address := fmt.Sprintf("http://%s/", server)

	data, err := j.JSONString()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(postGet, address, strings.NewReader(data))
	if err != nil {
		return nil, err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	jResp := new(JSON2Response)

	err = json.Unmarshal(body, jResp)
	if err != nil {
		return nil, err
	}

	return jResp, nil
}
<commit_msg>Added SetServer.<commit_after>package EthereumAPI

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

var (
	/*
		C++: http://localhost:8545
		Go: http://localhost:8545
		Py: http://localhost:4000
	*/

	server = "localhost:8545"
)

//https://github.com/ethereum/wiki/wiki/JSON-RPC

func SetServer(newServer string) {
	server = newServer
}

func Call(method string, params interface{}) (*JSON2Response, error) {
	j := NewJSON2RequestBlank()
	j.Method = method
	j.Params = params
	j.ID = 1

	postGet := "POST"

	address := fmt.Sprintf("http://%s/", server)

	data, err := j.JSONString()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(postGet, address, strings.NewReader(data))
	if err != nil {
		return nil, err
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	jResp := new(JSON2Response)

	err = json.Unmarshal(body, jResp)
	if err != nil {
		return nil, err
	}

	return jResp, nil
}
<|endoftext|>


<commit_before>package dependency

import (
	"reflect"
	"testing"
)

func TestCatalogNodeFetch(t *testing.T) {
	clients, consul := testConsulServer(t)
	defer consul.Stop()

	// AddService does not let me specify an ID or a port.
	consul.AddService("z", "passing", []string{"baz"})
	consul.AddService("a", "critical", []string{"foo", "bar"})

	dep := &CatalogNode{}
	result, _, err := dep.Fetch(clients, nil)
	if err != nil {
		t.Fatal(err)
	}

	typed, ok := result.(*NodeDetail)
	if !ok {
		t.Fatal("could not convert result to *NodeDetail")
	}

	if typed == nil {
		t.Fatal("Not expecting to get nil for a known node")
	}

	if typed.Node.Node != consul.Config.NodeName {
		t.Errorf("expected %q to be %q", typed.Node.Node, consul.Config.NodeName)
	}
	if typed.Node.Address != "127.0.0.1" {
		t.Errorf("expected %q to be %q", typed.Node.Address, "127.0.0.1")
	}
	if len(typed.Services) != 3 {
		t.Fatalf("expected 3 services got %d", len(typed.Services))
	}

	var s *NodeService

	s = typed.Services[0]
	if s.ID != "a" {
		t.Errorf("expecting %q to be \"a\"", s.ID)
	}
	if s.Service != "a" {
		t.Errorf("expecting %q to be \"a\"", s.Service)
	}
	if s.Port != 0 {
		t.Errorf("expecting %d to be 0", s.Port)
	}
	if len(s.Tags) != 2 {
		t.Fatalf("expecting %d to be 2", len(s.Tags))
	}
	if s.Tags[0] != "bar" {
		t.Errorf("expecting %q to be \"bar\"", s.Tags[0])
	}
	if s.Tags[1] != "foo" {
		t.Errorf("expecting %q to be \"foo\"", s.Tags[1])
	}

	s = typed.Services[1]
	if s.ID != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.ID)
	}
	if s.Service != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.Service)
	}
	if s.Port != consul.Config.Ports.Server {
		t.Errorf("expecting %d to be %d", s.Port, consul.Config.Ports.Server)
	}
	if len(s.Tags) != 0 {
		t.Fatalf("expecting %d to be 0", len(s.Tags))
	}

	s = typed.Services[2]
	if s.ID != "z" {
		t.Errorf("expecting %q to be \"z\"", s.ID)
	}
	if s.Service != "z" {
		t.Errorf("expecting %q to be \"z\"", s.Service)
	}
	if s.Port != 0 {
		t.Errorf("expecting %d to be 0", s.Port)
	}
	if len(s.Tags) != 1 {
		t.Fatalf("expecting %d to be 1", len(s.Tags))
	}
	if s.Tags[0] != "baz" {
		t.Errorf("expecting %q to be \"baz\"", s.Tags[0])
	}
}

func TestCatalogNodeFetch_unknownNode(t *testing.T) {
	clients, consul := testConsulServer(t)
	defer consul.Stop()

	dep := &CatalogNode{rawKey: "unknownNode"}
	result, _, err := dep.Fetch(clients, nil)
	if err != nil {
		t.Fatal(err)
	}

	typed, ok := result.(*NodeDetail)
	if !ok {
		t.Fatal("could not convert result to *NodeDetail")
	}

	if typed != nil {
		t.Fatal("Expecting to get nil for an unknown node")
	}
}

func TestCatalogNodeFetch_nameArgument(t *testing.T) {
	clients, consul := testConsulServer(t)
	defer consul.Stop()

	dep := &CatalogNode{
		rawKey: consul.Config.NodeName,
	}
	result, _, err := dep.Fetch(clients, nil)
	if err != nil {
		t.Fatal(err)
	}

	typed, ok := result.(*NodeDetail)
	if !ok {
		t.Fatal("could not convert result to *NodeDetail")
	}

	if typed == nil {
		t.Fatal("Not expecting to get nil for a known node")
	}

	if typed.Node.Node != consul.Config.NodeName {
		t.Errorf("expected %q to be %q", typed.Node.Node, consul.Config.NodeName)
	}
	if typed.Node.Address != "127.0.0.1" {
		t.Errorf("expected %q to be %q", typed.Node.Address, "127.0.0.1")
	}
	if len(typed.Services) != 1 {
		t.Fatalf("expected 1 services got %d", len(typed.Services))
	}

	var s *NodeService

	s = typed.Services[0]
	if s.ID != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.ID)
	}
	if s.Service != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.Service)
	}
	if s.Port != consul.Config.Ports.Server {
		t.Errorf("expecting %d to be %d", s.Port, consul.Config.Ports.Server)
	}
	if len(s.Tags) != 0 {
		t.Fatalf("expecting %d to be 0", len(s.Tags))
	}
}

func TestCatalogNodeHashCode_isUnique(t *testing.T) {
	dep1 := &CatalogNode{rawKey: ""}
	dep2 := &CatalogNode{rawKey: "node"}
	dep3 := &CatalogNode{rawKey: "", dataCenter: "@nyc1"}
	if dep1.HashCode() == dep2.HashCode() {
		t.Errorf("expected HashCode to be unique")
	}
	if dep1.HashCode() == dep3.HashCode() {
		t.Errorf("expected HashCode to be unique")
	}
	if dep2.HashCode() == dep3.HashCode() {
		t.Errorf("expected HashCode to be unique")
	}
}

func TestParseCatalogNodeNoArguments(t *testing.T) {
	nd, err := ParseCatalogNode()
	if err != nil {
		t.Fatal(err)
	}

	expected := &CatalogNode{}
	if !reflect.DeepEqual(nd, expected) {
		t.Errorf("expected %+v to equal %+v", nd, expected)
	}
}

func TestParseCatalogNodeOneArgument(t *testing.T) {
	nd, err := ParseCatalogNode("node")
	if err != nil {
		t.Fatal(err)
	}

	expected := &CatalogNode{
		rawKey: "node",
	}
	if !reflect.DeepEqual(nd, expected) {
		t.Errorf("expected %+v to equal %+v", nd, expected)
	}
}

func TestParseCatalogNodeTwoArguments(t *testing.T) {
	nd, err := ParseCatalogNode("node", "@nyc1")
	if err != nil {
		t.Fatal(err)
	}

	expected := &CatalogNode{
		rawKey:     "node",
		dataCenter: "nyc1",
	}
	if !reflect.DeepEqual(nd, expected) {
		t.Errorf("expected %+v to equal %+v", nd, expected)
	}
}
<commit_msg>Add test for NodeServiceList sort.<commit_after>package dependency

import (
	"reflect"
	"testing"
	"sort"
)

func TestCatalogNodeFetch(t *testing.T) {
	clients, consul := testConsulServer(t)
	defer consul.Stop()

	// AddService does not let me specify an ID or a port.
	consul.AddService("z", "passing", []string{"baz"})
	consul.AddService("a", "critical", []string{"foo", "bar"})

	dep := &CatalogNode{}
	result, _, err := dep.Fetch(clients, nil)
	if err != nil {
		t.Fatal(err)
	}

	typed, ok := result.(*NodeDetail)
	if !ok {
		t.Fatal("could not convert result to *NodeDetail")
	}

	if typed == nil {
		t.Fatal("Not expecting to get nil for a known node")
	}

	if typed.Node.Node != consul.Config.NodeName {
		t.Errorf("expected %q to be %q", typed.Node.Node, consul.Config.NodeName)
	}
	if typed.Node.Address != "127.0.0.1" {
		t.Errorf("expected %q to be %q", typed.Node.Address, "127.0.0.1")
	}
	if len(typed.Services) != 3 {
		t.Fatalf("expected 3 services got %d", len(typed.Services))
	}

	var s *NodeService

	s = typed.Services[0]
	if s.ID != "a" {
		t.Errorf("expecting %q to be \"a\"", s.ID)
	}
	if s.Service != "a" {
		t.Errorf("expecting %q to be \"a\"", s.Service)
	}
	if s.Port != 0 {
		t.Errorf("expecting %d to be 0", s.Port)
	}
	if len(s.Tags) != 2 {
		t.Fatalf("expecting %d to be 2", len(s.Tags))
	}
	if s.Tags[0] != "bar" {
		t.Errorf("expecting %q to be \"bar\"", s.Tags[0])
	}
	if s.Tags[1] != "foo" {
		t.Errorf("expecting %q to be \"foo\"", s.Tags[1])
	}

	s = typed.Services[1]
	if s.ID != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.ID)
	}
	if s.Service != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.Service)
	}
	if s.Port != consul.Config.Ports.Server {
		t.Errorf("expecting %d to be %d", s.Port, consul.Config.Ports.Server)
	}
	if len(s.Tags) != 0 {
		t.Fatalf("expecting %d to be 0", len(s.Tags))
	}

	s = typed.Services[2]
	if s.ID != "z" {
		t.Errorf("expecting %q to be \"z\"", s.ID)
	}
	if s.Service != "z" {
		t.Errorf("expecting %q to be \"z\"", s.Service)
	}
	if s.Port != 0 {
		t.Errorf("expecting %d to be 0", s.Port)
	}
	if len(s.Tags) != 1 {
		t.Fatalf("expecting %d to be 1", len(s.Tags))
	}
	if s.Tags[0] != "baz" {
		t.Errorf("expecting %q to be \"baz\"", s.Tags[0])
	}
}

func TestCatalogNodeFetch_unknownNode(t *testing.T) {
	clients, consul := testConsulServer(t)
	defer consul.Stop()

	dep := &CatalogNode{rawKey: "unknownNode"}
	result, _, err := dep.Fetch(clients, nil)
	if err != nil {
		t.Fatal(err)
	}

	typed, ok := result.(*NodeDetail)
	if !ok {
		t.Fatal("could not convert result to *NodeDetail")
	}

	if typed != nil {
		t.Fatal("Expecting to get nil for an unknown node")
	}
}

func TestCatalogNodeFetch_nameArgument(t *testing.T) {
	clients, consul := testConsulServer(t)
	defer consul.Stop()

	dep := &CatalogNode{
		rawKey: consul.Config.NodeName,
	}
	result, _, err := dep.Fetch(clients, nil)
	if err != nil {
		t.Fatal(err)
	}

	typed, ok := result.(*NodeDetail)
	if !ok {
		t.Fatal("could not convert result to *NodeDetail")
	}

	if typed == nil {
		t.Fatal("Not expecting to get nil for a known node")
	}

	if typed.Node.Node != consul.Config.NodeName {
		t.Errorf("expected %q to be %q", typed.Node.Node, consul.Config.NodeName)
	}
	if typed.Node.Address != "127.0.0.1" {
		t.Errorf("expected %q to be %q", typed.Node.Address, "127.0.0.1")
	}
	if len(typed.Services) != 1 {
		t.Fatalf("expected 1 services got %d", len(typed.Services))
	}

	var s *NodeService

	s = typed.Services[0]
	if s.ID != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.ID)
	}
	if s.Service != "consul" {
		t.Errorf("expecting %q to be \"consul\"", s.Service)
	}
	if s.Port != consul.Config.Ports.Server {
		t.Errorf("expecting %d to be %d", s.Port, consul.Config.Ports.Server)
	}
	if len(s.Tags) != 0 {
		t.Fatalf("expecting %d to be 0", len(s.Tags))
	}
}

func TestCatalogNodeHashCode_isUnique(t *testing.T) {
	dep1 := &CatalogNode{rawKey: ""}
	dep2 := &CatalogNode{rawKey: "node"}
	dep3 := &CatalogNode{rawKey: "", dataCenter: "@nyc1"}
	if dep1.HashCode() == dep2.HashCode() {
		t.Errorf("expected HashCode to be unique")
	}
	if dep1.HashCode() == dep3.HashCode() {
		t.Errorf("expected HashCode to be unique")
	}
	if dep2.HashCode() == dep3.HashCode() {
		t.Errorf("expected HashCode to be unique")
	}
}

func TestParseCatalogNodeNoArguments(t *testing.T) {
	nd, err := ParseCatalogNode()
	if err != nil {
		t.Fatal(err)
	}

	expected := &CatalogNode{}
	if !reflect.DeepEqual(nd, expected) {
		t.Errorf("expected %+v to equal %+v", nd, expected)
	}
}

func TestParseCatalogNodeOneArgument(t *testing.T) {
	nd, err := ParseCatalogNode("node")
	if err != nil {
		t.Fatal(err)
	}

	expected := &CatalogNode{
		rawKey: "node",
	}
	if !reflect.DeepEqual(nd, expected) {
		t.Errorf("expected %+v to equal %+v", nd, expected)
	}
}

func TestParseCatalogNodeTwoArguments(t *testing.T) {
	nd, err := ParseCatalogNode("node", "@nyc1")
	if err != nil {
		t.Fatal(err)
	}

	expected := &CatalogNode{
		rawKey:     "node",
		dataCenter: "nyc1",
	}
	if !reflect.DeepEqual(nd, expected) {
		t.Errorf("expected %+v to equal %+v", nd, expected)
	}
}

func TestNodeServiceListSort(t *testing.T) {
	services := make(NodeServiceList, 0, 2)
	services = append(services, &NodeService{
		ID:      "s-z",
		Service: "s",
		Tags:    make(ServiceTags, 0),
		Port:    2000,
	})
	services = append(services, &NodeService{
		ID:      "s-a",
		Service: "s",
		Tags:    make(ServiceTags, 0),
		Port:    1000,
	})
	sort.Stable(services)

	var s *NodeService

	s = services[0]
	if s.ID != "s-a" {
		t.Errorf("expecting %q to be \"s-a\"", s.ID)
	}
	if s.Service != "s" {
		t.Errorf("expecting %q to be \"s\"", s.Service)
	}

	s = services[1]
	if s.ID != "s-z" {
		t.Errorf("expecting %q to be \"s-z\"", s.ID)
	}
	if s.Service != "s" {
		t.Errorf("expecting %q to be \"s\"", s.Service)
	}

}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"fmt"
	"sort"

	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcwallet/waddrmgr"
	"github.com/btcsuite/btcwallet/wallet/txauthor"
	"github.com/btcsuite/btcwallet/wtxmgr"
)

// byAmount defines the methods needed to satisify sort.Interface to
// sort credits by their output amount.
type byAmount []wtxmgr.Credit

func (s byAmount) Len() int           { return len(s) }
func (s byAmount) Less(i, j int) bool { return s[i].Amount < s[j].Amount }
func (s byAmount) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func makeInputSource(eligible []wtxmgr.Credit) txauthor.InputSource {
	// Pick largest outputs first.  This is only done for compatibility with
	// previous tx creation code, not because it's a good idea.
	sort.Sort(sort.Reverse(byAmount(eligible)))

	// Current inputs and their total value.  These are closed over by the
	// returned input source and reused across multiple calls.
	currentTotal := btcutil.Amount(0)
	currentInputs := make([]*wire.TxIn, 0, len(eligible))
	currentScripts := make([][]byte, 0, len(eligible))
	currentInputValues := make([]btcutil.Amount, 0, len(eligible))

	return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn,
		[]btcutil.Amount, [][]byte, error) {

		for currentTotal < target && len(eligible) != 0 {
			nextCredit := &eligible[0]
			eligible = eligible[1:]
			nextInput := wire.NewTxIn(&nextCredit.OutPoint, nil, nil)
			currentTotal += nextCredit.Amount
			currentInputs = append(currentInputs, nextInput)
			currentScripts = append(currentScripts, nextCredit.PkScript)
			currentInputValues = append(currentInputValues, nextCredit.Amount)
		}
		return currentTotal, currentInputs, currentInputValues, currentScripts, nil
	}
}

// secretSource is an implementation of txauthor.SecretSource for the wallet's
// address manager.
type secretSource struct {
	*waddrmgr.Manager
}

func (s secretSource) GetKey(addr btcutil.Address) (*btcec.PrivateKey, bool, error) {
	ma, err := s.Address(addr)
	if err != nil {
		return nil, false, err
	}

	mpka, ok := ma.(waddrmgr.ManagedPubKeyAddress)
	if !ok {
		e := fmt.Errorf("managed address type for %v is `%T` but "+
			"want waddrmgr.ManagedPubKeyAddress", addr, ma)
		return nil, false, e
	}
	privKey, err := mpka.PrivKey()
	if err != nil {
		return nil, false, err
	}
	return privKey, ma.Compressed(), nil
}

func (s secretSource) GetScript(addr btcutil.Address) ([]byte, error) {
	ma, err := s.Address(addr)
	if err != nil {
		return nil, err
	}

	msa, ok := ma.(waddrmgr.ManagedScriptAddress)
	if !ok {
		e := fmt.Errorf("managed address type for %v is `%T` but "+
			"want waddrmgr.ManagedScriptAddress", addr, ma)
		return nil, e
	}
	return msa.Script()
}

// txToOutputs creates a signed transaction which includes each output from
// outputs.  Previous outputs to reedeem are chosen from the passed account's
// UTXO set and minconf policy. An additional output may be added to return
// change to the wallet.  An appropriate fee is included based on the wallet's
// current relay fee.  The wallet must be unlocked to create the transaction.
func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32, minconf int32) (*txauthor.AuthoredTx, error) {
	// Address manager must be unlocked to compose transaction.  Grab
	// the unlock if possible (to prevent future unlocks), or return the
	// error if already locked.
	heldUnlock, err := w.HoldUnlock()
	if err != nil {
		return nil, err
	}
	defer heldUnlock.Release()

	chainClient, err := w.requireChainClient()
	if err != nil {
		return nil, err
	}

	// Get current block's height and hash.
	bs, err := chainClient.BlockStamp()
	if err != nil {
		return nil, err
	}

	eligible, err := w.findEligibleOutputs(account, minconf, bs)
	if err != nil {
		return nil, err
	}

	inputSource := makeInputSource(eligible)
	changeSource := func() ([]byte, error) {
		// Derive the change output script.  As a hack to allow spending from
		// the imported account, change addresses are created from account 0.
		var changeAddr btcutil.Address
		if account == waddrmgr.ImportedAddrAccount {
			changeAddr, err = w.NewChangeAddress(0,
				waddrmgr.WitnessPubKey)
		} else {
			changeAddr, err = w.NewChangeAddress(account,
				waddrmgr.WitnessPubKey)
		}
		if err != nil {
			return nil, err
		}
		return txscript.PayToAddrScript(changeAddr)
	}
	tx, err := txauthor.NewUnsignedTransaction(outputs, w.RelayFee(),
		inputSource, changeSource)
	if err != nil {
		return nil, err
	}

	// Randomize change position, if change exists, before signing.  This
	// doesn't affect the serialize size, so the change amount will still be
	// valid.
	if tx.ChangeIndex >= 0 {
		tx.RandomizeChangePosition()
	}

	err = tx.AddAllInputScripts(secretSource{w.Manager})
	if err != nil {
		return nil, err
	}

	err = validateMsgTx(tx.Tx, tx.PrevScripts, tx.PrevInputValues)
	if err != nil {
		return nil, err
	}

	if tx.ChangeIndex >= 0 && account == waddrmgr.ImportedAddrAccount {
		changeAmount := btcutil.Amount(tx.Tx.TxOut[tx.ChangeIndex].Value)
		log.Warnf("Spend from imported account produced change: moving"+
			" %v from imported account into default account.", changeAmount)
	}

	return tx, nil
}

func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return nil, err
	}

	// TODO: Eventually all of these filters (except perhaps output locking)
	// should be handled by the call to UnspentOutputs (or similar).
	// Because one of these filters requires matching the output script to
	// the desired account, this change depends on making wtxmgr a waddrmgr
	// dependancy and requesting unspent outputs for a single account.
	eligible := make([]wtxmgr.Credit, 0, len(unspent))
	for i := range unspent {
		output := &unspent[i]

		// Only include this output if it meets the required number of
		// confirmations.  Coinbase transactions must have have reached
		// maturity before their outputs may be spent.
		if !confirmed(minconf, output.Height, bs.Height) {
			continue
		}
		if output.FromCoinBase {
			const target = blockchain.CoinbaseMaturity
			if !confirmed(target, output.Height, bs.Height) {
				continue
			}
		}

		// Locked unspent outputs are skipped.
		if w.LockedOutpoint(output.OutPoint) {
			continue
		}

		// Only include the output if it is associated with the passed
		// account.
		//
		// TODO: Handle multisig outputs by determining if enough of the
		// addresses are controlled.
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err != nil || len(addrs) != 1 {
			continue
		}
		addrAcct, err := w.Manager.AddrAccount(addrs[0])
		if err != nil || addrAcct != account {
			continue
		}

		eligible = append(eligible, *output)
	}
	return eligible, nil
}

// validateMsgTx verifies transaction input scripts for tx.  All previous output
// scripts from outputs redeemed by the transaction, in the same order they are
// spent, must be passed in the prevScripts slice.
func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte, inputValues []btcutil.Amount) error {
	for i, prevScript := range prevScripts {
		vm, err := txscript.NewEngine(prevScript, tx, i,
			txscript.StandardVerifyFlags, nil, nil, int64(inputValues[i]))
		if err != nil {
			return fmt.Errorf("cannot create script engine: %s", err)
		}
		err = vm.Execute()
		if err != nil {
			return fmt.Errorf("cannot validate transaction: %s", err)
		}
	}
	return nil
}
<commit_msg>wallet: use intermediate hash cache during validateMsgTx<commit_after>// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"fmt"
	"sort"

	"github.com/btcsuite/btcd/blockchain"
	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcwallet/waddrmgr"
	"github.com/btcsuite/btcwallet/wallet/txauthor"
	"github.com/btcsuite/btcwallet/wtxmgr"
)

// byAmount defines the methods needed to satisify sort.Interface to
// sort credits by their output amount.
type byAmount []wtxmgr.Credit

func (s byAmount) Len() int           { return len(s) }
func (s byAmount) Less(i, j int) bool { return s[i].Amount < s[j].Amount }
func (s byAmount) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func makeInputSource(eligible []wtxmgr.Credit) txauthor.InputSource {
	// Pick largest outputs first.  This is only done for compatibility with
	// previous tx creation code, not because it's a good idea.
	sort.Sort(sort.Reverse(byAmount(eligible)))

	// Current inputs and their total value.  These are closed over by the
	// returned input source and reused across multiple calls.
	currentTotal := btcutil.Amount(0)
	currentInputs := make([]*wire.TxIn, 0, len(eligible))
	currentScripts := make([][]byte, 0, len(eligible))
	currentInputValues := make([]btcutil.Amount, 0, len(eligible))

	return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn,
		[]btcutil.Amount, [][]byte, error) {

		for currentTotal < target && len(eligible) != 0 {
			nextCredit := &eligible[0]
			eligible = eligible[1:]
			nextInput := wire.NewTxIn(&nextCredit.OutPoint, nil, nil)
			currentTotal += nextCredit.Amount
			currentInputs = append(currentInputs, nextInput)
			currentScripts = append(currentScripts, nextCredit.PkScript)
			currentInputValues = append(currentInputValues, nextCredit.Amount)
		}
		return currentTotal, currentInputs, currentInputValues, currentScripts, nil
	}
}

// secretSource is an implementation of txauthor.SecretSource for the wallet's
// address manager.
type secretSource struct {
	*waddrmgr.Manager
}

func (s secretSource) GetKey(addr btcutil.Address) (*btcec.PrivateKey, bool, error) {
	ma, err := s.Address(addr)
	if err != nil {
		return nil, false, err
	}

	mpka, ok := ma.(waddrmgr.ManagedPubKeyAddress)
	if !ok {
		e := fmt.Errorf("managed address type for %v is `%T` but "+
			"want waddrmgr.ManagedPubKeyAddress", addr, ma)
		return nil, false, e
	}
	privKey, err := mpka.PrivKey()
	if err != nil {
		return nil, false, err
	}
	return privKey, ma.Compressed(), nil
}

func (s secretSource) GetScript(addr btcutil.Address) ([]byte, error) {
	ma, err := s.Address(addr)
	if err != nil {
		return nil, err
	}

	msa, ok := ma.(waddrmgr.ManagedScriptAddress)
	if !ok {
		e := fmt.Errorf("managed address type for %v is `%T` but "+
			"want waddrmgr.ManagedScriptAddress", addr, ma)
		return nil, e
	}
	return msa.Script()
}

// txToOutputs creates a signed transaction which includes each output from
// outputs.  Previous outputs to reedeem are chosen from the passed account's
// UTXO set and minconf policy. An additional output may be added to return
// change to the wallet.  An appropriate fee is included based on the wallet's
// current relay fee.  The wallet must be unlocked to create the transaction.
func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32, minconf int32) (*txauthor.AuthoredTx, error) {
	// Address manager must be unlocked to compose transaction.  Grab
	// the unlock if possible (to prevent future unlocks), or return the
	// error if already locked.
	heldUnlock, err := w.HoldUnlock()
	if err != nil {
		return nil, err
	}
	defer heldUnlock.Release()

	chainClient, err := w.requireChainClient()
	if err != nil {
		return nil, err
	}

	// Get current block's height and hash.
	bs, err := chainClient.BlockStamp()
	if err != nil {
		return nil, err
	}

	eligible, err := w.findEligibleOutputs(account, minconf, bs)
	if err != nil {
		return nil, err
	}

	inputSource := makeInputSource(eligible)
	changeSource := func() ([]byte, error) {
		// Derive the change output script.  As a hack to allow spending from
		// the imported account, change addresses are created from account 0.
		var changeAddr btcutil.Address
		if account == waddrmgr.ImportedAddrAccount {
			changeAddr, err = w.NewChangeAddress(0,
				waddrmgr.WitnessPubKey)
		} else {
			changeAddr, err = w.NewChangeAddress(account,
				waddrmgr.WitnessPubKey)
		}
		if err != nil {
			return nil, err
		}
		return txscript.PayToAddrScript(changeAddr)
	}
	tx, err := txauthor.NewUnsignedTransaction(outputs, w.RelayFee(),
		inputSource, changeSource)
	if err != nil {
		return nil, err
	}

	// Randomize change position, if change exists, before signing.  This
	// doesn't affect the serialize size, so the change amount will still be
	// valid.
	if tx.ChangeIndex >= 0 {
		tx.RandomizeChangePosition()
	}

	err = tx.AddAllInputScripts(secretSource{w.Manager})
	if err != nil {
		return nil, err
	}

	err = validateMsgTx(tx.Tx, tx.PrevScripts, tx.PrevInputValues)
	if err != nil {
		return nil, err
	}

	if tx.ChangeIndex >= 0 && account == waddrmgr.ImportedAddrAccount {
		changeAmount := btcutil.Amount(tx.Tx.TxOut[tx.ChangeIndex].Value)
		log.Warnf("Spend from imported account produced change: moving"+
			" %v from imported account into default account.", changeAmount)
	}

	return tx, nil
}

func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
	unspent, err := w.TxStore.UnspentOutputs()
	if err != nil {
		return nil, err
	}

	// TODO: Eventually all of these filters (except perhaps output locking)
	// should be handled by the call to UnspentOutputs (or similar).
	// Because one of these filters requires matching the output script to
	// the desired account, this change depends on making wtxmgr a waddrmgr
	// dependancy and requesting unspent outputs for a single account.
	eligible := make([]wtxmgr.Credit, 0, len(unspent))
	for i := range unspent {
		output := &unspent[i]

		// Only include this output if it meets the required number of
		// confirmations.  Coinbase transactions must have have reached
		// maturity before their outputs may be spent.
		if !confirmed(minconf, output.Height, bs.Height) {
			continue
		}
		if output.FromCoinBase {
			const target = blockchain.CoinbaseMaturity
			if !confirmed(target, output.Height, bs.Height) {
				continue
			}
		}

		// Locked unspent outputs are skipped.
		if w.LockedOutpoint(output.OutPoint) {
			continue
		}

		// Only include the output if it is associated with the passed
		// account.
		//
		// TODO: Handle multisig outputs by determining if enough of the
		// addresses are controlled.
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			output.PkScript, w.chainParams)
		if err != nil || len(addrs) != 1 {
			continue
		}
		addrAcct, err := w.Manager.AddrAccount(addrs[0])
		if err != nil || addrAcct != account {
			continue
		}

		eligible = append(eligible, *output)
	}
	return eligible, nil
}

// validateMsgTx verifies transaction input scripts for tx.  All previous output
// scripts from outputs redeemed by the transaction, in the same order they are
// spent, must be passed in the prevScripts slice.
func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte, inputValues []btcutil.Amount) error {
	hashCache := txscript.NewTxSigHashes(tx)
	for i, prevScript := range prevScripts {
		vm, err := txscript.NewEngine(prevScript, tx, i,
			txscript.StandardVerifyFlags, nil, hashCache, int64(inputValues[i]))
		if err != nil {
			return fmt.Errorf("cannot create script engine: %s", err)
		}
		err = vm.Execute()
		if err != nil {
			return fmt.Errorf("cannot validate transaction: %s", err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
  Copyright 2012 Sergey Cherepanov (https://github.com/cheggaaa)

  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 anteater

import (
	"os"
	"os/exec"
	"strings"
	"errors"
	"strconv"
	"fmt"
)

type Image struct {
	Filename string
	Type     string
	Width    int
	Height   int
}

func ImageIdenty(filename string) (*Image, error) {
	var err error
	cmd := exec.Command("identify", filename)
	res, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	image := &Image{
		Filename: filename,
	}

	params := strings.Split(string(res), " ")
	if len(params) < 3 {
		return nil, errors.New("Indetify return ivalid data: " + string(res))
	}

	image.Type = strings.ToLower(params[1])

	wh := strings.Split(params[2], "x")
	if len(wh) < 2 {
		return nil, errors.New("Can't decode identify width/height: " + params[2])
	}
	image.Width, err = strconv.Atoi(wh[0])
	if err != nil {
		return nil, err
	}
	image.Height, err = strconv.Atoi(wh[1])
	if err != nil {
		return nil, err
	}

	return image, nil
}

func (i *Image) Resize(format string, w, h, q int) error {
	wh := fmt.Sprintf("%dx%d", w, h)
	if w == 0 {
		wh = fmt.Sprintf("x%d", h)
	}
	if h == 0 {
		wh = fmt.Sprintf("%d", w)
	}

	if format == "" {
		format = i.Type
	}
	dst := i.Filename + "." + strings.ToLower(format)

	Log.Debugln("Convert from:", i.Filename, "to:", dst, "w:", w, "h:", h, "q:", q)
	var cmd *exec.Cmd
	if q > 0 {
		cmd = exec.Command("convert", i.Filename, "-strip",  "-resize", wh, "-quality",  fmt.Sprintf("%d", q), dst)
	} else {
		cmd = exec.Command("convert", i.Filename, "-strip",  "-resize", wh, dst)
	}
	_, err := cmd.Output()
	if err != nil {
		return err
	}

	os.Remove(i.Filename)
	err = os.Rename(dst, i.Filename)
	if err != nil {
		return err
	}
	i.Width = w
	i.Height = h
	i.Type = format
	return nil
}

func (i *Image) Crop(format string, w, h, q int) error {

	// if($w > $h){
   //   exec("convert ".$src." -resize x".$size." -quality 100 ".$dest);
   //}else{
   //   exec("convert ".$src." -resize ".$size." -quality 100 ".$dest);
   //}

   //exec("convert ".$dest." -gravity Center -crop ".$size."x".$size."+0+0 ".$dest);
	s := w
	if i.Width > i.Height {
		w = 0
	} else {
		h = 0
	}
	err := i.Resize(format, w, h, q)
	if err != nil {
		return err
	}
	crop := fmt.Sprintf("%dx%d+0+0", s, s)
	cmd := exec.Command("convert", i.Filename, "-gravity", "Center", "-crop", crop, i.Filename)
	Log.Debugln("Crop:", crop)
	o, err := cmd.Output()
	Log.Debugln("Output:", o)
	if err != nil {
		return err
	}
	i.Width = s
	i.Height = s
	return nil
} <commit_msg>Clean<commit_after>/*
  Copyright 2012 Sergey Cherepanov (https://github.com/cheggaaa)

  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 anteater

import (
	"os"
	"os/exec"
	"strings"
	"errors"
	"strconv"
	"fmt"
)

type Image struct {
	Filename string
	Type     string
	Width    int
	Height   int
}

func ImageIdenty(filename string) (*Image, error) {
	var err error
	cmd := exec.Command("identify", filename)
	res, err := cmd.Output()
	if err != nil {
		return nil, err
	}

	image := &Image{
		Filename: filename,
	}

	params := strings.Split(string(res), " ")
	if len(params) < 3 {
		return nil, errors.New("Indetify return ivalid data: " + string(res))
	}

	image.Type = strings.ToLower(params[1])

	wh := strings.Split(params[2], "x")
	if len(wh) < 2 {
		return nil, errors.New("Can't decode identify width/height: " + params[2])
	}
	image.Width, err = strconv.Atoi(wh[0])
	if err != nil {
		return nil, err
	}
	image.Height, err = strconv.Atoi(wh[1])
	if err != nil {
		return nil, err
	}

	return image, nil
}

func (i *Image) Resize(format string, w, h, q int) error {
	wh := fmt.Sprintf("%dx%d", w, h)
	if w == 0 {
		wh = fmt.Sprintf("x%d", h)
	}
	if h == 0 {
		wh = fmt.Sprintf("%d", w)
	}

	if format == "" {
		format = i.Type
	}
	dst := i.Filename + "." + strings.ToLower(format)

	Log.Debugln("Convert from:", i.Filename, "to:", dst, "w:", w, "h:", h, "q:", q)
	var cmd *exec.Cmd
	if q > 0 {
		cmd = exec.Command("convert", i.Filename, "-strip",  "-resize", wh, "-quality",  fmt.Sprintf("%d", q), dst)
	} else {
		cmd = exec.Command("convert", i.Filename, "-strip",  "-resize", wh, dst)
	}
	_, err := cmd.Output()
	if err != nil {
		return err
	}

	os.Remove(i.Filename)
	err = os.Rename(dst, i.Filename)
	if err != nil {
		return err
	}
	i.Width = w
	i.Height = h
	i.Type = format
	return nil
}

func (i *Image) Crop(format string, w, h, q int) error {
	s := w
	if i.Width > i.Height {
		w = 0
	} else {
		h = 0
	}
	err := i.Resize(format, w, h, q)
	if err != nil {
		return err
	}
	crop := fmt.Sprintf("%dx%d+0+0", s, s)
	cmd := exec.Command("convert", i.Filename, "-gravity", "Center", "-crop", crop, i.Filename)
	_, err = cmd.Output()
	if err != nil {
		return err
	}
	i.Width = s
	i.Height = s
	return nil
} <|endoftext|>


<commit_before>package strategy

import (
	"fmt"

	"github.com/ingvagabund/cluster-capacity/pkg/client/emulator/store"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/resource"
	apitesting "k8s.io/kubernetes/pkg/api/testing"
	"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
)

type Strategy interface {
	// Add new objects
	Add(obj interface{}) error

	// Update objects
	Update(obj interface{}) error

	// Delete objects
	Delete(obj interface{}) error
}

type predictiveStrategy struct {
	resourceStore store.ResourceStore

	// for each node keep its NodeInfo
	nodeInfo map[string]*schedulercache.NodeInfo
}

func newStubPod(nodeName string, node *api.Node) *api.Pod {
	// Generate unique pod's name.
	// Given all new pods get's name pod-INDEX or similar,
	// node's NodeInfo's pod names will never collide.
	// pod-stub or similar is sufficient.

	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{Name: "pod-stub", Namespace: nodeName, ResourceVersion: "10"},
		Spec:       apitesting.DeepEqualSafePodSpec(),
	}

	limitResourceList := make(map[api.ResourceName]resource.Quantity)
	requestsResourceList := make(map[api.ResourceName]resource.Quantity)
	for key, value := range node.Status.Allocatable {
		limitResourceList[key] = value
		requestsResourceList[key] = value
	}

	// set pod's resource consumption
	pod.Spec.Containers = []api.Container{
		{
			Resources: api.ResourceRequirements{
				Limits:   limitResourceList,
				Requests: requestsResourceList,
			},
		},
	}

	// schedule the pod on the node
	pod.Spec.NodeName = nodeName

	return pod
}

func (s *predictiveStrategy) addPod(pod *api.Pod) error {
	// 1. get node the pod is scheduled on
	node := &api.Node{
		ObjectMeta: api.ObjectMeta{Name: pod.Spec.NodeName},
	}
	foundNode, exists, err := s.resourceStore.Get("nodes", meta.Object(node))
	if err != nil {
		return fmt.Errorf("Unable to get node: %v", err)
	}
	if !exists {
		return fmt.Errorf("Unable to find scheduled pod's node")
	}
	scheduledNode := foundNode.(*api.Node)
	// 2. update the node info to include new pod's resources
	// The node's allocated resources are grabed from the system (cgroup's)
	// so the schedulercache.NodeInfo as actually never used.
	// Some pods can even be without resource limits.
	// However, the point of the cluster capacity is to calculate how many instances
	// of a pod of a given shape (with specified limits) can be scheduled.
	// Thus the assumption here is the operator chooses the limits the way
	// they would be actualy consumed by the underlying system and thus
	// calculated as a sum of the current captured allocation + multiple of addition of all pod's instances.

	// So, get the node's allocatable, create a pod that represents that amount
	// and populate node's NodeInfo with the pod.
	// Once new pods get added to the info, resources get re-computed accordingly to the assumption.

	// create stub pod representing the captured node's allocated resources
	stub := newStubPod(pod.Spec.NodeName, scheduledNode)
	// create node info with the pod (which computes consumed resources)
	info := schedulercache.NewNodeInfo(stub, pod)

	requested := info.RequestedResource()
	scheduledNode.Status.Allocatable[api.ResourceCPU] = *resource.NewMilliQuantity(requested.MilliCPU, resource.DecimalSI)
	scheduledNode.Status.Allocatable[api.ResourceMemory] = *resource.NewQuantity(requested.Memory, resource.BinarySI)
	scheduledNode.Status.Allocatable[api.ResourceNvidiaGPU] = *resource.NewQuantity(requested.NvidiaGPU, resource.DecimalSI)

	// 3. reflect new pod and updated node in the resource store
	err = s.resourceStore.Update("nodes", meta.Object(scheduledNode))
	if err != nil {
		return fmt.Errorf("Unable to update node: %v", err)
	}

	// here asuming the pod is already in the resource storage
	// so the update is needed to emit update event in case a handler is registered
	err = s.resourceStore.Update("pods", meta.Object(pod))
	if err != nil {
		return fmt.Errorf("Unable to add new node: %v", err)
	}

	return nil
}

// Simulate creation of new object (only pods currently supported)
// The method returns error on the first occurence of processing error.
// If so, all succesfully processed objects up to the first failed are reflected in the resource store.
func (s *predictiveStrategy) Add(obj interface{}) error {
	switch item := obj.(type) {
	case *api.Pod:
		return s.addPod(item)
	default:
		return fmt.Errorf("resource kind not recognized")
	}
}

func (s *predictiveStrategy) Update(obj interface{}) error {
	return fmt.Errorf("Not implemented yet")
}

func (s *predictiveStrategy) Delete(obj interface{}) error {
	return fmt.Errorf("Not implemented yet")
}

func NewPredictiveStrategy(resourceStore store.ResourceStore) *predictiveStrategy {
	return &predictiveStrategy{
		resourceStore: resourceStore,
	}
}
<commit_msg>remove modification of node in strategy, it is already handled in scheduler's cache<commit_after>package strategy

import (
	"fmt"

	"github.com/ingvagabund/cluster-capacity/pkg/client/emulator/store"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
)

type Strategy interface {
	// Add new objects
	Add(obj interface{}) error

	// Update objects
	Update(obj interface{}) error

	// Delete objects
	Delete(obj interface{}) error
}

type predictiveStrategy struct {
	resourceStore store.ResourceStore

	// for each node keep its NodeInfo
	nodeInfo map[string]*schedulercache.NodeInfo
}

func (s *predictiveStrategy) addPod(pod *api.Pod) error {
	// No need to update any node.
	// The scheduler keep resources consumed by all pods in its scheduler cache
	// which is than confronted with pod's node Allocatable field.

	// mark the pod as running rather than keeping the phase empty
	pod.Status.Phase = api.PodRunning

	// here asuming the pod is already in the resource storage
	// so the update is needed to emit update event in case a handler is registered
	err := s.resourceStore.Update("pods", meta.Object(pod))
	if err != nil {
		return fmt.Errorf("Unable to add new node: %v", err)
	}

	return nil
}

// Simulate creation of new object (only pods currently supported)
// The method returns error on the first occurence of processing error.
// If so, all succesfully processed objects up to the first failed are reflected in the resource store.
func (s *predictiveStrategy) Add(obj interface{}) error {
	switch item := obj.(type) {
	case *api.Pod:
		return s.addPod(item)
	default:
		return fmt.Errorf("resource kind not recognized")
	}
}

func (s *predictiveStrategy) Update(obj interface{}) error {
	return fmt.Errorf("Not implemented yet")
}

func (s *predictiveStrategy) Delete(obj interface{}) error {
	return fmt.Errorf("Not implemented yet")
}

func NewPredictiveStrategy(resourceStore store.ResourceStore) *predictiveStrategy {
	return &predictiveStrategy{
		resourceStore: resourceStore,
	}
}
<|endoftext|>


<commit_before>package accelerator

import (
	"fmt"

	"github.com/caicloud/nirvana/log"

	"github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
	api "github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
	"github.com/caicloud/cyclone/pkg/server/biz/usage"
	"github.com/caicloud/cyclone/pkg/server/common"
	"github.com/caicloud/cyclone/pkg/server/handler"
)

// CacheSizeLimit is cache size limit, it's percentage of the total PVC size.
const CacheSizeLimit = 0.8

// Accelerator ...
type Accelerator struct {
	// tenant name
	tenant string
	// project the wfr belongs to
	project string
	// wfr represents a workflowrun
	wfr *api.WorkflowRun
	// reporter reports PVC usage used for workflow in the tenant
	reporter usage.PVCReporter
}

// NewAccelerator new an accelerator
func NewAccelerator(tenant, project string, wfr *api.WorkflowRun) *Accelerator {
	reporter, err := usage.NewPVCReporter(handler.K8sClient, tenant)
	if err != nil {
		log.Warningf("Create pvc reporter for tenant %s error: %v", tenant, err)
	}

	return &Accelerator{
		tenant:   tenant,
		wfr:      wfr,
		project:  project,
		reporter: reporter,
	}
}

// Accelerate will check if the workflowrun has label 'workflowrun.cyclone.io/acceleration=true',
// True will mount some volumes into all stages under the related workflow to cache building dependencies.
// volumes including:
// - '/root/.m2'  maven dependency path
// - '/root/.gradle'  gradle dependency path
// - '/root/.npm'  npm dependency path
func (a *Accelerator) Accelerate() {
	if !a.allowed() {
		return
	}

	if a.wfr.Labels != nil && a.wfr.Labels[common.LabelAcceleration] == common.LabelTrueValue {
		a.wfr.Spec.PresetVolumes = []v1alpha1.PresetVolume{
			{
				Type:      v1alpha1.PresetVolumeTypePV,
				Path:      fmt.Sprintf("%s/%s/m2", common.CachePrefixPath, a.project),
				MountPath: "/root/.m2",
			},
			{
				Type:      v1alpha1.PresetVolumeTypePV,
				Path:      fmt.Sprintf("%s/%s/gradle", common.CachePrefixPath, a.project),
				MountPath: "/root/.gradle",
			},
			{
				Type:      v1alpha1.PresetVolumeTypePV,
				Path:      fmt.Sprintf("%s/%s/npm", common.CachePrefixPath, a.project),
				MountPath: "/root/.npm",
			},
		}
	}
}

// allowed determines whether it's allowed to open acceleration for the workflow execution. For the moment,
// only PVC storage constraint is enforced.
func (a *Accelerator) allowed() bool {
	if a.reporter == nil {
		return true
	}

	used, err := a.reporter.UsedPercentage("caches")
	if err != nil {
		log.Warningf("Get caches usage error: %v", err)
		return true
	}

	log.Infof("caches used %.2f PVC storage in tenant %s", used, a.tenant)
	if used >= float64(CacheSizeLimit) {
		log.Warningf("caches used %.2f PVC storage, exceeds limit %.2f, will stop acceleration, tenant: %s", used, CacheSizeLimit, a.tenant)
		return false
	}

	return true
}
<commit_msg>fix: fix acceleration coverd preset volumes (#883)<commit_after>package accelerator

import (
	"fmt"

	"github.com/caicloud/nirvana/log"

	"github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
	api "github.com/caicloud/cyclone/pkg/apis/cyclone/v1alpha1"
	"github.com/caicloud/cyclone/pkg/server/biz/usage"
	"github.com/caicloud/cyclone/pkg/server/common"
	"github.com/caicloud/cyclone/pkg/server/handler"
)

// CacheSizeLimit is cache size limit, it's percentage of the total PVC size.
const CacheSizeLimit = 0.8

// Accelerator ...
type Accelerator struct {
	// tenant name
	tenant string
	// project the wfr belongs to
	project string
	// wfr represents a workflowrun
	wfr *api.WorkflowRun
	// reporter reports PVC usage used for workflow in the tenant
	reporter usage.PVCReporter
}

// NewAccelerator new an accelerator
func NewAccelerator(tenant, project string, wfr *api.WorkflowRun) *Accelerator {
	reporter, err := usage.NewPVCReporter(handler.K8sClient, tenant)
	if err != nil {
		log.Warningf("Create pvc reporter for tenant %s error: %v", tenant, err)
	}

	return &Accelerator{
		tenant:   tenant,
		wfr:      wfr,
		project:  project,
		reporter: reporter,
	}
}

// Accelerate will check if the workflowrun has label 'workflowrun.cyclone.io/acceleration=true',
// True will mount some volumes into all stages under the related workflow to cache building dependencies.
// volumes including:
// - '/root/.m2'  maven dependency path
// - '/root/.gradle'  gradle dependency path
// - '/root/.npm'  npm dependency path
func (a *Accelerator) Accelerate() {
	if !a.allowed() {
		return
	}

	if a.wfr.Labels != nil && a.wfr.Labels[common.LabelAcceleration] == common.LabelTrueValue {
		a.wfr.Spec.PresetVolumes = append(a.wfr.Spec.PresetVolumes, []v1alpha1.PresetVolume{
			{
				Type:      v1alpha1.PresetVolumeTypePV,
				Path:      fmt.Sprintf("%s/%s/m2", common.CachePrefixPath, a.project),
				MountPath: "/root/.m2",
			},
			{
				Type:      v1alpha1.PresetVolumeTypePV,
				Path:      fmt.Sprintf("%s/%s/gradle", common.CachePrefixPath, a.project),
				MountPath: "/root/.gradle",
			},
			{
				Type:      v1alpha1.PresetVolumeTypePV,
				Path:      fmt.Sprintf("%s/%s/npm", common.CachePrefixPath, a.project),
				MountPath: "/root/.npm",
			},
		}...)
	}
}

// allowed determines whether it's allowed to open acceleration for the workflow execution. For the moment,
// only PVC storage constraint is enforced.
func (a *Accelerator) allowed() bool {
	if a.reporter == nil {
		return true
	}

	used, err := a.reporter.UsedPercentage("caches")
	if err != nil {
		log.Warningf("Get caches usage error: %v", err)
		return true
	}

	log.Infof("caches used %.2f PVC storage in tenant %s", used, a.tenant)
	if used >= float64(CacheSizeLimit) {
		log.Warningf("caches used %.2f PVC storage, exceeds limit %.2f, will stop acceleration, tenant: %s", used, CacheSizeLimit, a.tenant)
		return false
	}

	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold Authors

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

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

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

package bazel

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const sourceQuery = "kind('source file', deps('%[1]s')) union buildfiles('%[1]s')"

func query(target string) string {
	return fmt.Sprintf(sourceQuery, target)
}

// GetDependencies finds the sources dependencies for the given bazel artifact.
// All paths are relative to the workspace.
func GetDependencies(ctx context.Context, dir string, a *latest.BazelArtifact) ([]string, error) {
	timer := time.NewTimer(1 * time.Second)
	defer timer.Stop()

	go func() {
		<-timer.C
		logrus.Warnln("Retrieving Bazel dependencies can take a long time the first time")
	}()

	topLevelFolder, err := findWorkspace(dir)
	if err != nil {
		return nil, errors.Wrap(err, "unable to find the WORKSPACE file")
	}

	absDir, err := filepath.Abs(dir)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to find absolute path for %s", dir)
	}

	cmd := exec.CommandContext(ctx, "bazel", "query", query(a.BuildTarget), "--noimplicit_deps", "--order_output=no", "--output=label")
	cmd.Dir = dir
	stdout, err := util.RunCmdOut(cmd)
	if err != nil {
		return nil, errors.Wrap(err, "getting bazel dependencies")
	}

	labels := strings.Split(string(stdout), "\n")
	var deps []string
	for _, l := range labels {
		if strings.HasPrefix(l, "@") {
			continue
		}
		if strings.HasPrefix(l, "//external") {
			continue
		}
		if l == "" {
			continue
		}

		rel, err := filepath.Rel(absDir, filepath.Join(topLevelFolder, depToPath(l)))
		if err != nil {
			return nil, errors.Wrap(err, "unable to find absolute path")
		}
		deps = append(deps, rel)
	}

	rel, err := filepath.Rel(absDir, filepath.Join(topLevelFolder, "WORKSPACE"))
	if err != nil {
		return nil, errors.Wrap(err, "unable to find absolute path")
	}
	deps = append(deps, rel)

	logrus.Debugf("Found dependencies for bazel artifact: %v", deps)

	return deps, nil
}

func depToPath(dep string) string {
	return strings.TrimPrefix(strings.Replace(strings.TrimPrefix(dep, "//"), ":", "/", 1), "/")
}

func findWorkspace(workingDir string) (string, error) {
	dir, err := filepath.Abs(workingDir)
	if err != nil {
		return "", errors.Wrap(err, "invalid working dir")
	}

	for {
		if _, err := os.Stat(filepath.Join(dir, "WORKSPACE")); err == nil {
			return dir, nil
		}

		parent := filepath.Dir(dir)
		if parent == dir {
			return "", errors.New("no WORKSPACE file found")
		}
		dir = parent
	}
}
<commit_msg>Don’t print bazel slow warnings more than once.<commit_after>/*
Copyright 2019 The Skaffold Authors

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

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

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

package bazel

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const sourceQuery = "kind('source file', deps('%[1]s')) union buildfiles('%[1]s')"

func query(target string) string {
	return fmt.Sprintf(sourceQuery, target)
}

var once sync.Once

// GetDependencies finds the sources dependencies for the given bazel artifact.
// All paths are relative to the workspace.
func GetDependencies(ctx context.Context, dir string, a *latest.BazelArtifact) ([]string, error) {
	timer := time.NewTimer(1 * time.Second)
	defer timer.Stop()

	go func() {
		<-timer.C
		once.Do(func() { logrus.Warnln("Retrieving Bazel dependencies can take a long time the first time") })
	}()

	topLevelFolder, err := findWorkspace(dir)
	if err != nil {
		return nil, errors.Wrap(err, "unable to find the WORKSPACE file")
	}

	absDir, err := filepath.Abs(dir)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to find absolute path for %s", dir)
	}

	cmd := exec.CommandContext(ctx, "bazel", "query", query(a.BuildTarget), "--noimplicit_deps", "--order_output=no", "--output=label")
	cmd.Dir = dir
	stdout, err := util.RunCmdOut(cmd)
	if err != nil {
		return nil, errors.Wrap(err, "getting bazel dependencies")
	}

	labels := strings.Split(string(stdout), "\n")
	var deps []string
	for _, l := range labels {
		if strings.HasPrefix(l, "@") {
			continue
		}
		if strings.HasPrefix(l, "//external") {
			continue
		}
		if l == "" {
			continue
		}

		rel, err := filepath.Rel(absDir, filepath.Join(topLevelFolder, depToPath(l)))
		if err != nil {
			return nil, errors.Wrap(err, "unable to find absolute path")
		}
		deps = append(deps, rel)
	}

	rel, err := filepath.Rel(absDir, filepath.Join(topLevelFolder, "WORKSPACE"))
	if err != nil {
		return nil, errors.Wrap(err, "unable to find absolute path")
	}
	deps = append(deps, rel)

	logrus.Debugf("Found dependencies for bazel artifact: %v", deps)

	return deps, nil
}

func depToPath(dep string) string {
	return strings.TrimPrefix(strings.Replace(strings.TrimPrefix(dep, "//"), ":", "/", 1), "/")
}

func findWorkspace(workingDir string) (string, error) {
	dir, err := filepath.Abs(workingDir)
	if err != nil {
		return "", errors.Wrap(err, "invalid working dir")
	}

	for {
		if _, err := os.Stat(filepath.Join(dir, "WORKSPACE")); err == nil {
			return dir, nil
		}

		parent := filepath.Dir(dir)
		if parent == dir {
			return "", errors.New("no WORKSPACE file found")
		}
		dir = parent
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold Authors

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

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

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

package defaults

import (
	"context"
	"fmt"

	"github.com/google/uuid"
	"github.com/mitchellh/go-homedir"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/kaniko"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	schemautil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

const (
	defaultCloudBuildDockerImage = "gcr.io/cloud-builders/docker"
	defaultCloudBuildMavenImage  = "gcr.io/cloud-builders/mvn"
	defaultCloudBuildGradleImage = "gcr.io/cloud-builders/gradle"
	defaultCloudBuildKanikoImage = kaniko.DefaultImage
	defaultCloudBuildPackImage   = "gcr.io/k8s-skaffold/pack"
	defaultCloudBuildKoImage     = "gcr.io/k8s-skaffold/skaffold"
)

// Set makes sure default values are set on a SkaffoldConfig.
func Set(c *latest.SkaffoldConfig) error {
	defaultToLocalBuild(c)
	setDefaultTagger(c)
	setDefaultLogsConfig(c)

	for _, a := range c.Build.Artifacts {
		setDefaultWorkspace(a)
		setDefaultSync(a)

		if c.Build.Cluster != nil && a.CustomArtifact == nil && a.BuildpackArtifact == nil {
			defaultToKanikoArtifact(a)
		} else {
			defaultToDockerArtifact(a)
		}

		switch {
		case a.DockerArtifact != nil:
			setDockerArtifactDefaults(a.DockerArtifact)

		case a.KanikoArtifact != nil:
			setKanikoArtifactDefaults(a.KanikoArtifact)

		case a.CustomArtifact != nil:
			setCustomArtifactDefaults(a.CustomArtifact)

		case a.BuildpackArtifact != nil:
			setBuildpackArtifactDefaults(a.BuildpackArtifact)
		}

		for _, d := range a.Dependencies {
			setDefaultArtifactDependencyAlias(d)
		}
	}

	withLocalBuild(c, func(lb *latest.LocalBuild) {
		// don't set build concurrency if there are no artifacts in the current config
		if len(c.Build.Artifacts) > 0 {
			setDefaultConcurrency(lb)
		}
	})

	withCloudBuildConfig(c,
		setDefaultCloudBuildDockerImage,
		setDefaultCloudBuildMavenImage,
		setDefaultCloudBuildGradleImage,
		setDefaultCloudBuildKanikoImage,
		setDefaultCloudBuildPackImage,
		setDefaultCloudBuildKoImage,
	)

	if err := withClusterConfig(c,
		setDefaultClusterNamespace,
		setDefaultClusterTimeout,
		setDefaultClusterPullSecret,
		setDefaultClusterDockerConfigSecret,
	); err != nil {
		return err
	}

	for i, pf := range c.PortForward {
		if pf == nil {
			return fmt.Errorf("portForward[%d] of config with name '%s' is empty, Please check if it has valid values", i, c.Metadata.Name)
		}
		setDefaultLocalPort(pf)
		setDefaultAddress(pf)
	}

	setDefaultTestWorkspace(c)
	return nil
}

// SetDefaultRenderer sets the default manifests to rawYaml.
func SetDefaultRenderer(c *latest.SkaffoldConfig) {
	if len(c.Render.Generate.Kpt) > 0 {
		return
	}
	if len(c.Render.Generate.RawK8s) > 0 {
		if c.Deploy.KubectlDeploy == nil {
			c.Deploy.KubectlDeploy = &latest.KubectlDeploy{}
		}
		return
	}
	if c.Render.Generate.Kustomize != nil {
		return
	}
	if c.Render.Generate.Helm != nil {
		return
	}
	if c.Deploy.LegacyHelmDeploy != nil {
		return
	}
	if c.Deploy.DockerDeploy != nil {
		return
	}
	if c.Deploy.CloudRunDeploy != nil {
		return
	}
	// Set default manifests to "k8s/*.yaml", same as v1.
	c.Render.Generate.RawK8s = constants.DefaultKubectlManifests
}

// SetDefaultDeployer adds a default kubectl deploy configuration.
func SetDefaultDeployer(c *latest.SkaffoldConfig) {
	if c.Deploy.DeployType != (latest.DeployType{}) {
		return
	}

	log.Entry(context.TODO()).Debug("Defaulting deploy type to kubectl")
	c.Deploy.DeployType.KubectlDeploy = &latest.KubectlDeploy{}
}

func defaultToLocalBuild(c *latest.SkaffoldConfig) {
	if c.Build.BuildType != (latest.BuildType{}) {
		return
	}

	log.Entry(context.TODO()).Debug("Defaulting build type to local build")
	c.Build.BuildType.LocalBuild = &latest.LocalBuild{}
}

func withLocalBuild(c *latest.SkaffoldConfig, operations ...func(*latest.LocalBuild)) {
	if local := c.Build.LocalBuild; local != nil {
		for _, operation := range operations {
			operation(local)
		}
	}
}

func setDefaultConcurrency(local *latest.LocalBuild) {
	if local.Concurrency == nil {
		local.Concurrency = &constants.DefaultLocalConcurrency
	}
}

func withCloudBuildConfig(c *latest.SkaffoldConfig, operations ...func(*latest.GoogleCloudBuild)) {
	if gcb := c.Build.GoogleCloudBuild; gcb != nil {
		for _, operation := range operations {
			operation(gcb)
		}
	}
}

func setDefaultCloudBuildDockerImage(gcb *latest.GoogleCloudBuild) {
	gcb.DockerImage = valueOrDefault(gcb.DockerImage, defaultCloudBuildDockerImage)
}

func setDefaultCloudBuildMavenImage(gcb *latest.GoogleCloudBuild) {
	gcb.MavenImage = valueOrDefault(gcb.MavenImage, defaultCloudBuildMavenImage)
}

func setDefaultCloudBuildGradleImage(gcb *latest.GoogleCloudBuild) {
	gcb.GradleImage = valueOrDefault(gcb.GradleImage, defaultCloudBuildGradleImage)
}

func setDefaultCloudBuildKanikoImage(gcb *latest.GoogleCloudBuild) {
	gcb.KanikoImage = valueOrDefault(gcb.KanikoImage, defaultCloudBuildKanikoImage)
}

func setDefaultCloudBuildPackImage(gcb *latest.GoogleCloudBuild) {
	gcb.PackImage = valueOrDefault(gcb.PackImage, defaultCloudBuildPackImage)
}

func setDefaultCloudBuildKoImage(gcb *latest.GoogleCloudBuild) {
	gcb.KoImage = valueOrDefault(gcb.KoImage, defaultCloudBuildKoImage)
}

func setDefaultTagger(c *latest.SkaffoldConfig) {
	if c.Build.TagPolicy != (latest.TagPolicy{}) {
		return
	}

	c.Build.TagPolicy = latest.TagPolicy{GitTagger: &latest.GitTagger{}}
}

func setDefaultLogsConfig(c *latest.SkaffoldConfig) {
	if c.Deploy.Logs.Prefix == "" {
		c.Deploy.Logs.Prefix = "container"
	}
}

func defaultToDockerArtifact(a *latest.Artifact) {
	if a.ArtifactType == (latest.ArtifactType{}) {
		a.ArtifactType = latest.ArtifactType{
			DockerArtifact: &latest.DockerArtifact{},
		}
	}
}

func setCustomArtifactDefaults(a *latest.CustomArtifact) {
	if a.Dependencies == nil {
		a.Dependencies = &latest.CustomDependencies{
			Paths: []string{"."},
		}
	}
}

func setBuildpackArtifactDefaults(a *latest.BuildpackArtifact) {
	if a.ProjectDescriptor == "" {
		a.ProjectDescriptor = constants.DefaultProjectDescriptor
	}
	if a.Dependencies == nil {
		a.Dependencies = &latest.BuildpackDependencies{
			Paths: []string{"."},
		}
	}
	if a.Builder == "" && len(a.Buildpacks) == 0 {
		a.Builder = constants.DefaultBuildpacksBuilderImage
		a.TrustBuilder = true
	}
}

func setDockerArtifactDefaults(a *latest.DockerArtifact) {
	a.DockerfilePath = valueOrDefault(a.DockerfilePath, constants.DefaultDockerfilePath)
}

func setDefaultWorkspace(a *latest.Artifact) {
	a.Workspace = valueOrDefault(a.Workspace, ".")
}

func setDefaultSync(a *latest.Artifact) {
	if a.Sync != nil {
		if len(a.Sync.Manual) == 0 && len(a.Sync.Infer) == 0 && a.Sync.Auto == nil {
			switch {
			case a.JibArtifact != nil || a.BuildpackArtifact != nil:
				a.Sync.Auto = util.BoolPtr(true)
			default:
				a.Sync.Infer = []string{"**/*"}
			}
		}
	} else if a.BuildpackArtifact != nil {
		a.Sync = &latest.Sync{Auto: util.BoolPtr(true)}
	}
}

func withClusterConfig(c *latest.SkaffoldConfig, opts ...func(*latest.ClusterDetails) error) error {
	clusterDetails := c.Build.BuildType.Cluster
	if clusterDetails == nil {
		return nil
	}
	for _, o := range opts {
		if err := o(clusterDetails); err != nil {
			return err
		}
	}
	return nil
}

func setDefaultClusterNamespace(cluster *latest.ClusterDetails) error {
	if cluster.Namespace == "" {
		ns, err := currentNamespace()
		if err != nil {
			return fmt.Errorf("getting current namespace: %w", err)
		}
		cluster.Namespace = ns
	}
	return nil
}

func setDefaultClusterTimeout(cluster *latest.ClusterDetails) error {
	cluster.Timeout = valueOrDefault(cluster.Timeout, kaniko.DefaultTimeout)
	return nil
}

func setDefaultClusterPullSecret(cluster *latest.ClusterDetails) error {
	cluster.PullSecretMountPath = valueOrDefault(cluster.PullSecretMountPath, kaniko.DefaultSecretMountPath)
	if cluster.PullSecretPath != "" {
		absPath, err := homedir.Expand(cluster.PullSecretPath)
		if err != nil {
			return fmt.Errorf("unable to expand pullSecretPath %s", cluster.PullSecretPath)
		}
		cluster.PullSecretPath = absPath
		random := ""
		if cluster.RandomPullSecret {
			uid, _ := uuid.NewUUID()
			random = uid.String()
		}
		cluster.PullSecretName = valueOrDefault(cluster.PullSecretName, kaniko.DefaultSecretName+random)
		return nil
	}
	return nil
}

func setDefaultClusterDockerConfigSecret(cluster *latest.ClusterDetails) error {
	if cluster.DockerConfig == nil {
		return nil
	}

	random := ""
	if cluster.RandomDockerConfigSecret {
		uid, _ := uuid.NewUUID()
		random = uid.String()
	}

	cluster.DockerConfig.SecretName = valueOrDefault(cluster.DockerConfig.SecretName, kaniko.DefaultDockerConfigSecretName+random)

	if cluster.DockerConfig.Path == "" {
		return nil
	}

	absPath, err := homedir.Expand(cluster.DockerConfig.Path)
	if err != nil {
		return fmt.Errorf("unable to expand dockerConfig.path %s", cluster.DockerConfig.Path)
	}

	cluster.DockerConfig.Path = absPath
	return nil
}

func defaultToKanikoArtifact(artifact *latest.Artifact) {
	if artifact.KanikoArtifact == nil {
		artifact.KanikoArtifact = &latest.KanikoArtifact{}
	}
}

func setKanikoArtifactDefaults(a *latest.KanikoArtifact) {
	a.Image = valueOrDefault(a.Image, kaniko.DefaultImage)
	a.DockerfilePath = valueOrDefault(a.DockerfilePath, constants.DefaultDockerfilePath)
	a.InitImage = valueOrDefault(a.InitImage, constants.DefaultBusyboxImage)
}

func valueOrDefault(v, def string) string {
	if v != "" {
		return v
	}
	return def
}

func currentNamespace() (string, error) {
	cfg, err := kubectx.CurrentConfig()
	if err != nil {
		return "", err
	}

	current, present := cfg.Contexts[cfg.CurrentContext]
	if present {
		if current.Namespace != "" {
			return current.Namespace, nil
		}
	}

	return "default", nil
}

func setDefaultLocalPort(pf *latest.PortForwardResource) {
	if pf.LocalPort == 0 {
		if pf.Port.Type == schemautil.Int {
			pf.LocalPort = pf.Port.IntVal
		}
	}
}

func setDefaultAddress(pf *latest.PortForwardResource) {
	if pf.Address == "" {
		pf.Address = constants.DefaultPortForwardAddress
	}
}

func setDefaultArtifactDependencyAlias(d *latest.ArtifactDependency) {
	if d.Alias == "" {
		d.Alias = d.ImageName
	}
}

func setDefaultTestWorkspace(c *latest.SkaffoldConfig) {
	for _, tc := range c.Test {
		if tc == nil {
			continue
		}
		tc.Workspace = valueOrDefault(tc.Workspace, ".")
	}
}
<commit_msg>fix: Don't add kubectl deployer when cloud run deployer present.  (#7731)<commit_after>/*
Copyright 2019 The Skaffold Authors

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

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

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

package defaults

import (
	"context"
	"fmt"

	"github.com/google/uuid"
	"github.com/mitchellh/go-homedir"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/kaniko"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	schemautil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

const (
	defaultCloudBuildDockerImage = "gcr.io/cloud-builders/docker"
	defaultCloudBuildMavenImage  = "gcr.io/cloud-builders/mvn"
	defaultCloudBuildGradleImage = "gcr.io/cloud-builders/gradle"
	defaultCloudBuildKanikoImage = kaniko.DefaultImage
	defaultCloudBuildPackImage   = "gcr.io/k8s-skaffold/pack"
	defaultCloudBuildKoImage     = "gcr.io/k8s-skaffold/skaffold"
)

// Set makes sure default values are set on a SkaffoldConfig.
func Set(c *latest.SkaffoldConfig) error {
	defaultToLocalBuild(c)
	setDefaultTagger(c)
	setDefaultLogsConfig(c)

	for _, a := range c.Build.Artifacts {
		setDefaultWorkspace(a)
		setDefaultSync(a)

		if c.Build.Cluster != nil && a.CustomArtifact == nil && a.BuildpackArtifact == nil {
			defaultToKanikoArtifact(a)
		} else {
			defaultToDockerArtifact(a)
		}

		switch {
		case a.DockerArtifact != nil:
			setDockerArtifactDefaults(a.DockerArtifact)

		case a.KanikoArtifact != nil:
			setKanikoArtifactDefaults(a.KanikoArtifact)

		case a.CustomArtifact != nil:
			setCustomArtifactDefaults(a.CustomArtifact)

		case a.BuildpackArtifact != nil:
			setBuildpackArtifactDefaults(a.BuildpackArtifact)
		}

		for _, d := range a.Dependencies {
			setDefaultArtifactDependencyAlias(d)
		}
	}

	withLocalBuild(c, func(lb *latest.LocalBuild) {
		// don't set build concurrency if there are no artifacts in the current config
		if len(c.Build.Artifacts) > 0 {
			setDefaultConcurrency(lb)
		}
	})

	withCloudBuildConfig(c,
		setDefaultCloudBuildDockerImage,
		setDefaultCloudBuildMavenImage,
		setDefaultCloudBuildGradleImage,
		setDefaultCloudBuildKanikoImage,
		setDefaultCloudBuildPackImage,
		setDefaultCloudBuildKoImage,
	)

	if err := withClusterConfig(c,
		setDefaultClusterNamespace,
		setDefaultClusterTimeout,
		setDefaultClusterPullSecret,
		setDefaultClusterDockerConfigSecret,
	); err != nil {
		return err
	}

	for i, pf := range c.PortForward {
		if pf == nil {
			return fmt.Errorf("portForward[%d] of config with name '%s' is empty, Please check if it has valid values", i, c.Metadata.Name)
		}
		setDefaultLocalPort(pf)
		setDefaultAddress(pf)
	}

	setDefaultTestWorkspace(c)
	return nil
}

// SetDefaultRenderer sets the default manifests to rawYaml.
func SetDefaultRenderer(c *latest.SkaffoldConfig) {
	if len(c.Render.Generate.Kpt) > 0 {
		return
	}
	if len(c.Render.Generate.RawK8s) > 0 {
		if c.Deploy.KubectlDeploy == nil && c.Deploy.CloudRunDeploy == nil {
			log.Entry(context.TODO()).Debug("Found raw k8s manifests without cloud run deploy, adding kubectl deployer")
			c.Deploy.KubectlDeploy = &latest.KubectlDeploy{}
		}
		return
	}
	if c.Render.Generate.Kustomize != nil {
		return
	}
	if c.Render.Generate.Helm != nil {
		return
	}
	if c.Deploy.LegacyHelmDeploy != nil {
		return
	}
	if c.Deploy.DockerDeploy != nil {
		return
	}
	if c.Deploy.CloudRunDeploy != nil {
		return
	}
	// Set default manifests to "k8s/*.yaml", same as v1.
	c.Render.Generate.RawK8s = constants.DefaultKubectlManifests
}

// SetDefaultDeployer adds a default kubectl deploy configuration.
func SetDefaultDeployer(c *latest.SkaffoldConfig) {
	if c.Deploy.DeployType != (latest.DeployType{}) {
		return
	}

	log.Entry(context.TODO()).Debug("Defaulting deploy type to kubectl")
	c.Deploy.DeployType.KubectlDeploy = &latest.KubectlDeploy{}
}

func defaultToLocalBuild(c *latest.SkaffoldConfig) {
	if c.Build.BuildType != (latest.BuildType{}) {
		return
	}

	log.Entry(context.TODO()).Debug("Defaulting build type to local build")
	c.Build.BuildType.LocalBuild = &latest.LocalBuild{}
}

func withLocalBuild(c *latest.SkaffoldConfig, operations ...func(*latest.LocalBuild)) {
	if local := c.Build.LocalBuild; local != nil {
		for _, operation := range operations {
			operation(local)
		}
	}
}

func setDefaultConcurrency(local *latest.LocalBuild) {
	if local.Concurrency == nil {
		local.Concurrency = &constants.DefaultLocalConcurrency
	}
}

func withCloudBuildConfig(c *latest.SkaffoldConfig, operations ...func(*latest.GoogleCloudBuild)) {
	if gcb := c.Build.GoogleCloudBuild; gcb != nil {
		for _, operation := range operations {
			operation(gcb)
		}
	}
}

func setDefaultCloudBuildDockerImage(gcb *latest.GoogleCloudBuild) {
	gcb.DockerImage = valueOrDefault(gcb.DockerImage, defaultCloudBuildDockerImage)
}

func setDefaultCloudBuildMavenImage(gcb *latest.GoogleCloudBuild) {
	gcb.MavenImage = valueOrDefault(gcb.MavenImage, defaultCloudBuildMavenImage)
}

func setDefaultCloudBuildGradleImage(gcb *latest.GoogleCloudBuild) {
	gcb.GradleImage = valueOrDefault(gcb.GradleImage, defaultCloudBuildGradleImage)
}

func setDefaultCloudBuildKanikoImage(gcb *latest.GoogleCloudBuild) {
	gcb.KanikoImage = valueOrDefault(gcb.KanikoImage, defaultCloudBuildKanikoImage)
}

func setDefaultCloudBuildPackImage(gcb *latest.GoogleCloudBuild) {
	gcb.PackImage = valueOrDefault(gcb.PackImage, defaultCloudBuildPackImage)
}

func setDefaultCloudBuildKoImage(gcb *latest.GoogleCloudBuild) {
	gcb.KoImage = valueOrDefault(gcb.KoImage, defaultCloudBuildKoImage)
}

func setDefaultTagger(c *latest.SkaffoldConfig) {
	if c.Build.TagPolicy != (latest.TagPolicy{}) {
		return
	}

	c.Build.TagPolicy = latest.TagPolicy{GitTagger: &latest.GitTagger{}}
}

func setDefaultLogsConfig(c *latest.SkaffoldConfig) {
	if c.Deploy.Logs.Prefix == "" {
		c.Deploy.Logs.Prefix = "container"
	}
}

func defaultToDockerArtifact(a *latest.Artifact) {
	if a.ArtifactType == (latest.ArtifactType{}) {
		a.ArtifactType = latest.ArtifactType{
			DockerArtifact: &latest.DockerArtifact{},
		}
	}
}

func setCustomArtifactDefaults(a *latest.CustomArtifact) {
	if a.Dependencies == nil {
		a.Dependencies = &latest.CustomDependencies{
			Paths: []string{"."},
		}
	}
}

func setBuildpackArtifactDefaults(a *latest.BuildpackArtifact) {
	if a.ProjectDescriptor == "" {
		a.ProjectDescriptor = constants.DefaultProjectDescriptor
	}
	if a.Dependencies == nil {
		a.Dependencies = &latest.BuildpackDependencies{
			Paths: []string{"."},
		}
	}
	if a.Builder == "" && len(a.Buildpacks) == 0 {
		a.Builder = constants.DefaultBuildpacksBuilderImage
		a.TrustBuilder = true
	}
}

func setDockerArtifactDefaults(a *latest.DockerArtifact) {
	a.DockerfilePath = valueOrDefault(a.DockerfilePath, constants.DefaultDockerfilePath)
}

func setDefaultWorkspace(a *latest.Artifact) {
	a.Workspace = valueOrDefault(a.Workspace, ".")
}

func setDefaultSync(a *latest.Artifact) {
	if a.Sync != nil {
		if len(a.Sync.Manual) == 0 && len(a.Sync.Infer) == 0 && a.Sync.Auto == nil {
			switch {
			case a.JibArtifact != nil || a.BuildpackArtifact != nil:
				a.Sync.Auto = util.BoolPtr(true)
			default:
				a.Sync.Infer = []string{"**/*"}
			}
		}
	} else if a.BuildpackArtifact != nil {
		a.Sync = &latest.Sync{Auto: util.BoolPtr(true)}
	}
}

func withClusterConfig(c *latest.SkaffoldConfig, opts ...func(*latest.ClusterDetails) error) error {
	clusterDetails := c.Build.BuildType.Cluster
	if clusterDetails == nil {
		return nil
	}
	for _, o := range opts {
		if err := o(clusterDetails); err != nil {
			return err
		}
	}
	return nil
}

func setDefaultClusterNamespace(cluster *latest.ClusterDetails) error {
	if cluster.Namespace == "" {
		ns, err := currentNamespace()
		if err != nil {
			return fmt.Errorf("getting current namespace: %w", err)
		}
		cluster.Namespace = ns
	}
	return nil
}

func setDefaultClusterTimeout(cluster *latest.ClusterDetails) error {
	cluster.Timeout = valueOrDefault(cluster.Timeout, kaniko.DefaultTimeout)
	return nil
}

func setDefaultClusterPullSecret(cluster *latest.ClusterDetails) error {
	cluster.PullSecretMountPath = valueOrDefault(cluster.PullSecretMountPath, kaniko.DefaultSecretMountPath)
	if cluster.PullSecretPath != "" {
		absPath, err := homedir.Expand(cluster.PullSecretPath)
		if err != nil {
			return fmt.Errorf("unable to expand pullSecretPath %s", cluster.PullSecretPath)
		}
		cluster.PullSecretPath = absPath
		random := ""
		if cluster.RandomPullSecret {
			uid, _ := uuid.NewUUID()
			random = uid.String()
		}
		cluster.PullSecretName = valueOrDefault(cluster.PullSecretName, kaniko.DefaultSecretName+random)
		return nil
	}
	return nil
}

func setDefaultClusterDockerConfigSecret(cluster *latest.ClusterDetails) error {
	if cluster.DockerConfig == nil {
		return nil
	}

	random := ""
	if cluster.RandomDockerConfigSecret {
		uid, _ := uuid.NewUUID()
		random = uid.String()
	}

	cluster.DockerConfig.SecretName = valueOrDefault(cluster.DockerConfig.SecretName, kaniko.DefaultDockerConfigSecretName+random)

	if cluster.DockerConfig.Path == "" {
		return nil
	}

	absPath, err := homedir.Expand(cluster.DockerConfig.Path)
	if err != nil {
		return fmt.Errorf("unable to expand dockerConfig.path %s", cluster.DockerConfig.Path)
	}

	cluster.DockerConfig.Path = absPath
	return nil
}

func defaultToKanikoArtifact(artifact *latest.Artifact) {
	if artifact.KanikoArtifact == nil {
		artifact.KanikoArtifact = &latest.KanikoArtifact{}
	}
}

func setKanikoArtifactDefaults(a *latest.KanikoArtifact) {
	a.Image = valueOrDefault(a.Image, kaniko.DefaultImage)
	a.DockerfilePath = valueOrDefault(a.DockerfilePath, constants.DefaultDockerfilePath)
	a.InitImage = valueOrDefault(a.InitImage, constants.DefaultBusyboxImage)
}

func valueOrDefault(v, def string) string {
	if v != "" {
		return v
	}
	return def
}

func currentNamespace() (string, error) {
	cfg, err := kubectx.CurrentConfig()
	if err != nil {
		return "", err
	}

	current, present := cfg.Contexts[cfg.CurrentContext]
	if present {
		if current.Namespace != "" {
			return current.Namespace, nil
		}
	}

	return "default", nil
}

func setDefaultLocalPort(pf *latest.PortForwardResource) {
	if pf.LocalPort == 0 {
		if pf.Port.Type == schemautil.Int {
			pf.LocalPort = pf.Port.IntVal
		}
	}
}

func setDefaultAddress(pf *latest.PortForwardResource) {
	if pf.Address == "" {
		pf.Address = constants.DefaultPortForwardAddress
	}
}

func setDefaultArtifactDependencyAlias(d *latest.ArtifactDependency) {
	if d.Alias == "" {
		d.Alias = d.ImageName
	}
}

func setDefaultTestWorkspace(c *latest.SkaffoldConfig) {
	for _, tc := range c.Test {
		if tc == nil {
			continue
		}
		tc.Workspace = valueOrDefault(tc.Workspace, ".")
	}
}
<|endoftext|>