<commit_before>// Copyright 2017-2019 Authors of Cilium
//
// 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 helpers

import (
	"bytes"
	"fmt"
	"os"
	"time"

	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	"github.com/cilium/cilium/pkg/versioncheck"
	"github.com/cilium/cilium/test/ginkgo-ext"
)

var (
	// HelperTimeout is a predefined timeout value for commands.
	HelperTimeout = 4 * time.Minute

	// ShortCommandTimeout is a timeout for commands which should not take a
	// long time to execute.
	ShortCommandTimeout = 10 * time.Second

	// MidCommandTimeout is a timeout for commands which may take a bit longer
	// than ShortCommandTimeout, but less time than HelperTimeout to execute.
	MidCommandTimeout = 30 * time.Second

	// CiliumStartTimeout is a predefined timeout value for Cilium startup.
	CiliumStartTimeout = 100 * time.Second

	// CheckLogs newtes a new buffer where all the warnings and checks that
	// happens during the test are saved. This buffer will be printed in the
	// test output inside <checks> labels.
	CheckLogs = ginkgoext.NewWriter(new(bytes.Buffer))
)

const (

	//CiliumPath is the path where cilium test code is located.
	CiliumPath = "/src/github.com/cilium/cilium/test"

	// K8sManifestBase tells ginkgo suite where to look for manifests
	K8sManifestBase = "k8sT/manifests"

	// VM / Test suite constants.
	K8s     = "k8s"
	K8s1    = "k8s1"
	K8s2    = "k8s2"
	K8s3    = "k8s3"
	Runtime = "runtime"

	Enabled  = "enabled"
	Disabled = "disabled"
	Total    = "total"
	Public   = "public"
	Private  = "private"
	Name     = "Name"

	// CiliumAgentLabel is the label used for Cilium
	CiliumAgentLabel = "k8s-app=cilium"

	// CiliumOperatorLabel is the label used in the Cilium Operator deployment
	CiliumOperatorLabel = "io.cilium/app=operator"

	// PolicyEnforcement represents the PolicyEnforcement configuration option
	// for the Cilium agent.
	PolicyEnforcement = "PolicyEnforcement"

	// PolicyEnforcementDefault represents the default PolicyEnforcement mode
	// for Cilium.
	PolicyEnforcementDefault = "default"

	// PolicyEnforcementAlways represents the PolicyEnforcement mode
	// for Cilium in which traffic is denied by default even when no policy
	// is imported.
	PolicyEnforcementAlways = "always"

	// PolicyEnforcementNever represents the PolicyEnforcement mode
	// for Cilium in which traffic is always allowed even if there is a policy
	// selecting endpoints.
	PolicyEnforcementNever = "never"

	// CiliumDockerNetwork is the name of the Docker network which Cilium manages.
	CiliumDockerNetwork = "cilium-net"

	// HostDockerNetwork is the name of the host network driver.
	HostDockerNetwork = "host"

	// Names of commonly used containers in tests.
	Httpd1 = "httpd1"
	Httpd2 = "httpd2"
	Httpd3 = "httpd3"
	App1   = "app1"
	App2   = "app2"
	App3   = "app3"
	Client = "client"
	Server = "server"
	Host   = "host"
	// Container lifecycle actions.
	Create = "create"
	Delete = "delete"

	// IP Address families.
	IPv4 = "IPv4"
	IPv6 = "IPv6"

	// LogPerm is the permission for files that are created by this framework
	// that contain logs, outputs of Cilium CLI commands, etc.
	LogPerm = os.FileMode(0666)

	// Configuration options for endpoints. Copied from endpoint/endpoint.go
	// TODO: these should be converted into types for use in configuration
	// functions instead of using basic strings.

	OptionConntrackAccounting = "ConntrackAccounting"
	OptionConntrackLocal      = "ConntrackLocal"
	OptionConntrack           = "Conntrack"
	OptionDebug               = "Debug"
	OptionDropNotify          = "DropNotification"
	OptionTraceNotify         = "TraceNotification"
	OptionNAT46               = "NAT46"
	OptionIngressPolicy       = "IngressPolicy"
	OptionEgressPolicy        = "EgressPolicy"
	OptionIngress             = "ingress"
	OptionEgress              = "egress"
	OptionNone                = "none"
	OptionDisabled            = "Disabled"
	OptionEnabled             = "Enabled"

	StateTerminating = "Terminating"
	StateRunning     = "Running"

	PingCount = 5

	// CurlConnectTimeout is the timeout for the connect() call that curl
	// invokes
	CurlConnectTimeout = 5

	// CurlMaxTimeout is the hard timeout. It starts when curl is invoked
	// and interrupts curl regardless of whether curl is currently
	// connecting or transferring data. CurlMaxTimeout should be at least 5
	// seconds longer than CurlConnectTimeout to provide some time to
	// actually transfer data.
	CurlMaxTimeout = 8

	DefaultNamespace       = "default"
	KubeSystemNamespace    = "kube-system"
	CiliumNamespaceDefault = KubeSystemNamespace
	CiliumNamespaceGKE     = "cilium"

	TestResultsPath = "test_results/"
	RunDir          = "/var/run/cilium"
	LibDir          = "/var/lib/cilium"

	DaemonName             = "cilium"
	CiliumBugtool          = "cilium-bugtool"
	CiliumDockerDaemonName = "cilium-docker"
	AgentDaemon            = "cilium-agent"

	GeneratedHTMLManifest   = "html.yaml"
	GeneratedServerManifest = "server.yaml"
	GeneratedClientManifest = "client.yaml"

	KubectlCreate = ResourceLifeCycleAction("create")
	KubectlDelete = ResourceLifeCycleAction("delete")
	KubectlApply  = ResourceLifeCycleAction("apply")

	KubectlPolicyNameLabel      = k8sConst.PolicyLabelName
	KubectlPolicyNameSpaceLabel = k8sConst.PolicyLabelNamespace

	// CiliumStableHelmChartVersion should be the chart version that points
	// to the v1.X branch
	CiliumStableHelmChartVersion = "1.7-dev"
	CiliumStableVersion          = "v1.7"
	CiliumLatestHelmChartVersion = "1.7.90"
	CiliumLatestImageVersion     = "latest"

	MonitorLogFileName = "monitor.log"

	// CiliumTestLog is the filename where the cilium logs that happens during
	// the test are saved.
	CiliumTestLog = "cilium-test.log"

	// FakeIPv4WorldAddress is an IP which is used in some datapath tests
	// for simulating external IPv4 connectivity.
	FakeIPv4WorldAddress = "192.168.254.254"

	// FakeIPv6WorldAddress is an IP which is used in some datapath tests
	// for simulating external IPv6 connectivity.
	FakeIPv6WorldAddress = "fdff::ff"

	// DockerBridgeIP is the IP on the docker0 bridge
	DockerBridgeIP = "172.17.0.1"

	// Logs messages that should not be in the cilium logs.
	panicMessage      = "panic:"
	deadLockHeader    = "POTENTIAL DEADLOCK:"                  // from github.com/sasha-s/go-deadlock/deadlock.go:header
	segmentationFault = "segmentation fault"                   // from https://github.com/cilium/cilium/issues/3233
	NACKreceived      = "NACK received for version"            // from https://github.com/cilium/cilium/issues/4003
	RunInitFailed     = "JoinEP: "                             // from https://github.com/cilium/cilium/pull/5052
	sizeMismatch      = "size mismatch for BPF map"            // from https://github.com/cilium/cilium/issues/7851
	emptyBPFInitArg   = "empty argument passed to bpf/init.sh" // from https://github.com/cilium/cilium/issues/10228
	removingMap       = "Removing map to allow for property upgrade"

	// HelmTemplate is the location of the Helm templates to install Cilium
	HelmTemplate = "../install/kubernetes/cilium"
)

var (
	// CiliumNamespace is where cilium should run. In some deployments this cannot
	// be kube-system.
	CiliumNamespace = GetCiliumNamespace(GetCurrentIntegration())

	// LogGathererNamespace is where log-gatherer should run. It follows cilium
	// for simplicity.
	LogGathererNamespace = CiliumNamespace
)

// Re-definitions of stable constants in the API. The re-definition is on
// purpose to validate these values in the API. They may never change
const (
	// ReservedIdentityHealth is equivalent to pkg/identity.ReservedIdentityHealth
	ReservedIdentityHealth = 4
)

// NightlyStableUpgradesFrom maps the cilium image versions to the helm charts
// that will be used to run update tests in the Nightly test.
var NightlyStableUpgradesFrom = map[string]string{"v1.6": "1.6-dev", "v1.7": "1.7-dev"}

var (
	IsCiliumV1_5 = versioncheck.MustCompile(">=1.4.90 <1.6.0")
	IsCiliumV1_6 = versioncheck.MustCompile(">=1.5.90 <1.7.0")
	IsCiliumV1_7 = versioncheck.MustCompile(">=1.6.90 <1.8.0")
	IsCiliumV1_8 = versioncheck.MustCompile(">=1.7.90 <1.9.0")
)

// CiliumDefaultDSPatch is the default Cilium DaemonSet patch to be used in all tests.
const CiliumDefaultDSPatch = "cilium-ds-patch.yaml"

// CiliumConfigMapPatch is the default Cilium ConfigMap patch to be used in all tests.
const CiliumConfigMapPatch = "cilium-cm-patch.yaml"

// CiliumConfigMapPatchKvstoreAllocator is equivalent to CiliumConfigMapPatch
// except it uses the kvstore-based allocator instead of the CRD-based allocator.
const CiliumConfigMapPatchKvstoreAllocator = "cilium-cm-kvstore-allocator-patch.yaml"

// badLogMessages is a map which key is a part of a log message which indicates
// a failure if the message does not contain any part from value list.
var badLogMessages = map[string][]string{
	panicMessage:      nil,
	deadLockHeader:    nil,
	segmentationFault: nil,
	NACKreceived:      nil,
	RunInitFailed:     {"signal: terminated", "signal: killed"},
	sizeMismatch:      nil,
	emptyBPFInitArg:   nil,
	removingMap:       nil,
}

var ciliumCLICommands = map[string]string{
	"cilium endpoint list -o json":          "endpoint_list.txt",
	"cilium service list -o json":           "service_list.txt",
	"cilium config":                         "config.txt",
	"sudo cilium bpf lb list":               "bpf_lb_list.txt",
	"sudo cilium bpf ct list global":        "bpf_ct_list.txt",
	"sudo cilium bpf tunnel list":           "bpf_tunnel_list.txt",
	"cilium policy get":                     "policy_get.txt",
	"cilium status --all-controllers":       "status.txt",
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

// ciliumKubCLICommands these commands are the same as `ciliumCLICommands` but
// it'll run inside a container and it does not have sudo support
var ciliumKubCLICommands = map[string]string{
	"cilium endpoint list -o json":    "endpoint_list.txt",
	"cilium service list -o json":     "service_list.txt",
	"cilium config":                   "config.txt",
	"cilium bpf lb list":              "bpf_lb_list.txt",
	"cilium bpf ct list global":       "bpf_ct_list.txt",
	"cilium bpf tunnel list":          "bpf_tunnel_list.txt",
	"cilium policy get":               "policy_get.txt",
	"cilium status --all-controllers": "status.txt",
}

// ciliumKubCLICommandsKVStore contains commands related to querying the kvstore.
// It is separate from ciliumKubCLICommands because it has a higher likelihood
// of timing out in our CI, so we want to run it separately. Otherwise, we might
// lose out on getting other critical debugging output when a test fails.
var ciliumKubCLICommandsKVStore = map[string]string{
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

const (
	ciliumEtcdOperatorSA   = "cilium-etcd-operator-sa.yaml"
	ciliumEtcdOperatorRBAC = "cilium-etcd-operator-rbac.yaml"
	ciliumEtcdOperator     = "cilium-etcd-operator.yaml"
)

// K8s1VMName is the name of the Kubernetes master node when running K8s tests.
func K8s1VMName() string {
	return fmt.Sprintf("k8s1-%s", GetCurrentK8SEnv())
}

// K8s2VMName is the name of the Kubernetes worker node when running K8s tests.
func K8s2VMName() string {
	return fmt.Sprintf("k8s2-%s", GetCurrentK8SEnv())
}
<commit_msg>test: Fail in case of log buffer too small warning<commit_after>// Copyright 2017-2019 Authors of Cilium
//
// 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 helpers

import (
	"bytes"
	"fmt"
	"os"
	"time"

	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	"github.com/cilium/cilium/pkg/versioncheck"
	"github.com/cilium/cilium/test/ginkgo-ext"
)

var (
	// HelperTimeout is a predefined timeout value for commands.
	HelperTimeout = 4 * time.Minute

	// ShortCommandTimeout is a timeout for commands which should not take a
	// long time to execute.
	ShortCommandTimeout = 10 * time.Second

	// MidCommandTimeout is a timeout for commands which may take a bit longer
	// than ShortCommandTimeout, but less time than HelperTimeout to execute.
	MidCommandTimeout = 30 * time.Second

	// CiliumStartTimeout is a predefined timeout value for Cilium startup.
	CiliumStartTimeout = 100 * time.Second

	// CheckLogs newtes a new buffer where all the warnings and checks that
	// happens during the test are saved. This buffer will be printed in the
	// test output inside <checks> labels.
	CheckLogs = ginkgoext.NewWriter(new(bytes.Buffer))
)

const (

	//CiliumPath is the path where cilium test code is located.
	CiliumPath = "/src/github.com/cilium/cilium/test"

	// K8sManifestBase tells ginkgo suite where to look for manifests
	K8sManifestBase = "k8sT/manifests"

	// VM / Test suite constants.
	K8s     = "k8s"
	K8s1    = "k8s1"
	K8s2    = "k8s2"
	K8s3    = "k8s3"
	Runtime = "runtime"

	Enabled  = "enabled"
	Disabled = "disabled"
	Total    = "total"
	Public   = "public"
	Private  = "private"
	Name     = "Name"

	// CiliumAgentLabel is the label used for Cilium
	CiliumAgentLabel = "k8s-app=cilium"

	// CiliumOperatorLabel is the label used in the Cilium Operator deployment
	CiliumOperatorLabel = "io.cilium/app=operator"

	// PolicyEnforcement represents the PolicyEnforcement configuration option
	// for the Cilium agent.
	PolicyEnforcement = "PolicyEnforcement"

	// PolicyEnforcementDefault represents the default PolicyEnforcement mode
	// for Cilium.
	PolicyEnforcementDefault = "default"

	// PolicyEnforcementAlways represents the PolicyEnforcement mode
	// for Cilium in which traffic is denied by default even when no policy
	// is imported.
	PolicyEnforcementAlways = "always"

	// PolicyEnforcementNever represents the PolicyEnforcement mode
	// for Cilium in which traffic is always allowed even if there is a policy
	// selecting endpoints.
	PolicyEnforcementNever = "never"

	// CiliumDockerNetwork is the name of the Docker network which Cilium manages.
	CiliumDockerNetwork = "cilium-net"

	// HostDockerNetwork is the name of the host network driver.
	HostDockerNetwork = "host"

	// Names of commonly used containers in tests.
	Httpd1 = "httpd1"
	Httpd2 = "httpd2"
	Httpd3 = "httpd3"
	App1   = "app1"
	App2   = "app2"
	App3   = "app3"
	Client = "client"
	Server = "server"
	Host   = "host"
	// Container lifecycle actions.
	Create = "create"
	Delete = "delete"

	// IP Address families.
	IPv4 = "IPv4"
	IPv6 = "IPv6"

	// LogPerm is the permission for files that are created by this framework
	// that contain logs, outputs of Cilium CLI commands, etc.
	LogPerm = os.FileMode(0666)

	// Configuration options for endpoints. Copied from endpoint/endpoint.go
	// TODO: these should be converted into types for use in configuration
	// functions instead of using basic strings.

	OptionConntrackAccounting = "ConntrackAccounting"
	OptionConntrackLocal      = "ConntrackLocal"
	OptionConntrack           = "Conntrack"
	OptionDebug               = "Debug"
	OptionDropNotify          = "DropNotification"
	OptionTraceNotify         = "TraceNotification"
	OptionNAT46               = "NAT46"
	OptionIngressPolicy       = "IngressPolicy"
	OptionEgressPolicy        = "EgressPolicy"
	OptionIngress             = "ingress"
	OptionEgress              = "egress"
	OptionNone                = "none"
	OptionDisabled            = "Disabled"
	OptionEnabled             = "Enabled"

	StateTerminating = "Terminating"
	StateRunning     = "Running"

	PingCount = 5

	// CurlConnectTimeout is the timeout for the connect() call that curl
	// invokes
	CurlConnectTimeout = 5

	// CurlMaxTimeout is the hard timeout. It starts when curl is invoked
	// and interrupts curl regardless of whether curl is currently
	// connecting or transferring data. CurlMaxTimeout should be at least 5
	// seconds longer than CurlConnectTimeout to provide some time to
	// actually transfer data.
	CurlMaxTimeout = 8

	DefaultNamespace       = "default"
	KubeSystemNamespace    = "kube-system"
	CiliumNamespaceDefault = KubeSystemNamespace
	CiliumNamespaceGKE     = "cilium"

	TestResultsPath = "test_results/"
	RunDir          = "/var/run/cilium"
	LibDir          = "/var/lib/cilium"

	DaemonName             = "cilium"
	CiliumBugtool          = "cilium-bugtool"
	CiliumDockerDaemonName = "cilium-docker"
	AgentDaemon            = "cilium-agent"

	GeneratedHTMLManifest   = "html.yaml"
	GeneratedServerManifest = "server.yaml"
	GeneratedClientManifest = "client.yaml"

	KubectlCreate = ResourceLifeCycleAction("create")
	KubectlDelete = ResourceLifeCycleAction("delete")
	KubectlApply  = ResourceLifeCycleAction("apply")

	KubectlPolicyNameLabel      = k8sConst.PolicyLabelName
	KubectlPolicyNameSpaceLabel = k8sConst.PolicyLabelNamespace

	// CiliumStableHelmChartVersion should be the chart version that points
	// to the v1.X branch
	CiliumStableHelmChartVersion = "1.7-dev"
	CiliumStableVersion          = "v1.7"
	CiliumLatestHelmChartVersion = "1.7.90"
	CiliumLatestImageVersion     = "latest"

	MonitorLogFileName = "monitor.log"

	// CiliumTestLog is the filename where the cilium logs that happens during
	// the test are saved.
	CiliumTestLog = "cilium-test.log"

	// FakeIPv4WorldAddress is an IP which is used in some datapath tests
	// for simulating external IPv4 connectivity.
	FakeIPv4WorldAddress = "192.168.254.254"

	// FakeIPv6WorldAddress is an IP which is used in some datapath tests
	// for simulating external IPv6 connectivity.
	FakeIPv6WorldAddress = "fdff::ff"

	// DockerBridgeIP is the IP on the docker0 bridge
	DockerBridgeIP = "172.17.0.1"

	// Logs messages that should not be in the cilium logs.
	panicMessage      = "panic:"
	deadLockHeader    = "POTENTIAL DEADLOCK:"                        // from github.com/sasha-s/go-deadlock/deadlock.go:header
	segmentationFault = "segmentation fault"                         // from https://github.com/cilium/cilium/issues/3233
	NACKreceived      = "NACK received for version"                  // from https://github.com/cilium/cilium/issues/4003
	RunInitFailed     = "JoinEP: "                                   // from https://github.com/cilium/cilium/pull/5052
	sizeMismatch      = "size mismatch for BPF map"                  // from https://github.com/cilium/cilium/issues/7851
	emptyBPFInitArg   = "empty argument passed to bpf/init.sh"       // from https://github.com/cilium/cilium/issues/10228
	removingMap       = "Removing map to allow for property upgrade" // from https://github.com/cilium/cilium/pull/10626
	logBufferMessage  = "Log buffer too small to dump verifier log"  // from https://github.com/cilium/cilium/issues/10517

	// HelmTemplate is the location of the Helm templates to install Cilium
	HelmTemplate = "../install/kubernetes/cilium"
)

var (
	// CiliumNamespace is where cilium should run. In some deployments this cannot
	// be kube-system.
	CiliumNamespace = GetCiliumNamespace(GetCurrentIntegration())

	// LogGathererNamespace is where log-gatherer should run. It follows cilium
	// for simplicity.
	LogGathererNamespace = CiliumNamespace
)

// Re-definitions of stable constants in the API. The re-definition is on
// purpose to validate these values in the API. They may never change
const (
	// ReservedIdentityHealth is equivalent to pkg/identity.ReservedIdentityHealth
	ReservedIdentityHealth = 4
)

// NightlyStableUpgradesFrom maps the cilium image versions to the helm charts
// that will be used to run update tests in the Nightly test.
var NightlyStableUpgradesFrom = map[string]string{"v1.6": "1.6-dev", "v1.7": "1.7-dev"}

var (
	IsCiliumV1_5 = versioncheck.MustCompile(">=1.4.90 <1.6.0")
	IsCiliumV1_6 = versioncheck.MustCompile(">=1.5.90 <1.7.0")
	IsCiliumV1_7 = versioncheck.MustCompile(">=1.6.90 <1.8.0")
	IsCiliumV1_8 = versioncheck.MustCompile(">=1.7.90 <1.9.0")
)

// CiliumDefaultDSPatch is the default Cilium DaemonSet patch to be used in all tests.
const CiliumDefaultDSPatch = "cilium-ds-patch.yaml"

// CiliumConfigMapPatch is the default Cilium ConfigMap patch to be used in all tests.
const CiliumConfigMapPatch = "cilium-cm-patch.yaml"

// CiliumConfigMapPatchKvstoreAllocator is equivalent to CiliumConfigMapPatch
// except it uses the kvstore-based allocator instead of the CRD-based allocator.
const CiliumConfigMapPatchKvstoreAllocator = "cilium-cm-kvstore-allocator-patch.yaml"

// badLogMessages is a map which key is a part of a log message which indicates
// a failure if the message does not contain any part from value list.
var badLogMessages = map[string][]string{
	panicMessage:      nil,
	deadLockHeader:    nil,
	segmentationFault: nil,
	NACKreceived:      nil,
	RunInitFailed:     {"signal: terminated", "signal: killed"},
	sizeMismatch:      nil,
	emptyBPFInitArg:   nil,
	removingMap:       nil,
	logBufferMessage:  nil,
}

var ciliumCLICommands = map[string]string{
	"cilium endpoint list -o json":          "endpoint_list.txt",
	"cilium service list -o json":           "service_list.txt",
	"cilium config":                         "config.txt",
	"sudo cilium bpf lb list":               "bpf_lb_list.txt",
	"sudo cilium bpf ct list global":        "bpf_ct_list.txt",
	"sudo cilium bpf tunnel list":           "bpf_tunnel_list.txt",
	"cilium policy get":                     "policy_get.txt",
	"cilium status --all-controllers":       "status.txt",
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

// ciliumKubCLICommands these commands are the same as `ciliumCLICommands` but
// it'll run inside a container and it does not have sudo support
var ciliumKubCLICommands = map[string]string{
	"cilium endpoint list -o json":    "endpoint_list.txt",
	"cilium service list -o json":     "service_list.txt",
	"cilium config":                   "config.txt",
	"cilium bpf lb list":              "bpf_lb_list.txt",
	"cilium bpf ct list global":       "bpf_ct_list.txt",
	"cilium bpf tunnel list":          "bpf_tunnel_list.txt",
	"cilium policy get":               "policy_get.txt",
	"cilium status --all-controllers": "status.txt",
}

// ciliumKubCLICommandsKVStore contains commands related to querying the kvstore.
// It is separate from ciliumKubCLICommands because it has a higher likelihood
// of timing out in our CI, so we want to run it separately. Otherwise, we might
// lose out on getting other critical debugging output when a test fails.
var ciliumKubCLICommandsKVStore = map[string]string{
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

const (
	ciliumEtcdOperatorSA   = "cilium-etcd-operator-sa.yaml"
	ciliumEtcdOperatorRBAC = "cilium-etcd-operator-rbac.yaml"
	ciliumEtcdOperator     = "cilium-etcd-operator.yaml"
)

// K8s1VMName is the name of the Kubernetes master node when running K8s tests.
func K8s1VMName() string {
	return fmt.Sprintf("k8s1-%s", GetCurrentK8SEnv())
}

// K8s2VMName is the name of the Kubernetes worker node when running K8s tests.
func K8s2VMName() string {
	return fmt.Sprintf("k8s2-%s", GetCurrentK8SEnv())
}
<|endoftext|>


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

/*
Package app lets you write Apps for Android (and eventually, iOS).

There are two ways to use Go in an Android App. The first is as a
library called from Java, the second is to use a restricted set of
features but work entirely in Go.

Shared Library

A Go program can be compiled for Android as a shared library. JNI
methods can be implemented via cgo, or generated automatically with
gobind: http://godoc.org/code.google.com/p/go.mobile/cmd/gobind

The library must include a package main and a main function that does
not return until the process exits. Libraries can be cross-compiled
using the Android NDK and the Go tool:

	GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 \
	go build -ldflags="-shared" .

See code.google.com/p/go.mobile/example/libhello for an example of
calling into a Go shared library from a Java Android app.

Native App

An app can be written entirely in Go. This results in a significantly
simpler programming environment (and eventually, portability to iOS),
however only a very restricted set of Android APIs are available.

The provided interfaces are focused on games. It is expected that the
app will draw to the entire screen (via OpenGL, see the go.mobile/gl
package), and that none of the platform's screen management
infrastructure is exposed. On Android, this means a native app is
equivalent to a single Activity (in particular a NativeActivity) and
on iOS, a single UIWindow. Touch events will be accessible via this
package. When Android support is out of preview, all APIs supported by
the Android NDK will be exposed via a Go package.

See code.google.com/p/go.mobile/example/apphello for an example app.

Lifecycle in Native Apps

App execution begins in platform-specific code. Early on in the app's
life, the Go runtime is initialized and the Go main function is called.
(For Android, this is in ANativeActivity_onCreate, for iOS,
application:willFinishLaunchingWithOptions.)

An app is expected to call the Run function in its main. When the main
function exits, the app exits.

	package main

	import (
		"log"

		"google.golang.org/mobile/app"
	)

	func main() {
		app.Run(app.Callbacks{
			Draw: draw,
		})
	}

	func draw() {
		log.Print("In draw loop, can call OpenGL.")
	}

*/
package app
<commit_msg>go.mobile/app: fix import path in docs<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package app lets you write Apps for Android (and eventually, iOS).

There are two ways to use Go in an Android App. The first is as a
library called from Java, the second is to use a restricted set of
features but work entirely in Go.

Shared Library

A Go program can be compiled for Android as a shared library. JNI
methods can be implemented via cgo, or generated automatically with
gobind: http://godoc.org/code.google.com/p/go.mobile/cmd/gobind

The library must include a package main and a main function that does
not return until the process exits. Libraries can be cross-compiled
using the Android NDK and the Go tool:

	GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 \
	go build -ldflags="-shared" .

See code.google.com/p/go.mobile/example/libhello for an example of
calling into a Go shared library from a Java Android app.

Native App

An app can be written entirely in Go. This results in a significantly
simpler programming environment (and eventually, portability to iOS),
however only a very restricted set of Android APIs are available.

The provided interfaces are focused on games. It is expected that the
app will draw to the entire screen (via OpenGL, see the go.mobile/gl
package), and that none of the platform's screen management
infrastructure is exposed. On Android, this means a native app is
equivalent to a single Activity (in particular a NativeActivity) and
on iOS, a single UIWindow. Touch events will be accessible via this
package. When Android support is out of preview, all APIs supported by
the Android NDK will be exposed via a Go package.

See code.google.com/p/go.mobile/example/apphello for an example app.

Lifecycle in Native Apps

App execution begins in platform-specific code. Early on in the app's
life, the Go runtime is initialized and the Go main function is called.
(For Android, this is in ANativeActivity_onCreate, for iOS,
application:willFinishLaunchingWithOptions.)

An app is expected to call the Run function in its main. When the main
function exits, the app exits.

	package main

	import (
		"log"

		"code.google.com/p/go.mobile/app"
	)

	func main() {
		app.Run(app.Callbacks{
			Draw: draw,
		})
	}

	func draw() {
		log.Print("In draw loop, can call OpenGL.")
	}

*/
package app
<|endoftext|>


<commit_before>package transaction

import (
	"github.com/stefankopieczek/gossip/transaction"
	"github.com/stefankopieczek/gossip/transport"
	"testing"
)

// Tests we can start/stop a transaction manager repeatedly on the same port.
func TestStop(t *testing.T) {
	loops := 5
	for i := 0; i < loops; i++ {
		transport, err := transport.NewManager("udp")
		if err != nil {
			t.Fatalf("Failed to start transport manager on loop %v: %v\n", i, err)
		}

		m, err := transaction.NewManager(transport, "localhost:12345")
		if err != nil {
			t.Fatalf("Failed to start transaction manager on loop %v: %v\n", i, err)
		}

		m.Stop()
	}
}
<commit_msg>Fix package for test/manager_test.go<commit_after>package test

import (
	"github.com/stefankopieczek/gossip/transaction"
	"github.com/stefankopieczek/gossip/transport"
	"testing"
)

// Tests we can start/stop a transaction manager repeatedly on the same port.
func TestStop(t *testing.T) {
	loops := 5
	for i := 0; i < loops; i++ {
		transport, err := transport.NewManager("udp")
		if err != nil {
			t.Fatalf("Failed to start transport manager on loop %v: %v\n", i, err)
		}

		m, err := transaction.NewManager(transport, "localhost:12345")
		if err != nil {
			t.Fatalf("Failed to start transaction manager on loop %v: %v\n", i, err)
		}

		m.Stop()
	}
}
<|endoftext|>


<commit_before>// Package appcast provides functionality for working with appcasts to retrieve
// valuable information about software releases.
//
// Currently supports 3 providers: Sparkle RSS Feed, SourceForge RSS Feed and
// GitHub Atom Feed.
//
// See README.md for more info.
package appcast

import (
	"errors"
	"fmt"
	"io/ioutil"
	"regexp"
	"sort"
)

// A BaseAppcast represents the appcast itself and should be inherited by
// provider specific appcasts.
type BaseAppcast struct {
	// Request specifies a Request to be sent by a Client to the server. The
	// response should never be modified in the Request itself.
	Request Request

	// Content specifies the copy of the server response from the
	// Request.HTTPRequest. Unlike the response content from the Request, this can
	// be modified if needed.
	Content string

	// Provider specifies one of the supported providers or Provider.Unknown if
	// the appcast is not recognized by this library.
	Provider Provider

	// Checksum specifies the hash checksum for the original content from
	// Request.HTTPRequest. It also includes the used algorithm, source and the
	// checksum itself.
	Checksum Checksum

	// Releases specify an array of all application releases. All filtered
	// releases are stored here.
	Releases []Release

	// originalReleases specify an original array of all application releases. It
	// is used to restore the BaseAppcast.Releases using the
	// BaseAppcast.ResetFilters function.
	originalReleases []Release
}

// Sort holds different supported sorting behaviors.
type Sort int

const (
	// ASC represents the ascending order.
	ASC Sort = iota

	// DESC represents the descending order.
	DESC
)

// New returns a new BaseAppcast instance pointer.
func New() *BaseAppcast {
	a := &BaseAppcast{}

	return a
}

// LoadFromURL loads the appcast content from remote URL and attempts to guess
// the provider.
func (a *BaseAppcast) LoadFromURL(url string) error {
	req, err := NewRequest(url)
	if err != nil {
		return err
	}

	resp, err := DefaultClient.Do(req)
	if err != nil {
		return err
	}

	// content
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	a.Content = string(body)
	a.Checksum.Source = a.Content

	// provider
	a.Provider = GuessProviderFromURL(url)
	if a.Provider == Unknown {
		a.Provider = GuessProviderFromContent(a.Content)
	}

	return nil
}

// LoadFromFile loads the appcast content from local file and attempts to guess
// the provider.
func (a *BaseAppcast) LoadFromFile(path string) error {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	a.Content = string(data)
	a.Checksum.Source = a.Content
	a.Provider = GuessProviderFromContent(a.Content)

	return nil
}

// GenerateChecksum generates and returns the checksum based on provided
// algorithm from BaseAppcast.Checksum.Source. The checksum is also stored as a
// BaseAppcast.Checksum.Result value.
func (a *BaseAppcast) GenerateChecksum(algorithm ChecksumAlgorithm) string {
	a.Checksum.Algorithm = algorithm
	a.Checksum.Source = a.Content
	a.Checksum.Generate()

	return a.Checksum.Result
}

// GetChecksum is a convenience function to retrieve the checksum value stored
// as BaseAppcast.Checksum.Result.
func (a *BaseAppcast) GetChecksum() string {
	return a.Checksum.Result
}

// Uncomment uncomments the commented out lines by calling the appropriate
// provider specific Uncomment function from the supported providers. A
// successful call returns a "nil" error.
func (a *BaseAppcast) Uncomment() error {
	switch a.Provider {
	case SparkleRSSFeed:
		s := SparkleRSSFeedAppcast{BaseAppcast: *a}
		s.Uncomment()
		a.Content = s.BaseAppcast.Content
		break
	default:
		p := a.Provider.String()
		if p == "-" {
			p = "Unknown"
		}
		return fmt.Errorf("Uncommenting is not available for \"%s\" provider", p)
	}

	return nil
}

// ExtractReleases parses the BaseAppcast.Content by calling the appropriate
// provider specific ExtractReleases function. A successful call returns a "nil"
// error.
func (a *BaseAppcast) ExtractReleases() error {
	switch a.Provider {
	case SparkleRSSFeed:
		s := SparkleRSSFeedAppcast{BaseAppcast: *a}
		err := s.ExtractReleases()
		if err != nil {
			return err
		}
		a.Releases = s.BaseAppcast.Releases
		a.originalReleases = a.Releases
		break
	case SourceForgeRSSFeed:
		s := SourceForgeRSSFeedAppcast{BaseAppcast: *a}
		err := s.ExtractReleases()
		if err != nil {
			return err
		}
		a.Releases = s.BaseAppcast.Releases
		a.originalReleases = a.Releases
		break
	case GitHubAtomFeed:
		s := GitHubAtomFeedAppcast{BaseAppcast: *a}
		err := s.ExtractReleases()
		if err != nil {
			return err
		}
		a.Releases = s.BaseAppcast.Releases
		a.originalReleases = a.Releases
		break
	default:
		p := a.Provider.String()
		if p == "-" {
			p = "Unknown"
		}
		return fmt.Errorf("Releases can't be extracted from \"%s\" provider", p)
	}

	return nil
}

// SortReleasesByVersions sorts BaseAppcast.Releases array by versions. Can be
// useful if the versions order in the content is inconsistent.
func (a *BaseAppcast) SortReleasesByVersions(s Sort) {
	if s == ASC {
		sort.Sort(ByVersion(a.Releases))
	} else if s == DESC {
		sort.Sort(sort.Reverse(ByVersion(a.Releases)))
	}
}

// filterReleasesBy filters all BaseAppcast.Releases using the passed function.
// If inverse is set to "true", the unmatched releases will be used instead.
func (a *BaseAppcast) filterReleasesBy(f func(r Release) bool, inverse bool) {
	var result []Release

	for _, release := range a.Releases {
		if inverse == false && f(release) {
			result = append(result, release)
			continue
		}

		if inverse == true && !f(release) {
			result = append(result, release)
			continue
		}
	}

	a.Releases = result
}

// filterReleasesDownloadsBy filters all Downloads for BaseAppcast.Releases
// using the passed function. If inverse is set to "true", the unmatched
// releases will be used instead.
func (a *BaseAppcast) filterReleasesDownloadsBy(f func(d Download) bool, inverse bool) {
	var result []Release

	for _, release := range a.Releases {
		for _, download := range release.Downloads {
			if inverse == false && f(download) {
				result = append(result, release)
				continue
			}

			if inverse == true && !f(download) {
				result = append(result, release)
				continue
			}
		}
	}

	a.Releases = result
}

// FilterReleasesByTitle filters all BaseAppcast.Releases by matching the
// release title with the provided RegExp string. If inversed bool is set to
// "true", the unmatched releases will be used instead.
func (a *BaseAppcast) FilterReleasesByTitle(regexpStr string, inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesBy(func(r Release) bool {
		re := regexp.MustCompile(regexpStr)
		if re.MatchString(r.Title) {
			return true
		}
		return false
	}, inverse)
}

// FilterReleasesByMediaType filters all releases by matching the downloads
// media type with the provided RegExp string. If inversed bool is set to
// "true", the unmatched releases will be used instead.
func (a *BaseAppcast) FilterReleasesByMediaType(regexpStr string, inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesDownloadsBy(func(d Download) bool {
		re := regexp.MustCompile(regexpStr)
		if re.MatchString(d.Type) {
			return true
		}
		return false
	}, inverse)
}

// FilterReleasesByURL filters all BaseAppcast.Releases by matching the release
// download URL with the provided RegExp string. If inversed bool is set to
// "true", the unmatched releases will be used instead.
func (a *BaseAppcast) FilterReleasesByURL(regexpStr string, inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesDownloadsBy(func(d Download) bool {
		re := regexp.MustCompile(regexpStr)
		if re.MatchString(d.URL) {
			return true
		}
		return false
	}, inverse)
}

// FilterReleasesByPrerelease filters all BaseAppcast.Releases by matching only
// prereleases. If inversed bool is set to "true", the stable releases will be
// matched instead.
func (a *BaseAppcast) FilterReleasesByPrerelease(inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesBy(func(r Release) bool {
		if r.IsPrerelease == true {
			return true
		}
		return false
	}, inverse)
}

// ResetFilters resets the BaseAppcast.Releases to their original state before
// applying any filters.
func (a *BaseAppcast) ResetFilters() {
	a.Releases = a.originalReleases
}

// ExtractSemanticVersions extracts semantic versions from the provided data
// string.
func ExtractSemanticVersions(data string) ([]string, error) {
	var versions []string

	re := regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)(?:(\-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-\-\.]+)?`)
	if re.MatchString(data) {
		versionMatches := re.FindAllStringSubmatch(data, -1)
		for _, match := range versionMatches {
			versions = append(versions, match[0])
		}

		return versions, nil
	}

	return nil, errors.New("No semantic versions found")
}
<commit_msg>Add convenience functions GetReleasesLength() and GetFirstRelease() in appcast<commit_after>// Package appcast provides functionality for working with appcasts to retrieve
// valuable information about software releases.
//
// Currently supports 3 providers: Sparkle RSS Feed, SourceForge RSS Feed and
// GitHub Atom Feed.
//
// See README.md for more info.
package appcast

import (
	"errors"
	"fmt"
	"io/ioutil"
	"regexp"
	"sort"
)

// A BaseAppcast represents the appcast itself and should be inherited by
// provider specific appcasts.
type BaseAppcast struct {
	// Request specifies a Request to be sent by a Client to the server. The
	// response should never be modified in the Request itself.
	Request Request

	// Content specifies the copy of the server response from the
	// Request.HTTPRequest. Unlike the response content from the Request, this can
	// be modified if needed.
	Content string

	// Provider specifies one of the supported providers or Provider.Unknown if
	// the appcast is not recognized by this library.
	Provider Provider

	// Checksum specifies the hash checksum for the original content from
	// Request.HTTPRequest. It also includes the used algorithm, source and the
	// checksum itself.
	Checksum Checksum

	// Releases specify an array of all application releases. All filtered
	// releases are stored here.
	Releases []Release

	// url specifies current URL for the Request.HTTPRequest.
	url string

	// originalReleases specify an original array of all application releases. It
	// is used to restore the BaseAppcast.Releases using the
	// BaseAppcast.ResetFilters function.
	originalReleases []Release
}

// Sort holds different supported sorting behaviors.
type Sort int

const (
	// ASC represents the ascending order.
	ASC Sort = iota

	// DESC represents the descending order.
	DESC
)

// New returns a new BaseAppcast instance pointer.
func New() *BaseAppcast {
	a := &BaseAppcast{}

	return a
}

// LoadFromURL loads the appcast content from remote URL and attempts to guess
// the provider.
func (a *BaseAppcast) LoadFromURL(url string) error {
	a.url = url

	req, err := NewRequest(url)
	if err != nil {
		return err
	}

	resp, err := DefaultClient.Do(req)
	if err != nil {
		return err
	}

	// content
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	a.Content = string(body)
	a.Checksum.Source = a.Content

	// provider
	a.Provider = GuessProviderFromURL(url)
	if a.Provider == Unknown {
		a.Provider = GuessProviderFromContent(a.Content)
	}

	return nil
}

// LoadFromFile loads the appcast content from local file and attempts to guess
// the provider.
func (a *BaseAppcast) LoadFromFile(path string) error {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	a.Content = string(data)
	a.Checksum.Source = a.Content
	a.Provider = GuessProviderFromContent(a.Content)

	return nil
}

// GenerateChecksum generates and returns the checksum based on provided
// algorithm from BaseAppcast.Checksum.Source. The checksum is also stored as a
// BaseAppcast.Checksum.Result value.
func (a *BaseAppcast) GenerateChecksum(algorithm ChecksumAlgorithm) string {
	a.Checksum.Algorithm = algorithm
	a.Checksum.Source = a.Content
	a.Checksum.Generate()

	return a.Checksum.Result
}

// GetChecksum is a convenience function to retrieve the checksum value stored
// as BaseAppcast.Checksum.Result.
func (a *BaseAppcast) GetChecksum() string {
	return a.Checksum.Result
}

// GetProvider is a convenience function to retrieve the provider value stored
// as BaseAppcast.Provider.
func (a *BaseAppcast) GetProvider() Provider {
	return a.Provider
}

// GetURL is a convenience function to retrieve the current Request URL string
// value stored as BaseAppcast.url.
func (a *BaseAppcast) GetURL() string {
	return a.url
}

// Uncomment uncomments the commented out lines by calling the appropriate
// provider specific Uncomment function from the supported providers. A
// successful call returns a "nil" error.
func (a *BaseAppcast) Uncomment() error {
	switch a.Provider {
	case SparkleRSSFeed:
		s := SparkleRSSFeedAppcast{BaseAppcast: *a}
		s.Uncomment()
		a.Content = s.BaseAppcast.Content
		break
	default:
		p := a.Provider.String()
		if p == "-" {
			p = "Unknown"
		}
		return fmt.Errorf("Uncommenting is not available for \"%s\" provider", p)
	}

	return nil
}

// ExtractReleases parses the BaseAppcast.Content by calling the appropriate
// provider specific ExtractReleases function. A successful call returns a "nil"
// error.
func (a *BaseAppcast) ExtractReleases() error {
	switch a.Provider {
	case SparkleRSSFeed:
		s := SparkleRSSFeedAppcast{BaseAppcast: *a}
		err := s.ExtractReleases()
		if err != nil {
			return err
		}
		a.Releases = s.BaseAppcast.Releases
		a.originalReleases = a.Releases
		break
	case SourceForgeRSSFeed:
		s := SourceForgeRSSFeedAppcast{BaseAppcast: *a}
		err := s.ExtractReleases()
		if err != nil {
			return err
		}
		a.Releases = s.BaseAppcast.Releases
		a.originalReleases = a.Releases
		break
	case GitHubAtomFeed:
		s := GitHubAtomFeedAppcast{BaseAppcast: *a}
		err := s.ExtractReleases()
		if err != nil {
			return err
		}
		a.Releases = s.BaseAppcast.Releases
		a.originalReleases = a.Releases
		break
	default:
		p := a.Provider.String()
		if p == "-" {
			p = "Unknown"
		}
		return fmt.Errorf("Releases can't be extracted from \"%s\" provider", p)
	}

	return nil
}

// SortReleasesByVersions sorts BaseAppcast.Releases array by versions. Can be
// useful if the versions order in the content is inconsistent.
func (a *BaseAppcast) SortReleasesByVersions(s Sort) {
	if s == ASC {
		sort.Sort(ByVersion(a.Releases))
	} else if s == DESC {
		sort.Sort(sort.Reverse(ByVersion(a.Releases)))
	}
}

// filterReleasesBy filters all BaseAppcast.Releases using the passed function.
// If inverse is set to "true", the unmatched releases will be used instead.
func (a *BaseAppcast) filterReleasesBy(f func(r Release) bool, inverse bool) {
	var result []Release

	for _, release := range a.Releases {
		if inverse == false && f(release) {
			result = append(result, release)
			continue
		}

		if inverse == true && !f(release) {
			result = append(result, release)
			continue
		}
	}

	a.Releases = result
}

// filterReleasesDownloadsBy filters all Downloads for BaseAppcast.Releases
// using the passed function. If inverse is set to "true", the unmatched
// releases will be used instead.
func (a *BaseAppcast) filterReleasesDownloadsBy(f func(d Download) bool, inverse bool) {
	var result []Release

	for _, release := range a.Releases {
		for _, download := range release.Downloads {
			if inverse == false && f(download) {
				result = append(result, release)
				continue
			}

			if inverse == true && !f(download) {
				result = append(result, release)
				continue
			}
		}
	}

	a.Releases = result
}

// FilterReleasesByTitle filters all BaseAppcast.Releases by matching the
// release title with the provided RegExp string. If inversed bool is set to
// "true", the unmatched releases will be used instead.
func (a *BaseAppcast) FilterReleasesByTitle(regexpStr string, inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesBy(func(r Release) bool {
		re := regexp.MustCompile(regexpStr)
		if re.MatchString(r.Title) {
			return true
		}
		return false
	}, inverse)
}

// FilterReleasesByMediaType filters all releases by matching the downloads
// media type with the provided RegExp string. If inversed bool is set to
// "true", the unmatched releases will be used instead.
func (a *BaseAppcast) FilterReleasesByMediaType(regexpStr string, inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesDownloadsBy(func(d Download) bool {
		re := regexp.MustCompile(regexpStr)
		if re.MatchString(d.Type) {
			return true
		}
		return false
	}, inverse)
}

// FilterReleasesByURL filters all BaseAppcast.Releases by matching the release
// download URL with the provided RegExp string. If inversed bool is set to
// "true", the unmatched releases will be used instead.
func (a *BaseAppcast) FilterReleasesByURL(regexpStr string, inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesDownloadsBy(func(d Download) bool {
		re := regexp.MustCompile(regexpStr)
		if re.MatchString(d.URL) {
			return true
		}
		return false
	}, inverse)
}

// FilterReleasesByPrerelease filters all BaseAppcast.Releases by matching only
// prereleases. If inversed bool is set to "true", the stable releases will be
// matched instead.
func (a *BaseAppcast) FilterReleasesByPrerelease(inversed ...interface{}) {
	inverse := false
	if len(inversed) > 0 {
		inverse = inversed[0].(bool)
	}

	a.filterReleasesBy(func(r Release) bool {
		if r.IsPrerelease == true {
			return true
		}
		return false
	}, inverse)
}

// ResetFilters resets the BaseAppcast.Releases to their original state before
// applying any filters.
func (a *BaseAppcast) ResetFilters() {
	a.Releases = a.originalReleases
}

// GetReleasesLength is a convenience function to retrieve the total number of
// releases in BaseAppcast.Releases array.
func (a *BaseAppcast) GetReleasesLength() int {
	return len(a.Releases)
}

// GetFirstRelease is a convenience function to retrieve the first release
// pointer from BaseAppcast.Releases array.
func (a *BaseAppcast) GetFirstRelease() *Release {
	return &a.Releases[0]
}

// ExtractSemanticVersions extracts semantic versions from the provided data
// string.
func ExtractSemanticVersions(data string) ([]string, error) {
	var versions []string

	re := regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)(?:(\-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-\-\.]+)?`)
	if re.MatchString(data) {
		versionMatches := re.FindAllStringSubmatch(data, -1)
		for _, match := range versionMatches {
			versions = append(versions, match[0])
		}

		return versions, nil
	}

	return nil, errors.New("No semantic versions found")
}
<|endoftext|>


<commit_before>package Amqp

import (
	"flag"
	"fmt"
	"github.com/streadway/amqp"
)

var (
	flgPrefetch = flag.Int("prefetch", 64, "prefetch message from mq")
)

func ConnectMq(url string) (conn *amqp.Connection, channel *amqp.Channel, err error) {
	conn, err = amqp.Dial(url)
	if err == nil {
		channel, err = conn.Channel()
	}

	return
}

func NewMqExchange(channel *amqp.Channel, name, _type string, durable bool) error {
	return channel.ExchangeDeclare(
		name,    // name
		_type,   // type
		durable, // durable
		false,   // auto-delete
		false,   // internal
		false,   // nowait
		nil,     // args
	)
}

func Publish(channel *amqp.Channel, exchange, rkey string, msg []byte) error {
	if channel == nil {
		return fmt.Errorf("channel is nil")
	}

	return channel.Publish(exchange, rkey, false, false,
		amqp.Publishing{ContentType: "application/octet-stream", Body: msg})
}

func newMqConsumer(url, exchange, queue, rkey, ctag string, ack, durable, exclusive bool) (
	conn *amqp.Connection, channel *amqp.Channel, deliveries <-chan amqp.Delivery, err error) {

	conn, channel, err = ConnectMq(url)
	if err != nil {
		return
	}

	channel.QueueDeclare(
		queue,     // name of the queue
		durable,   // durable
		exclusive, // delete when usused
		exclusive, // exclusive
		false,     // noWait
		nil,       // arguments
	)

	if err = channel.QueueBind(
		queue,    // name of the queue
		rkey,     // bindingKey
		exchange, // sourceExchange
		false,    // noWait
		nil,      // arguments
	); err != nil {
		conn.Close()
		return
	}

	deliveries, err = channel.Consume(
		queue,     // name
		ctag,      // consumerTag,
		!ack,      // noAck
		exclusive, // exclusive
		false,     // noLocal
		false,     // noWait
		nil,       // arguments
	)
	if err != nil {
		conn.Close()
		return
	}

	if ack {
		channel.Qos(*flgPrefetch, 0, true)
	}
	return
}

func NewMqConsumer(url, exchange, queue, rkey, ctag string, ack, durable bool) (
	conn *amqp.Connection, channel *amqp.Channel, deliveries <-chan amqp.Delivery, err error) {

	return newMqConsumer(url, exchange, queue, rkey, ctag, ack, durable, false)
}

func NewExclusiveMqConsumer(url, exchange, queue, rkey, ctag string, ack, durable bool) (
	conn *amqp.Connection, channel *amqp.Channel, deliveries <-chan amqp.Delivery, err error) {

	return newMqConsumer(url, exchange, queue, rkey, ctag, ack, durable, true)
}
<commit_msg>add PriorityPublish<commit_after>package Amqp

import (
	"flag"
	"fmt"
	"github.com/streadway/amqp"
)

const (
	Transient  uint8 = 1
	Persistent uint8 = 2

	PriorityMax uint8 = 9
	PriorityMin uint8 = 0
)

var (
	flgPrefetch = flag.Int("prefetch", 64, "prefetch message from mq")
)

func ConnectMq(url string) (conn *amqp.Connection, channel *amqp.Channel, err error) {
	conn, err = amqp.Dial(url)
	if err == nil {
		channel, err = conn.Channel()
	}

	return
}

func NewMqExchange(channel *amqp.Channel, name, _type string, durable bool) error {
	return channel.ExchangeDeclare(
		name,    // name
		_type,   // type
		durable, // durable
		false,   // auto-delete
		false,   // internal
		false,   // nowait
		nil,     // args
	)
}

func Publish(channel *amqp.Channel, exchange, rkey string, msg []byte) error {
	if channel == nil {
		return fmt.Errorf("channel is nil")
	}

	return channel.Publish(exchange, rkey, false, false,
		amqp.Publishing{ContentType: "application/octet-stream", Body: msg})
}

func PriorityPublish(channel *amqp.Channel, exchange, rkey string, savedisk, priority uint8, msg []byte) error {
	if channel == nil {
		return fmt.Errorf("channel is nil")
	}

	return channel.Publish(exchange, rkey, false, false,
		amqp.Publishing{ContentType: "application/octet-stream", Body: msg,
			DeliveryMode: savedisk, Priority: priority})
}

func newMqConsumer(url, exchange, queue, rkey, ctag string, ack, durable, exclusive bool) (
	conn *amqp.Connection, channel *amqp.Channel, deliveries <-chan amqp.Delivery, err error) {

	conn, channel, err = ConnectMq(url)
	if err != nil {
		return
	}

	channel.QueueDeclare(
		queue,     // name of the queue
		durable,   // durable
		exclusive, // delete when usused
		exclusive, // exclusive
		false,     // noWait
		nil,       // arguments
	)

	if err = channel.QueueBind(
		queue,    // name of the queue
		rkey,     // bindingKey
		exchange, // sourceExchange
		false,    // noWait
		nil,      // arguments
	); err != nil {
		conn.Close()
		return
	}

	deliveries, err = channel.Consume(
		queue,     // name
		ctag,      // consumerTag,
		!ack,      // noAck
		exclusive, // exclusive
		false,     // noLocal
		false,     // noWait
		nil,       // arguments
	)
	if err != nil {
		conn.Close()
		return
	}

	if ack {
		channel.Qos(*flgPrefetch, 0, true)
	}
	return
}

func NewMqConsumer(url, exchange, queue, rkey, ctag string, ack, durable bool) (
	conn *amqp.Connection, channel *amqp.Channel, deliveries <-chan amqp.Delivery, err error) {

	return newMqConsumer(url, exchange, queue, rkey, ctag, ack, durable, false)
}

func NewExclusiveMqConsumer(url, exchange, queue, rkey, ctag string, ack, durable bool) (
	conn *amqp.Connection, channel *amqp.Channel, deliveries <-chan amqp.Delivery, err error) {

	return newMqConsumer(url, exchange, queue, rkey, ctag, ack, durable, true)
}
<|endoftext|>


<commit_before>/*
 Package search provides a mini-DSL for nexus.Client.Artifacts(). Nexus' API supports 4 different types of searches,
 but in the end, all we need is a map holding the parameters to pass along.
*/
package search

// Criteria compiles to a single map with the parameters to give Nexus. So it only supports queries which can be made
// in a single API call.
type Criteria interface {
	Parameters() map[string]string
}

// The zero value for criteria. There's no reason for more than one value to exist, so it's unexported and
// made bool for Go to allow a const.
type zeroCriteria bool

func (empty zeroCriteria) Parameters() map[string]string {
	return map[string]string{}
}

// The zero value for criteria. Its Parameters() method returns an empty map.
const CriteriaZero = zeroCriteria(false)

// Searches by Maven coordinates.
type ByCoordinates struct {
	GroupId    string
	ArtifactId string
	Version    string
	Packaging  string
	Classifier string
}

func (gav ByCoordinates) Parameters() map[string]string {
	return map[string]string{
		"g": gav.GroupId,
		"a": gav.ArtifactId,
		"v": gav.Version,
		"p": gav.Packaging,
		"c": gav.Classifier,
	}
}

// Searches by keywords.
type ByKeyword string

func (q ByKeyword) Parameters() map[string]string {
	return map[string]string{
		"q": string(q),
	}
}

// Searches by class name.
type ByClassname string

func (cn ByClassname) Parameters() map[string]string {
	return map[string]string{
		"cn": string(cn),
	}
}

// Searches by SHA1 checksum.
type ByChecksum string

func (sha1 ByChecksum) Parameters() map[string]string {
	return map[string]string{
		"sha1": string(sha1),
	}
}

// Searches all artifacts in the given repository ID.
type ByRepository string

func (byRepo ByRepository) Parameters() map[string]string {
	return map[string]string{
		"repositoryId": string(byRepo),
	}
}

// Searches for all artifacts in the given repository ID following the given criteria.
type InRepository struct {
	RepositoryId string

	Criteria
}

func (inRepo InRepository) Parameters() map[string]string {
	params := inRepo.Criteria.Parameters()
	params["repositoryId"] = inRepo.RepositoryId

	return params
}
<commit_msg>Little doc correction.<commit_after>/*
 Package search provides a mini-DSL for nexus.Client.Artifacts(). Nexus' API supports 4 different types of searches,
 but in the end, all we need is a map holding the parameters to pass along.
*/
package search

// Criteria compiles to a single map with the parameters to give Nexus. So it only supports queries which can be made
// in a single API call.
type Criteria interface {
	Parameters() map[string]string
}

// The zero value for criteria. There's no reason for more than one value to exist, so it's unexported and
// made bool for Go to allow a const.
type zeroCriteria bool

func (empty zeroCriteria) Parameters() map[string]string {
	return map[string]string{}
}

// The zero value for criteria. Its Parameters() method returns an empty map.
const CriteriaZero = zeroCriteria(false)

// Searches by Maven coordinates.
type ByCoordinates struct {
	GroupId    string
	ArtifactId string
	Version    string
	Packaging  string
	Classifier string
}

func (gav ByCoordinates) Parameters() map[string]string {
	return map[string]string{
		"g": gav.GroupId,
		"a": gav.ArtifactId,
		"v": gav.Version,
		"p": gav.Packaging,
		"c": gav.Classifier,
	}
}

// Searches by keywords.
type ByKeyword string

func (q ByKeyword) Parameters() map[string]string {
	return map[string]string{
		"q": string(q),
	}
}

// Searches by class name.
type ByClassname string

func (cn ByClassname) Parameters() map[string]string {
	return map[string]string{
		"cn": string(cn),
	}
}

// Searches by SHA1 checksum.
type ByChecksum string

func (sha1 ByChecksum) Parameters() map[string]string {
	return map[string]string{
		"sha1": string(sha1),
	}
}

// Searches for all artifacts in the given repository ID.
type ByRepository string

func (byRepo ByRepository) Parameters() map[string]string {
	return map[string]string{
		"repositoryId": string(byRepo),
	}
}

// Searches for all artifacts in the given repository ID following the given criteria.
type InRepository struct {
	RepositoryId string

	Criteria
}

func (inRepo InRepository) Parameters() map[string]string {
	params := inRepo.Criteria.Parameters()
	params["repositoryId"] = inRepo.RepositoryId

	return params
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/samuel/go-zookeeper/zk"
)

// The address of the NSDB.
var nsdbAddresses = flag.String("zookeeper_hosts", "127.0.0.1:2181",
	"The Addresses of ZooKeeper nodes separated by commas")

// The timout for the NSDB session in seconds.
const sessionTimeoutSec = 10

// The key for the ZOOM topology lock path.
const lockKey = "zoom-topology"

func connect() (*zk.Conn, <-chan zk.Event, error) {
	addresses := strings.Split(*nsdbAddresses, ",")
	for i := range addresses {
		addresses[i] = strings.TrimSpace(addresses[i])
	}
	return zk.Connect(addresses,
		time.Duration(sessionTimeoutSec)*time.Second)
}

func binding(portUuid, hostUuid, interfaceName string) error {
	log.Println("binding port " + portUuid + " to " + interfaceName)

	conn, _, err := connect()
	if err != nil {
		return err
	}
	defer conn.Close()

	lock := zk.NewLock(conn, GetLockPath(lockKey), zk.WorldACL(zk.PermAll))
	if err = lock.Lock(); err != nil {
		return err
	}
	defer lock.Unlock()

	portPath := GetPortPath(portUuid)
	var data []byte
	if data, _, err = conn.Get(portPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on getting port: %s\n", err.Error())
		return err
	}
	port := &WrappedPort{}
	if err = json.Unmarshal(data, port); err != nil {
		fmt.Fprintf(os.Stderr, "Error on deserializing port: %s\n", err.Error())
		return err
	}

	port.Data.HostId = hostUuid
	port.Data.InterfaceName = interfaceName

	updatedPort, err := json.Marshal(port)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on serializing port: %s\n", err.Error())
		return err
	}

	if _, err = conn.Set(portPath, updatedPort, -1); err != nil {
		fmt.Fprintf(os.Stderr, "Error on setting port: %s\n", err.Error())
		return err
	}

	vrnMappingPath := GetVrnMappingPath(hostUuid, portUuid)
	var exists bool
	if exists, _, err = conn.Exists(vrnMappingPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on examining vrnMapping %s\n", err.Error())
		return err
	}
	var vrnMappingData []byte
	vrnMapping := &WrappedVrnMapping{}
	if exists {
		if vrnMappingData, _, err = conn.Get(vrnMappingPath); err != nil {
			fmt.Fprintf(os.Stderr, "Error on getting vrnMapping %s\n", err.Error())
			return err
		}
		log.Println(fmt.Sprintf("Got vrnMapping data: %s", vrnMappingData))
		if err = json.Unmarshal(vrnMappingData, vrnMapping); err != nil {
			fmt.Fprintf(os.Stderr, "Error on deserializing vrnMapping:  %s\n", err.Error())
			return err
		}
	} else {
		data := &VrnMapping{}
		data.VirtualPortId = portUuid
		data.LocalDeviceName = interfaceName
		vrnMapping.Data = data
		vrnMapping.Version = port.Version
	}
	updatedVrnMapping, err := json.Marshal(vrnMapping)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on deserializing vrnMapping: %s\n", err.Error())
		return err
	}
	if exists {
		if _, err = conn.Set(vrnMappingPath, updatedVrnMapping, -1); err != nil {
			fmt.Fprintf(os.Stderr, "Error on setting vrnMapping: %s\n", err.Error())
			return err
		}
	} else {
		if _, err = conn.Create(vrnMappingPath, updatedVrnMapping, 0, zk.WorldACL(zk.PermAll)); err != nil {
			fmt.Fprintf(os.Stderr, "Error on creating a new vrnMapping:  %s\n", err.Error())
			return err
		}
	}

	log.Println("Succeded to bind the port")

	return nil
}

func unbinding(portUuid, hostUuid string) error {
	log.Println("unbinding port " + portUuid)

	conn, _, err := connect()
	if err != nil {
		return err
	}
	defer conn.Close()

	lock := zk.NewLock(conn, GetLockPath(lockKey), zk.WorldACL(zk.PermAll))
	if err = lock.Lock(); err != nil {
		return err
	}
	defer lock.Unlock()

	portPath := GetPortPath(portUuid)
	var data []byte
	if data, _, err = conn.Get(portPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on getting  port: %s\n", err.Error())
		return err
	}
	port := &WrappedPort{}
	if err = json.Unmarshal(data, port); err != nil {
		fmt.Fprintf(os.Stderr, "Error on deserializing port: %s\n", err.Error())
		return err
	}

	if port.Data.HostId != hostUuid {
		return errors.New("The given host ID didn't match with one in NSDB")
	}
	port.Data.InterfaceName = ""

	updatedPort, err := json.Marshal(port)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on serializing port: %s\n", err.Error())
		return err
	}

	if _, err = conn.Set(portPath, updatedPort, -1); err != nil {
		return err
	}

	vrnMappingPath := GetVrnMappingPath(hostUuid, portUuid)
	var exists bool
	if exists, _, err = conn.Exists(vrnMappingPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on examining vrnMapping %s\n", err.Error())
		return err
	}
	if exists {
		if err = conn.Delete(vrnMappingPath, -1); err != nil {
			fmt.Fprintf(os.Stderr, "Error on deleging vrnMapping %s\n", err.Error())
			return err
		}
	}
	log.Println("Succeded to unbind the port")

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

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/samuel/go-zookeeper/zk"
)

// The address of the NSDB.
var nsdbAddresses = flag.String("zookeeper_hosts", "127.0.0.1:2181",
	"The Addresses of ZooKeeper nodes separated by commas")

// The timout for the NSDB session in seconds.
const sessionTimeoutSec = 10

// The key for the ZOOM topology lock path.
const lockKey = "zoom-topology"

func connect() (*zk.Conn, <-chan zk.Event, error) {
	addresses := strings.Split(*nsdbAddresses, ",")
	for i := range addresses {
		addresses[i] = strings.TrimSpace(addresses[i])
	}
	return zk.Connect(addresses,
		time.Duration(sessionTimeoutSec)*time.Second)
}

func binding(portUuid, hostUuid, interfaceName string) error {
	log.Println("binding port " + portUuid + " to " + interfaceName)

	conn, _, err := connect()
	if err != nil {
		return err
	}
	defer conn.Close()

	lock := zk.NewLock(conn, GetLockPath(lockKey), zk.WorldACL(zk.PermAll))
	if err = lock.Lock(); err != nil {
		return err
	}
	defer lock.Unlock()

	portPath := GetPortPath(portUuid)
	var data []byte
	if data, _, err = conn.Get(portPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on getting port %s: %s\n",
			portPath, err.Error())
		return err
	}
	port := &WrappedPort{}
	if err = json.Unmarshal(data, port); err != nil {
		fmt.Fprintf(os.Stderr, "Error on deserializing port %s: %s\n",
			portPath, err.Error())
		return err
	}

	port.Data.HostId = hostUuid
	port.Data.InterfaceName = interfaceName

	updatedPort, err := json.Marshal(port)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on serializing port %s: %s\n",
			portPath, err.Error())
		return err
	}

	if _, err = conn.Set(portPath, updatedPort, -1); err != nil {
		fmt.Fprintf(os.Stderr, "Error on setting port %s: %s\n",
			portPath, err.Error())
		return err
	}

	vrnMappingPath := GetVrnMappingPath(hostUuid, portUuid)
	var exists bool
	if exists, _, err = conn.Exists(vrnMappingPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on examining vrnMapping %s: %s\n",
			vrnMappingPath, err.Error())
		return err
	}
	var vrnMappingData []byte
	vrnMapping := &WrappedVrnMapping{}
	if exists {
		if vrnMappingData, _, err = conn.Get(vrnMappingPath); err != nil {
			fmt.Fprintf(os.Stderr, "Error on getting vrnMapping %s: %s\n",
				vrnMappingPath, err.Error())
			return err
		}
		log.Println(fmt.Sprintf("Got vrnMapping data: %s", vrnMappingData))
		if err = json.Unmarshal(vrnMappingData, vrnMapping); err != nil {
			fmt.Fprintf(os.Stderr, "Error on deserializing vrnMapping %s: %s\n",
				vrnMappingPath, err.Error())
			return err
		}
	} else {
		data := &VrnMapping{}
		data.VirtualPortId = portUuid
		data.LocalDeviceName = interfaceName
		vrnMapping.Data = data
		vrnMapping.Version = port.Version
	}
	updatedVrnMapping, err := json.Marshal(vrnMapping)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on deserializing vrnMapping %s: %s\n",
			vrnMappingPath, err.Error())
		return err
	}
	if exists {
		if _, err = conn.Set(vrnMappingPath, updatedVrnMapping, -1); err != nil {
			fmt.Fprintf(os.Stderr, "Error on setting vrnMapping %s: %s\n",
				vrnMappingPath, err.Error())
			return err
		}
	} else {
		if _, err = conn.Create(vrnMappingPath, updatedVrnMapping, 0, zk.WorldACL(zk.PermAll)); err != nil {
			fmt.Fprintf(os.Stderr, "Error on creating a new vrnMapping %s: %s\n",
				vrnMappingPath, err.Error())
			return err
		}
	}

	log.Println("Succeded to bind the port")

	return nil
}

func unbinding(portUuid, hostUuid string) error {
	log.Println("unbinding port " + portUuid)

	conn, _, err := connect()
	if err != nil {
		return err
	}
	defer conn.Close()

	lock := zk.NewLock(conn, GetLockPath(lockKey), zk.WorldACL(zk.PermAll))
	if err = lock.Lock(); err != nil {
		return err
	}
	defer lock.Unlock()

	portPath := GetPortPath(portUuid)
	var data []byte
	if data, _, err = conn.Get(portPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on getting  port %s: %s\n",
			portPath, err.Error())
		return err
	}
	port := &WrappedPort{}
	if err = json.Unmarshal(data, port); err != nil {
		fmt.Fprintf(os.Stderr, "Error on deserializing port %s: %s\n",
			portPath, err.Error())
		return err
	}

	if port.Data.HostId != hostUuid {
		return errors.New("The given host ID didn't match with one in NSDB")
	}
	port.Data.InterfaceName = ""

	updatedPort, err := json.Marshal(port)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on serializing port %s: %s\n",
			portPath, err.Error())
		return err
	}

	if _, err = conn.Set(portPath, updatedPort, -1); err != nil {
		return err
	}

	vrnMappingPath := GetVrnMappingPath(hostUuid, portUuid)
	var exists bool
	if exists, _, err = conn.Exists(vrnMappingPath); err != nil {
		fmt.Fprintf(os.Stderr, "Error on examining vrnMapping %s: %s\n",
			vrnMappingPath, err.Error())
		return err
	}
	if exists {
		if err = conn.Delete(vrnMappingPath, -1); err != nil {
			fmt.Fprintf(os.Stderr, "Error on deleging vrnMapping %s: %s\n",
				vrnMappingPath, err.Error())
			return err
		}
	}
	log.Println("Succeded to unbind the port")

	return nil
}
<|endoftext|>


<commit_before>/*
 * Ferret
 * Copyright (c) 2016 Yieldbot, Inc.
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

// Package search provides search interface and functionality
package search

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"reflect"
	"sort"
	"strconv"
	"time"

	prov "github.com/yieldbot/ferret/providers"
	"github.com/yieldbot/gocli"
	"golang.org/x/net/context"
)

var (
	goCommand     = "open"
	searchTimeout = "5000ms"
	providers     = make(map[string]Provider)
)

func init() {
	if e := os.Getenv("FERRET_GOTO_CMD"); e != "" {
		goCommand = e
	}
	if e := os.Getenv("FERRET_SEARCH_TIMEOUT"); e != "" {
		searchTimeout = e
	}

	prov.Register(ProviderRegister)
}

// Provider represents a provider
type Provider struct {
	Name     string
	Title    string
	Enabled  bool
	Noui     bool
	Priority int64
	Doer
}

// Providers returns a sorted list of the names of the providers
func Providers() []string {
	l := []string{}
	for n := range providers {
		l = append(l, n)
	}
	sort.Strings(l)
	return l
}

// ProviderByName returns a provider by the given name
func ProviderByName(name string) (Provider, error) {
	p, ok := providers[name]
	if !ok {
		return p, errors.New("provider " + name + " couldn't be found")
	}
	return p, nil
}

// ProviderRegister registers a search provider
func ProviderRegister(provider interface{}) error {

	// Init provider
	p, ok := provider.(Doer)
	if !ok {
		return errors.New("invalid provider")
	}

	var name, title string
	var enabled, noui bool
	var priority int64

	// Get the value of the provider
	v := reflect.Indirect(reflect.ValueOf(p))
	// Iterate the provider fields
	for i := 0; i < v.NumField(); i++ {
		fn := v.Type().Field(i).Name
		ft := v.Field(i).Type().Name()

		if fn == "name" && ft == "string" {
			name = v.Field(i).String()
		} else if fn == "title" && ft == "string" {
			title = v.Field(i).String()
		} else if fn == "enabled" && ft == "bool" {
			enabled = v.Field(i).Bool()
		} else if fn == "noui" && ft == "bool" {
			noui = v.Field(i).Bool()
		} else if fn == "priority" && ft == "int64" {
			priority = v.Field(i).Int()
		}
	}
	if name == "" {
		return errors.New("invalid provider name")
	}
	if title == "" {
		title = name
	}

	// Init provider
	if _, ok := providers[name]; ok {
		return errors.New("search provider " + name + " is already registered")
	}
	np := Provider{
		Name:     name,
		Title:    title,
		Enabled:  enabled,
		Noui:     noui,
		Priority: priority,
		Doer:     p,
	}
	providers[name] = np

	return nil
}

// Doer is the interface that must be implemented by a search provider
type Doer interface {
	// Search makes a search
	Search(ctx context.Context, args map[string]interface{}) ([]map[string]interface{}, error)
}

// Do makes a search query by the given query
func Do(query Query) (Query, error) {

	// Provider
	provider, ok := providers[query.Provider]
	if !ok {
		query.HTTPStatus = http.StatusBadRequest
		return query, fmt.Errorf("invalid search provider. Possible search providers are %s", Providers())
	}

	// Keyword
	if query.Keyword == "" {
		query.HTTPStatus = http.StatusBadRequest
		return query, errors.New("missing keyword")
	}

	// Page
	if query.Page <= 0 {
		query.HTTPStatus = http.StatusBadRequest
		return query, errors.New("invalid page #. It should be greater than 0")
	}

	// Limit
	if query.Limit <= 0 {
		query.HTTPStatus = http.StatusBadRequest
		return query, errors.New("invalid limit. It should be greater than 0")
	}

	// Search
	query.Start = time.Now()
	ctx, cancel := context.WithTimeout(context.Background(), query.Timeout)
	defer cancel()
	sq := map[string]interface{}{"page": query.Page, "limit": query.Limit, "keyword": query.Keyword}
	sr, err := provider.Search(ctx, sq)
	if err != nil {
		if err == context.DeadlineExceeded {
			query.HTTPStatus = http.StatusGatewayTimeout
			return query, errors.New("timeout")
		} else if err == context.Canceled {
			query.HTTPStatus = http.StatusInternalServerError
			return query, errors.New("canceled")
		}
		query.HTTPStatus = http.StatusInternalServerError
		return query, errors.New("failed to search due to " + err.Error())
	}
	query.Elapsed = time.Since(query.Start)
	for _, srv := range sr {
		var d string
		if _, ok := srv["Description"]; ok {
			d = srv["Description"].(string)
		}

		var t time.Time
		if _, ok := srv["Date"]; ok {
			t = srv["Date"].(time.Time)
		}

		query.Results = append(query.Results, Result{
			Link:        srv["Link"].(string),
			Title:       srv["Title"].(string),
			Description: d,
			Date:        t,
			From:        provider.Title,
		})
	}

	// Goto
	if query.Goto != 0 {
		if query.Goto < 0 || query.Goto > len(query.Results) {
			return query, fmt.Errorf("invalid result # to go. It should be between 1 and %d", len(query.Results))
		}
		link := query.Results[query.Goto-1].Link
		if _, err = exec.Command(goCommand, link).Output(); err != nil {
			return query, fmt.Errorf("failed to go to %s due to %s. Check FERRET_GOTO_CMD environment variable", link, err.Error())
		}
		return query, nil
	}

	return query, nil
}

// PrintResults prints the given search results
func PrintResults(query Query, err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if query.Goto == 0 {
		t := gocli.Table{}
		t.AddRow(1, "#", "TITLE")
		for i, v := range query.Results {
			ts := ""
			if !v.Date.IsZero() {
				ts = fmt.Sprintf(" (%d-%02d-%02d)", v.Date.Year(), v.Date.Month(), v.Date.Day())
			}
			t.AddRow(i+2, fmt.Sprintf("%d", i+1), fmt.Sprintf("%s%s", v.Title, ts))
		}
		t.PrintData()
		fmt.Printf("\n%dms\n", int64(query.Elapsed/time.Millisecond))
	}
}

// ParsePage parses page from a given string
func ParsePage(page string) int {
	p := 1
	if page != "" {
		i, err := strconv.Atoi(page)
		if err == nil && i > 0 {
			p = i
		}
	}
	return p
}

// ParseGoto parses goto from a given string
func ParseGoto(gt string) int {
	g := 0
	if gt != "" {
		i, err := strconv.Atoi(gt)
		if err == nil && i > 0 {
			g = i
		}
	}
	return g
}

// ParseTimeout parses timeout from a given string
func ParseTimeout(timeout string) time.Duration {
	t := 5000 * time.Millisecond
	if timeout != "" {
		d, err := time.ParseDuration(timeout)
		if err == nil {
			t = d
		}
	} else {
		d, err := time.ParseDuration(searchTimeout)
		if err == nil {
			t = d
		}
	}
	return t
}

// ParseLimit parses limit from a given string
func ParseLimit(limit string) int {
	l := 10
	if limit != "" {
		i, err := strconv.Atoi(limit)
		if err == nil && i > 0 {
			l = i
		}
	}
	return l
}
<commit_msg>Rename doer interface to searcher (rollback)<commit_after>/*
 * Ferret
 * Copyright (c) 2016 Yieldbot, Inc.
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

// Package search provides search interface and functionality
package search

import (
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"reflect"
	"sort"
	"strconv"
	"time"

	prov "github.com/yieldbot/ferret/providers"
	"github.com/yieldbot/gocli"
	"golang.org/x/net/context"
)

var (
	goCommand     = "open"
	searchTimeout = "5000ms"
	providers     = make(map[string]Provider)
)

func init() {
	if e := os.Getenv("FERRET_GOTO_CMD"); e != "" {
		goCommand = e
	}
	if e := os.Getenv("FERRET_SEARCH_TIMEOUT"); e != "" {
		searchTimeout = e
	}

	prov.Register(ProviderRegister)
}

// Provider represents a provider
type Provider struct {
	Name     string
	Title    string
	Enabled  bool
	Noui     bool
	Priority int64
	Searcher
}

// Providers returns a sorted list of the names of the providers
func Providers() []string {
	l := []string{}
	for n := range providers {
		l = append(l, n)
	}
	sort.Strings(l)
	return l
}

// ProviderByName returns a provider by the given name
func ProviderByName(name string) (Provider, error) {
	p, ok := providers[name]
	if !ok {
		return p, errors.New("provider " + name + " couldn't be found")
	}
	return p, nil
}

// ProviderRegister registers a search provider
func ProviderRegister(provider interface{}) error {

	// Init provider
	p, ok := provider.(Searcher)
	if !ok {
		return errors.New("invalid provider")
	}

	var name, title string
	var enabled, noui bool
	var priority int64

	// Get the value of the provider
	v := reflect.Indirect(reflect.ValueOf(p))
	// Iterate the provider fields
	for i := 0; i < v.NumField(); i++ {
		fn := v.Type().Field(i).Name
		ft := v.Field(i).Type().Name()

		if fn == "name" && ft == "string" {
			name = v.Field(i).String()
		} else if fn == "title" && ft == "string" {
			title = v.Field(i).String()
		} else if fn == "enabled" && ft == "bool" {
			enabled = v.Field(i).Bool()
		} else if fn == "noui" && ft == "bool" {
			noui = v.Field(i).Bool()
		} else if fn == "priority" && ft == "int64" {
			priority = v.Field(i).Int()
		}
	}
	if name == "" {
		return errors.New("invalid provider name")
	}
	if title == "" {
		title = name
	}

	// Init provider
	if _, ok := providers[name]; ok {
		return errors.New("search provider " + name + " is already registered")
	}
	np := Provider{
		Name:     name,
		Title:    title,
		Enabled:  enabled,
		Noui:     noui,
		Priority: priority,
		Searcher: p,
	}
	providers[name] = np

	return nil
}

// Searcher is the interface that must be implemented by a search provider
type Searcher interface {
	// Search makes a search
	Search(ctx context.Context, args map[string]interface{}) ([]map[string]interface{}, error)
}

// Do makes a search query by the given query
func Do(query Query) (Query, error) {

	// Provider
	provider, ok := providers[query.Provider]
	if !ok {
		query.HTTPStatus = http.StatusBadRequest
		return query, fmt.Errorf("invalid search provider. Possible search providers are %s", Providers())
	}

	// Keyword
	if query.Keyword == "" {
		query.HTTPStatus = http.StatusBadRequest
		return query, errors.New("missing keyword")
	}

	// Page
	if query.Page <= 0 {
		query.HTTPStatus = http.StatusBadRequest
		return query, errors.New("invalid page #. It should be greater than 0")
	}

	// Limit
	if query.Limit <= 0 {
		query.HTTPStatus = http.StatusBadRequest
		return query, errors.New("invalid limit. It should be greater than 0")
	}

	// Search
	query.Start = time.Now()
	ctx, cancel := context.WithTimeout(context.Background(), query.Timeout)
	defer cancel()
	sq := map[string]interface{}{"page": query.Page, "limit": query.Limit, "keyword": query.Keyword}
	sr, err := provider.Search(ctx, sq)
	if err != nil {
		if err == context.DeadlineExceeded {
			query.HTTPStatus = http.StatusGatewayTimeout
			return query, errors.New("timeout")
		} else if err == context.Canceled {
			query.HTTPStatus = http.StatusInternalServerError
			return query, errors.New("canceled")
		}
		query.HTTPStatus = http.StatusInternalServerError
		return query, errors.New("failed to search due to " + err.Error())
	}
	query.Elapsed = time.Since(query.Start)
	for _, srv := range sr {
		var d string
		if _, ok := srv["Description"]; ok {
			d = srv["Description"].(string)
		}

		var t time.Time
		if _, ok := srv["Date"]; ok {
			t = srv["Date"].(time.Time)
		}

		query.Results = append(query.Results, Result{
			Link:        srv["Link"].(string),
			Title:       srv["Title"].(string),
			Description: d,
			Date:        t,
			From:        provider.Title,
		})
	}

	// Goto
	if query.Goto != 0 {
		if query.Goto < 0 || query.Goto > len(query.Results) {
			return query, fmt.Errorf("invalid result # to go. It should be between 1 and %d", len(query.Results))
		}
		link := query.Results[query.Goto-1].Link
		if _, err = exec.Command(goCommand, link).Output(); err != nil {
			return query, fmt.Errorf("failed to go to %s due to %s. Check FERRET_GOTO_CMD environment variable", link, err.Error())
		}
		return query, nil
	}

	return query, nil
}

// PrintResults prints the given search results
func PrintResults(query Query, err error) {
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if query.Goto == 0 {
		t := gocli.Table{}
		t.AddRow(1, "#", "TITLE")
		for i, v := range query.Results {
			ts := ""
			if !v.Date.IsZero() {
				ts = fmt.Sprintf(" (%d-%02d-%02d)", v.Date.Year(), v.Date.Month(), v.Date.Day())
			}
			t.AddRow(i+2, fmt.Sprintf("%d", i+1), fmt.Sprintf("%s%s", v.Title, ts))
		}
		t.PrintData()
		fmt.Printf("\n%dms\n", int64(query.Elapsed/time.Millisecond))
	}
}

// ParsePage parses page from a given string
func ParsePage(page string) int {
	p := 1
	if page != "" {
		i, err := strconv.Atoi(page)
		if err == nil && i > 0 {
			p = i
		}
	}
	return p
}

// ParseGoto parses goto from a given string
func ParseGoto(gt string) int {
	g := 0
	if gt != "" {
		i, err := strconv.Atoi(gt)
		if err == nil && i > 0 {
			g = i
		}
	}
	return g
}

// ParseTimeout parses timeout from a given string
func ParseTimeout(timeout string) time.Duration {
	t := 5000 * time.Millisecond
	if timeout != "" {
		d, err := time.ParseDuration(timeout)
		if err == nil {
			t = d
		}
	} else {
		d, err := time.ParseDuration(searchTimeout)
		if err == nil {
			t = d
		}
	}
	return t
}

// ParseLimit parses limit from a given string
func ParseLimit(limit string) int {
	l := 10
	if limit != "" {
		i, err := strconv.Atoi(limit)
		if err == nil && i > 0 {
			l = i
		}
	}
	return l
}
<|endoftext|>


<commit_before>package etcd

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

type Decoder interface {
	Decode(string, interface{}) error
	SkipMissing(bool)
}

type decoder struct {
	client      client.KeysAPI
	skipMissing bool
}

func NewDecoder(client client.KeysAPI) Decoder {
	return &decoder{
		client: client,
	}
}

func (d *decoder) SkipMissing(skip bool) {
	d.skipMissing = skip
}

func (d *decoder) Decode(path string, v interface{}) error {
	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		return errors.New("destination has to be a pointer")
	}

	return d.decode(path, value.Elem())
}

func (d *decoder) decode(path string, value reflect.Value) error {
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	switch value.Kind() {
	case reflect.Func:
		return errors.New("can't decode func: not implemented")

	case reflect.Chan:
		return errors.New("can't decode channel: not implemented")

	case reflect.Interface:
		v := reflect.New(value.Elem().Type()).Elem()
		d.decode(path, v)
		value.Set(v)

	case reflect.Struct:
		if err := d.decodeStruct(path, value); err != nil {
			return err
		}

	case reflect.Map:
		if err := d.decodeMap(path, value); err != nil {
			return err
		}

	case reflect.Slice, reflect.Array:
		if err := d.decodeSlice(path, value); err != nil {
			return err
		}

	default:
		r, err := d.client.Get(context.Background(), path, &client.GetOptions{})
		if err != nil {
			if e, ok := err.(client.Error); ok && d.skipMissing && e.Code == client.ErrorCodeKeyNotFound {
				return nil
			}
			return err
		}
		return decodePrimitive(r.Node.Value, value)
	}

	return nil
}

func (d *decoder) decodeSlice(path string, value reflect.Value) error {
	r, _ := d.client.Get(context.Background(), path, &client.GetOptions{})
	if !r.Node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeSlice(value.Type(), value.Len(), value.Cap()))
	}

	for _, node := range r.Node.Nodes {
		sliceValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, sliceValue); err != nil {
			return err
		}
		value.Set(reflect.Append(value, sliceValue))
	}

	return nil
}

func (d *decoder) decodeMap(path string, value reflect.Value) error {
	r, _ := d.client.Get(context.Background(), path, &client.GetOptions{})
	if !r.Node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeMap(value.Type()))
	}

	for _, node := range r.Node.Nodes {
		mapValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, mapValue); err != nil {
			return err
		}

		mapKey := reflect.New(value.Type().Key()).Elem()
		p := strings.Split(node.Key, "/")
		mapKey.SetString(p[len(p)-1])
		value.SetMapIndex(mapKey, mapValue)
	}

	return nil
}

func (d *decoder) decodeStruct(path string, value reflect.Value) error {
	for i := 0; i < value.NumField(); i++ {
		typeField := value.Type().Field(i)
		name := typeField.Tag.Get("etcd")
		if name != "-" {
			if name == "" {
				name = typeField.Name
			}
			if err := d.decode(fmt.Sprintf("%s/%s", path, name), value.Field(i)); err != nil {
				return err
			}
		}
	}

	return nil
}

func decodePrimitive(nodeValue string, value reflect.Value) error {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		v, err := strconv.ParseInt(nodeValue, 10, 64)
		if err != nil {
			return err
		}
		value.SetInt(v)

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		v, err := strconv.ParseUint(nodeValue, 10, 64)
		if err != nil {
			return err
		}
		value.SetUint(v)

	case reflect.Float32, reflect.Float64:
		v, err := strconv.ParseFloat(nodeValue, 64)
		if err != nil {
			return err
		}
		value.SetFloat(v)

	case reflect.Bool:
		v, err := strconv.ParseBool(nodeValue)
		if err != nil {
			return err
		}
		value.SetBool(v)
	case reflect.String:
		value.SetString(nodeValue)
	default:
		return errors.New(fmt.Sprintf("can't decode value of type %s", value.Type()))
	}

	return nil
}
<commit_msg>added usage of skipMissing flag while decoding maps and slices<commit_after>package etcd

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

type Decoder interface {
	Decode(string, interface{}) error
	SkipMissing(bool)
}

type decoder struct {
	client      client.KeysAPI
	skipMissing bool
}

func NewDecoder(client client.KeysAPI) Decoder {
	return &decoder{
		client: client,
	}
}

func (d *decoder) SkipMissing(skip bool) {
	d.skipMissing = skip
}

func (d *decoder) Decode(path string, v interface{}) error {
	value := reflect.ValueOf(v)
	if value.Kind() != reflect.Ptr {
		return errors.New("destination has to be a pointer")
	}

	return d.decode(path, value.Elem())
}

func (d *decoder) decode(path string, value reflect.Value) error {
	if value.Kind() == reflect.Ptr {
		value = value.Elem()
	}

	switch value.Kind() {
	case reflect.Func:
		return errors.New("can't decode func: not implemented")

	case reflect.Chan:
		return errors.New("can't decode channel: not implemented")

	case reflect.Interface:
		v := reflect.New(value.Elem().Type()).Elem()
		d.decode(path, v)
		value.Set(v)

	case reflect.Struct:
		if err := d.decodeStruct(path, value); err != nil {
			return err
		}

	case reflect.Map:
		if err := d.decodeMap(path, value); err != nil {
			return err
		}

	case reflect.Slice, reflect.Array:
		if err := d.decodeSlice(path, value); err != nil {
			return err
		}

	default:
		r, err := d.client.Get(context.Background(), path, &client.GetOptions{})
		if err != nil {
			if canSkipMissing(err, d.skipMissing) {
				return nil
			}
			return err
		}
		return decodePrimitive(r.Node.Value, value)
	}

	return nil
}

func (d *decoder) decodeSlice(path string, value reflect.Value) error {
	r, err := d.client.Get(context.Background(), path, &client.GetOptions{})
	if err != nil {
		if canSkipMissing(err, d.skipMissing) {
			return nil
		}
		return err
	}

	if !r.Node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeSlice(value.Type(), value.Len(), value.Cap()))
	}

	for _, node := range r.Node.Nodes {
		sliceValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, sliceValue); err != nil {
			return err
		}
		value.Set(reflect.Append(value, sliceValue))
	}

	return nil
}

func (d *decoder) decodeMap(path string, value reflect.Value) error {
	r, err := d.client.Get(context.Background(), path, &client.GetOptions{})
	if err != nil {
		if canSkipMissing(err, d.skipMissing) {
			return nil
		}
		return err
	}

	if !r.Node.Dir {
		return errors.New(fmt.Sprintf("%s is not a dir", path))
	}

	if value.IsNil() {
		value.Set(reflect.MakeMap(value.Type()))
	}

	for _, node := range r.Node.Nodes {
		mapValue := reflect.New(value.Type().Elem()).Elem()
		if err := d.decode(node.Key, mapValue); err != nil {
			return err
		}

		mapKey := reflect.New(value.Type().Key()).Elem()
		p := strings.Split(node.Key, "/")
		mapKey.SetString(p[len(p)-1])
		value.SetMapIndex(mapKey, mapValue)
	}

	return nil
}

func (d *decoder) decodeStruct(path string, value reflect.Value) error {
	for i := 0; i < value.NumField(); i++ {
		typeField := value.Type().Field(i)
		name := typeField.Tag.Get("etcd")
		if name != "-" {
			if name == "" {
				name = typeField.Name
			}
			if err := d.decode(fmt.Sprintf("%s/%s", path, name), value.Field(i)); err != nil {
				return err
			}
		}
	}

	return nil
}

func decodePrimitive(nodeValue string, value reflect.Value) error {
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		v, err := strconv.ParseInt(nodeValue, 10, 64)
		if err != nil {
			return err
		}
		value.SetInt(v)

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		v, err := strconv.ParseUint(nodeValue, 10, 64)
		if err != nil {
			return err
		}
		value.SetUint(v)

	case reflect.Float32, reflect.Float64:
		v, err := strconv.ParseFloat(nodeValue, 64)
		if err != nil {
			return err
		}
		value.SetFloat(v)

	case reflect.Bool:
		v, err := strconv.ParseBool(nodeValue)
		if err != nil {
			return err
		}
		value.SetBool(v)
	case reflect.String:
		value.SetString(nodeValue)
	default:
		return errors.New(fmt.Sprintf("can't decode value of type %s", value.Type()))
	}

	return nil
}

func canSkipMissing(err error, skipFlag bool) bool {
	if e, ok := err.(client.Error); ok && skipFlag && e.Code == client.ErrorCodeKeyNotFound {
		return true
	}

	return false
}
<|endoftext|>