<commit_before>// SPDX-License-Identifier: Apache-2.0
// Copyright 2017 Authors of Cilium

package helpers

import (
	"context"
	"fmt"
	"strconv"
	"strings"
)

// PerfTest represents a type of test to run when running `netperf`.
type PerfTest string

const (
	// TCP_RR represents a netperf test for TCP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_RR = PerfTest("TCP_RR")

	// TCP_STREAM represents a netperf test for TCP throughput performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_STREAM = PerfTest("TCP_STREAM")

	// TCP_MAERTS represents a netperf test for TCP throughput performance (reverse direction of TCP_STREAM).
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_MAERTS = PerfTest("TCP_MAERTS")

	// TCP_CRR represents a netperf test that connects and sends single request/response
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_CRR = PerfTest("TCP_CRR")

	// UDP_RR represents a netperf test for UDP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	UDP_RR = PerfTest("UDP_RR")
)

// PingWithCount returns the string representing the ping command to ping the
// specified endpoint, and takes a custom number of requests to send.
func PingWithCount(endpoint string, count uint) string {
	return fmt.Sprintf("ping -W %d -c %d %s", PingTimeout, count, endpoint)
}

// Ping returns the string representing the ping command to ping the specified
// endpoint.
func Ping(endpoint string) string {
	return PingWithCount(endpoint, PingCount)
}

// Ping6 returns the string representing the ping6 command to ping6 the
// specified endpoint.
func Ping6(endpoint string) string {
	return fmt.Sprintf("ping6 -c %d %s", PingCount, endpoint)
}

func Ping6WithID(endpoint string, icmpID uint16) string {
	return fmt.Sprintf("xping -6 -W %d -c %d -x %d %s", PingTimeout, PingCount, icmpID, endpoint)
}

func PingWithID(endpoint string, icmpID uint16) string {
	return fmt.Sprintf("xping -W %d -c %d -x %d %s", PingTimeout, PingCount, icmpID, endpoint)
}

// Wrk runs a standard wrk test for http
func Wrk(endpoint string) string {
	return fmt.Sprintf("wrk -t2 -c100 -d30s -R2000 http://%s", endpoint)
}

// CurlFail returns the string representing the curl command with `-s` and
// `--fail` options enabled to curl the specified endpoint.  It takes a
// variadic optionalValues argument. This is passed on to fmt.Sprintf() and
// used into the curl message.
func CurlFail(endpoint string, optionalValues ...interface{}) string {
	statsInfo := `time-> DNS: '%{time_namelookup}(%{remote_ip})', Connect: '%{time_connect}',` +
		`Transfer '%{time_starttransfer}', total '%{time_total}'`

	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf(
		`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %d --max-time %d %s -w "%s"`,
		CurlConnectTimeout, CurlMaxTimeout, endpoint, statsInfo)
}

// CurlFailNoStats does the same as CurlFail() except that it does not print
// the stats info.
func CurlFailNoStats(endpoint string, optionalValues ...interface{}) string {
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf(
		`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %[1]d --max-time %[2]d %[3]s`,
		CurlConnectTimeout, CurlMaxTimeout, endpoint)
}

// CurlWithHTTPCode retunrs the string representation of the curl command which
// only outputs the HTTP code returned by its execution against the specified
// endpoint. It takes a variadic optinalValues argument. This is passed on to
// fmt.Sprintf() and uses into the curl message
func CurlWithHTTPCode(endpoint string, optionalValues ...interface{}) string {
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}

	return fmt.Sprintf(
		`curl --path-as-is -s  -D /dev/stderr --output /dev/stderr -w '%%{http_code}' --connect-timeout %d %s`,
		CurlConnectTimeout, endpoint)
}

// CurlWithRetries returns the string representation of the curl command that
// retries the request if transient problems occur. The parameter "retries"
// indicates the maximum number of attempts.  If flag "fail" is true, the
// function will call CurlFail() and add --retry flag at the end of the command
// and return.  If flag "fail" is false, the function will generate the command
// with --retry flag and return.
func CurlWithRetries(endpoint string, retries int, fail bool, optionalValues ...interface{}) string {
	if fail {
		return fmt.Sprintf(
			`%s --retry %d`,
			CurlFail(endpoint, optionalValues...), retries)
	}
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf(
		`curl --path-as-is -s  -D /dev/stderr --output /dev/stderr --retry %d %s`,
		retries, endpoint)
}

// Netperf returns the string representing the netperf command to use when testing
// connectivity between endpoints.
func Netperf(endpoint string, perfTest PerfTest, options string) string {
	return fmt.Sprintf("netperf -l 3 -t %s -H %s %s", perfTest, endpoint, options)
}

// SuperNetperf returns the string representing the super_netperf command to use when
// testing connectivity between endpoints.
func SuperNetperf(sessions int, endpoint string, perfTest PerfTest, options string) string {
	return fmt.Sprintf("super_netperf %d -t %s -H %s %s", sessions, perfTest, endpoint, options)
}

// Netcat returns the string representing the netcat command to the specified
// endpoint. It takes a variadic optionalValues arguments, This is passed to
// fmt.Sprintf uses in the netcat message.
func Netcat(endpoint string, optionalValues ...interface{}) string {
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf("nc -w 4 %s", endpoint)
}

// PythonBind returns the string representing a python3 command which will try
// to bind a socket on the given address and port. Python is available in the
// log-gatherer pod.
func PythonBind(addr string, port uint16, proto string) string {
	var opts []string
	if strings.Contains(addr, ":") {
		opts = append(opts, "family=socket.AF_INET6")
	} else {
		opts = append(opts, "family=socket.AF_INET")
	}

	switch strings.ToLower(proto) {
	case "tcp":
		opts = append(opts, "type=socket.SOCK_STREAM")
	case "udp":
		opts = append(opts, "type=socket.SOCK_DGRAM")
	}

	return fmt.Sprintf(
		`/usr/bin/python3 -c 'import socket; socket.socket(%s).bind((%q, %d))`,
		strings.Join(opts, ", "), addr, port)
}

// ReadFile returns the string representing a cat command to read the file at
// the give path.
func ReadFile(path string) string {
	return fmt.Sprintf("cat %q", path)
}

// OpenSSLShowCerts retrieve the TLS certificate presented at the given
// host:port when serverName is requested. The openssl cli is available in the
// Cilium pod.
func OpenSSLShowCerts(host string, port uint16, serverName string) string {
	serverNameFlag := ""
	if serverName != "" {
		serverNameFlag = fmt.Sprintf("-servername %q", serverName)
	}
	return fmt.Sprintf("openssl s_client -connect %s:%d %s -showcerts | openssl x509 -outform PEM", host, port, serverNameFlag)
}

// GetBPFPacketsCount returns the number of packets for a given drop reason and
// direction by parsing BPF metrics.
func GetBPFPacketsCount(kubectl *Kubectl, pod, reason, direction string) (int, error) {
	cmd := fmt.Sprintf("cilium bpf metrics list -o json | jq '.[] | select(.description == \"%s\").values.%s.packets'", reason, direction)

	res := kubectl.CiliumExecMustSucceed(context.TODO(), pod, cmd)

	return strconv.Atoi(strings.TrimSpace(res.Stdout()))
}
<commit_msg>test/helpers: Clarify expected usage of variadic params<commit_after>// SPDX-License-Identifier: Apache-2.0
// Copyright 2017 Authors of Cilium

package helpers

import (
	"context"
	"fmt"
	"strconv"
	"strings"
)

// PerfTest represents a type of test to run when running `netperf`.
type PerfTest string

const (
	// TCP_RR represents a netperf test for TCP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_RR = PerfTest("TCP_RR")

	// TCP_STREAM represents a netperf test for TCP throughput performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_STREAM = PerfTest("TCP_STREAM")

	// TCP_MAERTS represents a netperf test for TCP throughput performance (reverse direction of TCP_STREAM).
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_MAERTS = PerfTest("TCP_MAERTS")

	// TCP_CRR represents a netperf test that connects and sends single request/response
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_CRR = PerfTest("TCP_CRR")

	// UDP_RR represents a netperf test for UDP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	UDP_RR = PerfTest("UDP_RR")
)

// PingWithCount returns the string representing the ping command to ping the
// specified endpoint, and takes a custom number of requests to send.
func PingWithCount(endpoint string, count uint) string {
	return fmt.Sprintf("ping -W %d -c %d %s", PingTimeout, count, endpoint)
}

// Ping returns the string representing the ping command to ping the specified
// endpoint.
func Ping(endpoint string) string {
	return PingWithCount(endpoint, PingCount)
}

// Ping6 returns the string representing the ping6 command to ping6 the
// specified endpoint.
func Ping6(endpoint string) string {
	return fmt.Sprintf("ping6 -c %d %s", PingCount, endpoint)
}

func Ping6WithID(endpoint string, icmpID uint16) string {
	return fmt.Sprintf("xping -6 -W %d -c %d -x %d %s", PingTimeout, PingCount, icmpID, endpoint)
}

func PingWithID(endpoint string, icmpID uint16) string {
	return fmt.Sprintf("xping -W %d -c %d -x %d %s", PingTimeout, PingCount, icmpID, endpoint)
}

// Wrk runs a standard wrk test for http
func Wrk(endpoint string) string {
	return fmt.Sprintf("wrk -t2 -c100 -d30s -R2000 http://%s", endpoint)
}

// CurlFail returns the string representing the curl command with `-s` and
// `--fail` options enabled to curl the specified endpoint.  It takes a
// variadic optionalValues argument. This is passed on to fmt.Sprintf() and
// used into the curl message. Note that `endpoint` is expected to be a format
// string (first argument to fmt.Sprintf()) if optionalValues are used.
func CurlFail(endpoint string, optionalValues ...interface{}) string {
	statsInfo := `time-> DNS: '%{time_namelookup}(%{remote_ip})', Connect: '%{time_connect}',` +
		`Transfer '%{time_starttransfer}', total '%{time_total}'`

	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf(
		`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %d --max-time %d %s -w "%s"`,
		CurlConnectTimeout, CurlMaxTimeout, endpoint, statsInfo)
}

// CurlFailNoStats does the same as CurlFail() except that it does not print
// the stats info. See note about optionalValues on CurlFail().
func CurlFailNoStats(endpoint string, optionalValues ...interface{}) string {
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf(
		`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %[1]d --max-time %[2]d %[3]s`,
		CurlConnectTimeout, CurlMaxTimeout, endpoint)
}

// CurlWithHTTPCode retunrs the string representation of the curl command which
// only outputs the HTTP code returned by its execution against the specified
// endpoint. It takes a variadic optinalValues argument. This is passed on to
// fmt.Sprintf() and uses into the curl message. See note about optionalValues
// on CurlFail().
func CurlWithHTTPCode(endpoint string, optionalValues ...interface{}) string {
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}

	return fmt.Sprintf(
		`curl --path-as-is -s  -D /dev/stderr --output /dev/stderr -w '%%{http_code}' --connect-timeout %d %s`,
		CurlConnectTimeout, endpoint)
}

// CurlWithRetries returns the string representation of the curl command that
// retries the request if transient problems occur. The parameter "retries"
// indicates the maximum number of attempts.  If flag "fail" is true, the
// function will call CurlFail() and add --retry flag at the end of the command
// and return.  If flag "fail" is false, the function will generate the command
// with --retry flag and return. See note about optionalValues on CurlFail().
func CurlWithRetries(endpoint string, retries int, fail bool, optionalValues ...interface{}) string {
	if fail {
		return fmt.Sprintf(
			`%s --retry %d`,
			CurlFail(endpoint, optionalValues...), retries)
	}
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf(
		`curl --path-as-is -s  -D /dev/stderr --output /dev/stderr --retry %d %s`,
		retries, endpoint)
}

// Netperf returns the string representing the netperf command to use when testing
// connectivity between endpoints.
func Netperf(endpoint string, perfTest PerfTest, options string) string {
	return fmt.Sprintf("netperf -l 3 -t %s -H %s %s", perfTest, endpoint, options)
}

// SuperNetperf returns the string representing the super_netperf command to use when
// testing connectivity between endpoints.
func SuperNetperf(sessions int, endpoint string, perfTest PerfTest, options string) string {
	return fmt.Sprintf("super_netperf %d -t %s -H %s %s", sessions, perfTest, endpoint, options)
}

// Netcat returns the string representing the netcat command to the specified
// endpoint. It takes a variadic optionalValues arguments, This is passed to
// fmt.Sprintf uses in the netcat message. See note about optionalValues on
// CurlFail().
func Netcat(endpoint string, optionalValues ...interface{}) string {
	if len(optionalValues) > 0 {
		endpoint = fmt.Sprintf(endpoint, optionalValues...)
	}
	return fmt.Sprintf("nc -w 4 %s", endpoint)
}

// PythonBind returns the string representing a python3 command which will try
// to bind a socket on the given address and port. Python is available in the
// log-gatherer pod.
func PythonBind(addr string, port uint16, proto string) string {
	var opts []string
	if strings.Contains(addr, ":") {
		opts = append(opts, "family=socket.AF_INET6")
	} else {
		opts = append(opts, "family=socket.AF_INET")
	}

	switch strings.ToLower(proto) {
	case "tcp":
		opts = append(opts, "type=socket.SOCK_STREAM")
	case "udp":
		opts = append(opts, "type=socket.SOCK_DGRAM")
	}

	return fmt.Sprintf(
		`/usr/bin/python3 -c 'import socket; socket.socket(%s).bind((%q, %d))`,
		strings.Join(opts, ", "), addr, port)
}

// ReadFile returns the string representing a cat command to read the file at
// the give path.
func ReadFile(path string) string {
	return fmt.Sprintf("cat %q", path)
}

// OpenSSLShowCerts retrieve the TLS certificate presented at the given
// host:port when serverName is requested. The openssl cli is available in the
// Cilium pod.
func OpenSSLShowCerts(host string, port uint16, serverName string) string {
	serverNameFlag := ""
	if serverName != "" {
		serverNameFlag = fmt.Sprintf("-servername %q", serverName)
	}
	return fmt.Sprintf("openssl s_client -connect %s:%d %s -showcerts | openssl x509 -outform PEM", host, port, serverNameFlag)
}

// GetBPFPacketsCount returns the number of packets for a given drop reason and
// direction by parsing BPF metrics.
func GetBPFPacketsCount(kubectl *Kubectl, pod, reason, direction string) (int, error) {
	cmd := fmt.Sprintf("cilium bpf metrics list -o json | jq '.[] | select(.description == \"%s\").values.%s.packets'", reason, direction)

	res := kubectl.CiliumExecMustSucceed(context.TODO(), pod, cmd)

	return strconv.Atoi(strings.TrimSpace(res.Stdout()))
}
<|endoftext|>


<commit_before>// Copyright 2017 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 "fmt"

// PerfTest represents a type of test to run when running `netperf`.
type PerfTest string

const (
	// TCP_RR represents a netperf test for TCP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_RR = PerfTest("TCP_RR")

	// UDP_RR represents a netperf test for UDP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	UDP_RR = PerfTest("UDP_RR")
)

// Ping returns the string representing the ping command to ping the specified
// endpoint.
func Ping(endpoint string) string {
	return fmt.Sprintf("ping -c %d %s", PingCount, endpoint)
}

// Ping6 returns the string representing the ping6 command to ping6 the
// specified endpoint.
func Ping6(endpoint string) string {
	return fmt.Sprintf("ping6 -c %d %s", PingCount, endpoint)
}

// CurlFail returns the string representing the curl command with `-s` and `--fail`
// options enabled to curl the specified endpoint.
func CurlFail(endpoint string) string {
	return fmt.Sprintf("curl -s --fail --connect-timeout %d %s", CurlConnectTimeout, endpoint)
}

// Netperf returns the string representing the netperf command to use when testing
// connectivity between endpoints.
func Netperf(endpoint string, perfTest PerfTest) string {
	return fmt.Sprintf("netperf -l 3 -t %s -H %s", perfTest, endpoint)
}
<commit_msg>helpers: add TODO for Ping wrapper<commit_after>// Copyright 2017 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 "fmt"

// PerfTest represents a type of test to run when running `netperf`.
type PerfTest string

const (
	// TCP_RR represents a netperf test for TCP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_RR = PerfTest("TCP_RR")

	// UDP_RR represents a netperf test for UDP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	UDP_RR = PerfTest("UDP_RR")
)

// Ping returns the string representing the ping command to ping the specified
// endpoint.
func Ping(endpoint string) string {
	// TODO: add W -2 ?
	return fmt.Sprintf("ping -c %d %s", PingCount, endpoint)
}

// Ping6 returns the string representing the ping6 command to ping6 the
// specified endpoint.
func Ping6(endpoint string) string {
	return fmt.Sprintf("ping6 -c %d %s", PingCount, endpoint)
}

// CurlFail returns the string representing the curl command with `-s` and `--fail`
// options enabled to curl the specified endpoint.
func CurlFail(endpoint string) string {
	return fmt.Sprintf("curl -s --fail --connect-timeout %d %s", CurlConnectTimeout, endpoint)
}

// Netperf returns the string representing the netperf command to use when testing
// connectivity between endpoints.
func Netperf(endpoint string, perfTest PerfTest) string {
	return fmt.Sprintf("netperf -l 3 -t %s -H %s", perfTest, endpoint)
}
<|endoftext|>


<commit_before>// Copyright 2017 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 "fmt"

// PerfTest represents a type of test to run when running `netperf`.
type PerfTest string

const (
	// TCP_RR represents a netperf test for TCP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_RR = PerfTest("TCP_RR")

	// UDP_RR represents a netperf test for UDP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	UDP_RR = PerfTest("UDP_RR")
)

// Ping returns the string representing the ping command to ping the specified
// endpoint.
func Ping(endpoint string) string {
	// TODO: add W -2 ?
	return fmt.Sprintf("ping -c %d %s", PingCount, endpoint)
}

// Ping6 returns the string representing the ping6 command to ping6 the
// specified endpoint.
func Ping6(endpoint string) string {
	return fmt.Sprintf("ping6 -c %d %s", PingCount, endpoint)
}

// CurlFail returns the string representing the curl command with `-s` and `--fail`
// options enabled to curl the specified endpoint.
func CurlFail(endpoint string) string {
	return fmt.Sprintf("curl -s --fail --connect-timeout %d %s", CurlConnectTimeout, endpoint)
}

// Netperf returns the string representing the netperf command to use when testing
// connectivity between endpoints.
func Netperf(endpoint string, perfTest PerfTest) string {
	return fmt.Sprintf("netperf -l 3 -t %s -H %s", perfTest, endpoint)
}
<commit_msg>Ginkgo: Added MaxTime option in Curl.<commit_after>// Copyright 2017 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 "fmt"

// PerfTest represents a type of test to run when running `netperf`.
type PerfTest string

const (
	// TCP_RR represents a netperf test for TCP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	TCP_RR = PerfTest("TCP_RR")

	// UDP_RR represents a netperf test for UDP Request/Response performance.
	// For more information, consult : http://www.cs.kent.edu/~farrell/dist/ref/Netperf.html
	UDP_RR = PerfTest("UDP_RR")
)

// Ping returns the string representing the ping command to ping the specified
// endpoint.
func Ping(endpoint string) string {
	// TODO: add W -2 ?
	return fmt.Sprintf("ping -c %d %s", PingCount, endpoint)
}

// Ping6 returns the string representing the ping6 command to ping6 the
// specified endpoint.
func Ping6(endpoint string) string {
	return fmt.Sprintf("ping6 -c %d %s", PingCount, endpoint)
}

// CurlFail returns the string representing the curl command with `-s` and `--fail`
// options enabled to curl the specified endpoint.
func CurlFail(endpoint string) string {
	return fmt.Sprintf("curl -s --fail --connect-timeout %[1]d --max-time %[1]d %[2]s",
		CurlConnectTimeout, endpoint)
}

// Netperf returns the string representing the netperf command to use when testing
// connectivity between endpoints.
func Netperf(endpoint string, perfTest PerfTest) string {
	return fmt.Sprintf("netperf -l 3 -t %s -H %s", perfTest, endpoint)
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver/internal"

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"

	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/model/labels"
	"github.com/prometheus/prometheus/model/textparse"
	"github.com/prometheus/prometheus/model/value"
	"go.opentelemetry.io/collector/pdata/pmetric"
	"go.uber.org/zap"
)

var (
	notUsefulLabelsHistogram = sortString([]string{model.MetricNameLabel, model.InstanceLabel, model.SchemeLabel, model.MetricsPathLabel, model.JobLabel, model.BucketLabel})
	notUsefulLabelsSummary   = sortString([]string{model.MetricNameLabel, model.InstanceLabel, model.SchemeLabel, model.MetricsPathLabel, model.JobLabel, model.QuantileLabel})
	notUsefulLabelsOther     = sortString([]string{model.MetricNameLabel, model.InstanceLabel, model.SchemeLabel, model.MetricsPathLabel, model.JobLabel})
)

func sortString(strs []string) []string {
	sort.Strings(strs)
	return strs
}

func getSortedNotUsefulLabels(mType pmetric.MetricDataType) []string {
	switch mType {
	case pmetric.MetricDataTypeHistogram:
		return notUsefulLabelsHistogram
	case pmetric.MetricDataTypeSummary:
		return notUsefulLabelsSummary
	default:
		return notUsefulLabelsOther
	}
}

func getBoundary(metricType pmetric.MetricDataType, labels labels.Labels) (float64, error) {
	val := ""
	switch metricType {
	case pmetric.MetricDataTypeHistogram:
		val = labels.Get(model.BucketLabel)
		if val == "" {
			return 0, errEmptyLeLabel
		}
	case pmetric.MetricDataTypeSummary:
		val = labels.Get(model.QuantileLabel)
		if val == "" {
			return 0, errEmptyQuantileLabel
		}
	default:
		return 0, errNoBoundaryLabel
	}

	return strconv.ParseFloat(val, 64)
}

// convToMetricType returns the data type and if it is monotonic
func convToMetricType(metricType textparse.MetricType) (pmetric.MetricDataType, bool) {
	switch metricType {
	case textparse.MetricTypeCounter:
		// always use float64, as it's the internal data type used in prometheus
		return pmetric.MetricDataTypeSum, true
	// textparse.MetricTypeUnknown is converted to gauge by default to prevent Prometheus untyped metrics from being dropped
	case textparse.MetricTypeGauge, textparse.MetricTypeUnknown:
		return pmetric.MetricDataTypeGauge, false
	case textparse.MetricTypeHistogram:
		return pmetric.MetricDataTypeHistogram, true
	// dropping support for gaugehistogram for now until we have an official spec of its implementation
	// a draft can be found in: https://docs.google.com/document/d/1KwV0mAXwwbvvifBvDKH_LU1YjyXE_wxCkHNoCGq1GX0/edit#heading=h.1cvzqd4ksd23
	// case textparse.MetricTypeGaugeHistogram:
	//	return <pdata gauge histogram type>
	case textparse.MetricTypeSummary:
		return pmetric.MetricDataTypeSummary, true
	case textparse.MetricTypeInfo, textparse.MetricTypeStateset:
		return pmetric.MetricDataTypeSum, false
	default:
		// including: textparse.MetricTypeGaugeHistogram
		return pmetric.MetricDataTypeNone, false
	}
}

type metricBuilder struct {
	families             map[string]*metricFamily
	hasData              bool
	mc                   MetadataCache
	useStartTimeMetric   bool
	startTimeMetricRegex *regexp.Regexp
	startTime            float64
	logger               *zap.Logger
}

// newMetricBuilder creates a MetricBuilder which is allowed to feed all the datapoints from a single prometheus
// scraped page by calling its AddDataPoint function, and turn them into a pmetric.Metrics object.
// by calling its Build function
func newMetricBuilder(mc MetadataCache, useStartTimeMetric bool, startTimeMetricRegex *regexp.Regexp, logger *zap.Logger) *metricBuilder {
	return &metricBuilder{
		families:             map[string]*metricFamily{},
		mc:                   mc,
		logger:               logger,
		useStartTimeMetric:   useStartTimeMetric,
		startTimeMetricRegex: startTimeMetricRegex,
	}
}

func (b *metricBuilder) matchStartTimeMetric(metricName string) bool {
	if b.startTimeMetricRegex != nil {
		return b.startTimeMetricRegex.MatchString(metricName)
	}

	return metricName == startTimeMetricName
}

// AddDataPoint is for feeding prometheus data values in its processing order
func (b *metricBuilder) AddDataPoint(ls labels.Labels, t int64, v float64) error {
	// Any datapoint with duplicate labels MUST be rejected per:
	// * https://github.com/open-telemetry/wg-prometheus/issues/44
	// * https://github.com/open-telemetry/opentelemetry-collector/issues/3407
	// as Prometheus rejects such too as of version 2.16.0, released on 2020-02-13.
	var dupLabels []string
	for i := 0; i < len(ls)-1; i++ {
		if ls[i].Name == ls[i+1].Name {
			dupLabels = append(dupLabels, ls[i].Name)
		}
	}
	if len(dupLabels) != 0 {
		sort.Strings(dupLabels)
		return fmt.Errorf("invalid sample: non-unique label names: %q", dupLabels)
	}

	metricName := ls.Get(model.MetricNameLabel)
	switch {
	case metricName == "":
		return errMetricNameNotFound
	case isInternalMetric(metricName):
		// See https://www.prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series
		// up: 1 if the instance is healthy, i.e. reachable, or 0 if the scrape failed.
		// But it can also be a staleNaN, which is inserted when the target goes away.
		if metricName == scrapeUpMetricName && v != 1.0 && !value.IsStaleNaN(v) {
			if v == 0.0 {
				b.logger.Warn("Failed to scrape Prometheus endpoint",
					zap.Int64("scrape_timestamp", t),
					zap.Stringer("target_labels", ls))
			} else {
				b.logger.Warn("The 'up' metric contains invalid value",
					zap.Float64("value", v),
					zap.Int64("scrape_timestamp", t),
					zap.Stringer("target_labels", ls))
			}
		}
	case b.useStartTimeMetric && b.matchStartTimeMetric(metricName):
		b.startTime = v
	}

	b.hasData = true

	curMF, ok := b.families[metricName]
	if !ok {
		familyName := normalizeMetricName(metricName)
		if mf, ok := b.families[familyName]; ok && mf.includesMetric(metricName) {
			curMF = mf
		} else {
			curMF = newMetricFamily(metricName, b.mc, b.logger)
			b.families[curMF.name] = curMF
		}
	}

	return curMF.Add(metricName, ls, t, v)
}

// appendMetrics appends all metrics to the given slice.
// The only error returned by this function is errNoDataToBuild.
func (b *metricBuilder) appendMetrics(metrics pmetric.MetricSlice) error {
	if !b.hasData {
		return errNoDataToBuild
	}

	for _, mf := range b.families {
		mf.appendMetric(metrics)
	}

	return nil
}
<commit_msg>[chore] prometheusreceiver: remove unnecessary member, hasData equivalent len is not 0 (#13941)<commit_after>// Copyright The OpenTelemetry 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 internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver/internal"

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"

	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/model/labels"
	"github.com/prometheus/prometheus/model/textparse"
	"github.com/prometheus/prometheus/model/value"
	"go.opentelemetry.io/collector/pdata/pmetric"
	"go.uber.org/zap"
)

var (
	notUsefulLabelsHistogram = sortString([]string{model.MetricNameLabel, model.InstanceLabel, model.SchemeLabel, model.MetricsPathLabel, model.JobLabel, model.BucketLabel})
	notUsefulLabelsSummary   = sortString([]string{model.MetricNameLabel, model.InstanceLabel, model.SchemeLabel, model.MetricsPathLabel, model.JobLabel, model.QuantileLabel})
	notUsefulLabelsOther     = sortString([]string{model.MetricNameLabel, model.InstanceLabel, model.SchemeLabel, model.MetricsPathLabel, model.JobLabel})
)

func sortString(strs []string) []string {
	sort.Strings(strs)
	return strs
}

func getSortedNotUsefulLabels(mType pmetric.MetricDataType) []string {
	switch mType {
	case pmetric.MetricDataTypeHistogram:
		return notUsefulLabelsHistogram
	case pmetric.MetricDataTypeSummary:
		return notUsefulLabelsSummary
	default:
		return notUsefulLabelsOther
	}
}

func getBoundary(metricType pmetric.MetricDataType, labels labels.Labels) (float64, error) {
	val := ""
	switch metricType {
	case pmetric.MetricDataTypeHistogram:
		val = labels.Get(model.BucketLabel)
		if val == "" {
			return 0, errEmptyLeLabel
		}
	case pmetric.MetricDataTypeSummary:
		val = labels.Get(model.QuantileLabel)
		if val == "" {
			return 0, errEmptyQuantileLabel
		}
	default:
		return 0, errNoBoundaryLabel
	}

	return strconv.ParseFloat(val, 64)
}

// convToMetricType returns the data type and if it is monotonic
func convToMetricType(metricType textparse.MetricType) (pmetric.MetricDataType, bool) {
	switch metricType {
	case textparse.MetricTypeCounter:
		// always use float64, as it's the internal data type used in prometheus
		return pmetric.MetricDataTypeSum, true
	// textparse.MetricTypeUnknown is converted to gauge by default to prevent Prometheus untyped metrics from being dropped
	case textparse.MetricTypeGauge, textparse.MetricTypeUnknown:
		return pmetric.MetricDataTypeGauge, false
	case textparse.MetricTypeHistogram:
		return pmetric.MetricDataTypeHistogram, true
	// dropping support for gaugehistogram for now until we have an official spec of its implementation
	// a draft can be found in: https://docs.google.com/document/d/1KwV0mAXwwbvvifBvDKH_LU1YjyXE_wxCkHNoCGq1GX0/edit#heading=h.1cvzqd4ksd23
	// case textparse.MetricTypeGaugeHistogram:
	//	return <pdata gauge histogram type>
	case textparse.MetricTypeSummary:
		return pmetric.MetricDataTypeSummary, true
	case textparse.MetricTypeInfo, textparse.MetricTypeStateset:
		return pmetric.MetricDataTypeSum, false
	default:
		// including: textparse.MetricTypeGaugeHistogram
		return pmetric.MetricDataTypeNone, false
	}
}

type metricBuilder struct {
	families             map[string]*metricFamily
	mc                   MetadataCache
	useStartTimeMetric   bool
	startTimeMetricRegex *regexp.Regexp
	startTime            float64
	logger               *zap.Logger
}

// newMetricBuilder creates a MetricBuilder which is allowed to feed all the datapoints from a single prometheus
// scraped page by calling its AddDataPoint function, and turn them into a pmetric.Metrics object.
// by calling its Build function
func newMetricBuilder(mc MetadataCache, useStartTimeMetric bool, startTimeMetricRegex *regexp.Regexp, logger *zap.Logger) *metricBuilder {
	return &metricBuilder{
		families:             map[string]*metricFamily{},
		mc:                   mc,
		logger:               logger,
		useStartTimeMetric:   useStartTimeMetric,
		startTimeMetricRegex: startTimeMetricRegex,
	}
}

func (b *metricBuilder) matchStartTimeMetric(metricName string) bool {
	if b.startTimeMetricRegex != nil {
		return b.startTimeMetricRegex.MatchString(metricName)
	}

	return metricName == startTimeMetricName
}

// AddDataPoint is for feeding prometheus data values in its processing order
func (b *metricBuilder) AddDataPoint(ls labels.Labels, t int64, v float64) error {
	// Any datapoint with duplicate labels MUST be rejected per:
	// * https://github.com/open-telemetry/wg-prometheus/issues/44
	// * https://github.com/open-telemetry/opentelemetry-collector/issues/3407
	// as Prometheus rejects such too as of version 2.16.0, released on 2020-02-13.
	var dupLabels []string
	for i := 0; i < len(ls)-1; i++ {
		if ls[i].Name == ls[i+1].Name {
			dupLabels = append(dupLabels, ls[i].Name)
		}
	}
	if len(dupLabels) != 0 {
		sort.Strings(dupLabels)
		return fmt.Errorf("invalid sample: non-unique label names: %q", dupLabels)
	}

	metricName := ls.Get(model.MetricNameLabel)
	switch {
	case metricName == "":
		return errMetricNameNotFound
	case isInternalMetric(metricName):
		// See https://www.prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series
		// up: 1 if the instance is healthy, i.e. reachable, or 0 if the scrape failed.
		// But it can also be a staleNaN, which is inserted when the target goes away.
		if metricName == scrapeUpMetricName && v != 1.0 && !value.IsStaleNaN(v) {
			if v == 0.0 {
				b.logger.Warn("Failed to scrape Prometheus endpoint",
					zap.Int64("scrape_timestamp", t),
					zap.Stringer("target_labels", ls))
			} else {
				b.logger.Warn("The 'up' metric contains invalid value",
					zap.Float64("value", v),
					zap.Int64("scrape_timestamp", t),
					zap.Stringer("target_labels", ls))
			}
		}
	case b.useStartTimeMetric && b.matchStartTimeMetric(metricName):
		b.startTime = v
	}

	curMF, ok := b.families[metricName]
	if !ok {
		familyName := normalizeMetricName(metricName)
		if mf, ok := b.families[familyName]; ok && mf.includesMetric(metricName) {
			curMF = mf
		} else {
			curMF = newMetricFamily(metricName, b.mc, b.logger)
			b.families[curMF.name] = curMF
		}
	}

	return curMF.Add(metricName, ls, t, v)
}

// appendMetrics appends all metrics to the given slice.
// The only error returned by this function is errNoDataToBuild.
func (b *metricBuilder) appendMetrics(metrics pmetric.MetricSlice) error {
	if len(b.families) == 0 {
		return errNoDataToBuild
	}

	for _, mf := range b.families {
		mf.appendMetric(metrics)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

/* CHECKLIST
 * [ ] Uses interfaces as appropriate
 * [ ] Private package variables use underscore prefix
 * [ ] All parameters validated
 * [ ] All errors handled
 * [ ] Reviewed for concurrency safety
 * [ ] Code complete
 * [ ] Full test coverage
 */

import (
	"fmt"
	"os"

	"github.com/tidepool-org/platform/app"
	"github.com/tidepool-org/platform/config"
	"github.com/tidepool-org/platform/data"
	"github.com/tidepool-org/platform/data/deduplicator"
	"github.com/tidepool-org/platform/data/deduplicator/delegate"
	"github.com/tidepool-org/platform/data/deduplicator/truncate"
	"github.com/tidepool-org/platform/data/factory"
	"github.com/tidepool-org/platform/data/store"
	"github.com/tidepool-org/platform/data/store/mongo"
	"github.com/tidepool-org/platform/dataservices/service"
	"github.com/tidepool-org/platform/dataservices/service/api"
	"github.com/tidepool-org/platform/dataservices/service/server"
	"github.com/tidepool-org/platform/environment"
	"github.com/tidepool-org/platform/log"
	"github.com/tidepool-org/platform/userservices/client"
	"github.com/tidepool-org/platform/version"
)

var (
	VersionBase        string
	VersionShortCommit string
	VersionFullCommit  string
)

func main() {
	environmentReporter, err := initializeEnvironmentReporter()
	if err != nil {
		fmt.Printf("ERROR: Failure initializing environment reporter: %s\n", err.Error())
		os.Exit(1)
	}

	versionReporter, err := initializeVersionReporter()
	if err != nil {
		fmt.Printf("ERROR: Failure initializing version reporter: %s\n", err.Error())
		os.Exit(1)
	}

	configLoader, err := initializeConfigLoader(environmentReporter)
	if err != nil {
		fmt.Printf("ERROR: Failure initializing config loader: %s\n", err.Error())
		os.Exit(1)
	}

	logger, err := initializeLogger(configLoader, versionReporter)
	if err != nil {
		fmt.Printf("ERROR: Failure initializing logger: %s\n", err.Error())
		os.Exit(1)
	}

	dataFactory, err := initializeDataFactory(logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data factory")
		os.Exit(1)
	}

	dataStore, err := initializeDataStore(configLoader, logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data store")
		os.Exit(1)
	}
	defer dataStore.Close()

	dataDeduplicatorFactory, err := initializeDataDeduplicatorFactory(logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data deduplicator factory")
		os.Exit(1)
	}

	userServicesClient, err := initializeUserServicesClient(configLoader, logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing userservices client")
		os.Exit(1)
	}
	defer userServicesClient.Close()

	api, err := initializeAPI(logger, dataFactory, dataStore, dataDeduplicatorFactory, userServicesClient, versionReporter, environmentReporter)
	if err != nil {
		logger.WithError(err).Error("Failure initializing API")
		os.Exit(1)
	}

	server, err := initializeServer(configLoader, logger, api)
	if err != nil {
		logger.WithError(err).Error("Failure initializing server")
		os.Exit(1)
	}

	if err = server.Serve(); err != nil {
		logger.WithError(err).Error("Failure running server")
		os.Exit(1)
	}
}

// TODO: Wrap this up into an object

func initializeEnvironmentReporter() (environment.Reporter, error) {
	return environment.NewReporter(os.Getenv("TIDEPOOL_ENV"))
}

func initializeVersionReporter() (version.Reporter, error) {
	return version.NewReporter(VersionBase, VersionShortCommit, VersionFullCommit)
}

func initializeConfigLoader(environmentReporter environment.Reporter) (config.Loader, error) {
	return config.NewLoader(os.Getenv("TIDEPOOL_CONFIG_DIRECTORY"), "TIDEPOOL", environmentReporter)
}

func initializeLogger(configLoader config.Loader, versionReporter version.Reporter) (log.Logger, error) {
	loggerConfig := &log.Config{}
	if err := configLoader.Load("logger", loggerConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load logger config")
	}

	logger, err := log.NewLogger(loggerConfig, versionReporter)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to initialize logger")
	}

	logger.Info(fmt.Sprintf("Logger level is %s", loggerConfig.Level))

	return logger, nil
}

func initializeDataFactory(logger log.Logger) (data.Factory, error) {

	logger.Debug("Creating data factory")

	standardDataFactory, err := factory.NewStandard()
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create standard data factory")
	}

	return standardDataFactory, nil
}

func initializeDataStore(configLoader config.Loader, logger log.Logger) (store.Store, error) {

	// TODO: Consider alternate data stores

	logger.Debug("Loading mongo data store config")

	mongoDataStoreConfig := &mongo.Config{}
	if err := configLoader.Load("data_store", mongoDataStoreConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load mongo data store config")
	}
	mongoDataStoreConfig.Collection = "deviceData"

	logger.Debug("Creating mongo data store")

	mongoDataStore, err := mongo.New(logger, mongoDataStoreConfig)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create mongo data store")
	}

	return mongoDataStore, nil
}

func initializeDataDeduplicatorFactory(logger log.Logger) (deduplicator.Factory, error) {

	logger.Debug("Creating data deduplicator factory")

	truncateDeduplicatorFactory, err := truncate.NewFactory()
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create truncate deduplicator factory")
	}

	factories := []deduplicator.Factory{
		truncateDeduplicatorFactory,
	}

	delegateDeduplicatorFactory, err := delegate.NewFactory(factories)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create delegate deduplicator factory")
	}

	return delegateDeduplicatorFactory, nil
}

func initializeUserServicesClient(configLoader config.Loader, logger log.Logger) (client.Client, error) {

	logger.Debug("Loading userservices client config")

	userServicesClientConfig := &client.Config{}
	if err := configLoader.Load("userservices_client", userServicesClientConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load userservices client config")
	}

	logger.Debug("Creating userservices client")

	userServicesClient, err := client.NewStandard(logger, userServicesClientConfig)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create userservices client")
	}

	logger.Debug("Starting userservices client")
	if err = userServicesClient.Start(); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to start userservices client")
	}

	return userServicesClient, nil
}

func initializeAPI(logger log.Logger, dataFactory data.Factory, dataStore store.Store, dataDeduplicatorFactory deduplicator.Factory, userServicesClient client.Client, versionReporter version.Reporter, environmentReporter environment.Reporter) (service.API, error) {
	return api.NewStandard(logger, dataFactory, dataStore, dataDeduplicatorFactory, userServicesClient, versionReporter, environmentReporter)
}

func initializeServer(configLoader config.Loader, logger log.Logger, api service.API) (service.Server, error) {

	logger.Debug("Loading dataservices server config")

	dataservicesServerConfig := &server.Config{}
	if err := configLoader.Load("dataservices_server", dataservicesServerConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load dataservices server config")
	}

	logger.Debug("Creating dataservices server")

	dataservicesServer, err := server.NewStandard(logger, api, dataservicesServerConfig)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create dataservices server")
	}

	return dataservicesServer, nil
}
<commit_msg>Minor rename/refactor of initialization functions for dataservices<commit_after>package main

/* CHECKLIST
 * [ ] Uses interfaces as appropriate
 * [ ] Private package variables use underscore prefix
 * [ ] All parameters validated
 * [ ] All errors handled
 * [ ] Reviewed for concurrency safety
 * [ ] Code complete
 * [ ] Full test coverage
 */

import (
	"fmt"
	"os"

	"github.com/tidepool-org/platform/app"
	"github.com/tidepool-org/platform/config"
	"github.com/tidepool-org/platform/data"
	"github.com/tidepool-org/platform/data/deduplicator"
	"github.com/tidepool-org/platform/data/deduplicator/delegate"
	"github.com/tidepool-org/platform/data/deduplicator/truncate"
	"github.com/tidepool-org/platform/data/factory"
	"github.com/tidepool-org/platform/data/store"
	"github.com/tidepool-org/platform/data/store/mongo"
	"github.com/tidepool-org/platform/dataservices/service"
	"github.com/tidepool-org/platform/dataservices/service/api"
	"github.com/tidepool-org/platform/dataservices/service/server"
	"github.com/tidepool-org/platform/environment"
	"github.com/tidepool-org/platform/log"
	"github.com/tidepool-org/platform/userservices/client"
	"github.com/tidepool-org/platform/version"
)

var (
	VersionBase        string
	VersionShortCommit string
	VersionFullCommit  string
)

func main() {
	environmentReporter, err := initializeEnvironmentReporter()
	if err != nil {
		fmt.Printf("ERROR: Failure initializing environment reporter: %s\n", err.Error())
		os.Exit(1)
	}

	versionReporter, err := initializeVersionReporter()
	if err != nil {
		fmt.Printf("ERROR: Failure initializing version reporter: %s\n", err.Error())
		os.Exit(1)
	}

	configLoader, err := initializeConfigLoader(environmentReporter)
	if err != nil {
		fmt.Printf("ERROR: Failure initializing config loader: %s\n", err.Error())
		os.Exit(1)
	}

	logger, err := initializeLogger(configLoader, versionReporter)
	if err != nil {
		fmt.Printf("ERROR: Failure initializing logger: %s\n", err.Error())
		os.Exit(1)
	}

	dataFactory, err := initializeDataFactory(logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data factory")
		os.Exit(1)
	}

	dataStore, err := initializeDataStore(configLoader, logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data store")
		os.Exit(1)
	}
	defer dataStore.Close()

	dataDeduplicatorFactory, err := initializeDataDeduplicatorFactory(logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data deduplicator factory")
		os.Exit(1)
	}

	userServicesClient, err := initializeUserServicesClient(configLoader, logger)
	if err != nil {
		logger.WithError(err).Error("Failure initializing user services client")
		os.Exit(1)
	}
	defer userServicesClient.Close()

	dataServicesAPI, err := initializeDataServicesAPI(logger, dataFactory, dataStore, dataDeduplicatorFactory, userServicesClient, versionReporter, environmentReporter)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data services API")
		os.Exit(1)
	}

	dataServicesServer, err := initializeDataServicesServer(configLoader, logger, dataServicesAPI)
	if err != nil {
		logger.WithError(err).Error("Failure initializing data services server")
		os.Exit(1)
	}

	if err = dataServicesServer.Serve(); err != nil {
		logger.WithError(err).Error("Failure running data services server")
		os.Exit(1)
	}
}

// TODO: Wrap this up into an object

func initializeEnvironmentReporter() (environment.Reporter, error) {
	return environment.NewReporter(os.Getenv("TIDEPOOL_ENV"))
}

func initializeVersionReporter() (version.Reporter, error) {
	return version.NewReporter(VersionBase, VersionShortCommit, VersionFullCommit)
}

func initializeConfigLoader(environmentReporter environment.Reporter) (config.Loader, error) {
	return config.NewLoader(os.Getenv("TIDEPOOL_CONFIG_DIRECTORY"), "TIDEPOOL", environmentReporter)
}

func initializeLogger(configLoader config.Loader, versionReporter version.Reporter) (log.Logger, error) {
	loggerConfig := &log.Config{}
	if err := configLoader.Load("logger", loggerConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load logger config")
	}

	logger, err := log.NewLogger(loggerConfig, versionReporter)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to initialize logger")
	}

	logger.Info(fmt.Sprintf("Logger level is %s", loggerConfig.Level))

	return logger, nil
}

func initializeDataFactory(logger log.Logger) (data.Factory, error) {

	logger.Debug("Creating data factory")

	standardDataFactory, err := factory.NewStandard()
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create standard data factory")
	}

	return standardDataFactory, nil
}

func initializeDataStore(configLoader config.Loader, logger log.Logger) (store.Store, error) {

	// TODO: Consider alternate data stores

	logger.Debug("Loading mongo data store config")

	mongoDataStoreConfig := &mongo.Config{}
	if err := configLoader.Load("data_store", mongoDataStoreConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load mongo data store config")
	}
	mongoDataStoreConfig.Collection = "deviceData"

	logger.Debug("Creating mongo data store")

	mongoDataStore, err := mongo.New(logger, mongoDataStoreConfig)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create mongo data store")
	}

	return mongoDataStore, nil
}

func initializeDataDeduplicatorFactory(logger log.Logger) (deduplicator.Factory, error) {

	logger.Debug("Creating data deduplicator factory")

	truncateDeduplicatorFactory, err := truncate.NewFactory()
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create truncate deduplicator factory")
	}

	factories := []deduplicator.Factory{
		truncateDeduplicatorFactory,
	}

	delegateDeduplicatorFactory, err := delegate.NewFactory(factories)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create delegate deduplicator factory")
	}

	return delegateDeduplicatorFactory, nil
}

func initializeUserServicesClient(configLoader config.Loader, logger log.Logger) (client.Client, error) {

	logger.Debug("Loading user services client config")

	userServicesClientConfig := &client.Config{}
	if err := configLoader.Load("userservices_client", userServicesClientConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load user services client config")
	}

	logger.Debug("Creating user services client")

	userServicesClient, err := client.NewStandard(logger, userServicesClientConfig)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create user services client")
	}

	logger.Debug("Starting user services client")
	if err = userServicesClient.Start(); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to start user services client")
	}

	return userServicesClient, nil
}

func initializeDataServicesAPI(logger log.Logger, dataFactory data.Factory, dataStore store.Store, dataDeduplicatorFactory deduplicator.Factory, userServicesClient client.Client, versionReporter version.Reporter, environmentReporter environment.Reporter) (service.API, error) {

	logger.Debug("Creating data services api")

	dataServicesAPI, err := api.NewStandard(logger, dataFactory, dataStore, dataDeduplicatorFactory, userServicesClient, versionReporter, environmentReporter)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create data services api")
	}

	return dataServicesAPI, nil
}

func initializeDataServicesServer(configLoader config.Loader, logger log.Logger, api service.API) (service.Server, error) {

	logger.Debug("Loading data services server config")

	dataServicesServerConfig := &server.Config{}
	if err := configLoader.Load("dataservices_server", dataServicesServerConfig); err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to load data services server config")
	}

	logger.Debug("Creating data services server")

	dataServicesServer, err := server.NewStandard(logger, api, dataServicesServerConfig)
	if err != nil {
		return nil, app.ExtError(err, "dataservices", "unable to create data services server")
	}

	return dataServicesServer, nil
}
<|endoftext|>


<commit_before>// Clock counts down to or up from a target time.
package main

import (
	"fmt"
	"os"
	"time"
)

func main() {
	target := time.Date(2018, 10, 28, 0, 0, 0, 0, time.UTC)
	motto := "Just Go"
	printTargetTime(target, motto)
	exitOnEnterKey()

	var previous time.Time
	for {
		now := time.Now().Truncate(time.Second)
		if now != previous {
			previous = now
			countdown := now.Sub(target) // Negative times are before the target
			printCountdown(now, countdown)
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func exitOnEnterKey() {
	go func() {
		buf := make([]byte, 1)
		_, _ = os.Stdin.Read(buf)
		os.Exit(0)
	}()
}

const (
	highlightStart = "\x1b[1;35m"
	highlightEnd   = "\x1b[0m"
	indent         = "\t"
)

func printTargetTime(target time.Time, motto string) {
	fmt.Print(indent, highlightStart, motto, highlightEnd, "\n")
	fmt.Print(indent, target.Format(time.UnixDate), "\n")
}

func printCountdown(now time.Time, countdown time.Duration) {
	var sign string
	if countdown >= 0 {
		sign = "+"
	} else {
		sign = "-"
		countdown = -countdown
	}

	days := int(countdown / (24 * time.Hour))
	countdown = countdown % (24 * time.Hour)

	fmt.Print(indent, now.Format(time.UnixDate), "  ", sign)
	if days > 0 {
		fmt.Print(days, "d")
	}
	fmt.Print(countdown, "          \r")
	os.Stdout.Sync()
}
<commit_msg>1.11.1 on 11/1<commit_after>// Clock counts down to or up from a target time.
package main

import (
	"fmt"
	"os"
	"time"
)

func main() {
	target := time.Date(2018, 11, 1, 0, 0, 0, 0, time.Local)
	motto := "Just Go"
	printTargetTime(target, motto)
	exitOnEnterKey()

	var previous time.Time
	for {
		now := time.Now().Truncate(time.Second)
		if now != previous {
			previous = now
			countdown := now.Sub(target) // Negative times are before the target
			printCountdown(now, countdown)
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func exitOnEnterKey() {
	go func() {
		buf := make([]byte, 1)
		_, _ = os.Stdin.Read(buf)
		os.Exit(0)
	}()
}

const (
	highlightStart = "\x1b[1;35m"
	highlightEnd   = "\x1b[0m"
	indent         = "\t"
)

func printTargetTime(target time.Time, motto string) {
	fmt.Print(indent, highlightStart, motto, highlightEnd, "\n")
	fmt.Print(indent, target.Format(time.UnixDate), "\n")
}

func printCountdown(now time.Time, countdown time.Duration) {
	var sign string
	if countdown >= 0 {
		sign = "+"
	} else {
		sign = "-"
		countdown = -countdown
	}

	days := int(countdown / (24 * time.Hour))
	countdown = countdown % (24 * time.Hour)

	fmt.Print(indent, now.Format(time.UnixDate), "  ", sign)
	if days > 0 {
		fmt.Print(days, "d")
	}
	fmt.Print(countdown, "          \r")
	os.Stdout.Sync()
}
<|endoftext|>


<commit_before>package provision

import (
	"bytes"
	"errors"
	"fmt"
	"time"

	docker "github.com/fsouza/go-dockerclient"
)

func FnClient(endPoint string) (client *docker.Client) {
	if endPoint == "" {
		endPoint = "unix:///var/run/docker.sock"
	}

	client, err := docker.NewClient(endPoint)
	if err != nil {
		panic(err)
	}
	return
}

func FnContainer(client *docker.Client, image string) (Stdout *bytes.Buffer) {
	t := time.Now()
	container, err := client.CreateContainer(docker.CreateContainerOptions{
		Name: fmt.Sprintf("gofn-%s", t.Format("20060102150405")),
		Config: &docker.Config{
			Image:     image,
			StdinOnce: true,
			OpenStdin: true,
		},
	})
	if err != nil {
		panic(err)
	}

	defer client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID, Force: true})
	client.StartContainer(container.ID, nil)
	client.WaitContainerWithContext(container.ID, nil)
	stdout := new(bytes.Buffer)
	client.Logs(docker.LogsOptions{
		Container:    container.ID,
		Stdout:       true,
		OutputStream: stdout,
	})
	Stdout = stdout
	return
}

func FnImageBuild(client *docker.Client, contextDir, dockerFile, imageName string) (Name string, Stdout *bytes.Buffer) {
	if dockerFile == "" {
		dockerFile = "Dockerfile"
	}
	stdout := new(bytes.Buffer)
	Name = "gofn/" + imageName
	err := client.BuildImage(docker.BuildImageOptions{
		Name:           Name,
		Dockerfile:     dockerFile,
		SuppressOutput: true,
		OutputStream:   stdout,
		ContextDir:     contextDir,
	})
	if err != nil {
		panic(err)
	}
	Stdout = stdout
	return
}

// FnFindImage returns image data by name
func FnFindImage(client *docker.Client, imageName string) (image docker.APIImages, err error) {
	var imgs []docker.APIImages
	name := "gofn/" + imageName

	imgs, err = client.ListImages(docker.ListImagesOptions{Filter: name})
	if err != nil {
		return
	}

	if len(imgs) == 0 {
		err = errors.New("Image not found")
		return
	}

	image = imgs[0]
	return
}
<commit_msg>API Drafting<commit_after>package provision

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"time"

	docker "github.com/fsouza/go-dockerclient"
)

func FnClient(endPoint string) (client *docker.Client) {
	if endPoint == "" {
		endPoint = "unix:///var/run/docker.sock"
	}

	client, err := docker.NewClient(endPoint)
	if err != nil {
		panic(err)
	}
	return
}

// FnRemove remove container
func FnRemove(client *docker.Client, containerID string) (err error) {
	err = client.RemoveContainer(docker.RemoveContainerOptions{ID: containerID, Force: true})
	return
}

// FnContainer create container
func FnContainer(client *docker.Client, image string) (container *docker.Container, err error) {
	t := time.Now()
	container, err = client.CreateContainer(docker.CreateContainerOptions{
		Name: fmt.Sprintf("gofn-%s", t.Format("20060102150405")),
		Config: &docker.Config{
			Image:     image,
			StdinOnce: true,
			OpenStdin: true,
		},
	})
	return
}

func FnImageBuild(client *docker.Client, contextDir, dockerFile, imageName string) (Name string, Stdout *bytes.Buffer) {
	if dockerFile == "" {
		dockerFile = "Dockerfile"
	}
	stdout := new(bytes.Buffer)
	Name = "gofn/" + imageName
	err := client.BuildImage(docker.BuildImageOptions{
		Name:           Name,
		Dockerfile:     dockerFile,
		SuppressOutput: true,
		OutputStream:   stdout,
		ContextDir:     contextDir,
	})
	if err != nil {
		panic(err)
	}
	Stdout = stdout
	return
}

// FnFindImage returns image data by name
func FnFindImage(client *docker.Client, imageName string) (image docker.APIImages, err error) {
	var imgs []docker.APIImages
	name := "gofn/" + imageName

	imgs, err = client.ListImages(docker.ListImagesOptions{Filter: name})
	if err != nil {
		return
	}

	if len(imgs) == 0 {
		err = errors.New("Image not found")
		return
	}

	image = imgs[0]
	return
}

// FnFindContainer return container by image name
func FnFindContainer(client *docker.Client, imageName string) (container docker.APIContainers, err error) {
	var containers []docker.APIContainers
	containers, err = client.ListContainers(docker.ListContainersOptions{All: true})
	if err != nil {
		return
	}

	name := "gofn/" + imageName

	for _, v := range containers {
		if v.Image == name {
			container = v
			return
		}
	}
	err = errors.New("Container not found")
	return
}

func FnKillContainer(client *docker.Client, containerID string) (err error) {
	err = client.KillContainer(docker.KillContainerOptions{ID: containerID})
	return
}

// FnRun runs the container
func FnRun(client *docker.Client, containerID string) (Stdout *bytes.Buffer) {
	err := client.StartContainer(containerID, nil)
	if err != nil {
		log.Println(err)
	}
	client.WaitContainerWithContext(containerID, nil)
	stdout := new(bytes.Buffer)

	client.Logs(docker.LogsOptions{
		Container:    containerID,
		Stdout:       true,
		OutputStream: stdout,
	})
	Stdout = stdout
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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 configmap

import (
	"fmt"

	corev1 "k8s.io/api/core/v1"
)

// NewFixedWatcher returns a StaticWatcher that exposes a collection of ConfigMaps.
//
// Deprecated: Use NewStaticWatcher
func NewFixedWatcher(cms ...*corev1.ConfigMap) *StaticWatcher {
	return NewStaticWatcher(cms...)
}

// NewStaticWatcher returns an StaticWatcher that exposes a collection of ConfigMaps.
func NewStaticWatcher(cms ...*corev1.ConfigMap) *StaticWatcher {
	cmm := make(map[string]*corev1.ConfigMap)
	for _, cm := range cms {
		cmm[cm.Name] = cm
	}
	return &StaticWatcher{cfgs: cmm}
}

// StaticWatcher is a Watcher with static ConfigMaps. Callbacks will
// occur when Watch is invoked for a specific Observer
type StaticWatcher struct {
	cfgs map[string]*corev1.ConfigMap
}

// Asserts that fixedImpl implements Watcher.
var _ Watcher = (*StaticWatcher)(nil)

// Watch implements Watcher
func (di *StaticWatcher) Watch(name string, o ...Observer) {
	cm, ok := di.cfgs[name]
	if ok {
		for _, observer := range o {
			observer(cm)
		}
	} else {
		panic(fmt.Sprintf("Tried to watch unknown config with name %q", name))
	}
}

// Start implements Watcher
func (di *StaticWatcher) Start(<-chan struct{}) error {
	return nil
}
<commit_msg>Remove deprecated NewFixedWatcher (#699)<commit_after>/*
Copyright 2018 The Knative 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 configmap

import (
	"fmt"

	corev1 "k8s.io/api/core/v1"
)

// NewStaticWatcher returns an StaticWatcher that exposes a collection of ConfigMaps.
func NewStaticWatcher(cms ...*corev1.ConfigMap) *StaticWatcher {
	cmm := make(map[string]*corev1.ConfigMap)
	for _, cm := range cms {
		cmm[cm.Name] = cm
	}
	return &StaticWatcher{cfgs: cmm}
}

// StaticWatcher is a Watcher with static ConfigMaps. Callbacks will
// occur when Watch is invoked for a specific Observer
type StaticWatcher struct {
	cfgs map[string]*corev1.ConfigMap
}

// Asserts that fixedImpl implements Watcher.
var _ Watcher = (*StaticWatcher)(nil)

// Watch implements Watcher
func (di *StaticWatcher) Watch(name string, o ...Observer) {
	cm, ok := di.cfgs[name]
	if ok {
		for _, observer := range o {
			observer(cm)
		}
	} else {
		panic(fmt.Sprintf("Tried to watch unknown config with name %q", name))
	}
}

// Start implements Watcher
func (di *StaticWatcher) Start(<-chan struct{}) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"

	"github.com/toorop/govh"
	"github.com/toorop/govh/cloud"
)

// getCloudCmds return commands for cloud subsection
func getCloudCmds(client *govh.OVHClient) (cloudCmds []cli.Command) {
	cloud, err := cloud.New(client)
	if err != nil {
		return
	}
	cloudCmds = []cli.Command{
		{
			Name:        "passports",
			Usage:       "Return a list of cloud passports",
			Description: "Example: ovh cloud passports",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
			},
			Action: func(c *cli.Context) {
				passports, err := cloud.GetPassports()
				dieOnError(err)
				println(formatOutput(passports, c.Bool("json")))
				dieOk()
			},
		}, {
			Name:        "prices",
			Usage:       "Return a list of cloud prices",
			Description: "Example: ovh cloud prices",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
			},
			Action: func(c *cli.Context) {
				prices, err := cloud.GetPrices()
				dieOnError(err)
				println(formatOutput(prices, c.Bool("json")))
				dieOk()
			},
		}, {

			Name:        "projectids",
			Usage:       "Return a list of projects ID",
			Description: "Example: ovh cloud projectsid",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
			},
			Action: func(c *cli.Context) {
				ids, err := cloud.GetProjectIDs()
				dieOnError(err)
				println(formatOutput(ids, c.Bool("json")))
				dieOk()
			},
		}, {
			Name:        "project",
			Usage:       "Return project info",
			Description: "Example: ovh cloud project PROJECT_ID",
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				project, err := cloud.GetProject(c.Args().First())
				dieOnError(err)
				println(formatOutput(project, c.Bool("json")))
				dieOk()
			},
		},
		/*
		   ovh cloud instance list PROJECTID
		   ovh cloud instance snapshot create PROJECTID INSTANCEID
		   ovh cloud instance snapshot list PROJECTID
		   ovh cloud instance snapshot delete PROJECTID SNAPSHOTID
		   ovh cloud instance snapshot info PROJECTID SNAPSHOTID
		   ovh cloud instance backup PROJECTID INSTANCEID --hourly 24 --daily 7
		*/
		{
			Name:        "instance",
			Usage:       "Manage instances",
			Description: "desc",
			Subcommands: []cli.Command{
				{
					Name:        "list",
					Usage:       "List instances of project",
					Description: "Example: ovh cloud instance list PROJECTID",
					Flags: []cli.Flag{
						cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						instances, err := cloud.GetInstances(c.Args().First())

						dieOnError(err)
						println(formatOutput(instances, c.Bool("json")))
						dieOk()
					},
				},
			},
		},
	}
	return
}
<commit_msg>cloud snapshot list<commit_after>package main

import (
	"github.com/codegangsta/cli"

	"github.com/toorop/govh"
	"github.com/toorop/govh/cloud"
)

// getCloudCmds return commands for cloud subsection
func getCloudCmds(client *govh.OVHClient) (cloudCmds []cli.Command) {
	cloud, err := cloud.New(client)
	if err != nil {
		return
	}
	cloudCmds = []cli.Command{
		{
			Name:        "passports",
			Usage:       "Return a list of cloud passports",
			Description: "Example: ovh cloud passports",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
			},
			Action: func(c *cli.Context) {
				passports, err := cloud.GetPassports()
				dieOnError(err)
				println(formatOutput(passports, c.Bool("json")))
				dieOk()
			},
		}, {
			Name:        "prices",
			Usage:       "Return a list of cloud prices",
			Description: "Example: ovh cloud prices",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
			},
			Action: func(c *cli.Context) {
				prices, err := cloud.GetPrices()
				dieOnError(err)
				println(formatOutput(prices, c.Bool("json")))
				dieOk()
			},
		}, {

			Name:        "projectids",
			Usage:       "Return a list of projects ID",
			Description: "Example: ovh cloud projectsid",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
			},
			Action: func(c *cli.Context) {
				ids, err := cloud.GetProjectIDs()
				dieOnError(err)
				println(formatOutput(ids, c.Bool("json")))
				dieOk()
			},
		}, {
			Name:        "project",
			Usage:       "Return project info",
			Description: "Example: ovh cloud project PROJECT_ID",
			Action: func(c *cli.Context) {
				dieIfArgsMiss(len(c.Args()), 1)
				project, err := cloud.GetProject(c.Args().First())
				dieOnError(err)
				println(formatOutput(project, c.Bool("json")))
				dieOk()
			},
		},
		/*
					   ovh cloud instance list PROJECTID
			           ovh cloud snapshot list PROJECTID
			           ovh cloud snapshot info PROJECTID SNAPSHOTID
					   ovh cloud snapshot create PROJECTID INSTANCEID
					   ovh cloud snapshot delete PROJECTID SNAPSHOTID
					   ovh cloud snapshot info PROJECTID SNAPSHOTID
					   ovh cloud instance backup PROJECTID INSTANCEID --hourly 24 --daily 7
		*/
		{
			Name:        "instance",
			Usage:       "Manage instances",
			Description: "desc",
			Subcommands: []cli.Command{
				{
					Name:        "list",
					Usage:       "List instances of project",
					Description: "Example: ovh cloud instance list PROJECTID",
					Flags: []cli.Flag{
						cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						instances, err := cloud.GetInstances(c.Args().First())
						dieOnError(err)
						println(formatOutput(instances, c.Bool("json")))
						dieOk()
					},
				},
			},
		},
		{
			Name:        "snapshot",
			Usage:       "Manage snapshot (usage)",
			Description: "Manage snapshot",
			Subcommands: []cli.Command{
				{
					Name:        "list",
					Usage:       "Example: ovh cloud snapshot list PROJECTID",
					Description: "List snapshot of a project",
					Flags: []cli.Flag{
						cli.BoolFlag{Name: "json", Usage: "if set output as JSON"},
					},
					Action: func(c *cli.Context) {
						dieIfArgsMiss(len(c.Args()), 1)
						instances, err := cloud.GetSnapshots(c.Args().First())
						dieOnError(err)
						println(formatOutput(instances, c.Bool("json")))
						dieOk()
					},
				},
			},
		},
	}
	return
}
<|endoftext|>


<commit_before>package fauxgl

import (
	"fmt"
	"image/color"
	"math"
	"strings"
)

var (
	Discard     = Color{}
	Transparent = Color{}
	Black       = Color{0, 0, 0, 1}
	White       = Color{1, 1, 1, 1}
)

type Color struct {
	R, G, B, A float64
}

func Gray(x float64) Color {
	return Color{x, x, x, 1}
}

func MakeColor(c color.Color) Color {
	r, g, b, a := c.RGBA()
	const d = 0xffff
	return Color{float64(r) / d, float64(g) / d, float64(b) / d, float64(a) / d}
}

func HexColor(x string) Color {
	x = strings.Trim(x, "#")
	var r, g, b, a int
	a = 255
	switch len(x) {
	case 3:
		fmt.Sscanf(x, "%1x%1x%1x", &r, &g, &b)
		r = (r << 4) | r
		g = (g << 4) | g
		b = (b << 4) | b
	case 4:
		fmt.Sscanf(x, "%1x%1x%1x%1x", &r, &g, &b, &a)
		r = (r << 4) | r
		g = (g << 4) | g
		b = (b << 4) | b
		a = (a << 4) | a
	case 6:
		fmt.Sscanf(x, "%02x%02x%02x", &r, &g, &b)
	case 8:
		fmt.Sscanf(x, "%02x%02x%02x%02x", &r, &g, &b, &a)
	}
	const d = 0xff
	return Color{float64(r) / d, float64(g) / d, float64(b) / d, float64(a) / d}
}

func (c Color) NRGBA() color.NRGBA {
	const d = 0xff
	r := Clamp(c.R, 0, 1)
	g := Clamp(c.G, 0, 1)
	b := Clamp(c.B, 0, 1)
	a := Clamp(c.A, 0, 1)
	return color.NRGBA{uint8(r * d), uint8(g * d), uint8(b * d), uint8(a * d)}
}

func (a Color) Opaque() Color {
	return Color{a.R, a.G, a.B, 1}
}

func (a Color) Alpha(alpha float64) Color {
	return Color{a.R, a.G, a.B, alpha}
}

func (a Color) Add(b Color) Color {
	return Color{a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A}
}

func (a Color) Sub(b Color) Color {
	return Color{a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A}
}

func (a Color) Mul(b Color) Color {
	return Color{a.R * b.R, a.G * b.G, a.B * b.B, a.A * b.A}
}

func (a Color) Div(b Color) Color {
	return Color{a.R / b.R, a.G / b.G, a.B / b.B, a.A / b.A}
}

func (a Color) AddScalar(b float64) Color {
	return Color{a.R + b, a.G + b, a.B + b, a.A + b}
}

func (a Color) SubScalar(b float64) Color {
	return Color{a.R - b, a.G - b, a.B - b, a.A - b}
}

func (a Color) MulScalar(b float64) Color {
	return Color{a.R * b, a.G * b, a.B * b, a.A * b}
}

func (a Color) DivScalar(b float64) Color {
	return Color{a.R / b, a.G / b, a.B / b, a.A / b}
}

func (a Color) Pow(b float64) Color {
	return Color{math.Pow(a.R, b), math.Pow(a.G, b), math.Pow(a.B, b), math.Pow(a.A, b)}
}

func (a Color) Min(b Color) Color {
	return Color{math.Min(a.R, b.R), math.Min(a.G, b.G), math.Min(a.B, b.B), math.Min(a.A, b.A)}
}

func (a Color) Max(b Color) Color {
	return Color{math.Max(a.R, b.R), math.Max(a.G, b.G), math.Max(a.B, b.B), math.Max(a.A, b.A)}
}
<commit_msg>Color.Lerp<commit_after>package fauxgl

import (
	"fmt"
	"image/color"
	"math"
	"strings"
)

var (
	Discard     = Color{}
	Transparent = Color{}
	Black       = Color{0, 0, 0, 1}
	White       = Color{1, 1, 1, 1}
)

type Color struct {
	R, G, B, A float64
}

func Gray(x float64) Color {
	return Color{x, x, x, 1}
}

func MakeColor(c color.Color) Color {
	r, g, b, a := c.RGBA()
	const d = 0xffff
	return Color{float64(r) / d, float64(g) / d, float64(b) / d, float64(a) / d}
}

func HexColor(x string) Color {
	x = strings.Trim(x, "#")
	var r, g, b, a int
	a = 255
	switch len(x) {
	case 3:
		fmt.Sscanf(x, "%1x%1x%1x", &r, &g, &b)
		r = (r << 4) | r
		g = (g << 4) | g
		b = (b << 4) | b
	case 4:
		fmt.Sscanf(x, "%1x%1x%1x%1x", &r, &g, &b, &a)
		r = (r << 4) | r
		g = (g << 4) | g
		b = (b << 4) | b
		a = (a << 4) | a
	case 6:
		fmt.Sscanf(x, "%02x%02x%02x", &r, &g, &b)
	case 8:
		fmt.Sscanf(x, "%02x%02x%02x%02x", &r, &g, &b, &a)
	}
	const d = 0xff
	return Color{float64(r) / d, float64(g) / d, float64(b) / d, float64(a) / d}
}

func (c Color) NRGBA() color.NRGBA {
	const d = 0xff
	r := Clamp(c.R, 0, 1)
	g := Clamp(c.G, 0, 1)
	b := Clamp(c.B, 0, 1)
	a := Clamp(c.A, 0, 1)
	return color.NRGBA{uint8(r * d), uint8(g * d), uint8(b * d), uint8(a * d)}
}

func (a Color) Opaque() Color {
	return Color{a.R, a.G, a.B, 1}
}

func (a Color) Alpha(alpha float64) Color {
	return Color{a.R, a.G, a.B, alpha}
}

func (a Color) Lerp(b Color, t float64) Color {
	return a.Add(b.Sub(a).MulScalar(t))
}

func (a Color) Add(b Color) Color {
	return Color{a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A}
}

func (a Color) Sub(b Color) Color {
	return Color{a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A}
}

func (a Color) Mul(b Color) Color {
	return Color{a.R * b.R, a.G * b.G, a.B * b.B, a.A * b.A}
}

func (a Color) Div(b Color) Color {
	return Color{a.R / b.R, a.G / b.G, a.B / b.B, a.A / b.A}
}

func (a Color) AddScalar(b float64) Color {
	return Color{a.R + b, a.G + b, a.B + b, a.A + b}
}

func (a Color) SubScalar(b float64) Color {
	return Color{a.R - b, a.G - b, a.B - b, a.A - b}
}

func (a Color) MulScalar(b float64) Color {
	return Color{a.R * b, a.G * b, a.B * b, a.A * b}
}

func (a Color) DivScalar(b float64) Color {
	return Color{a.R / b, a.G / b, a.B / b, a.A / b}
}

func (a Color) Pow(b float64) Color {
	return Color{math.Pow(a.R, b), math.Pow(a.G, b), math.Pow(a.B, b), math.Pow(a.A, b)}
}

func (a Color) Min(b Color) Color {
	return Color{math.Min(a.R, b.R), math.Min(a.G, b.G), math.Min(a.B, b.B), math.Min(a.A, b.A)}
}

func (a Color) Max(b Color) Color {
	return Color{math.Max(a.R, b.R), math.Max(a.G, b.G), math.Max(a.B, b.B), math.Max(a.A, b.A)}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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 kube

import (
	"errors"
	"fmt"
	"io/ioutil"

	"github.com/sirupsen/logrus"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

func localConfig() (*rest.Config, error) {
	return rest.InClusterConfig()
}

func kubeConfigs(kubeconfig string) (map[string]rest.Config, string, error) {
	// Attempt to load external clusters too
	var loader clientcmd.ClientConfigLoader
	if kubeconfig != "" { // load from --kubeconfig
		loader = &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}
	} else {
		loader = clientcmd.NewDefaultClientConfigLoadingRules()
	}

	cfg, err := loader.Load()
	if err != nil && kubeconfig != "" {
		return nil, "", fmt.Errorf("load: %v", err)
	}
	if err != nil {
		logrus.WithError(err).Warn("Cannot load kubecfg")
		return nil, "", nil
	}
	configs := map[string]rest.Config{}
	for context := range cfg.Contexts {
		contextCfg, err := clientcmd.NewNonInteractiveClientConfig(*cfg, context, &clientcmd.ConfigOverrides{}, loader).ClientConfig()
		if err != nil {
			return nil, "", fmt.Errorf("create %s client: %v", context, err)
		}
		configs[context] = *contextCfg
		logrus.Infof("Parsed kubeconfig context: %s", context)
	}
	return configs, cfg.CurrentContext, nil
}

func buildConfigs(buildCluster string) (map[string]rest.Config, error) {
	if buildCluster == "" { // load from --build-cluster
		return nil, nil
	}
	data, err := ioutil.ReadFile(buildCluster)
	if err != nil {
		return nil, fmt.Errorf("read: %v", err)
	}
	raw, err := UnmarshalClusterMap(data)
	if err != nil {
		return nil, fmt.Errorf("unmarshal: %v", err)
	}
	cfg := &clientcmdapi.Config{
		Clusters:  map[string]*clientcmdapi.Cluster{},
		AuthInfos: map[string]*clientcmdapi.AuthInfo{},
		Contexts:  map[string]*clientcmdapi.Context{},
	}
	for alias, config := range raw {
		cfg.Clusters[alias] = &clientcmdapi.Cluster{
			Server:                   config.Endpoint,
			CertificateAuthorityData: config.ClusterCACertificate,
		}
		cfg.AuthInfos[alias] = &clientcmdapi.AuthInfo{
			ClientCertificateData: config.ClientCertificate,
			ClientKeyData:         config.ClientKey,
		}
		cfg.Contexts[alias] = &clientcmdapi.Context{
			Cluster:  alias,
			AuthInfo: alias,
			// TODO(fejta): Namespace?
		}
	}
	configs := map[string]rest.Config{}
	for context := range cfg.Contexts {
		logrus.Infof("* %s", context)
		contextCfg, err := clientcmd.NewNonInteractiveClientConfig(*cfg, context, &clientcmd.ConfigOverrides{}, nil).ClientConfig()
		if err != nil {
			return nil, fmt.Errorf("create %s client: %v", context, err)
		}
		configs[context] = *contextCfg
	}
	return configs, nil
}

func mergeConfigs(local *rest.Config, foreign map[string]rest.Config, currentContext string, buildClusters map[string]rest.Config) (map[string]rest.Config, error) {
	if buildClusters != nil {
		if _, ok := buildClusters[DefaultClusterAlias]; !ok {
			return nil, fmt.Errorf("build-cluster must have a %s context", DefaultClusterAlias)
		}
	}
	ret := map[string]rest.Config{}
	for ctx, cfg := range foreign {
		ret[ctx] = cfg
	}
	for ctx, cfg := range buildClusters {
		ret[ctx] = cfg
	}
	if local != nil {
		ret[InClusterContext] = *local
	} else if currentContext != "" {
		ret[InClusterContext] = ret[currentContext]
	} else {
		return nil, errors.New("no prow cluster access: in-cluster current kubecfg context required")
	}
	if len(ret) == 0 {
		return nil, errors.New("no client contexts found")
	}
	if _, ok := ret[DefaultClusterAlias]; !ok {
		ret[DefaultClusterAlias] = ret[InClusterContext]
	}
	return ret, nil
}

// LoadClusterConfigs loads rest.Configs for creation of clients, by using either a normal
// .kube/config file, a custom `Cluster` file, or both. The configs are returned in a mapping
// of context --> config. The default context is included in this mapping and specified as a
// return vaule. Errors are returned if .kube/config is specified and invalid or if no valid
// contexts are found.
func LoadClusterConfigs(kubeconfig, buildCluster string) (map[string]rest.Config, error) {

	logrus.Infof("Loading cluster contexts...")
	// This will work if we are running inside kubernetes
	localCfg, err := localConfig()
	if err != nil {
		logrus.WithError(err).Warn("Could not create in-cluster config (expected when running outside the cluster).")
	}

	kubeCfgs, currentContext, err := kubeConfigs(kubeconfig)
	if err != nil {
		return nil, fmt.Errorf("kubecfg: %v", err)
	}

	// TODO(fejta): drop build-cluster support
	buildCfgs, err := buildConfigs(buildCluster)
	if err != nil {
		return nil, fmt.Errorf("build-cluster: %v", err)
	}

	return mergeConfigs(localCfg, kubeCfgs, currentContext, buildCfgs)
}
<commit_msg>Use 100QPS for all kube clients<commit_after>/*
Copyright 2019 The Kubernetes 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 kube

import (
	"errors"
	"fmt"
	"io/ioutil"

	"github.com/sirupsen/logrus"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

func localConfig() (*rest.Config, error) {
	return rest.InClusterConfig()
}

func kubeConfigs(kubeconfig string) (map[string]rest.Config, string, error) {
	// Attempt to load external clusters too
	var loader clientcmd.ClientConfigLoader
	if kubeconfig != "" { // load from --kubeconfig
		loader = &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}
	} else {
		loader = clientcmd.NewDefaultClientConfigLoadingRules()
	}

	cfg, err := loader.Load()
	if err != nil && kubeconfig != "" {
		return nil, "", fmt.Errorf("load: %v", err)
	}
	if err != nil {
		logrus.WithError(err).Warn("Cannot load kubecfg")
		return nil, "", nil
	}
	configs := map[string]rest.Config{}
	for context := range cfg.Contexts {
		contextCfg, err := clientcmd.NewNonInteractiveClientConfig(*cfg, context, &clientcmd.ConfigOverrides{}, loader).ClientConfig()
		if err != nil {
			return nil, "", fmt.Errorf("create %s client: %v", context, err)
		}
		configs[context] = *contextCfg
		logrus.Infof("Parsed kubeconfig context: %s", context)
	}
	return configs, cfg.CurrentContext, nil
}

func buildConfigs(buildCluster string) (map[string]rest.Config, error) {
	if buildCluster == "" { // load from --build-cluster
		return nil, nil
	}
	data, err := ioutil.ReadFile(buildCluster)
	if err != nil {
		return nil, fmt.Errorf("read: %v", err)
	}
	raw, err := UnmarshalClusterMap(data)
	if err != nil {
		return nil, fmt.Errorf("unmarshal: %v", err)
	}
	cfg := &clientcmdapi.Config{
		Clusters:  map[string]*clientcmdapi.Cluster{},
		AuthInfos: map[string]*clientcmdapi.AuthInfo{},
		Contexts:  map[string]*clientcmdapi.Context{},
	}
	for alias, config := range raw {
		cfg.Clusters[alias] = &clientcmdapi.Cluster{
			Server:                   config.Endpoint,
			CertificateAuthorityData: config.ClusterCACertificate,
		}
		cfg.AuthInfos[alias] = &clientcmdapi.AuthInfo{
			ClientCertificateData: config.ClientCertificate,
			ClientKeyData:         config.ClientKey,
		}
		cfg.Contexts[alias] = &clientcmdapi.Context{
			Cluster:  alias,
			AuthInfo: alias,
			// TODO(fejta): Namespace?
		}
	}
	configs := map[string]rest.Config{}
	for context := range cfg.Contexts {
		logrus.Infof("* %s", context)
		contextCfg, err := clientcmd.NewNonInteractiveClientConfig(*cfg, context, &clientcmd.ConfigOverrides{}, nil).ClientConfig()
		if err != nil {
			return nil, fmt.Errorf("create %s client: %v", context, err)
		}
		// An arbitrary high number we expect to not exceed. There are various components that need more than the default 5 QPS/10 Burst, e.G.
		// hook for creating ProwJobs and Plank for creating Pods.
		contextCfg.QPS = 100
		contextCfg.Burst = 1000
		configs[context] = *contextCfg
	}
	return configs, nil
}

func mergeConfigs(local *rest.Config, foreign map[string]rest.Config, currentContext string, buildClusters map[string]rest.Config) (map[string]rest.Config, error) {
	if buildClusters != nil {
		if _, ok := buildClusters[DefaultClusterAlias]; !ok {
			return nil, fmt.Errorf("build-cluster must have a %s context", DefaultClusterAlias)
		}
	}
	ret := map[string]rest.Config{}
	for ctx, cfg := range foreign {
		ret[ctx] = cfg
	}
	for ctx, cfg := range buildClusters {
		ret[ctx] = cfg
	}
	if local != nil {
		ret[InClusterContext] = *local
	} else if currentContext != "" {
		ret[InClusterContext] = ret[currentContext]
	} else {
		return nil, errors.New("no prow cluster access: in-cluster current kubecfg context required")
	}
	if len(ret) == 0 {
		return nil, errors.New("no client contexts found")
	}
	if _, ok := ret[DefaultClusterAlias]; !ok {
		ret[DefaultClusterAlias] = ret[InClusterContext]
	}
	return ret, nil
}

// LoadClusterConfigs loads rest.Configs for creation of clients, by using either a normal
// .kube/config file, a custom `Cluster` file, or both. The configs are returned in a mapping
// of context --> config. The default context is included in this mapping and specified as a
// return vaule. Errors are returned if .kube/config is specified and invalid or if no valid
// contexts are found.
func LoadClusterConfigs(kubeconfig, buildCluster string) (map[string]rest.Config, error) {

	logrus.Infof("Loading cluster contexts...")
	// This will work if we are running inside kubernetes
	localCfg, err := localConfig()
	if err != nil {
		logrus.WithError(err).Warn("Could not create in-cluster config (expected when running outside the cluster).")
	}

	kubeCfgs, currentContext, err := kubeConfigs(kubeconfig)
	if err != nil {
		return nil, fmt.Errorf("kubecfg: %v", err)
	}

	// TODO(fejta): drop build-cluster support
	buildCfgs, err := buildConfigs(buildCluster)
	if err != nil {
		return nil, fmt.Errorf("build-cluster: %v", err)
	}

	return mergeConfigs(localCfg, kubeCfgs, currentContext, buildCfgs)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 sidecar is a library that knows how to report on the
// output of a process that writes it's output and exit code to
// disk
package sidecar
<commit_msg>Update doc.go<commit_after>/*
Copyright 2018 The Kubernetes 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 sidecar is a library that knows how to report on the
// output of a process that writes its output and exit code to
// disk
package sidecar
<|endoftext|>


<commit_before>package main

import (
	"net"
	"net/url"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/pkg/logrusx"
	"github.com/cerana/cerana/provider"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/dhcp"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

func defaultNetwork() net.IPNet {
	_, net, _ := net.ParseCIDR("172.16.0.0/16")
	return *net
}

func comm(tracker *acomm.Tracker, coordinator *url.URL, task string, args interface{}, resp interface{}) error {
	ch := make(chan *acomm.Response, 1)
	handler := func(_ *acomm.Request, resp *acomm.Response) {
		ch <- resp
	}

	req, err := acomm.NewRequest(acomm.RequestOptions{
		Task:           task,
		Args:           args,
		ResponseHook:   tracker.URL(),
		SuccessHandler: handler,
		ErrorHandler:   handler,
	})
	logrusx.DieOnError(err, "create request object")
	logrusx.DieOnError(tracker.TrackRequest(req, 0), "track request")
	logrusx.DieOnError(acomm.Send(coordinator, req), "send request")

	aResp := <-ch
	if aResp.Error != nil {
		return aResp.Error
	}

	logrusx.DieOnError(aResp.UnmarshalResult(resp), "deserialize result")
	return nil
}

func getDHCPConfig(tracker *acomm.Tracker, coordinator *url.URL) *clusterconf.DHCPConfig {
	dconf := &clusterconf.DHCPConfig{}
	err := comm(tracker, coordinator, "get-dhcp-config", nil, dconf)
	logrusx.DieOnError(err, "get dhcp configuration")
	return dconf
}

func joinDNS(dns []net.IP) string {
	strs := make([]string, len(dns))
	for i := range dns {
		strs[i] = dns[i].String()
	}
	return strings.Join(strs, ",")
}

func main() {
	logrus.SetFormatter(&logrusx.JSONFormatter{})

	v := viper.New()
	f := pflag.NewFlagSet("dhcp-provider", pflag.ExitOnError)

	config := dhcp.NewConfig(f, v)
	logrusx.DieOnError(f.Parse(os.Args), "parse arguments")
	logrusx.DieOnError(config.LoadConfig(), "load configuration")
	logrusx.DieOnError(config.SetupLogging(), "setup logging")

	server, err := provider.NewServer(config.Config)
	logrusx.DieOnError(err, "create provider")
	logrusx.DieOnError(server.Tracker().Start(), "start tracker")

	storedConfig := getDHCPConfig(server.Tracker(), config.CoordinatorURL())

	v.Set("dns-servers", storedConfig.DNS)
	v.Set("gateway", storedConfig.Gateway)
	v.Set("lease-duration", storedConfig.Duration)
	v.Set("network", storedConfig.Net)

	d, err := dhcp.New(config, server.Tracker())
	logrusx.DieOnError(err, "create dhcp server")

	logrus.WithField("config", storedConfig).Info("starting provider")

	d.RegisterTasks(server)
	if len(server.RegisteredTasks()) == 0 {
		logrus.Warn("no registered tasks, exiting")
		os.Exit(1)
	}
	logrusx.DieOnError(server.Start(), "successfully run dhcp provider")
	server.StopOnSignal()
}
<commit_msg>cmd/dhcp-provider: loop until dhcp config is fetched<commit_after>package main

import (
	"net"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/pkg/logrusx"
	"github.com/cerana/cerana/provider"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/dhcp"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

func defaultNetwork() net.IPNet {
	_, net, _ := net.ParseCIDR("172.16.0.0/16")
	return *net
}

func comm(tracker *acomm.Tracker, coordinator *url.URL, task string, args interface{}, resp interface{}) error {
	ch := make(chan *acomm.Response, 1)
	handler := func(_ *acomm.Request, resp *acomm.Response) {
		ch <- resp
	}

	req, err := acomm.NewRequest(acomm.RequestOptions{
		Task:           task,
		Args:           args,
		ResponseHook:   tracker.URL(),
		SuccessHandler: handler,
		ErrorHandler:   handler,
	})
	logrusx.DieOnError(err, "create request object")
	logrusx.DieOnError(tracker.TrackRequest(req, 0), "track request")
	logrusx.DieOnError(acomm.Send(coordinator, req), "send request")

	aResp := <-ch
	if aResp.Error != nil {
		return aResp.Error
	}

	logrusx.DieOnError(aResp.UnmarshalResult(resp), "deserialize result")
	return nil
}

func getDHCPConfig(tracker *acomm.Tracker, coordinator *url.URL) (*clusterconf.DHCPConfig, error) {
	dconf := &clusterconf.DHCPConfig{}
	return dconf, comm(tracker, coordinator, "get-dhcp-config", nil, dconf)
}

func joinDNS(dns []net.IP) string {
	strs := make([]string, len(dns))
	for i := range dns {
		strs[i] = dns[i].String()
	}
	return strings.Join(strs, ",")
}

func main() {
	logrus.SetFormatter(&logrusx.JSONFormatter{})

	v := viper.New()
	f := pflag.NewFlagSet("dhcp-provider", pflag.ExitOnError)

	config := dhcp.NewConfig(f, v)
	logrusx.DieOnError(f.Parse(os.Args), "parse arguments")
	logrusx.DieOnError(config.LoadConfig(), "load configuration")
	logrusx.DieOnError(config.SetupLogging(), "setup logging")

	server, err := provider.NewServer(config.Config)
	logrusx.DieOnError(err, "create provider")
	logrusx.DieOnError(server.Tracker().Start(), "start tracker")

	var storedConfig *clusterconf.DHCPConfig
	for {
		storedConfig, err = getDHCPConfig(server.Tracker(), config.CoordinatorURL())
		if err == nil {
			break
		}
		logrus.Debug(err)
		time.Sleep(1 * time.Second)
	}

	v.Set("dns-servers", storedConfig.DNS)
	v.Set("gateway", storedConfig.Gateway)
	v.Set("lease-duration", storedConfig.Duration)
	v.Set("network", storedConfig.Net)

	d, err := dhcp.New(config, server.Tracker())
	logrusx.DieOnError(err, "create dhcp server")

	logrus.WithField("config", storedConfig).Info("starting provider")

	d.RegisterTasks(server)
	if len(server.RegisteredTasks()) == 0 {
		logrus.Warn("no registered tasks, exiting")
		os.Exit(1)
	}
	logrusx.DieOnError(server.Start(), "successfully run dhcp provider")
	server.StopOnSignal()
}
<|endoftext|>