<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

func deploy(dockerHost string, application *Application, composeFilePath string) (string, error) {
	var err error

	compose := NewCompose(dockerHost, composeFilePath, application.ProjectName)

	fmt.Println("=====> Building ...")

	if err = compose.Build(); err != nil {
		return "", err
	}

	fmt.Println("=====> Pulling ...")

	if err = compose.Pull(); err != nil {
		return "", err
	}

	fmt.Println("=====> Deploying ...")

	if err = compose.Up(); err != nil {
		return "", err
	}

	webContainerId, err := compose.GetContainerId("web")

	if err != nil {
		return "", err
	}

	return webContainerId, nil
}

func injectEnvironmentVariables(application *Application, composeFile *ComposeFile, etcd *Etcd) error {
	userDirectoryKey := "/paus/users/" + application.Username

	if !etcd.HasKey(userDirectoryKey) {
		return nil
	}

	appDirectoryKey := userDirectoryKey + "/" + application.AppName

	if !etcd.HasKey(appDirectoryKey) {
		return nil
	}

	envDirectoryKey := appDirectoryKey + "/envs/"

	if !etcd.HasKey(envDirectoryKey) {
		return nil
	}

	envKeys, err := etcd.List(envDirectoryKey, false)

	if err != nil {
		return err
	}

	environmentVariables := map[string]string{}

	for _, key := range envKeys {
		value, err := etcd.Get(key)

		if err != nil {
			return err
		}

		environmentVariables[strings.Replace(key, envDirectoryKey, "", 1)] = value
	}

	composeFile.InjectEnvironmentVariables(environmentVariables)

	return nil
}

func registerApplicationMetadata(application *Application, etcd *Etcd) error {
	userDirectoryKey := "/paus/users/" + application.Username

	if !etcd.HasKey(userDirectoryKey) {
		_ = etcd.Mkdir(userDirectoryKey)
	}

	appDirectoryKey := userDirectoryKey + "/" + application.AppName

	if !etcd.HasKey(appDirectoryKey) {
		_ = etcd.Mkdir(appDirectoryKey)
		_ = etcd.Mkdir(appDirectoryKey + "/envs")
		_ = etcd.Mkdir(appDirectoryKey + "/revisions")
	}

	if err := etcd.Set(appDirectoryKey+"/revisions/"+application.Revision, strconv.FormatInt(time.Now().Unix(), 10)); err != nil {
		return err
	}

	return nil
}

func registerVulcandInformation(application *Application, baseDomain string, webContainer *Container, etcd *Etcd) error {
	vulcandDirectoryKeyBase := "/vulcand"

	// {"Type": "http"}
	if err := etcd.Set(vulcandDirectoryKeyBase+"/backends/"+application.ProjectName+"/backend", "{\"Type\": \"http\"}"); err != nil {
		return err
	}

	// {"URL": "http://$web_container_host_ip:$web_container_port"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/backends/"+application.ProjectName+"/servers/"+webContainer.ContainerId,
		"{\"URL\": \"http://"+webContainer.HostIP()+":"+webContainer.HostPort()+"\"}",
	); err != nil {
		return err
	}

	// {"Type": "http", "BackendId": "$PROJECT_NAME", "Route": "Host(`$PROJECT_NAME.$BASE_DOMAIN`) && PathRegexp(`/`)"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/frontends/"+application.ProjectName+"/frontend",
		"{\"Type\": \"http\", \"BackendId\": \""+application.ProjectName+"\", \"Route\": \"Host(`"+application.ProjectName+"."+baseDomain+"`) && PathRegexp(`/`)\"}",
	); err != nil {
		return err
	}

	// {"Type": "http", "BackendId": "$PROJECT_NAME", "Route": "Host(`$USER_NAME.$BASE_DOMAIN`) && PathRegexp(`/`)"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/frontends/"+application.Username+"/frontend",
		"{\"Type\": \"http\", \"BackendId\": \""+application.ProjectName+"\", \"Route\": \"Host(`"+application.Username+"."+baseDomain+"`) && PathRegexp(`/`)\"}",
	); err != nil {
		return err
	}

	// {"Type": "http", "BackendId": "$PROJECT_NAME", "Route": "Host(`$APP_NAME.$BASE_DOMAIN`) && PathRegexp(`/`)"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/frontends/"+application.AppName+"/frontend",
		"{\"Type\": \"http\", \"BackendId\": \""+application.ProjectName+"\", \"Route\": \"Host(`"+application.AppName+"."+baseDomain+"`) && PathRegexp(`/`)\"}",
	); err != nil {
		return err
	}

	return nil
}

func removeUnpackedFiles(repositoryPath, newComposeFilePath string) error {
	files, err := ioutil.ReadDir(repositoryPath)

	if err != nil {
		return err
	}

	for _, file := range files {
		if filepath.Join(repositoryPath, file.Name()) != newComposeFilePath {
			if err = os.RemoveAll(filepath.Join(repositoryPath, file.Name())); err != nil {
				return err
			}
		}
	}

	return nil
}

func unpackReceivedFiles(repositoryDir, username, projectName string, stdin io.Reader) (string, error) {
	repositoryPath := filepath.Join(repositoryDir, username, projectName)

	if err := os.MkdirAll(repositoryPath, 0777); err != nil {
		return "", err
	}

	reader := tar.NewReader(stdin)

	for {
		header, err := reader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			return "", err
		}

		buffer := new(bytes.Buffer)
		outPath := filepath.Join(repositoryPath, header.Name)

		switch header.Typeflag {
		case tar.TypeDir:
			if _, err = os.Stat(outPath); err != nil {
				os.MkdirAll(outPath, 0755)
			}

		case tar.TypeReg, tar.TypeRegA:
			if _, err = io.Copy(buffer, reader); err != nil {
				return "", err
			}

			if err = ioutil.WriteFile(outPath, buffer.Bytes(), os.FileMode(header.Mode)); err != nil {
				return "", err
			}
		}
	}

	return repositoryPath, nil
}

func main() {
	config, err := LoadConfig()

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	application := ApplicationFromArgs(os.Args[1:])
	repositoryPath, err := unpackReceivedFiles(config.RepositoryDir, application.Username, application.ProjectName, os.Stdin)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err = os.Chdir(repositoryPath); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}

	composeFilePath := filepath.Join(repositoryPath, "docker-compose.yml")

	if _, err := os.Stat(composeFilePath); err != nil {
		fmt.Fprintln(os.Stderr, "=====> docker-compose.yml was NOT found!")
		os.Exit(1)
	}

	fmt.Println("=====> docker-compose.yml was found")
	composeFile, err := NewComposeFile(composeFilePath)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	etcd, err := NewEtcd(config.EtcdEndpoint)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err = injectEnvironmentVariables(application, composeFile, etcd); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	composeFile.RewritePortBindings()
	newComposeFilePath := filepath.Join(repositoryPath, "docker-compose-"+strconv.FormatInt(time.Now().Unix(), 10)+".yml")

	if err = composeFile.SaveAs(newComposeFilePath); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	webContainerId, err := deploy(config.DockerHost, application, newComposeFilePath)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Println("=====> Application container is launched.")

	webContainer, err := ContainerFromID(config.DockerHost, webContainerId)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Println("=====> Registering metadata ...")

	if err = registerApplicationMetadata(application, etcd); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err = registerVulcandInformation(application, config.BaseDomain, webContainer, etcd); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	urlList := []string{
		"http://" + application.ProjectName + "." + config.BaseDomain,
		"http://" + application.Username + "." + config.BaseDomain,
		"http://" + application.AppName + "." + config.BaseDomain,
	}

	fmt.Println("=====> " + application.Repository + " was successfully deployed at:")

	for _, url := range urlList {
		fmt.Println("         " + url)
	}

	if err = removeUnpackedFiles(repositoryPath, newComposeFilePath); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Inject build-time variables in receiver<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

func deploy(dockerHost string, application *Application, composeFilePath string) (string, error) {
	var err error

	compose := NewCompose(dockerHost, composeFilePath, application.ProjectName)

	fmt.Println("=====> Building ...")

	if err = compose.Build(); err != nil {
		return "", err
	}

	fmt.Println("=====> Pulling ...")

	if err = compose.Pull(); err != nil {
		return "", err
	}

	fmt.Println("=====> Deploying ...")

	if err = compose.Up(); err != nil {
		return "", err
	}

	webContainerId, err := compose.GetContainerId("web")

	if err != nil {
		return "", err
	}

	return webContainerId, nil
}

func injectBuildArgs(application *Application, composeFile *ComposeFile, etcd *Etcd) error {
	userDirectoryKey := "/paus/users/" + application.Username

	if !etcd.HasKey(userDirectoryKey) {
		return nil
	}

	appDirectoryKey := userDirectoryKey + "/" + application.AppName

	if !etcd.HasKey(appDirectoryKey) {
		return nil
	}

	buildArgsKey := appDirectoryKey + "/build-args/"

	if !etcd.HasKey(buildArgsKey) {
		return nil
	}

	buildArgKeys, err := etcd.List(buildArgsKey, false)

	if err != nil {
		return err
	}

	buildArgs := map[string]string{}

	for _, key := range buildArgKeys {
		value, err := etcd.Get(key)

		if err != nil {
			return err
		}

		buildArgs[strings.Replace(key, buildArgsKey, "", 1)] = value
	}

	composeFile.InjectBuildArgs(buildArgs)

	return nil
}

func injectEnvironmentVariables(application *Application, composeFile *ComposeFile, etcd *Etcd) error {
	userDirectoryKey := "/paus/users/" + application.Username

	if !etcd.HasKey(userDirectoryKey) {
		return nil
	}

	appDirectoryKey := userDirectoryKey + "/" + application.AppName

	if !etcd.HasKey(appDirectoryKey) {
		return nil
	}

	envDirectoryKey := appDirectoryKey + "/envs/"

	if !etcd.HasKey(envDirectoryKey) {
		return nil
	}

	envKeys, err := etcd.List(envDirectoryKey, false)

	if err != nil {
		return err
	}

	environmentVariables := map[string]string{}

	for _, key := range envKeys {
		value, err := etcd.Get(key)

		if err != nil {
			return err
		}

		environmentVariables[strings.Replace(key, envDirectoryKey, "", 1)] = value
	}

	composeFile.InjectEnvironmentVariables(environmentVariables)

	return nil
}

func registerApplicationMetadata(application *Application, etcd *Etcd) error {
	userDirectoryKey := "/paus/users/" + application.Username

	if !etcd.HasKey(userDirectoryKey) {
		_ = etcd.Mkdir(userDirectoryKey)
	}

	appDirectoryKey := userDirectoryKey + "/" + application.AppName

	if !etcd.HasKey(appDirectoryKey) {
		_ = etcd.Mkdir(appDirectoryKey)
		_ = etcd.Mkdir(appDirectoryKey + "/envs")
		_ = etcd.Mkdir(appDirectoryKey + "/revisions")
	}

	if err := etcd.Set(appDirectoryKey+"/revisions/"+application.Revision, strconv.FormatInt(time.Now().Unix(), 10)); err != nil {
		return err
	}

	return nil
}

func registerVulcandInformation(application *Application, baseDomain string, webContainer *Container, etcd *Etcd) error {
	vulcandDirectoryKeyBase := "/vulcand"

	// {"Type": "http"}
	if err := etcd.Set(vulcandDirectoryKeyBase+"/backends/"+application.ProjectName+"/backend", "{\"Type\": \"http\"}"); err != nil {
		return err
	}

	// {"URL": "http://$web_container_host_ip:$web_container_port"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/backends/"+application.ProjectName+"/servers/"+webContainer.ContainerId,
		"{\"URL\": \"http://"+webContainer.HostIP()+":"+webContainer.HostPort()+"\"}",
	); err != nil {
		return err
	}

	// {"Type": "http", "BackendId": "$PROJECT_NAME", "Route": "Host(`$PROJECT_NAME.$BASE_DOMAIN`) && PathRegexp(`/`)"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/frontends/"+application.ProjectName+"/frontend",
		"{\"Type\": \"http\", \"BackendId\": \""+application.ProjectName+"\", \"Route\": \"Host(`"+application.ProjectName+"."+baseDomain+"`) && PathRegexp(`/`)\"}",
	); err != nil {
		return err
	}

	// {"Type": "http", "BackendId": "$PROJECT_NAME", "Route": "Host(`$USER_NAME.$BASE_DOMAIN`) && PathRegexp(`/`)"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/frontends/"+application.Username+"/frontend",
		"{\"Type\": \"http\", \"BackendId\": \""+application.ProjectName+"\", \"Route\": \"Host(`"+application.Username+"."+baseDomain+"`) && PathRegexp(`/`)\"}",
	); err != nil {
		return err
	}

	// {"Type": "http", "BackendId": "$PROJECT_NAME", "Route": "Host(`$APP_NAME.$BASE_DOMAIN`) && PathRegexp(`/`)"}
	if err := etcd.Set(
		vulcandDirectoryKeyBase+"/frontends/"+application.AppName+"/frontend",
		"{\"Type\": \"http\", \"BackendId\": \""+application.ProjectName+"\", \"Route\": \"Host(`"+application.AppName+"."+baseDomain+"`) && PathRegexp(`/`)\"}",
	); err != nil {
		return err
	}

	return nil
}

func removeUnpackedFiles(repositoryPath, newComposeFilePath string) error {
	files, err := ioutil.ReadDir(repositoryPath)

	if err != nil {
		return err
	}

	for _, file := range files {
		if filepath.Join(repositoryPath, file.Name()) != newComposeFilePath {
			if err = os.RemoveAll(filepath.Join(repositoryPath, file.Name())); err != nil {
				return err
			}
		}
	}

	return nil
}

func unpackReceivedFiles(repositoryDir, username, projectName string, stdin io.Reader) (string, error) {
	repositoryPath := filepath.Join(repositoryDir, username, projectName)

	if err := os.MkdirAll(repositoryPath, 0777); err != nil {
		return "", err
	}

	reader := tar.NewReader(stdin)

	for {
		header, err := reader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			return "", err
		}

		buffer := new(bytes.Buffer)
		outPath := filepath.Join(repositoryPath, header.Name)

		switch header.Typeflag {
		case tar.TypeDir:
			if _, err = os.Stat(outPath); err != nil {
				os.MkdirAll(outPath, 0755)
			}

		case tar.TypeReg, tar.TypeRegA:
			if _, err = io.Copy(buffer, reader); err != nil {
				return "", err
			}

			if err = ioutil.WriteFile(outPath, buffer.Bytes(), os.FileMode(header.Mode)); err != nil {
				return "", err
			}
		}
	}

	return repositoryPath, nil
}

func main() {
	config, err := LoadConfig()

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	application := ApplicationFromArgs(os.Args[1:])
	repositoryPath, err := unpackReceivedFiles(config.RepositoryDir, application.Username, application.ProjectName, os.Stdin)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err = os.Chdir(repositoryPath); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}

	composeFilePath := filepath.Join(repositoryPath, "docker-compose.yml")

	if _, err := os.Stat(composeFilePath); err != nil {
		fmt.Fprintln(os.Stderr, "=====> docker-compose.yml was NOT found!")
		os.Exit(1)
	}

	fmt.Println("=====> docker-compose.yml was found")
	composeFile, err := NewComposeFile(composeFilePath)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	etcd, err := NewEtcd(config.EtcdEndpoint)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err = injectEnvironmentVariables(application, composeFile, etcd); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	composeFile.RewritePortBindings()
	newComposeFilePath := filepath.Join(repositoryPath, "docker-compose-"+strconv.FormatInt(time.Now().Unix(), 10)+".yml")

	if err = composeFile.SaveAs(newComposeFilePath); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	webContainerId, err := deploy(config.DockerHost, application, newComposeFilePath)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Println("=====> Application container is launched.")

	webContainer, err := ContainerFromID(config.DockerHost, webContainerId)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	fmt.Println("=====> Registering metadata ...")

	if err = registerApplicationMetadata(application, etcd); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	if err = registerVulcandInformation(application, config.BaseDomain, webContainer, etcd); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	urlList := []string{
		"http://" + application.ProjectName + "." + config.BaseDomain,
		"http://" + application.Username + "." + config.BaseDomain,
		"http://" + application.AppName + "." + config.BaseDomain,
	}

	fmt.Println("=====> " + application.Repository + " was successfully deployed at:")

	for _, url := range urlList {
		fmt.Println("         " + url)
	}

	if err = removeUnpackedFiles(repositoryPath, newComposeFilePath); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019, 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 stackdriverexporter contains the wrapper for OpenTelemetry-Stackdriver
// exporter to be used in opentelemetry-collector.
package stackdriverexporter

import (
	"context"
	"fmt"
	"strings"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenterror"
	"go.opentelemetry.io/collector/consumer/consumerdata"
	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/exporter/exporterhelper"
	"go.opentelemetry.io/collector/translator/internaldata"
	traceexport "go.opentelemetry.io/otel/sdk/export/trace"
	"google.golang.org/api/option"
	"google.golang.org/grpc"
)

const name = "stackdriver"

// traceExporter is a wrapper struct of OT cloud trace exporter
type traceExporter struct {
	texporter *cloudtrace.Exporter
}

// metricsExporter is a wrapper struct of OC stackdriver exporter
type metricsExporter struct {
	mexporter *stackdriver.Exporter
}

func (*traceExporter) Name() string {
	return name
}

func (*metricsExporter) Name() string {
	return name
}

func (te *traceExporter) Shutdown(ctx context.Context) error {
	return te.texporter.Shutdown(ctx)
}

func (me *metricsExporter) Shutdown(context.Context) error {
	me.mexporter.Flush()
	me.mexporter.StopMetricsExporter()
	return nil
}

func generateClientOptions(cfg *Config, version string) ([]option.ClientOption, error) {
	userAgent := strings.ReplaceAll(cfg.UserAgent, "{{version}}", version)
	var copts []option.ClientOption
	if userAgent != "" {
		copts = append(copts, option.WithUserAgent(userAgent))
	}
	if cfg.Endpoint != "" {
		if cfg.UseInsecure {
			// WithGRPCConn option takes precedent over all other supplied options so need to provide user agent here as well
			var dialOpts []grpc.DialOption
			if userAgent != "" {
				dialOpts = append(dialOpts, grpc.WithUserAgent(userAgent))
			}
			conn, err := grpc.Dial(cfg.Endpoint, append(dialOpts, grpc.WithInsecure())...)
			if err != nil {
				return nil, fmt.Errorf("cannot configure grpc conn: %w", err)
			}
			copts = append(copts, option.WithGRPCConn(conn))
		} else {
			copts = append(copts, option.WithEndpoint(cfg.Endpoint))
		}
	}
	if cfg.GetClientOptions != nil {
		copts = append(copts, cfg.GetClientOptions()...)
	}
	return copts, nil
}

func newStackdriverTraceExporter(cfg *Config, params component.ExporterCreateParams) (component.TracesExporter, error) {
	topts := []cloudtrace.Option{
		cloudtrace.WithProjectID(cfg.ProjectID),
		cloudtrace.WithTimeout(cfg.Timeout),
	}

	copts, err := generateClientOptions(cfg, params.ApplicationStartInfo.Version)
	if err != nil {
		return nil, err
	}
	topts = append(topts, cloudtrace.WithTraceClientOptions(copts))
	if cfg.NumOfWorkers > 0 {
		topts = append(topts, cloudtrace.WithMaxNumberOfWorkers(cfg.NumOfWorkers))
	}

	topts, err = appendBundleOptions(topts, cfg.TraceConfig)
	if err != nil {
		return nil, err
	}

	exp, err := cloudtrace.NewExporter(topts...)
	if err != nil {
		return nil, fmt.Errorf("error creating Stackdriver Trace exporter: %w", err)
	}

	tExp := &traceExporter{texporter: exp}

	return exporterhelper.NewTraceExporter(
		cfg,
		params.Logger,
		tExp.pushTraces,
		exporterhelper.WithShutdown(tExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

func appendBundleOptions(topts []cloudtrace.Option, cfg TraceConfig) ([]cloudtrace.Option, error) {
	topts, err := validateAndAppendDurationOption(topts, "BundleDelayThreshold", cfg.BundleDelayThreshold, cloudtrace.WithBundleDelayThreshold(cfg.BundleDelayThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleCountThreshold", cfg.BundleCountThreshold, cloudtrace.WithBundleCountThreshold(cfg.BundleCountThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteThreshold", cfg.BundleByteThreshold, cloudtrace.WithBundleByteThreshold(cfg.BundleByteThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteLimit", cfg.BundleByteLimit, cloudtrace.WithBundleByteLimit(cfg.BundleByteLimit))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BufferMaxBytes", cfg.BufferMaxBytes, cloudtrace.WithBufferMaxBytes(cfg.BufferMaxBytes))
	if err != nil {
		return nil, err
	}

	return topts, nil
}

func validateAndAppendIntOption(topts []cloudtrace.Option, name string, val int, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func validateAndAppendDurationOption(topts []cloudtrace.Option, name string, val time.Duration, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func newStackdriverMetricsExporter(cfg *Config, params component.ExporterCreateParams) (component.MetricsExporter, error) {
	// TODO:  For each ProjectID, create a different exporter
	// or at least a unique Stackdriver client per ProjectID.
	options := stackdriver.Options{
		// If the project ID is an empty string, it will be set by default based on
		// the project this is running on in GCP.
		ProjectID: cfg.ProjectID,

		MetricPrefix: cfg.MetricConfig.Prefix,

		// Set DefaultMonitoringLabels to an empty map to avoid getting the "opencensus_task" label
		DefaultMonitoringLabels: &stackdriver.Labels{},

		Timeout: cfg.Timeout,
	}

	copts, err := generateClientOptions(cfg, params.ApplicationStartInfo.Version)
	if err != nil {
		return nil, err
	}
	options.TraceClientOptions = copts
	options.MonitoringClientOptions = copts

	if cfg.NumOfWorkers > 0 {
		options.NumberOfWorkers = cfg.NumOfWorkers
	}
	if cfg.MetricConfig.SkipCreateMetricDescriptor {
		options.SkipCMD = true
	}
	if len(cfg.ResourceMappings) > 0 {
		rm := resourceMapper{
			mappings: cfg.ResourceMappings,
		}
		options.MapResource = rm.mapResource
	}

	sde, serr := stackdriver.NewExporter(options)
	if serr != nil {
		return nil, fmt.Errorf("cannot configure Stackdriver metric exporter: %w", serr)
	}
	mExp := &metricsExporter{mexporter: sde}

	return exporterhelper.NewMetricsExporter(
		cfg,
		params.Logger,
		mExp.pushMetrics,
		exporterhelper.WithShutdown(mExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

// pushMetrics calls StackdriverExporter.PushMetricsProto on each element of the given metrics
func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) (int, error) {
	var errors []error
	var totalDropped int

	mds := internaldata.MetricsToOC(m)
	for _, md := range mds {
		if len(md.Metrics) == 0 {
			continue
		}

		points := numPoints(md)
		dropped, err := me.mexporter.PushMetricsProto(ctx, md.Node, md.Resource, md.Metrics)
		recordPointCount(ctx, points-dropped, dropped, err)
		totalDropped += dropped
		if err != nil {
			errors = append(errors, err)
		}
	}

	if len(errors) > 0 {
		return totalDropped, componenterror.CombineErrors(errors)
	}

	return totalDropped, nil
}

// pushTraces calls texporter.ExportSpan for each span in the given traces
func (te *traceExporter) pushTraces(ctx context.Context, td pdata.Traces) (int, error) {
	var errs []error
	resourceSpans := td.ResourceSpans()
	numSpans := td.SpanCount()
	spans := make([]*traceexport.SpanData, 0, numSpans)

	for i := 0; i < resourceSpans.Len(); i++ {
		sd, err := pdataResourceSpansToOTSpanData(resourceSpans.At(i))
		if err == nil {
			spans = append(spans, sd...)
		} else {
			errs = append(errs, err)
		}
	}

	err := te.texporter.ExportSpans(ctx, spans)
	if err != nil {
		errs = append(errs, err)
	}
	return numSpans - len(spans), componenterror.CombineErrors(errs)
}

func numPoints(md consumerdata.MetricsData) int {
	numPoints := 0
	for _, metric := range md.Metrics {
		tss := metric.GetTimeseries()
		for _, ts := range tss {
			numPoints += len(ts.GetPoints())
		}
	}
	return numPoints
}
<commit_msg>Set options.UserAgent so that the OpenCensus exporter does not overri… (#1620)<commit_after>// Copyright 2019, 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 stackdriverexporter contains the wrapper for OpenTelemetry-Stackdriver
// exporter to be used in opentelemetry-collector.
package stackdriverexporter

import (
	"context"
	"fmt"
	"strings"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenterror"
	"go.opentelemetry.io/collector/consumer/consumerdata"
	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/exporter/exporterhelper"
	"go.opentelemetry.io/collector/translator/internaldata"
	traceexport "go.opentelemetry.io/otel/sdk/export/trace"
	"google.golang.org/api/option"
	"google.golang.org/grpc"
)

const name = "stackdriver"

// traceExporter is a wrapper struct of OT cloud trace exporter
type traceExporter struct {
	texporter *cloudtrace.Exporter
}

// metricsExporter is a wrapper struct of OC stackdriver exporter
type metricsExporter struct {
	mexporter *stackdriver.Exporter
}

func (*traceExporter) Name() string {
	return name
}

func (*metricsExporter) Name() string {
	return name
}

func (te *traceExporter) Shutdown(ctx context.Context) error {
	return te.texporter.Shutdown(ctx)
}

func (me *metricsExporter) Shutdown(context.Context) error {
	me.mexporter.Flush()
	me.mexporter.StopMetricsExporter()
	return nil
}

func setVersionInUserAgent(cfg *Config, version string) {
	cfg.UserAgent = strings.ReplaceAll(cfg.UserAgent, "{{version}}", version)
}

func generateClientOptions(cfg *Config) ([]option.ClientOption, error) {
	var copts []option.ClientOption
	// option.WithUserAgent is used by the Trace exporter, but not the Metric exporter (see comment below)
	if cfg.UserAgent != "" {
		copts = append(copts, option.WithUserAgent(cfg.UserAgent))
	}
	if cfg.Endpoint != "" {
		if cfg.UseInsecure {
			// option.WithGRPCConn option takes precedent over all other supplied options so the
			// following user agent will be used by both exporters if we reach this branch
			var dialOpts []grpc.DialOption
			if cfg.UserAgent != "" {
				dialOpts = append(dialOpts, grpc.WithUserAgent(cfg.UserAgent))
			}
			conn, err := grpc.Dial(cfg.Endpoint, append(dialOpts, grpc.WithInsecure())...)
			if err != nil {
				return nil, fmt.Errorf("cannot configure grpc conn: %w", err)
			}
			copts = append(copts, option.WithGRPCConn(conn))
		} else {
			copts = append(copts, option.WithEndpoint(cfg.Endpoint))
		}
	}
	if cfg.GetClientOptions != nil {
		copts = append(copts, cfg.GetClientOptions()...)
	}
	return copts, nil
}

func newStackdriverTraceExporter(cfg *Config, params component.ExporterCreateParams) (component.TracesExporter, error) {
	setVersionInUserAgent(cfg, params.ApplicationStartInfo.Version)

	topts := []cloudtrace.Option{
		cloudtrace.WithProjectID(cfg.ProjectID),
		cloudtrace.WithTimeout(cfg.Timeout),
	}

	copts, err := generateClientOptions(cfg)
	if err != nil {
		return nil, err
	}
	topts = append(topts, cloudtrace.WithTraceClientOptions(copts))
	if cfg.NumOfWorkers > 0 {
		topts = append(topts, cloudtrace.WithMaxNumberOfWorkers(cfg.NumOfWorkers))
	}

	topts, err = appendBundleOptions(topts, cfg.TraceConfig)
	if err != nil {
		return nil, err
	}

	exp, err := cloudtrace.NewExporter(topts...)
	if err != nil {
		return nil, fmt.Errorf("error creating Stackdriver Trace exporter: %w", err)
	}

	tExp := &traceExporter{texporter: exp}

	return exporterhelper.NewTraceExporter(
		cfg,
		params.Logger,
		tExp.pushTraces,
		exporterhelper.WithShutdown(tExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

func appendBundleOptions(topts []cloudtrace.Option, cfg TraceConfig) ([]cloudtrace.Option, error) {
	topts, err := validateAndAppendDurationOption(topts, "BundleDelayThreshold", cfg.BundleDelayThreshold, cloudtrace.WithBundleDelayThreshold(cfg.BundleDelayThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleCountThreshold", cfg.BundleCountThreshold, cloudtrace.WithBundleCountThreshold(cfg.BundleCountThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteThreshold", cfg.BundleByteThreshold, cloudtrace.WithBundleByteThreshold(cfg.BundleByteThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteLimit", cfg.BundleByteLimit, cloudtrace.WithBundleByteLimit(cfg.BundleByteLimit))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BufferMaxBytes", cfg.BufferMaxBytes, cloudtrace.WithBufferMaxBytes(cfg.BufferMaxBytes))
	if err != nil {
		return nil, err
	}

	return topts, nil
}

func validateAndAppendIntOption(topts []cloudtrace.Option, name string, val int, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func validateAndAppendDurationOption(topts []cloudtrace.Option, name string, val time.Duration, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func newStackdriverMetricsExporter(cfg *Config, params component.ExporterCreateParams) (component.MetricsExporter, error) {
	setVersionInUserAgent(cfg, params.ApplicationStartInfo.Version)

	// TODO:  For each ProjectID, create a different exporter
	// or at least a unique Stackdriver client per ProjectID.
	options := stackdriver.Options{
		// If the project ID is an empty string, it will be set by default based on
		// the project this is running on in GCP.
		ProjectID: cfg.ProjectID,

		MetricPrefix: cfg.MetricConfig.Prefix,

		// Set DefaultMonitoringLabels to an empty map to avoid getting the "opencensus_task" label
		DefaultMonitoringLabels: &stackdriver.Labels{},

		Timeout: cfg.Timeout,
	}

	// note options.UserAgent overrides the option.WithUserAgent client option in the Metric exporter
	if cfg.UserAgent != "" {
		options.UserAgent = cfg.UserAgent
	}

	copts, err := generateClientOptions(cfg)
	if err != nil {
		return nil, err
	}
	options.TraceClientOptions = copts
	options.MonitoringClientOptions = copts

	if cfg.NumOfWorkers > 0 {
		options.NumberOfWorkers = cfg.NumOfWorkers
	}
	if cfg.MetricConfig.SkipCreateMetricDescriptor {
		options.SkipCMD = true
	}
	if len(cfg.ResourceMappings) > 0 {
		rm := resourceMapper{
			mappings: cfg.ResourceMappings,
		}
		options.MapResource = rm.mapResource
	}

	sde, serr := stackdriver.NewExporter(options)
	if serr != nil {
		return nil, fmt.Errorf("cannot configure Stackdriver metric exporter: %w", serr)
	}
	mExp := &metricsExporter{mexporter: sde}

	return exporterhelper.NewMetricsExporter(
		cfg,
		params.Logger,
		mExp.pushMetrics,
		exporterhelper.WithShutdown(mExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

// pushMetrics calls StackdriverExporter.PushMetricsProto on each element of the given metrics
func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) (int, error) {
	var errors []error
	var totalDropped int

	mds := internaldata.MetricsToOC(m)
	for _, md := range mds {
		if len(md.Metrics) == 0 {
			continue
		}

		points := numPoints(md)
		dropped, err := me.mexporter.PushMetricsProto(ctx, md.Node, md.Resource, md.Metrics)
		recordPointCount(ctx, points-dropped, dropped, err)
		totalDropped += dropped
		if err != nil {
			errors = append(errors, err)
		}
	}

	if len(errors) > 0 {
		return totalDropped, componenterror.CombineErrors(errors)
	}

	return totalDropped, nil
}

// pushTraces calls texporter.ExportSpan for each span in the given traces
func (te *traceExporter) pushTraces(ctx context.Context, td pdata.Traces) (int, error) {
	var errs []error
	resourceSpans := td.ResourceSpans()
	numSpans := td.SpanCount()
	spans := make([]*traceexport.SpanData, 0, numSpans)

	for i := 0; i < resourceSpans.Len(); i++ {
		sd, err := pdataResourceSpansToOTSpanData(resourceSpans.At(i))
		if err == nil {
			spans = append(spans, sd...)
		} else {
			errs = append(errs, err)
		}
	}

	err := te.texporter.ExportSpans(ctx, spans)
	if err != nil {
		errs = append(errs, err)
	}
	return numSpans - len(spans), componenterror.CombineErrors(errs)
}

func numPoints(md consumerdata.MetricsData) int {
	numPoints := 0
	for _, metric := range md.Metrics {
		tss := metric.GetTimeseries()
		for _, ts := range tss {
			numPoints += len(ts.GetPoints())
		}
	}
	return numPoints
}
<|endoftext|>


<commit_before>// Copyright 2019, 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 stackdriverexporter contains the wrapper for OpenTelemetry-Stackdriver
// exporter to be used in opentelemetry-collector.
package stackdriverexporter

import (
	"context"
	"fmt"
	"strings"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenterror"
	"go.opentelemetry.io/collector/consumer/consumerdata"
	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/exporter/exporterhelper"
	"go.opentelemetry.io/collector/translator/internaldata"
	traceexport "go.opentelemetry.io/otel/sdk/export/trace"
	"google.golang.org/api/option"
	"google.golang.org/grpc"
)

const name = "stackdriver"

// traceExporter is a wrapper struct of OT cloud trace exporter
type traceExporter struct {
	texporter *cloudtrace.Exporter
}

// metricsExporter is a wrapper struct of OC stackdriver exporter
type metricsExporter struct {
	mexporter *stackdriver.Exporter
}

func (*traceExporter) Name() string {
	return name
}

func (*metricsExporter) Name() string {
	return name
}

func (te *traceExporter) Shutdown(ctx context.Context) error {
	return te.texporter.Shutdown(ctx)
}

func (me *metricsExporter) Shutdown(context.Context) error {
	me.mexporter.Flush()
	me.mexporter.StopMetricsExporter()
	return nil
}

func generateClientOptions(cfg *Config, version string) ([]option.ClientOption, error) {
	userAgent := strings.ReplaceAll(cfg.UserAgent, "{{version}}", version)
	var copts []option.ClientOption
	if userAgent != "" {
		copts = append(copts, option.WithUserAgent(userAgent))
	}
	if cfg.Endpoint != "" {
		if cfg.UseInsecure {
			// WithGRPCConn option takes precedent over all other supplied options so need to provide user agent here as well
			var dialOpts []grpc.DialOption
			if userAgent != "" {
				dialOpts = append(dialOpts, grpc.WithUserAgent(userAgent))
			}
			conn, err := grpc.Dial(cfg.Endpoint, append(dialOpts, grpc.WithInsecure())...)
			if err != nil {
				return nil, fmt.Errorf("cannot configure grpc conn: %w", err)
			}
			copts = append(copts, option.WithGRPCConn(conn))
		} else {
			copts = append(copts, option.WithEndpoint(cfg.Endpoint))
		}
	}
	if cfg.GetClientOptions != nil {
		copts = append(copts, cfg.GetClientOptions()...)
	}
	return copts, nil
}

func newStackdriverTraceExporter(cfg *Config, params component.ExporterCreateParams) (component.TracesExporter, error) {
	topts := []cloudtrace.Option{
		cloudtrace.WithProjectID(cfg.ProjectID),
		cloudtrace.WithTimeout(cfg.Timeout),
	}

	copts, err := generateClientOptions(cfg, params.ApplicationStartInfo.Version)
	if err != nil {
		return nil, err
	}
	topts = append(topts, cloudtrace.WithTraceClientOptions(copts))
	if cfg.NumOfWorkers > 0 {
		topts = append(topts, cloudtrace.WithMaxNumberOfWorkers(cfg.NumOfWorkers))
	}

	topts, err = appendBundleOptions(topts, cfg.TraceConfig)
	if err != nil {
		return nil, err
	}

	exp, err := cloudtrace.NewExporter(topts...)
	if err != nil {
		return nil, fmt.Errorf("error creating Stackdriver Trace exporter: %w", err)
	}

	tExp := &traceExporter{texporter: exp}

	return exporterhelper.NewTraceExporter(
		cfg,
		params.Logger,
		tExp.pushTraces,
		exporterhelper.WithShutdown(tExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

func appendBundleOptions(topts []cloudtrace.Option, cfg TraceConfig) ([]cloudtrace.Option, error) {
	topts, err := validateAndAppendDurationOption(topts, "BundleDelayThreshold", cfg.BundleDelayThreshold, cloudtrace.WithBundleDelayThreshold(cfg.BundleDelayThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleCountThreshold", cfg.BundleCountThreshold, cloudtrace.WithBundleCountThreshold(cfg.BundleCountThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteThreshold", cfg.BundleByteThreshold, cloudtrace.WithBundleByteThreshold(cfg.BundleByteThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteLimit", cfg.BundleByteLimit, cloudtrace.WithBundleByteLimit(cfg.BundleByteLimit))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BufferMaxBytes", cfg.BufferMaxBytes, cloudtrace.WithBufferMaxBytes(cfg.BufferMaxBytes))
	if err != nil {
		return nil, err
	}

	return topts, nil
}

func validateAndAppendIntOption(topts []cloudtrace.Option, name string, val int, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func validateAndAppendDurationOption(topts []cloudtrace.Option, name string, val time.Duration, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func newStackdriverMetricsExporter(cfg *Config, params component.ExporterCreateParams) (component.MetricsExporter, error) {
	// TODO:  For each ProjectID, create a different exporter
	// or at least a unique Stackdriver client per ProjectID.
	options := stackdriver.Options{
		// If the project ID is an empty string, it will be set by default based on
		// the project this is running on in GCP.
		ProjectID: cfg.ProjectID,

		MetricPrefix: cfg.MetricConfig.Prefix,

		// Set DefaultMonitoringLabels to an empty map to avoid getting the "opencensus_task" label
		DefaultMonitoringLabels: &stackdriver.Labels{},

		Timeout: cfg.Timeout,
	}

	copts, err := generateClientOptions(cfg, params.ApplicationStartInfo.Version)
	if err != nil {
		return nil, err
	}
	options.TraceClientOptions = copts
	options.MonitoringClientOptions = copts

	if cfg.NumOfWorkers > 0 {
		options.NumberOfWorkers = cfg.NumOfWorkers
	}
	if cfg.MetricConfig.SkipCreateMetricDescriptor {
		options.SkipCMD = true
	}
	if len(cfg.ResourceMappings) > 0 {
		rm := resourceMapper{
			mappings: cfg.ResourceMappings,
		}
		options.MapResource = rm.mapResource
	}

	sde, serr := stackdriver.NewExporter(options)
	if serr != nil {
		return nil, fmt.Errorf("cannot configure Stackdriver metric exporter: %w", serr)
	}
	mExp := &metricsExporter{mexporter: sde}

	return exporterhelper.NewMetricsExporter(
		cfg,
		params.Logger,
		mExp.pushMetrics,
		exporterhelper.WithShutdown(mExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

// pushMetrics calls StackdriverExporter.PushMetricsProto on each element of the given metrics
func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) (int, error) {
	var errors []error
	var totalDropped int

	mds := internaldata.MetricsToOC(m)
	for _, md := range mds {
		points := numPoints(md)
		dropped, err := me.mexporter.PushMetricsProto(ctx, md.Node, md.Resource, md.Metrics)
		recordPointCount(ctx, points-dropped, dropped, err)
		totalDropped += dropped
		if err != nil {
			errors = append(errors, err)
		}
	}

	if len(errors) > 0 {
		return totalDropped, componenterror.CombineErrors(errors)
	}

	return totalDropped, nil
}

// pushTraces calls texporter.ExportSpan for each span in the given traces
func (te *traceExporter) pushTraces(ctx context.Context, td pdata.Traces) (int, error) {
	var errs []error
	resourceSpans := td.ResourceSpans()
	numSpans := td.SpanCount()
	spans := make([]*traceexport.SpanData, 0, numSpans)

	for i := 0; i < resourceSpans.Len(); i++ {
		sd, err := pdataResourceSpansToOTSpanData(resourceSpans.At(i))
		if err == nil {
			spans = append(spans, sd...)
		} else {
			errs = append(errs, err)
		}
	}

	err := te.texporter.ExportSpans(ctx, spans)
	if err != nil {
		errs = append(errs, err)
	}
	return numSpans - len(spans), componenterror.CombineErrors(errs)
}

func numPoints(md consumerdata.MetricsData) int {
	numPoints := 0
	for _, metric := range md.Metrics {
		tss := metric.GetTimeseries()
		for _, ts := range tss {
			numPoints += len(ts.GetPoints())
		}
	}
	return numPoints
}
<commit_msg>Skip processing empty metric slice in Stackdriver exporter (#1494)<commit_after>// Copyright 2019, 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 stackdriverexporter contains the wrapper for OpenTelemetry-Stackdriver
// exporter to be used in opentelemetry-collector.
package stackdriverexporter

import (
	"context"
	"fmt"
	"strings"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/component/componenterror"
	"go.opentelemetry.io/collector/consumer/consumerdata"
	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/exporter/exporterhelper"
	"go.opentelemetry.io/collector/translator/internaldata"
	traceexport "go.opentelemetry.io/otel/sdk/export/trace"
	"google.golang.org/api/option"
	"google.golang.org/grpc"
)

const name = "stackdriver"

// traceExporter is a wrapper struct of OT cloud trace exporter
type traceExporter struct {
	texporter *cloudtrace.Exporter
}

// metricsExporter is a wrapper struct of OC stackdriver exporter
type metricsExporter struct {
	mexporter *stackdriver.Exporter
}

func (*traceExporter) Name() string {
	return name
}

func (*metricsExporter) Name() string {
	return name
}

func (te *traceExporter) Shutdown(ctx context.Context) error {
	return te.texporter.Shutdown(ctx)
}

func (me *metricsExporter) Shutdown(context.Context) error {
	me.mexporter.Flush()
	me.mexporter.StopMetricsExporter()
	return nil
}

func generateClientOptions(cfg *Config, version string) ([]option.ClientOption, error) {
	userAgent := strings.ReplaceAll(cfg.UserAgent, "{{version}}", version)
	var copts []option.ClientOption
	if userAgent != "" {
		copts = append(copts, option.WithUserAgent(userAgent))
	}
	if cfg.Endpoint != "" {
		if cfg.UseInsecure {
			// WithGRPCConn option takes precedent over all other supplied options so need to provide user agent here as well
			var dialOpts []grpc.DialOption
			if userAgent != "" {
				dialOpts = append(dialOpts, grpc.WithUserAgent(userAgent))
			}
			conn, err := grpc.Dial(cfg.Endpoint, append(dialOpts, grpc.WithInsecure())...)
			if err != nil {
				return nil, fmt.Errorf("cannot configure grpc conn: %w", err)
			}
			copts = append(copts, option.WithGRPCConn(conn))
		} else {
			copts = append(copts, option.WithEndpoint(cfg.Endpoint))
		}
	}
	if cfg.GetClientOptions != nil {
		copts = append(copts, cfg.GetClientOptions()...)
	}
	return copts, nil
}

func newStackdriverTraceExporter(cfg *Config, params component.ExporterCreateParams) (component.TracesExporter, error) {
	topts := []cloudtrace.Option{
		cloudtrace.WithProjectID(cfg.ProjectID),
		cloudtrace.WithTimeout(cfg.Timeout),
	}

	copts, err := generateClientOptions(cfg, params.ApplicationStartInfo.Version)
	if err != nil {
		return nil, err
	}
	topts = append(topts, cloudtrace.WithTraceClientOptions(copts))
	if cfg.NumOfWorkers > 0 {
		topts = append(topts, cloudtrace.WithMaxNumberOfWorkers(cfg.NumOfWorkers))
	}

	topts, err = appendBundleOptions(topts, cfg.TraceConfig)
	if err != nil {
		return nil, err
	}

	exp, err := cloudtrace.NewExporter(topts...)
	if err != nil {
		return nil, fmt.Errorf("error creating Stackdriver Trace exporter: %w", err)
	}

	tExp := &traceExporter{texporter: exp}

	return exporterhelper.NewTraceExporter(
		cfg,
		params.Logger,
		tExp.pushTraces,
		exporterhelper.WithShutdown(tExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

func appendBundleOptions(topts []cloudtrace.Option, cfg TraceConfig) ([]cloudtrace.Option, error) {
	topts, err := validateAndAppendDurationOption(topts, "BundleDelayThreshold", cfg.BundleDelayThreshold, cloudtrace.WithBundleDelayThreshold(cfg.BundleDelayThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleCountThreshold", cfg.BundleCountThreshold, cloudtrace.WithBundleCountThreshold(cfg.BundleCountThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteThreshold", cfg.BundleByteThreshold, cloudtrace.WithBundleByteThreshold(cfg.BundleByteThreshold))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BundleByteLimit", cfg.BundleByteLimit, cloudtrace.WithBundleByteLimit(cfg.BundleByteLimit))
	if err != nil {
		return nil, err
	}

	topts, err = validateAndAppendIntOption(topts, "BufferMaxBytes", cfg.BufferMaxBytes, cloudtrace.WithBufferMaxBytes(cfg.BufferMaxBytes))
	if err != nil {
		return nil, err
	}

	return topts, nil
}

func validateAndAppendIntOption(topts []cloudtrace.Option, name string, val int, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func validateAndAppendDurationOption(topts []cloudtrace.Option, name string, val time.Duration, opt cloudtrace.Option) ([]cloudtrace.Option, error) {
	if val < 0 {
		return nil, fmt.Errorf("invalid value for: %s", name)
	}

	if val > 0 {
		topts = append(topts, opt)
	}

	return topts, nil
}

func newStackdriverMetricsExporter(cfg *Config, params component.ExporterCreateParams) (component.MetricsExporter, error) {
	// TODO:  For each ProjectID, create a different exporter
	// or at least a unique Stackdriver client per ProjectID.
	options := stackdriver.Options{
		// If the project ID is an empty string, it will be set by default based on
		// the project this is running on in GCP.
		ProjectID: cfg.ProjectID,

		MetricPrefix: cfg.MetricConfig.Prefix,

		// Set DefaultMonitoringLabels to an empty map to avoid getting the "opencensus_task" label
		DefaultMonitoringLabels: &stackdriver.Labels{},

		Timeout: cfg.Timeout,
	}

	copts, err := generateClientOptions(cfg, params.ApplicationStartInfo.Version)
	if err != nil {
		return nil, err
	}
	options.TraceClientOptions = copts
	options.MonitoringClientOptions = copts

	if cfg.NumOfWorkers > 0 {
		options.NumberOfWorkers = cfg.NumOfWorkers
	}
	if cfg.MetricConfig.SkipCreateMetricDescriptor {
		options.SkipCMD = true
	}
	if len(cfg.ResourceMappings) > 0 {
		rm := resourceMapper{
			mappings: cfg.ResourceMappings,
		}
		options.MapResource = rm.mapResource
	}

	sde, serr := stackdriver.NewExporter(options)
	if serr != nil {
		return nil, fmt.Errorf("cannot configure Stackdriver metric exporter: %w", serr)
	}
	mExp := &metricsExporter{mexporter: sde}

	return exporterhelper.NewMetricsExporter(
		cfg,
		params.Logger,
		mExp.pushMetrics,
		exporterhelper.WithShutdown(mExp.Shutdown),
		// Disable exporterhelper Timeout, since we are using a custom mechanism
		// within exporter itself
		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}))
}

// pushMetrics calls StackdriverExporter.PushMetricsProto on each element of the given metrics
func (me *metricsExporter) pushMetrics(ctx context.Context, m pdata.Metrics) (int, error) {
	var errors []error
	var totalDropped int

	mds := internaldata.MetricsToOC(m)
	for _, md := range mds {
		if len(md.Metrics) == 0 {
			continue
		}

		points := numPoints(md)
		dropped, err := me.mexporter.PushMetricsProto(ctx, md.Node, md.Resource, md.Metrics)
		recordPointCount(ctx, points-dropped, dropped, err)
		totalDropped += dropped
		if err != nil {
			errors = append(errors, err)
		}
	}

	if len(errors) > 0 {
		return totalDropped, componenterror.CombineErrors(errors)
	}

	return totalDropped, nil
}

// pushTraces calls texporter.ExportSpan for each span in the given traces
func (te *traceExporter) pushTraces(ctx context.Context, td pdata.Traces) (int, error) {
	var errs []error
	resourceSpans := td.ResourceSpans()
	numSpans := td.SpanCount()
	spans := make([]*traceexport.SpanData, 0, numSpans)

	for i := 0; i < resourceSpans.Len(); i++ {
		sd, err := pdataResourceSpansToOTSpanData(resourceSpans.At(i))
		if err == nil {
			spans = append(spans, sd...)
		} else {
			errs = append(errs, err)
		}
	}

	err := te.texporter.ExportSpans(ctx, spans)
	if err != nil {
		errs = append(errs, err)
	}
	return numSpans - len(spans), componenterror.CombineErrors(errs)
}

func numPoints(md consumerdata.MetricsData) int {
	numPoints := 0
	for _, metric := range md.Metrics {
		tss := metric.GetTimeseries()
		for _, ts := range tss {
			numPoints += len(ts.GetPoints())
		}
	}
	return numPoints
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"os"
	"subutai/agent"
	"subutai/cli"
	"subutai/config"
	"subutai/log"
)

func init() {
	os.Setenv("PATH", "/apps/subutai/current/bin:/apps/subutai-mng/current/bin:"+os.Getenv("PATH"))
	if len(os.Args) > 1 {
		if os.Args[1] == "-d" {
			log.Level(log.DebugLevel)
		}
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "Subutai"
	app.Version = "4.0.0 RC5"
	app.Usage = "daemon and command line interface binary"

	app.Flags = []cli.Flag{cli.BoolFlag{
		Name:  "d",
		Usage: "debug mode"}}

	app.Commands = []cli.Command{{

		Name: "clone", Usage: "clone Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "e", Usage: "set environment id for container"},
			cli.StringFlag{Name: "i", Usage: "set container IP address and VLAN"},
			cli.StringFlag{Name: "t", Usage: "token to verify with MH"}},
		Action: func(c *cli.Context) {
			lib.LxcClone(c.Args().Get(0), c.Args().Get(1), c.String("e"), c.String("i"), c.String("t"))
		}}, {

		Name: "cleanup", Usage: "clean Subutai environment",
		Action: func(c *cli.Context) {
			lib.Cleanup(c.Args().Get(0))
		}}, {

		Name: "collect", Usage: "collect performance stats",
		Action: func(c *cli.Context) {
			lib.CollectStats()
		}}, {

		Name: "config", Usage: "containerName add/del key value",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "o", Usage: "add/del key value"},
			cli.StringFlag{Name: "k", Usage: "add/del key value"},
			cli.StringFlag{Name: "v", Usage: "add/del key value"},
		},
		Action: func(c *cli.Context) {
			lib.LxcConfig(c.Args().Get(0), c.String("o"), c.String("k"), c.String("v"))
		}}, {

		Name: "daemon", Usage: "start an agent",
		Action: func(c *cli.Context) {
			config.InitAgentDebug()
			agent.Start(c)
		},
		Flags: []cli.Flag{
			cli.StringFlag{Name: "server", Value: config.Management.Host, Usage: "management host ip address/host name"},
			cli.StringFlag{Name: "port", Value: config.Management.Port, Usage: "management host port number"},
			cli.StringFlag{Name: "user", Value: config.Agent.GpgUser, Usage: "gpg user name/email to encrypt/decrypt messages"},
			cli.StringFlag{Name: "secret", Value: config.Management.Secret, Usage: "send secret passphrase via flag"},
		}}, {

		Name: "demote", Usage: "demote Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "i", Usage: "network value ie 192.168.1.1/24"},
			cli.StringFlag{Name: "v", Usage: "vlan id"},
		},
		Action: func(c *cli.Context) {
			lib.LxcDemote(c.Args().Get(0), c.String("i"), c.String("v"))
		}}, {

		Name: "destroy", Usage: "destroy Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcDestroy(c.Args().Get(0))
		}}, {

		Name: "export", Usage: "export Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcExport(c.Args().Get(0))
		}}, {

		Name: "import", Usage: "import Subutai template",
		Action: func(c *cli.Context) {
			lib.LxcImport(c.Args().Get(0))
		}}, {

		Name: "list", Usage: "list Subutai container",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "c", Usage: "containers only"},
			cli.BoolFlag{Name: "t", Usage: "templates only"},
			cli.BoolFlag{Name: "r", Usage: "registered only"},
			cli.BoolFlag{Name: "i", Usage: "info ???? only"},
			cli.BoolFlag{Name: "a", Usage: "with ancestors"},
			cli.BoolFlag{Name: "f", Usage: "fancy mode"},
			cli.BoolFlag{Name: "p", Usage: "with parent"}},
		Action: func(c *cli.Context) {
			lib.LxcList(c.Args().Get(0), c.Bool("c"), c.Bool("t"), c.Bool("r"), c.Bool("i"), c.Bool("a"), c.Bool("f"), c.Bool("p"))
		}}, {

		Name: "management_network", Usage: "configure management network",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "listtunnel, l", Usage: "-l"},
			cli.StringFlag{Name: "createtunnel, c", Usage: "-c TUNNELPORTNAME TUNNELIPADDRESS TUNNELTYPE"},
			cli.StringFlag{Name: "removetunnel, r", Usage: "-r tunnerPortName"},

			cli.BoolFlag{Name: "listvnimap, v", Usage: "-v"},
			cli.StringFlag{Name: "createvnimap, m", Usage: "-m TUNNELPORTNAME VNI VLANID ENV_ID"},
			cli.StringFlag{Name: "reservvni, E", Usage: "-E vni, vlanid, envid"},
			cli.StringFlag{Name: "removevni, M", Usage: "-M TUNNELPORTNAME VNI VLANID"},

			cli.StringFlag{Name: "deletegateway, D", Usage: "-D VLANID"},
			cli.StringFlag{Name: "creategateway, T", Usage: "-T VLANIP/SUBNET VLANID"},
			cli.StringFlag{Name: "vniop, Z", Usage: "-Z [deleteall] | [list]"}},

		Subcommands: []cli.Command{{
			Name:  "p2p",
			Usage: "p2p network operation",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "c", Usage: "create p2p instance (p2p -c interfaceName localPeepIPAddr hash key ttl)"},
				cli.BoolFlag{Name: "d", Usage: "delete p2p instance (p2p -d hash)"},
				cli.BoolFlag{Name: "u", Usage: "update p2p instance encryption key (p2p -u hash newkey ttl)"},
				cli.BoolFlag{Name: "l", Usage: "list of p2p instances (p2p -l)"},
				cli.BoolFlag{Name: "p", Usage: "list of p2p participants (p2p -p hash)"}},
			Action: func(c *cli.Context) {
				lib.P2P(c.Bool("c"), c.Bool("d"), c.Bool("u"), c.Bool("l"), c.Bool("p"), os.Args)
			}}},

		Action: func(c *cli.Context) {
			lib.LxcManagementNetwork(os.Args)
		}}, {

		Name: "metrics", Usage: "list Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "s", Usage: "start time"},
			cli.StringFlag{Name: "e", Usage: "end time"}},
		Action: func(c *cli.Context) {
			lib.HostMetrics(c.Args().Get(0), c.String("s"), c.String("e"))
		}}, {

		Name: "network", Usage: "containerName set/remove/list network vlan id",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "set, s", Usage: "IPADDRESS/NETMASK"},
			cli.StringFlag{Name: "vlan, v", Usage: "vlanid"},
			cli.BoolFlag{Name: "remove, r", Usage: ""},
			cli.BoolFlag{Name: "list, l", Usage: ""},
		},
		Action: func(c *cli.Context) {
			lib.LxcNetwork(c.Args().Get(0), c.String("s"), c.String("vlan"), c.Bool("r"), c.Bool("l"))
		}}, {

		Name: "promote", Usage: "promote Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcPromote(c.Args().Get(0))
		}}, {

		Name: "proxy", Usage: "Subutai reverse proxy",
		Subcommands: []cli.Command{
			{
				Name:     "add",
				Usage:    "add reverse proxy component",
				HideHelp: true,
				Flags: []cli.Flag{
					cli.StringFlag{Name: "domain,d", Usage: "add domain to vlan"},
					cli.StringFlag{Name: "host, h", Usage: "add host to domain on vlan"},
					cli.StringFlag{Name: "policy, p", Usage: "set load balance policy (rr|lb|hash)"},
					cli.StringFlag{Name: "file, f", Usage: "specify pem certificate file"}},
				Action: func(c *cli.Context) {
					lib.ProxyAdd(c.Args().Get(0), c.String("d"), c.String("h"), c.String("p"), c.String("c"))
				},
			},
			{
				Name:     "del",
				Usage:    "del reverse proxy component",
				HideHelp: true,
				Flags: []cli.Flag{
					cli.BoolFlag{Name: "domain, d", Usage: "delete domain from vlan"},
					cli.StringFlag{Name: "host, h", Usage: "delete host from domain on vlan"}},
				Action: func(c *cli.Context) {
					lib.ProxyDel(c.Args().Get(0), c.String("h"), c.Bool("d"))
				},
			},
			{
				Name:     "check",
				Usage:    "check existing domain or host",
				HideHelp: true,
				Flags: []cli.Flag{
					cli.BoolFlag{Name: "domain,d", Usage: "check domains on vlan"},
					cli.StringFlag{Name: "host, h", Usage: "check hosts on vlan"}},
				Action: func(c *cli.Context) {
					lib.ProxyCheck(c.Args().Get(0), c.String("h"), c.Bool("d"))
				},
			},
		}}, {

		Name: "quota", Usage: "set quotas for Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "s", Usage: "set quota for the specified resource type"},
			cli.StringFlag{Name: "m", Usage: "get the maximum quota can be set to the specified container and resource_type in their default units"}},
		Action: func(c *cli.Context) {
			lib.LxcQuota(c.Args().Get(0), c.Args().Get(1), c.String("s"), c.String("m"))
		}}, {

		Name: "rename", Usage: "rename Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcRename(c.Args().Get(0), c.Args().Get(1))
		}}, {

		Name: "register", Usage: "register Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcRegister(c.Args().Get(0))
		}}, {

		Name: "stats", Usage: "statistics from host",
		Action: func(c *cli.Context) {
			lib.Stats(c.Args().Get(0), c.Args().Get(1), c.Args().Get(2))
		}}, {

		Name: "start", Usage: "start Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcStart(c.Args().Get(0))
		}}, {

		Name: "stop", Usage: "stop Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcStop(c.Args().Get(0))
		}}, {

		Name: "tunnel", Usage: "create SSH tunnel to container",
		Action: func(c *cli.Context) {
			lib.SshTunnel(c.Args().Get(0), c.Args().Get(1))
		}}, {

		Name: "unregister", Usage: "unregister Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcUnregister(c.Args().Get(0))
		}},
	}

	app.Run(os.Args)
}
<commit_msg>Container token for kurjun. SS-4083<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"os"
	"subutai/agent"
	"subutai/cli"
	"subutai/config"
	"subutai/log"
)

func init() {
	os.Setenv("PATH", "/apps/subutai/current/bin:/apps/subutai-mng/current/bin:"+os.Getenv("PATH"))
	if len(os.Args) > 1 {
		if os.Args[1] == "-d" {
			log.Level(log.DebugLevel)
		}
	}
}

func main() {
	app := cli.NewApp()
	app.Name = "Subutai"
	app.Version = "4.0.0 RC5"
	app.Usage = "daemon and command line interface binary"

	app.Flags = []cli.Flag{cli.BoolFlag{
		Name:  "d",
		Usage: "debug mode"}}

	app.Commands = []cli.Command{{

		Name: "clone", Usage: "clone Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "e", Usage: "set environment id for container"},
			cli.StringFlag{Name: "i", Usage: "set container IP address and VLAN"},
			cli.StringFlag{Name: "t", Usage: "token to verify with MH"}},
		Action: func(c *cli.Context) {
			lib.LxcClone(c.Args().Get(0), c.Args().Get(1), c.String("e"), c.String("i"), c.String("t"))
		}}, {

		Name: "cleanup", Usage: "clean Subutai environment",
		Action: func(c *cli.Context) {
			lib.Cleanup(c.Args().Get(0))
		}}, {

		Name: "collect", Usage: "collect performance stats",
		Action: func(c *cli.Context) {
			lib.CollectStats()
		}}, {

		Name: "config", Usage: "containerName add/del key value",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "o", Usage: "add/del key value"},
			cli.StringFlag{Name: "k", Usage: "add/del key value"},
			cli.StringFlag{Name: "v", Usage: "add/del key value"},
		},
		Action: func(c *cli.Context) {
			lib.LxcConfig(c.Args().Get(0), c.String("o"), c.String("k"), c.String("v"))
		}}, {

		Name: "daemon", Usage: "start an agent",
		Action: func(c *cli.Context) {
			config.InitAgentDebug()
			agent.Start(c)
		},
		Flags: []cli.Flag{
			cli.StringFlag{Name: "server", Value: config.Management.Host, Usage: "management host ip address/host name"},
			cli.StringFlag{Name: "port", Value: config.Management.Port, Usage: "management host port number"},
			cli.StringFlag{Name: "user", Value: config.Agent.GpgUser, Usage: "gpg user name/email to encrypt/decrypt messages"},
			cli.StringFlag{Name: "secret", Value: config.Management.Secret, Usage: "send secret passphrase via flag"},
		}}, {

		Name: "demote", Usage: "demote Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "i", Usage: "network value ie 192.168.1.1/24"},
			cli.StringFlag{Name: "v", Usage: "vlan id"},
		},
		Action: func(c *cli.Context) {
			lib.LxcDemote(c.Args().Get(0), c.String("i"), c.String("v"))
		}}, {

		Name: "destroy", Usage: "destroy Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcDestroy(c.Args().Get(0))
		}}, {

		Name: "export", Usage: "export Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcExport(c.Args().Get(0))
		}}, {

		Name: "import", Usage: "import Subutai template",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "t", Usage: "token to access kurjun repo"}},
		Action: func(c *cli.Context) {
			lib.LxcImport(c.Args().Get(0), c.String("t"))
		}}, {

		Name: "list", Usage: "list Subutai container",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "c", Usage: "containers only"},
			cli.BoolFlag{Name: "t", Usage: "templates only"},
			cli.BoolFlag{Name: "r", Usage: "registered only"},
			cli.BoolFlag{Name: "i", Usage: "info ???? only"},
			cli.BoolFlag{Name: "a", Usage: "with ancestors"},
			cli.BoolFlag{Name: "f", Usage: "fancy mode"},
			cli.BoolFlag{Name: "p", Usage: "with parent"}},
		Action: func(c *cli.Context) {
			lib.LxcList(c.Args().Get(0), c.Bool("c"), c.Bool("t"), c.Bool("r"), c.Bool("i"), c.Bool("a"), c.Bool("f"), c.Bool("p"))
		}}, {

		Name: "management_network", Usage: "configure management network",
		Flags: []cli.Flag{
			cli.BoolFlag{Name: "listtunnel, l", Usage: "-l"},
			cli.StringFlag{Name: "createtunnel, c", Usage: "-c TUNNELPORTNAME TUNNELIPADDRESS TUNNELTYPE"},
			cli.StringFlag{Name: "removetunnel, r", Usage: "-r tunnerPortName"},

			cli.BoolFlag{Name: "listvnimap, v", Usage: "-v"},
			cli.StringFlag{Name: "createvnimap, m", Usage: "-m TUNNELPORTNAME VNI VLANID ENV_ID"},
			cli.StringFlag{Name: "reservvni, E", Usage: "-E vni, vlanid, envid"},
			cli.StringFlag{Name: "removevni, M", Usage: "-M TUNNELPORTNAME VNI VLANID"},

			cli.StringFlag{Name: "deletegateway, D", Usage: "-D VLANID"},
			cli.StringFlag{Name: "creategateway, T", Usage: "-T VLANIP/SUBNET VLANID"},
			cli.StringFlag{Name: "vniop, Z", Usage: "-Z [deleteall] | [list]"}},

		Subcommands: []cli.Command{{
			Name:  "p2p",
			Usage: "p2p network operation",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "c", Usage: "create p2p instance (p2p -c interfaceName localPeepIPAddr hash key ttl)"},
				cli.BoolFlag{Name: "d", Usage: "delete p2p instance (p2p -d hash)"},
				cli.BoolFlag{Name: "u", Usage: "update p2p instance encryption key (p2p -u hash newkey ttl)"},
				cli.BoolFlag{Name: "l", Usage: "list of p2p instances (p2p -l)"},
				cli.BoolFlag{Name: "p", Usage: "list of p2p participants (p2p -p hash)"}},
			Action: func(c *cli.Context) {
				lib.P2P(c.Bool("c"), c.Bool("d"), c.Bool("u"), c.Bool("l"), c.Bool("p"), os.Args)
			}}},

		Action: func(c *cli.Context) {
			lib.LxcManagementNetwork(os.Args)
		}}, {

		Name: "metrics", Usage: "list Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "s", Usage: "start time"},
			cli.StringFlag{Name: "e", Usage: "end time"}},
		Action: func(c *cli.Context) {
			lib.HostMetrics(c.Args().Get(0), c.String("s"), c.String("e"))
		}}, {

		Name: "network", Usage: "containerName set/remove/list network vlan id",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "set, s", Usage: "IPADDRESS/NETMASK"},
			cli.StringFlag{Name: "vlan, v", Usage: "vlanid"},
			cli.BoolFlag{Name: "remove, r", Usage: ""},
			cli.BoolFlag{Name: "list, l", Usage: ""},
		},
		Action: func(c *cli.Context) {
			lib.LxcNetwork(c.Args().Get(0), c.String("s"), c.String("vlan"), c.Bool("r"), c.Bool("l"))
		}}, {

		Name: "promote", Usage: "promote Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcPromote(c.Args().Get(0))
		}}, {

		Name: "proxy", Usage: "Subutai reverse proxy",
		Subcommands: []cli.Command{
			{
				Name:     "add",
				Usage:    "add reverse proxy component",
				HideHelp: true,
				Flags: []cli.Flag{
					cli.StringFlag{Name: "domain,d", Usage: "add domain to vlan"},
					cli.StringFlag{Name: "host, h", Usage: "add host to domain on vlan"},
					cli.StringFlag{Name: "policy, p", Usage: "set load balance policy (rr|lb|hash)"},
					cli.StringFlag{Name: "file, f", Usage: "specify pem certificate file"}},
				Action: func(c *cli.Context) {
					lib.ProxyAdd(c.Args().Get(0), c.String("d"), c.String("h"), c.String("p"), c.String("c"))
				},
			},
			{
				Name:     "del",
				Usage:    "del reverse proxy component",
				HideHelp: true,
				Flags: []cli.Flag{
					cli.BoolFlag{Name: "domain, d", Usage: "delete domain from vlan"},
					cli.StringFlag{Name: "host, h", Usage: "delete host from domain on vlan"}},
				Action: func(c *cli.Context) {
					lib.ProxyDel(c.Args().Get(0), c.String("h"), c.Bool("d"))
				},
			},
			{
				Name:     "check",
				Usage:    "check existing domain or host",
				HideHelp: true,
				Flags: []cli.Flag{
					cli.BoolFlag{Name: "domain,d", Usage: "check domains on vlan"},
					cli.StringFlag{Name: "host, h", Usage: "check hosts on vlan"}},
				Action: func(c *cli.Context) {
					lib.ProxyCheck(c.Args().Get(0), c.String("h"), c.Bool("d"))
				},
			},
		}}, {

		Name: "quota", Usage: "set quotas for Subutai container",
		Flags: []cli.Flag{
			cli.StringFlag{Name: "s", Usage: "set quota for the specified resource type"},
			cli.StringFlag{Name: "m", Usage: "get the maximum quota can be set to the specified container and resource_type in their default units"}},
		Action: func(c *cli.Context) {
			lib.LxcQuota(c.Args().Get(0), c.Args().Get(1), c.String("s"), c.String("m"))
		}}, {

		Name: "rename", Usage: "rename Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcRename(c.Args().Get(0), c.Args().Get(1))
		}}, {

		Name: "register", Usage: "register Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcRegister(c.Args().Get(0))
		}}, {

		Name: "stats", Usage: "statistics from host",
		Action: func(c *cli.Context) {
			lib.Stats(c.Args().Get(0), c.Args().Get(1), c.Args().Get(2))
		}}, {

		Name: "start", Usage: "start Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcStart(c.Args().Get(0))
		}}, {

		Name: "stop", Usage: "stop Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcStop(c.Args().Get(0))
		}}, {

		Name: "tunnel", Usage: "create SSH tunnel to container",
		Action: func(c *cli.Context) {
			lib.SshTunnel(c.Args().Get(0), c.Args().Get(1))
		}}, {

		Name: "unregister", Usage: "unregister Subutai container",
		Action: func(c *cli.Context) {
			lib.LxcUnregister(c.Args().Get(0))
		}},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package reflect

import (
	"github.com/anaminus/rbxmk"
)

func All() []func() rbxmk.Type {
	return []func() rbxmk.Type{
		Array,
		Axes,
		BinaryString,
		Bool,
		BrickColor,
		CFrame,
		Color3,
		Color3uint8,
		ColorSequence,
		ColorSequenceKeypoint,
		Content,
		Dictionary,
		Double,
		Faces,
		Float,
		Instance,
		Instances,
		Int,
		Int64,
		Nil,
		Number,
		NumberRange,
		NumberSequence,
		NumberSequenceKeypoint,
		PhysicalProperties,
		ProtectedString,
		Ray,
		Rect,
		Region3,
		Region3int16,
		SharedString,
		String,
		Symbol,
		Table,
		Tuple,
		UDim,
		UDim2,
		Variant,
		Vector2,
		Vector2int16,
		Vector3,
		Vector3int16,
	}
}
<commit_msg>Fix Token not being registered.<commit_after>package reflect

import (
	"github.com/anaminus/rbxmk"
)

func All() []func() rbxmk.Type {
	return []func() rbxmk.Type{
		Array,
		Axes,
		BinaryString,
		Bool,
		BrickColor,
		CFrame,
		Color3,
		Color3uint8,
		ColorSequence,
		ColorSequenceKeypoint,
		Content,
		Dictionary,
		Double,
		Faces,
		Float,
		Instance,
		Instances,
		Int,
		Int64,
		Nil,
		Number,
		NumberRange,
		NumberSequence,
		NumberSequenceKeypoint,
		PhysicalProperties,
		ProtectedString,
		Ray,
		Rect,
		Region3,
		Region3int16,
		SharedString,
		String,
		Symbol,
		Table,
		Token,
		Tuple,
		UDim,
		UDim2,
		Variant,
		Vector2,
		Vector2int16,
		Vector3,
		Vector3int16,
	}
}
<|endoftext|>


<commit_before>package acceptance_test

import (
	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

func TestAcceptance(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "azure acceptance tests")
}

var pathToBBL string

var _ = BeforeSuite(func() {
	var err error

	pathToBBL, err = gexec.Build("github.com/cloudfoundry/bosh-bootloader/bbl")
	Expect(err).NotTo(HaveOccurred())
})

var _ = AfterSuite(func() {
	gexec.CleanupBuildArtifacts()
})
<commit_msg>Remove azure init acceptance.<commit_after><|endoftext|>


<commit_before>package validate

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/intelrdt"
	selinux "github.com/opencontainers/selinux/go-selinux"
	"golang.org/x/sys/unix"
)

type Validator interface {
	Validate(*configs.Config) error
}

func New() Validator {
	return &ConfigValidator{}
}

type ConfigValidator struct {
}

func (v *ConfigValidator) Validate(config *configs.Config) error {
	if err := v.rootfs(config); err != nil {
		return err
	}
	if err := v.network(config); err != nil {
		return err
	}
	if err := v.hostname(config); err != nil {
		return err
	}
	if err := v.security(config); err != nil {
		return err
	}
	if err := v.usernamespace(config); err != nil {
		return err
	}
	if err := v.cgroupnamespace(config); err != nil {
		return err
	}
	if err := v.sysctl(config); err != nil {
		return err
	}
	if err := v.intelrdt(config); err != nil {
		return err
	}
	if config.RootlessEUID {
		if err := v.rootlessEUID(config); err != nil {
			return err
		}
	}
	return nil
}

// rootfs validates if the rootfs is an absolute path and is not a symlink
// to the container's root filesystem.
func (v *ConfigValidator) rootfs(config *configs.Config) error {
	if _, err := os.Stat(config.Rootfs); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("rootfs (%s) does not exist", config.Rootfs)
		}
		return err
	}
	cleaned, err := filepath.Abs(config.Rootfs)
	if err != nil {
		return err
	}
	if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil {
		return err
	}
	if filepath.Clean(config.Rootfs) != cleaned {
		return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs)
	}
	return nil
}

func (v *ConfigValidator) network(config *configs.Config) error {
	if !config.Namespaces.Contains(configs.NEWNET) {
		if len(config.Networks) > 0 || len(config.Routes) > 0 {
			return errors.New("unable to apply network settings without a private NET namespace")
		}
	}
	return nil
}

func (v *ConfigValidator) hostname(config *configs.Config) error {
	if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) {
		return errors.New("unable to set hostname without a private UTS namespace")
	}
	return nil
}

func (v *ConfigValidator) security(config *configs.Config) error {
	// restrict sys without mount namespace
	if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) &&
		!config.Namespaces.Contains(configs.NEWNS) {
		return errors.New("unable to restrict sys entries without a private MNT namespace")
	}
	if config.ProcessLabel != "" && !selinux.GetEnabled() {
		return errors.New("selinux label is specified in config, but selinux is disabled or not supported")
	}

	return nil
}

func (v *ConfigValidator) usernamespace(config *configs.Config) error {
	if config.Namespaces.Contains(configs.NEWUSER) {
		if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
			return errors.New("USER namespaces aren't enabled in the kernel")
		}
	} else {
		if config.UidMappings != nil || config.GidMappings != nil {
			return errors.New("User namespace mappings specified, but USER namespace isn't enabled in the config")
		}
	}
	return nil
}

func (v *ConfigValidator) cgroupnamespace(config *configs.Config) error {
	if config.Namespaces.Contains(configs.NEWCGROUP) {
		if _, err := os.Stat("/proc/self/ns/cgroup"); os.IsNotExist(err) {
			return errors.New("cgroup namespaces aren't enabled in the kernel")
		}
	}
	return nil
}

// sysctl validates that the specified sysctl keys are valid or not.
// /proc/sys isn't completely namespaced and depending on which namespaces
// are specified, a subset of sysctls are permitted.
func (v *ConfigValidator) sysctl(config *configs.Config) error {
	validSysctlMap := map[string]bool{
		"kernel.msgmax":          true,
		"kernel.msgmnb":          true,
		"kernel.msgmni":          true,
		"kernel.sem":             true,
		"kernel.shmall":          true,
		"kernel.shmmax":          true,
		"kernel.shmmni":          true,
		"kernel.shm_rmid_forced": true,
	}

	var (
		netOnce    sync.Once
		hostnet    bool
		hostnetErr error
	)

	for s := range config.Sysctl {
		if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") {
			if config.Namespaces.Contains(configs.NEWIPC) {
				continue
			} else {
				return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s)
			}
		}
		if strings.HasPrefix(s, "net.") {
			// Is container using host netns?
			// Here "host" means "current", not "initial".
			netOnce.Do(func() {
				if !config.Namespaces.Contains(configs.NEWNET) {
					hostnet = true
					return
				}
				path := config.Namespaces.PathOf(configs.NEWNET)
				if path == "" {
					// own netns, so hostnet = false
					return
				}
				hostnet, hostnetErr = isHostNetNS(path)
			})
			if hostnetErr != nil {
				return hostnetErr
			}
			if hostnet {
				return fmt.Errorf("sysctl %q not allowed in host network namespace", s)
			}
			continue
		}
		if config.Namespaces.Contains(configs.NEWUTS) {
			switch s {
			case "kernel.domainname":
				// This is namespaced and there's no explicit OCI field for it.
				continue
			case "kernel.hostname":
				// This is namespaced but there's a conflicting (dedicated) OCI field for it.
				return fmt.Errorf("sysctl %q is not allowed as it conflicts with the OCI %q field", s, "hostname")
			}
		}
		return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
	}

	return nil
}

func (v *ConfigValidator) intelrdt(config *configs.Config) error {
	if config.IntelRdt != nil {
		if !intelrdt.IsCATEnabled() && !intelrdt.IsMBAEnabled() {
			return errors.New("intelRdt is specified in config, but Intel RDT is not supported or enabled")
		}

		if !intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema != "" {
			return errors.New("intelRdt.l3CacheSchema is specified in config, but Intel RDT/CAT is not enabled")
		}
		if !intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema != "" {
			return errors.New("intelRdt.memBwSchema is specified in config, but Intel RDT/MBA is not enabled")
		}

		if intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema == "" {
			return errors.New("Intel RDT/CAT is enabled and intelRdt is specified in config, but intelRdt.l3CacheSchema is empty")
		}
		if intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema == "" {
			return errors.New("Intel RDT/MBA is enabled and intelRdt is specified in config, but intelRdt.memBwSchema is empty")
		}
	}

	return nil
}

func isHostNetNS(path string) (bool, error) {
	const currentProcessNetns = "/proc/self/ns/net"

	var st1, st2 unix.Stat_t

	if err := unix.Stat(currentProcessNetns, &st1); err != nil {
		return false, fmt.Errorf("unable to stat %q: %s", currentProcessNetns, err)
	}
	if err := unix.Stat(path, &st2); err != nil {
		return false, fmt.Errorf("unable to stat %q: %s", path, err)
	}

	return (st1.Dev == st2.Dev) && (st1.Ino == st2.Ino), nil
}
<commit_msg>libct/configs/validator: add some cgroup support<commit_after>package validate

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/intelrdt"
	selinux "github.com/opencontainers/selinux/go-selinux"
	"golang.org/x/sys/unix"
)

type Validator interface {
	Validate(*configs.Config) error
}

func New() Validator {
	return &ConfigValidator{}
}

type ConfigValidator struct {
}

func (v *ConfigValidator) Validate(config *configs.Config) error {
	if err := v.rootfs(config); err != nil {
		return err
	}
	if err := v.network(config); err != nil {
		return err
	}
	if err := v.hostname(config); err != nil {
		return err
	}
	if err := v.security(config); err != nil {
		return err
	}
	if err := v.usernamespace(config); err != nil {
		return err
	}
	if err := v.cgroupnamespace(config); err != nil {
		return err
	}
	if err := v.sysctl(config); err != nil {
		return err
	}
	if err := v.intelrdt(config); err != nil {
		return err
	}
	if config.RootlessEUID {
		if err := v.rootlessEUID(config); err != nil {
			return err
		}
	}
	if err := v.cgroups(config); err != nil {
		return err
	}

	return nil
}

// rootfs validates if the rootfs is an absolute path and is not a symlink
// to the container's root filesystem.
func (v *ConfigValidator) rootfs(config *configs.Config) error {
	if _, err := os.Stat(config.Rootfs); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("rootfs (%s) does not exist", config.Rootfs)
		}
		return err
	}
	cleaned, err := filepath.Abs(config.Rootfs)
	if err != nil {
		return err
	}
	if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil {
		return err
	}
	if filepath.Clean(config.Rootfs) != cleaned {
		return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs)
	}
	return nil
}

func (v *ConfigValidator) network(config *configs.Config) error {
	if !config.Namespaces.Contains(configs.NEWNET) {
		if len(config.Networks) > 0 || len(config.Routes) > 0 {
			return errors.New("unable to apply network settings without a private NET namespace")
		}
	}
	return nil
}

func (v *ConfigValidator) hostname(config *configs.Config) error {
	if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) {
		return errors.New("unable to set hostname without a private UTS namespace")
	}
	return nil
}

func (v *ConfigValidator) security(config *configs.Config) error {
	// restrict sys without mount namespace
	if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) &&
		!config.Namespaces.Contains(configs.NEWNS) {
		return errors.New("unable to restrict sys entries without a private MNT namespace")
	}
	if config.ProcessLabel != "" && !selinux.GetEnabled() {
		return errors.New("selinux label is specified in config, but selinux is disabled or not supported")
	}

	return nil
}

func (v *ConfigValidator) usernamespace(config *configs.Config) error {
	if config.Namespaces.Contains(configs.NEWUSER) {
		if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
			return errors.New("USER namespaces aren't enabled in the kernel")
		}
	} else {
		if config.UidMappings != nil || config.GidMappings != nil {
			return errors.New("User namespace mappings specified, but USER namespace isn't enabled in the config")
		}
	}
	return nil
}

func (v *ConfigValidator) cgroupnamespace(config *configs.Config) error {
	if config.Namespaces.Contains(configs.NEWCGROUP) {
		if _, err := os.Stat("/proc/self/ns/cgroup"); os.IsNotExist(err) {
			return errors.New("cgroup namespaces aren't enabled in the kernel")
		}
	}
	return nil
}

// sysctl validates that the specified sysctl keys are valid or not.
// /proc/sys isn't completely namespaced and depending on which namespaces
// are specified, a subset of sysctls are permitted.
func (v *ConfigValidator) sysctl(config *configs.Config) error {
	validSysctlMap := map[string]bool{
		"kernel.msgmax":          true,
		"kernel.msgmnb":          true,
		"kernel.msgmni":          true,
		"kernel.sem":             true,
		"kernel.shmall":          true,
		"kernel.shmmax":          true,
		"kernel.shmmni":          true,
		"kernel.shm_rmid_forced": true,
	}

	var (
		netOnce    sync.Once
		hostnet    bool
		hostnetErr error
	)

	for s := range config.Sysctl {
		if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") {
			if config.Namespaces.Contains(configs.NEWIPC) {
				continue
			} else {
				return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s)
			}
		}
		if strings.HasPrefix(s, "net.") {
			// Is container using host netns?
			// Here "host" means "current", not "initial".
			netOnce.Do(func() {
				if !config.Namespaces.Contains(configs.NEWNET) {
					hostnet = true
					return
				}
				path := config.Namespaces.PathOf(configs.NEWNET)
				if path == "" {
					// own netns, so hostnet = false
					return
				}
				hostnet, hostnetErr = isHostNetNS(path)
			})
			if hostnetErr != nil {
				return hostnetErr
			}
			if hostnet {
				return fmt.Errorf("sysctl %q not allowed in host network namespace", s)
			}
			continue
		}
		if config.Namespaces.Contains(configs.NEWUTS) {
			switch s {
			case "kernel.domainname":
				// This is namespaced and there's no explicit OCI field for it.
				continue
			case "kernel.hostname":
				// This is namespaced but there's a conflicting (dedicated) OCI field for it.
				return fmt.Errorf("sysctl %q is not allowed as it conflicts with the OCI %q field", s, "hostname")
			}
		}
		return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
	}

	return nil
}

func (v *ConfigValidator) intelrdt(config *configs.Config) error {
	if config.IntelRdt != nil {
		if !intelrdt.IsCATEnabled() && !intelrdt.IsMBAEnabled() {
			return errors.New("intelRdt is specified in config, but Intel RDT is not supported or enabled")
		}

		if !intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema != "" {
			return errors.New("intelRdt.l3CacheSchema is specified in config, but Intel RDT/CAT is not enabled")
		}
		if !intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema != "" {
			return errors.New("intelRdt.memBwSchema is specified in config, but Intel RDT/MBA is not enabled")
		}

		if intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema == "" {
			return errors.New("Intel RDT/CAT is enabled and intelRdt is specified in config, but intelRdt.l3CacheSchema is empty")
		}
		if intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema == "" {
			return errors.New("Intel RDT/MBA is enabled and intelRdt is specified in config, but intelRdt.memBwSchema is empty")
		}
	}

	return nil
}

func (v *ConfigValidator) cgroups(config *configs.Config) error {
	c := config.Cgroups
	if c == nil {
		return nil
	}

	if (c.Name != "" || c.Parent != "") && c.Path != "" {
		return fmt.Errorf("cgroup: either Path or Name and Parent should be used, got %+v", c)
	}

	r := c.Resources
	if r == nil {
		return nil
	}

	if !cgroups.IsCgroup2UnifiedMode() && r.Unified != nil {
		return cgroups.ErrV1NoUnified
	}

	if cgroups.IsCgroup2UnifiedMode() {
		_, err := cgroups.ConvertMemorySwapToCgroupV2Value(r.MemorySwap, r.Memory)
		if err != nil {
			return err
		}
	}

	return nil
}

func isHostNetNS(path string) (bool, error) {
	const currentProcessNetns = "/proc/self/ns/net"

	var st1, st2 unix.Stat_t

	if err := unix.Stat(currentProcessNetns, &st1); err != nil {
		return false, fmt.Errorf("unable to stat %q: %s", currentProcessNetns, err)
	}
	if err := unix.Stat(path, &st2); err != nil {
		return false, fmt.Errorf("unable to stat %q: %s", path, err)
	}

	return (st1.Dev == st2.Dev) && (st1.Ino == st2.Ino), nil
}
<|endoftext|>


<commit_before>package amesh

import (
	"fmt"
	"time"
)

type MeshService struct {
	client *Client
}

type MeshGetOptions struct {
	DateTime time.Time
}

func (s *MeshService) Get(opt *MeshGetOptions) (*Response, error) {
	var t time.Time
	if opt.DateTime.IsZero() {
		t = time.Now()
	} else {
		t = opt.DateTime
	}
	timefmt := "200601021504"
	d := t.Format(timefmt)
	path := fmt.Sprintf(
		"mesh/100/%s0.gif", d[:len(d)-1])
	req, err := s.client.NewRequest(path)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Do(req, nil)
	return resp, err
}
<commit_msg>add public function to format time for mesh image path<commit_after>package amesh

import (
	"fmt"
	"time"
)

type MeshService struct {
	client *Client
}

type MeshGetOptions struct {
	DateTime time.Time
}

func FormatMeshDatetime(t time.Time) string {
	const timefmt = "200601021504"
	s := t.Format(timefmt)
	return fmt.Sprintf("%s0", s[:len(s)-1])
}

func (s *MeshService) Get(opt *MeshGetOptions) (*Response, error) {
	var t time.Time
	if opt.DateTime.IsZero() {
		t = time.Now()
	} else {
		t = opt.DateTime
	}
	d := FormatMeshDatetime(t)
	path := fmt.Sprintf("mesh/100/%s.gif", d)
	req, err := s.client.NewRequest(path)
	if err != nil {
		return nil, err
	}

	resp, err := s.client.Do(req, nil)
	return resp, err
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/martinp/atbapi/atb"
	"strconv"
)

type BusStops struct {
	Stops []BusStop `json:"stops"`
}

type BusStop struct {
	StopId      int     `json:"stopId"`
	NodeId      int     `json:"nodeId"`
	Description string  `json:"description"`
	Longitude   float64 `json:"longitude"`
	Latitude    float64 `json:"latitude"`
	MobileCode  string  `json:"mobileCode"`
	MobileName  string  `json:"mobileName"`
}

type Departures struct {
	TowardsCentrum bool        `json:"isGoingTowardsCentrum"`
	Departures     []Departure `json:"departures"`
}

type Departure struct {
	LineId                  string `json:"line"`
	RegisteredDepartureTime string `json:"registeredDepartureTime"`
	ScheduledDepartureTime  string `json:"scheduledDepartureTime"`
	Destination             string `json:"destination"`
}

func convertBusStop(s atb.BusStop) (BusStop, error) {
	nodeId, err := strconv.Atoi(s.NodeId)
	if err != nil {
		return BusStop{}, err
	}
	longitude, err := strconv.ParseFloat(s.Longitude, 64)
	if err != nil {
		return BusStop{}, err
	}
	latitude := float64(s.Latitude)
	return BusStop{
		StopId:      s.StopId,
		NodeId:      nodeId,
		Description: s.Description,
		Longitude:   longitude,
		Latitude:    latitude,
		MobileCode:  s.MobileCode,
		MobileName:  s.MobileName,
	}, nil
}

func convertBusStops(s atb.BusStops) (BusStops, error) {
	stops := make([]BusStop, 0, len(s.Stops))
	for _, stop := range s.Stops {
		converted, err := convertBusStop(stop)
		if err != nil {
			return BusStops{}, err
		}
		stops = append(stops, converted)
	}
	return BusStops{Stops: stops}, nil
}

func convertForecast(f atb.Forecast) (Departure, error) {
	return Departure{
		LineId:                  f.LineId,
		Destination:             f.Destination,
		RegisteredDepartureTime: f.RegisteredDepartureTime,
		ScheduledDepartureTime:  f.ScheduledDepartureTime,
	}, nil
}

func convertForecasts(f atb.Forecasts) (Departures, error) {
	towardsCentrum := false
	if len(f.Nodes) > 0 {
		nodeId, err := strconv.Atoi(f.Nodes[0].NodeId)
		if err != nil {
			return Departures{}, err
		}
		towardsCentrum = (nodeId/1000)%2 == 1
	}
	departures := make([]Departure, 0, len(f.Forecasts))
	for _, forecast := range f.Forecasts {
		departure, err := convertForecast(forecast)
		if err != nil {
			return Departures{}, err
		}
		departures = append(departures, departure)
	}
	return Departures{
		TowardsCentrum: towardsCentrum,
		Departures:     departures,
	}, nil
}
<commit_msg>Add isRealtimeData field<commit_after>package api

import (
	"github.com/martinp/atbapi/atb"
	"strconv"
)

type BusStops struct {
	Stops []BusStop `json:"stops"`
}

type BusStop struct {
	StopId      int     `json:"stopId"`
	NodeId      int     `json:"nodeId"`
	Description string  `json:"description"`
	Longitude   float64 `json:"longitude"`
	Latitude    float64 `json:"latitude"`
	MobileCode  string  `json:"mobileCode"`
	MobileName  string  `json:"mobileName"`
}

type Departures struct {
	TowardsCentrum bool        `json:"isGoingTowardsCentrum"`
	Departures     []Departure `json:"departures"`
}

type Departure struct {
	LineId                  string `json:"line"`
	RegisteredDepartureTime string `json:"registeredDepartureTime"`
	ScheduledDepartureTime  string `json:"scheduledDepartureTime"`
	Destination             string `json:"destination"`
	IsRealtimeData          bool   `json:"isRealtimeData"`
}

func convertBusStop(s atb.BusStop) (BusStop, error) {
	nodeId, err := strconv.Atoi(s.NodeId)
	if err != nil {
		return BusStop{}, err
	}
	longitude, err := strconv.ParseFloat(s.Longitude, 64)
	if err != nil {
		return BusStop{}, err
	}
	latitude := float64(s.Latitude)
	return BusStop{
		StopId:      s.StopId,
		NodeId:      nodeId,
		Description: s.Description,
		Longitude:   longitude,
		Latitude:    latitude,
		MobileCode:  s.MobileCode,
		MobileName:  s.MobileName,
	}, nil
}

func convertBusStops(s atb.BusStops) (BusStops, error) {
	stops := make([]BusStop, 0, len(s.Stops))
	for _, stop := range s.Stops {
		converted, err := convertBusStop(stop)
		if err != nil {
			return BusStops{}, err
		}
		stops = append(stops, converted)
	}
	return BusStops{Stops: stops}, nil
}

func isRealtime(s string) bool {
	return strings.EqualFold(s, "prev")
}

func convertForecast(f atb.Forecast) (Departure, error) {
	return Departure{
		LineId:                  f.LineId,
		Destination:             f.Destination,
		RegisteredDepartureTime: f.RegisteredDepartureTime,
		ScheduledDepartureTime:  f.ScheduledDepartureTime,
		IsRealtimeData:          isRealtime(f.StationForecast),
	}, nil
}

func convertForecasts(f atb.Forecasts) (Departures, error) {
	towardsCentrum := false
	if len(f.Nodes) > 0 {
		nodeId, err := strconv.Atoi(f.Nodes[0].NodeId)
		if err != nil {
			return Departures{}, err
		}
		towardsCentrum = (nodeId/1000)%2 == 1
	}
	departures := make([]Departure, 0, len(f.Forecasts))
	for _, forecast := range f.Forecasts {
		departure, err := convertForecast(forecast)
		if err != nil {
			return Departures{}, err
		}
		departures = append(departures, departure)
	}
	return Departures{
		TowardsCentrum: towardsCentrum,
		Departures:     departures,
	}, nil
}
<|endoftext|>


<commit_before>package zest

import "fmt"

// APIError defines the format of Zest API errors.
type APIError struct {
	Status      int    `json:"status"`
	Description string `json:"description"`
	Raw         string `json:"raw"`
	ErrorCode   string `json:"errorCode"`
}

func (e APIError) Error() string {
	return fmt.Sprintf("%s : %s", e.ErrorCode, e.Description)
}
<commit_msg>Comments added on the APIError struct.<commit_after>package zest

import "fmt"

// APIError defines the format of Zest API errors.
type APIError struct {
	// The status code.
	Status int `json:"status"`
	// The description of the API error.
	Description string `json:"description"`
	// A raw description of what triggered the API error.
	Raw string `json:"raw"`
	// The token uniquely identifying the API error.
	ErrorCode string `json:"errorCode"`
}

func (e APIError) Error() string {
	return fmt.Sprintf("%s : %s", e.ErrorCode, e.Description)
}
<|endoftext|>