<commit_before>package isvcs

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced"

	"bufio"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path"
	"runtime"
	"strings"
)

type ISvc struct {
	Name       string
	Repository string
	Tag        string
	Ports      []int
}

func (s *ISvc) exists() (bool, error) {

	cmd := exec.Command("sh", "-c", fmt.Sprintf("docker images -a %s | tail -n +2 | awk '{ print $2 }'", s.Repository))
	output, err := cmd.Output()
	if err != nil {
		return false, err
	}
	tagsFound := strings.Split(strings.TrimSpace(string(output)), "\n")
	for _, tag := range tagsFound {
		if tag == s.Tag {
			return true, nil
		}
	}
	return false, nil
}

func (s *ISvc) create() error {
	if exists, err := s.exists(); err != nil || exists {
		return err
	}
	glog.V(1).Infof("Looking for existing tar export of %s:%s", s.Repository, s.Tag)
	imageTar := fmt.Sprintf("%s/%s/%s.tar", imagesDir(), s.Repository, s.Tag)

	if _, err := os.Stat(imageTar); os.IsNotExist(err) {
		glog.Errorf("Could not locate: %s", imageTar)
		return err
	} else {
		if err != nil {
			return err
		}
	}

	file, err := os.Open(imageTar)
	if err != nil {
		glog.Errorf("Could not open %s: %s", imageTar, err)
		return err
	}

	cmd := exec.Command("docker", "load")
	cmd.Stdin = bufio.NewReader(file)
	glog.V(1).Infof("Importing docker image %s", imageTar)
	if err := cmd.Run(); err != nil {
		glog.Errorf("Could not load %s: %s", imageTar, err)
		return err
	}
	return nil
}

func (s *ISvc) Running() (bool, error) {
	containerId, _ := s.getContainerId()
	if len(containerId) == 0 {
		return false, nil
	}
	cmd := exec.Command("docker", "ps")
	output, err := cmd.Output()
	if err != nil {
		return false, err
	}
	return strings.Contains(string(output), containerId), nil
}

func (s *ISvc) Run() error {

	err := s.create()
	if err != nil {
		return err
	}

	running, err := s.Running()
	if err != nil || running {
		return err
	}
	glog.Infof("%s is not running", s.Repository)

	containerId, err := s.getContainerId()
	if err != nil && !IsSvcNotFoundErr(err) {
		return err
	}

	var cmd *exec.Cmd
	if containerId != "" {
		cmd = exec.Command("docker", "start", containerId)
	} else {
		args := "docker run -d "
		// add the ports to the arg list
		for _, port := range s.Ports {
			args += fmt.Sprintf(" -p %d:%d", port, port)
		}
		// bind mount the resources directory, always make it /usr/local/serviced to simplify the dockerfile commands
		containerServiceDResources := "/usr/local/serviced/resources"
		args += " -v"
		args += fmt.Sprintf(" %s:%s", resourcesDir(), containerServiceDResources)

		// specify the image
		args += fmt.Sprintf(" %s:%s", s.Repository, s.Tag)
		cmd = exec.Command("sh", "-c", args)
	}
	glog.Info("Running docker cmd: ", cmd)
	return cmd.Run()
}

func (s *ISvc) Stop() error {
	containerId, err := s.getContainerId()
	if err != nil {
		return err
	}
	cmd := exec.Command("docker", "stop", containerId)
	return cmd.Run()
}

func (s *ISvc) Kill() error {
	containerId, err := s.getContainerId()
	if err != nil {
		return err
	}
	cmd := exec.Command("docker", "kill", containerId)
	return cmd.Run()
}

var SvcNotFoundErr error
var IsSvcNotFoundErr func(error) bool

const msgSvcNotFoundErr = "svc not found"

func init() {
	SvcNotFoundErr = fmt.Errorf(msgSvcNotFoundErr)
	IsSvcNotFoundErr = func(err error) bool {
		if err == nil {
			return false
		}
		return err.Error() == msgSvcNotFoundErr
	}
}

func (s *ISvc) getContainerId() (string, error) {
	cmd := exec.Command("sh", "-c", `docker ps -a | tail -n +2 | awk '{ print $1 " " $2 }'`)
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	repoAndName := s.Repository + ":" + s.Tag
	for _, line := range strings.Split(string(output), "\n") {
		fields := strings.Fields(line)
		if len(fields) == 2 && fields[1] == repoAndName {
			return fields[0], nil
		}
	}
	return "", SvcNotFoundErr
}

func localDir(p string) string {
	homeDir := serviced.ServiceDHome()
	if len(homeDir) == 0 {
		_, filename, _, _ := runtime.Caller(1)
		homeDir = path.Dir(filename)
	}
	return path.Join(homeDir, p)
}

func resourcesDir() string {
	return localDir("resources")
}

func imagesDir() string {
	homeDir := serviced.ServiceDHome()
	if len(homeDir) == 0 {
		current, err := user.Current()
		if err != nil {
			panic("Could not get current user info")
		}
		return fmt.Sprintf("/tmp/serviced-%s-tmp/images", current.Username)
	}
	return path.Join(homeDir, "images")
}
<commit_msg>use sane logic when testing if tar exists<commit_after>package isvcs

import (
	"github.com/zenoss/glog"
	"github.com/zenoss/serviced"

	"bufio"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path"
	"runtime"
	"strings"
)

type ISvc struct {
	Name       string
	Repository string
	Tag        string
	Ports      []int
}

func (s *ISvc) exists() (bool, error) {

	cmd := exec.Command("sh", "-c", fmt.Sprintf("docker images -a %s | tail -n +2 | awk '{ print $2 }'", s.Repository))
	output, err := cmd.Output()
	if err != nil {
		return false, err
	}
	tagsFound := strings.Split(strings.TrimSpace(string(output)), "\n")
	for _, tag := range tagsFound {
		if tag == s.Tag {
			return true, nil
		}
	}
	return false, nil
}

func (s *ISvc) create() error {
	if exists, err := s.exists(); err != nil || exists {
		return err
	}
	glog.V(1).Infof("Looking for existing tar export of %s:%s", s.Repository, s.Tag)
	imageTar := fmt.Sprintf("%s/%s/%s.tar", imagesDir(), s.Repository, s.Tag)

	if _, err := os.Stat(imageTar); err != nil {
		if os.IsNotExist(err) {
			glog.Errorf("Could not locate: %s", imageTar)
		}
		return err
	}

	file, err := os.Open(imageTar)
	if err != nil {
		glog.Errorf("Could not open %s: %s", imageTar, err)
		return err
	}

	cmd := exec.Command("docker", "load")
	cmd.Stdin = bufio.NewReader(file)
	glog.V(1).Infof("Importing docker image %s", imageTar)
	if err := cmd.Run(); err != nil {
		glog.Errorf("Could not load %s: %s", imageTar, err)
		return err
	}
	return nil
}

func (s *ISvc) Running() (bool, error) {
	containerId, _ := s.getContainerId()
	if len(containerId) == 0 {
		return false, nil
	}
	cmd := exec.Command("docker", "ps")
	output, err := cmd.Output()
	if err != nil {
		return false, err
	}
	return strings.Contains(string(output), containerId), nil
}

func (s *ISvc) Run() error {

	err := s.create()
	if err != nil {
		return err
	}

	running, err := s.Running()
	if err != nil || running {
		return err
	}
	glog.Infof("%s is not running", s.Repository)

	containerId, err := s.getContainerId()
	if err != nil && !IsSvcNotFoundErr(err) {
		return err
	}

	var cmd *exec.Cmd
	if containerId != "" {
		cmd = exec.Command("docker", "start", containerId)
	} else {
		args := "docker run -d "
		// add the ports to the arg list
		for _, port := range s.Ports {
			args += fmt.Sprintf(" -p %d:%d", port, port)
		}
		// bind mount the resources directory, always make it /usr/local/serviced to simplify the dockerfile commands
		containerServiceDResources := "/usr/local/serviced/resources"
		args += " -v"
		args += fmt.Sprintf(" %s:%s", resourcesDir(), containerServiceDResources)

		// specify the image
		args += fmt.Sprintf(" %s:%s", s.Repository, s.Tag)
		cmd = exec.Command("sh", "-c", args)
	}
	glog.Info("Running docker cmd: ", cmd)
	return cmd.Run()
}

func (s *ISvc) Stop() error {
	containerId, err := s.getContainerId()
	if err != nil {
		return err
	}
	cmd := exec.Command("docker", "stop", containerId)
	return cmd.Run()
}

func (s *ISvc) Kill() error {
	containerId, err := s.getContainerId()
	if err != nil {
		return err
	}
	cmd := exec.Command("docker", "kill", containerId)
	return cmd.Run()
}

var SvcNotFoundErr error
var IsSvcNotFoundErr func(error) bool

const msgSvcNotFoundErr = "svc not found"

func init() {
	SvcNotFoundErr = fmt.Errorf(msgSvcNotFoundErr)
	IsSvcNotFoundErr = func(err error) bool {
		if err == nil {
			return false
		}
		return err.Error() == msgSvcNotFoundErr
	}
}

func (s *ISvc) getContainerId() (string, error) {
	cmd := exec.Command("sh", "-c", `docker ps -a | tail -n +2 | awk '{ print $1 " " $2 }'`)
	output, err := cmd.Output()
	if err != nil {
		return "", err
	}
	repoAndName := s.Repository + ":" + s.Tag
	for _, line := range strings.Split(string(output), "\n") {
		fields := strings.Fields(line)
		if len(fields) == 2 && fields[1] == repoAndName {
			return fields[0], nil
		}
	}
	return "", SvcNotFoundErr
}

func localDir(p string) string {
	homeDir := serviced.ServiceDHome()
	if len(homeDir) == 0 {
		_, filename, _, _ := runtime.Caller(1)
		homeDir = path.Dir(filename)
	}
	return path.Join(homeDir, p)
}

func resourcesDir() string {
	return localDir("resources")
}

func imagesDir() string {
	homeDir := serviced.ServiceDHome()
	if len(homeDir) == 0 {
		current, err := user.Current()
		if err != nil {
			panic("Could not get current user info")
		}
		return fmt.Sprintf("/tmp/serviced-%s-tmp/images", current.Username)
	}
	return path.Join(homeDir, "images")
}
<|endoftext|>


<commit_before>//
// Copyright 2019, Matej Velikonja
//
// 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 gitlab

import (
	"fmt"
	"net/url"
	"time"
)

// ProjectClustersService handles communication with the
// project clusters related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html
type ProjectClustersService struct {
	client *Client
}

// ProjectCluster represents a GitLab Project Cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html
type ProjectCluster struct {
	ID                 int                `json:"id,omitempty"`
	Name               string             `json:"name,omitempty"`
	Enabled            *bool              `json:"enabled,omitempty"`
	CreatedAt          *time.Time         `json:"created_at,omitempty"`
	ProviderType       string             `json:"provider_type,omitempty"`
	PlatformType       string             `json:"platform_type,omitempty"`
	EnvironmentScope   string             `json:"environment_scope,omitempty"`
	ClusterType        string             `json:"cluster_type,omitempty"`
	User               *User              `json:"user,omitempty"`
	PlatformKubernetes PlatformKubernetes `json:"platform_kubernetes,omitempty"`
	Project            *Project           `json:"project,omitempty"`
}

func (v ProjectCluster) String() string {
	return Stringify(v)
}

type PlatformKubernetes struct {
	ApiUrl            string `json:"api_url,omitempty"`
	Token             string `json:"token,omitempty"`
	CaCert            string `json:"ca_cert,omitempty"`
	Namespace         string `json:"namespace,omitempty"`
	AuthorizationType string `json:"authorization_type,omitempty"`
}

// ListClusters gets a list of all clusters in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#list-project-clusters
func (s *ProjectClustersService) ListClusters(pid interface{}, options ...OptionFunc) ([]*ProjectCluster, *Response, error) {
	projectID, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters", url.QueryEscape(projectID))

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var vs []*ProjectCluster
	resp, err := s.client.Do(req, &vs)
	if err != nil {
		return nil, resp, err
	}

	return vs, resp, err
}

// GetCluster gets a cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#get-a-single-project-cluster
func (s *ProjectClustersService) GetCluster(pid interface{}, clusterID int, options ...OptionFunc) (*ProjectCluster, *Response, error) {
	projectID, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/%d", url.QueryEscape(projectID), clusterID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var vs *ProjectCluster
	resp, err := s.client.Do(req, &vs)
	if err != nil {
		return nil, resp, err
	}

	return vs, resp, err
}

// AddClusterOptions represents the available AddCluster() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project
type AddClusterOptions struct {
	Name               string             `json:"name,omitempty"`
	Enabled            *bool              `json:"enabled,omitempty"`
	EnvironmentScope   string             `json:"environment_scope,omitempty"`
	PlatformKubernetes PlatformKubernetes `json:"platform_kubernetes_attributes,omitempty"`
}

// AddCluster adds an existing cluster to the project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project
func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/user", url.QueryEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}
	//b, _ := ioutil.ReadAll(req.Body)
	//fmt.Println(string(b))
	pc := new(ProjectCluster)
	resp, err := s.client.Do(req, pc)
	if err != nil {
		return nil, resp, err
	}

	return pc, resp, err
}

// EditClusterOptions represents the available EditCluster() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster
type EditClusterOptions struct {
	Name               string             `json:"name"`
	EnvironmentScope   string             `json:"environment_scope,omitempty"`
	PlatformKubernetes PlatformKubernetes `json:"platform_kubernetes_attributes,omitempty"`
}

// EditCluster updates an existing project cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster
func (s *ProjectClustersService) EditCluster(pid interface{}, cid int, opt *EditClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/%d", url.QueryEscape(project), cid)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pc := new(ProjectCluster)
	resp, err := s.client.Do(req, pc)
	if err != nil {
		return nil, resp, err
	}

	return pc, resp, err
}

// DeleteCluster deletes an existing project cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#delete-project-cluster
func (s *ProjectClustersService) DeleteCluster(pid interface{}, cid int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/%d", url.QueryEscape(project), cid)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>fix lint issues<commit_after>//
// Copyright 2019, Matej Velikonja
//
// 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 gitlab

import (
	"fmt"
	"net/url"
	"time"
)

// ProjectClustersService handles communication with the
// project clusters related methods of the GitLab API.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html
type ProjectClustersService struct {
	client *Client
}

// ProjectCluster represents a GitLab Project Cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html
type ProjectCluster struct {
	ID                 int                `json:"id,omitempty"`
	Name               string             `json:"name,omitempty"`
	Enabled            *bool              `json:"enabled,omitempty"`
	CreatedAt          *time.Time         `json:"created_at,omitempty"`
	ProviderType       string             `json:"provider_type,omitempty"`
	PlatformType       string             `json:"platform_type,omitempty"`
	EnvironmentScope   string             `json:"environment_scope,omitempty"`
	ClusterType        string             `json:"cluster_type,omitempty"`
	User               *User              `json:"user,omitempty"`
	PlatformKubernetes PlatformKubernetes `json:"platform_kubernetes,omitempty"`
	Project            *Project           `json:"project,omitempty"`
}

func (v ProjectCluster) String() string {
	return Stringify(v)
}

// PlatformKubernetes represents a GitLab Project Cluster PlatformKubernetes.
type PlatformKubernetes struct {
	APIURL            string `json:"api_url,omitempty"`
	Token             string `json:"token,omitempty"`
	CaCert            string `json:"ca_cert,omitempty"`
	Namespace         string `json:"namespace,omitempty"`
	AuthorizationType string `json:"authorization_type,omitempty"`
}

// ListClusters gets a list of all clusters in a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#list-project-clusters
func (s *ProjectClustersService) ListClusters(pid interface{}, options ...OptionFunc) ([]*ProjectCluster, *Response, error) {
	projectID, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters", url.QueryEscape(projectID))

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var vs []*ProjectCluster
	resp, err := s.client.Do(req, &vs)
	if err != nil {
		return nil, resp, err
	}

	return vs, resp, err
}

// GetCluster gets a cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#get-a-single-project-cluster
func (s *ProjectClustersService) GetCluster(pid interface{}, clusterID int, options ...OptionFunc) (*ProjectCluster, *Response, error) {
	projectID, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/%d", url.QueryEscape(projectID), clusterID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var vs *ProjectCluster
	resp, err := s.client.Do(req, &vs)
	if err != nil {
		return nil, resp, err
	}

	return vs, resp, err
}

// AddClusterOptions represents the available AddCluster() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project
type AddClusterOptions struct {
	Name               string             `json:"name,omitempty"`
	Enabled            *bool              `json:"enabled,omitempty"`
	EnvironmentScope   string             `json:"environment_scope,omitempty"`
	PlatformKubernetes PlatformKubernetes `json:"platform_kubernetes_attributes,omitempty"`
}

// AddCluster adds an existing cluster to the project.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project
func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/user", url.QueryEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}
	//b, _ := ioutil.ReadAll(req.Body)
	//fmt.Println(string(b))
	pc := new(ProjectCluster)
	resp, err := s.client.Do(req, pc)
	if err != nil {
		return nil, resp, err
	}

	return pc, resp, err
}

// EditClusterOptions represents the available EditCluster() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster
type EditClusterOptions struct {
	Name               string             `json:"name"`
	EnvironmentScope   string             `json:"environment_scope,omitempty"`
	PlatformKubernetes PlatformKubernetes `json:"platform_kubernetes_attributes,omitempty"`
}

// EditCluster updates an existing project cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster
func (s *ProjectClustersService) EditCluster(pid interface{}, cid int, opt *EditClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/%d", url.QueryEscape(project), cid)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	pc := new(ProjectCluster)
	resp, err := s.client.Do(req, pc)
	if err != nil {
		return nil, resp, err
	}

	return pc, resp, err
}

// DeleteCluster deletes an existing project cluster.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/project_clusters.html#delete-project-cluster
func (s *ProjectClustersService) DeleteCluster(pid interface{}, cid int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/clusters/%d", url.QueryEscape(project), cid)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Ashley Jeffs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package output

import (
	"sync/atomic"
	"time"

	"github.com/Jeffail/benthos/lib/log"
	"github.com/Jeffail/benthos/lib/message/tracing"
	"github.com/Jeffail/benthos/lib/metrics"
	"github.com/Jeffail/benthos/lib/output/writer"
	"github.com/Jeffail/benthos/lib/response"
	"github.com/Jeffail/benthos/lib/types"
	"github.com/Jeffail/benthos/lib/util/throttle"
)

//------------------------------------------------------------------------------

// Writer is an output type that writes messages to a writer.Type.
type Writer struct {
	running     int32
	isConnected int32

	typeStr string
	writer  writer.Type

	log   log.Modular
	stats metrics.Type

	transactions <-chan types.Transaction

	closeChan  chan struct{}
	closedChan chan struct{}
}

// NewWriter creates a new Writer output type.
func NewWriter(
	typeStr string,
	w writer.Type,
	log log.Modular,
	stats metrics.Type,
) (Type, error) {
	return &Writer{
		running:      1,
		typeStr:      typeStr,
		writer:       w,
		log:          log,
		stats:        stats,
		transactions: nil,
		closeChan:    make(chan struct{}),
		closedChan:   make(chan struct{}),
	}, nil
}

//------------------------------------------------------------------------------

// loop is an internal loop that brokers incoming messages to output pipe.
func (w *Writer) loop() {
	// Metrics paths
	var (
		mRunning      = w.stats.GetGauge("running")
		mCount        = w.stats.GetCounter("count")
		mPartsCount   = w.stats.GetCounter("parts.count")
		mSuccess      = w.stats.GetCounter("send.success")
		mPartsSuccess = w.stats.GetCounter("parts.send.success")
		mError        = w.stats.GetCounter("send.error")
		mSent         = w.stats.GetCounter("batch.sent")
		mPartsSent    = w.stats.GetCounter("sent")
		mConn         = w.stats.GetCounter("connection.up")
		mFailedConn   = w.stats.GetCounter("connection.failed")
		mLostConn     = w.stats.GetCounter("connection.lost")
	)

	defer func() {
		err := w.writer.WaitForClose(time.Second)
		for ; err != nil; err = w.writer.WaitForClose(time.Second) {
		}
		mRunning.Decr(1)
		atomic.StoreInt32(&w.isConnected, 0)
		close(w.closedChan)
	}()
	mRunning.Incr(1)

	throt := throttle.New(throttle.OptCloseChan(w.closeChan))

	for {
		if err := w.writer.Connect(); err != nil {
			// Close immediately if our writer is closed.
			if err == types.ErrTypeClosed {
				return
			}

			w.log.Errorf("Failed to connect to %v: %v\n", w.typeStr, err)
			mFailedConn.Incr(1)
			if !throt.Retry() {
				return
			}
		} else {
			break
		}
	}
	mConn.Incr(1)
	atomic.StoreInt32(&w.isConnected, 1)

	for atomic.LoadInt32(&w.running) == 1 {
		var ts types.Transaction
		var open bool
		select {
		case ts, open = <-w.transactions:
			if !open {
				return
			}
			mCount.Incr(1)
			mPartsCount.Incr(int64(ts.Payload.Len()))
		case <-w.closeChan:
			return
		}

		spans := tracing.CreateChildSpans("output_"+w.typeStr, ts.Payload)
		err := w.writer.Write(ts.Payload)

		// If our writer says it is not connected.
		if err == types.ErrNotConnected {
			mLostConn.Incr(1)
			atomic.StoreInt32(&w.isConnected, 0)

			// Continue to try to reconnect while still active.
			for atomic.LoadInt32(&w.running) == 1 {
				if err = w.writer.Connect(); err != nil {
					// Close immediately if our writer is closed.
					if err == types.ErrTypeClosed {
						return
					}

					w.log.Errorf("Failed to reconnect to %v: %v\n", w.typeStr, err)
					mFailedConn.Incr(1)
					if !throt.Retry() {
						return
					}
				} else if err = w.writer.Write(ts.Payload); err != types.ErrNotConnected {
					atomic.StoreInt32(&w.isConnected, 1)
					mConn.Incr(1)
					break
				} else if !throt.Retry() {
					return
				}
			}
		}

		// Close immediately if our writer is closed.
		if err == types.ErrTypeClosed {
			return
		}

		if err != nil {
			w.log.Errorf("Failed to send message to %v: %v\n", w.typeStr, err)
			mError.Incr(1)
			if !throt.Retry() {
				return
			}
		} else {
			mSuccess.Incr(1)
			mPartsSuccess.Incr(int64(ts.Payload.Len()))
			mSent.Incr(1)
			mPartsSent.Incr(int64(ts.Payload.Len()))
			throt.Reset()
		}

		for _, s := range spans {
			s.Finish()
		}

		select {
		case ts.ResponseChan <- response.NewError(err):
		case <-w.closeChan:
			// The pipeline is terminating but we still want to attempt to
			// propagate an acknowledgement from in-transit messages.
			//
			// TODO: Replace this timer with a value linked to our service
			// shutdown timer.
			select {
			case ts.ResponseChan <- response.NewError(err):
			case <-time.After(time.Second):
			}
			return
		}
	}
}

// Consume assigns a messages channel for the output to read.
func (w *Writer) Consume(ts <-chan types.Transaction) error {
	if w.transactions != nil {
		return types.ErrAlreadyStarted
	}
	w.transactions = ts
	go w.loop()
	return nil
}

// Connected returns a boolean indicating whether this output is currently
// connected to its target.
func (w *Writer) Connected() bool {
	return atomic.LoadInt32(&w.isConnected) == 1
}

// CloseAsync shuts down the File output and stops processing messages.
func (w *Writer) CloseAsync() {
	if atomic.CompareAndSwapInt32(&w.running, 1, 0) {
		w.writer.CloseAsync()
		close(w.closeChan)
	}
}

// WaitForClose blocks until the File output has closed down.
func (w *Writer) WaitForClose(timeout time.Duration) error {
	select {
	case <-w.closedChan:
	case <-time.After(timeout):
		return types.ErrTimeout
	}
	return nil
}

//------------------------------------------------------------------------------
<commit_msg>Separate writer's obsolete metrics<commit_after>// Copyright (c) 2018 Ashley Jeffs
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package output

import (
	"sync/atomic"
	"time"

	"github.com/Jeffail/benthos/lib/log"
	"github.com/Jeffail/benthos/lib/message/tracing"
	"github.com/Jeffail/benthos/lib/metrics"
	"github.com/Jeffail/benthos/lib/output/writer"
	"github.com/Jeffail/benthos/lib/response"
	"github.com/Jeffail/benthos/lib/types"
	"github.com/Jeffail/benthos/lib/util/throttle"
)

//------------------------------------------------------------------------------

// Writer is an output type that writes messages to a writer.Type.
type Writer struct {
	running     int32
	isConnected int32

	typeStr string
	writer  writer.Type

	log   log.Modular
	stats metrics.Type

	transactions <-chan types.Transaction

	closeChan  chan struct{}
	closedChan chan struct{}
}

// NewWriter creates a new Writer output type.
func NewWriter(
	typeStr string,
	w writer.Type,
	log log.Modular,
	stats metrics.Type,
) (Type, error) {
	return &Writer{
		running:      1,
		typeStr:      typeStr,
		writer:       w,
		log:          log,
		stats:        stats,
		transactions: nil,
		closeChan:    make(chan struct{}),
		closedChan:   make(chan struct{}),
	}, nil
}

//------------------------------------------------------------------------------

// loop is an internal loop that brokers incoming messages to output pipe.
func (w *Writer) loop() {
	// Metrics paths
	var (
		mCount      = w.stats.GetCounter("count")
		mPartsSent  = w.stats.GetCounter("sent")
		mSent       = w.stats.GetCounter("batch.sent")
		mConn       = w.stats.GetCounter("connection.up")
		mFailedConn = w.stats.GetCounter("connection.failed")
		mLostConn   = w.stats.GetCounter("connection.lost")
		// following metrics are left for backward compatibility
		// and should be considered as deprecated
		// TODO: V3 Remove obsolete metrics
		mRunning      = w.stats.GetGauge("running")
		mPartsCount   = w.stats.GetCounter("parts.count")
		mSuccess      = w.stats.GetCounter("send.success")
		mPartsSuccess = w.stats.GetCounter("parts.send.success")
		mError        = w.stats.GetCounter("send.error")
	)

	defer func() {
		err := w.writer.WaitForClose(time.Second)
		for ; err != nil; err = w.writer.WaitForClose(time.Second) {
		}
		mRunning.Decr(1)
		atomic.StoreInt32(&w.isConnected, 0)
		close(w.closedChan)
	}()
	mRunning.Incr(1)

	throt := throttle.New(throttle.OptCloseChan(w.closeChan))

	for {
		if err := w.writer.Connect(); err != nil {
			// Close immediately if our writer is closed.
			if err == types.ErrTypeClosed {
				return
			}

			w.log.Errorf("Failed to connect to %v: %v\n", w.typeStr, err)
			mFailedConn.Incr(1)
			if !throt.Retry() {
				return
			}
		} else {
			break
		}
	}
	mConn.Incr(1)
	atomic.StoreInt32(&w.isConnected, 1)

	for atomic.LoadInt32(&w.running) == 1 {
		var ts types.Transaction
		var open bool
		select {
		case ts, open = <-w.transactions:
			if !open {
				return
			}
			mCount.Incr(1)
			mPartsCount.Incr(int64(ts.Payload.Len()))
		case <-w.closeChan:
			return
		}

		spans := tracing.CreateChildSpans("output_"+w.typeStr, ts.Payload)
		err := w.writer.Write(ts.Payload)

		// If our writer says it is not connected.
		if err == types.ErrNotConnected {
			mLostConn.Incr(1)
			atomic.StoreInt32(&w.isConnected, 0)

			// Continue to try to reconnect while still active.
			for atomic.LoadInt32(&w.running) == 1 {
				if err = w.writer.Connect(); err != nil {
					// Close immediately if our writer is closed.
					if err == types.ErrTypeClosed {
						return
					}

					w.log.Errorf("Failed to reconnect to %v: %v\n", w.typeStr, err)
					mFailedConn.Incr(1)
					if !throt.Retry() {
						return
					}
				} else if err = w.writer.Write(ts.Payload); err != types.ErrNotConnected {
					atomic.StoreInt32(&w.isConnected, 1)
					mConn.Incr(1)
					break
				} else if !throt.Retry() {
					return
				}
			}
		}

		// Close immediately if our writer is closed.
		if err == types.ErrTypeClosed {
			return
		}

		if err != nil {
			w.log.Errorf("Failed to send message to %v: %v\n", w.typeStr, err)
			mError.Incr(1)
			if !throt.Retry() {
				return
			}
		} else {
			mSuccess.Incr(1)
			mPartsSuccess.Incr(int64(ts.Payload.Len()))
			mSent.Incr(1)
			mPartsSent.Incr(int64(ts.Payload.Len()))
			throt.Reset()
		}

		for _, s := range spans {
			s.Finish()
		}

		select {
		case ts.ResponseChan <- response.NewError(err):
		case <-w.closeChan:
			// The pipeline is terminating but we still want to attempt to
			// propagate an acknowledgement from in-transit messages.
			//
			// TODO: Replace this timer with a value linked to our service
			// shutdown timer.
			select {
			case ts.ResponseChan <- response.NewError(err):
			case <-time.After(time.Second):
			}
			return
		}
	}
}

// Consume assigns a messages channel for the output to read.
func (w *Writer) Consume(ts <-chan types.Transaction) error {
	if w.transactions != nil {
		return types.ErrAlreadyStarted
	}
	w.transactions = ts
	go w.loop()
	return nil
}

// Connected returns a boolean indicating whether this output is currently
// connected to its target.
func (w *Writer) Connected() bool {
	return atomic.LoadInt32(&w.isConnected) == 1
}

// CloseAsync shuts down the File output and stops processing messages.
func (w *Writer) CloseAsync() {
	if atomic.CompareAndSwapInt32(&w.running, 1, 0) {
		w.writer.CloseAsync()
		close(w.closeChan)
	}
}

// WaitForClose blocks until the File output has closed down.
func (w *Writer) WaitForClose(timeout time.Duration) error {
	select {
	case <-w.closedChan:
	case <-time.After(timeout):
		return types.ErrTimeout
	}
	return nil
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>// +build linux

package vdlog

import (
	"fmt"
	"os/exec"
)

// Mapping between journalctl and vdlog levels

//    journald            vdlog
//    0: emerg            0:LevelError
//    1: alert            0:LevelError
//    2: crit             0:LevelError
//    3: err              0:LevelError
//    4: warning          1:LevelWarn
//    5: notice           2:LevelInfo
//    6: info             3:LevelVerbose
//    7: debug            4:LevelDebug

func createJournaldSink(host string, port int, tcp, local bool) func(e Event) error {
	return func(e Event) error {
		var journaldPriority int
		switch e.Level {
		case LevelError:
			journaldPriority = 3
			break
		case LevelWarn:
			journaldPriority = 4
			break
		case LevelInfo:
			journaldPriority = 5
			break
		case LevelVerbose:
			journaldPriority = 6
			break
		case LevelDebug:
			journaldPriority = 7
			break
		default:
			journaldPriority = 5
		}
		var cmd *exec.Cmd
		if local {
			cmd = exec.Command("/bin/logger", "--journald")
		} else {
			var protoParams string
			if tcp {
				protoParams = "--tcp"
			} else {
				protoParams = "--udp"
			}
			cmd = exec.Command("/bin/logger",
				"--journald",
				fmt.Sprintf("--server %s", host),
				fmt.Sprintf("--port %v", port),
				protoParams,
			)
		}
		stdin, err := cmd.StdinPipe()
		if err != nil {
			return err
		}
		//http://man7.org/linux/man-pages/man7/systemd.journal-fields.7.html
		fmt.Fprintf(stdin, "MESSAGE_ID=%s\n", e.UUID)
		fmt.Fprintf(stdin, "CODE_FILE=%s\n", e.Filename)
		fmt.Fprintf(stdin, "CODE_LINE=%v\n", e.Line)
		fmt.Fprintf(stdin, "SYSLOG_IDENTIFIER=%s\n", e.Type)
		fmt.Fprintf(stdin, "MESSAGE=%q\n", fmt.Sprint(e.Metadata))
		fmt.Fprintf(stdin, "PRIORITY=%d\n", journaldPriority)
		fmt.Fprintf(stdin, "SYSLOG_PID=%d\n", e.Pid)
		fmt.Fprintf(stdin, "OBJECT_PID=%d\n", e.Pid)
		fmt.Fprintf(stdin, "VDLOG_LEVEL=%s\n", e.LevelString)
		fmt.Fprintf(stdin, "VDLOG_CALLED=%s\n", e.Called)
		for k,v := range e.Metadata {
			fmt.Fprintf(stdin, "VDLOG_META_%s=%q\n", k, fmt.Sprint(v))
		}
		err = stdin.Close()
		if err != nil {
			return err
		}
		err = cmd.Run()
		if err != nil {
			return err
		}
		return nil
	}
}

//LogToLocalJournald allows to send messages directly to journald daemon via logger command
func LogToLocalJournald() {
	AddSink("journald local", createJournaldSink("localhost", 514, true, true))
}

//LogToRemoteJournaldViaTCP allows to send messages directly to remote journald daemon via logger command and TCP
func LogToRemoteJournaldViaTCP(server string, port int) {
	AddSink(fmt.Sprintf("journald tcp(%s:%v)", server, port), createJournaldSink(server, port, true, false))
}

//LogToRemoteJournaldViaUDP allows to send messages directly to remote journald daemon via logger command and UDP
func LogToRemoteJournaldViaUDP(server string, port int) {
	AddSink(fmt.Sprintf("journald udp(%s:%v)", server, port), createJournaldSink(server, port, false, false))
}
<commit_msg>fix saving metadata to journalctl<commit_after>// +build linux

package vdlog

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

// Mapping between journalctl and vdlog levels

//    journald            vdlog
//    0: emerg            0:LevelError
//    1: alert            0:LevelError
//    2: crit             0:LevelError
//    3: err              0:LevelError
//    4: warning          1:LevelWarn
//    5: notice           2:LevelInfo
//    6: info             3:LevelVerbose
//    7: debug            4:LevelDebug

func createJournaldSink(host string, port int, tcp, local bool) func(e Event) error {
	return func(e Event) error {
		var journaldPriority int
		switch e.Level {
		case LevelError:
			journaldPriority = 3
			break
		case LevelWarn:
			journaldPriority = 4
			break
		case LevelInfo:
			journaldPriority = 5
			break
		case LevelVerbose:
			journaldPriority = 6
			break
		case LevelDebug:
			journaldPriority = 7
			break
		default:
			journaldPriority = 5
		}
		var cmd *exec.Cmd
		if local {
			cmd = exec.Command("/bin/logger", "--journald")
		} else {
			var protoParams string
			if tcp {
				protoParams = "--tcp"
			} else {
				protoParams = "--udp"
			}
			cmd = exec.Command("/bin/logger",
				"--journald",
				fmt.Sprintf("--server %s", host),
				fmt.Sprintf("--port %v", port),
				protoParams,
			)
		}
		stdin, err := cmd.StdinPipe()
		if err != nil {
			return err
		}
		//http://man7.org/linux/man-pages/man7/systemd.journal-fields.7.html
		fmt.Fprintf(stdin, "MESSAGE_ID=%s\n", e.UUID)
		fmt.Fprintf(stdin, "CODE_FILE=%s\n", e.Filename)
		fmt.Fprintf(stdin, "CODE_LINE=%v\n", e.Line)
		fmt.Fprintf(stdin, "SYSLOG_IDENTIFIER=%s\n", e.Type)
		fmt.Fprintf(stdin, "MESSAGE=%q\n", fmt.Sprint(e.Metadata))
		fmt.Fprintf(stdin, "PRIORITY=%d\n", journaldPriority)
		fmt.Fprintf(stdin, "SYSLOG_PID=%d\n", e.Pid)
		fmt.Fprintf(stdin, "OBJECT_PID=%d\n", e.Pid)
		fmt.Fprintf(stdin, "VDLOG_LEVEL=%s\n", e.LevelString)
		fmt.Fprintf(stdin, "VDLOG_CALLED=%s\n", e.Called)
		for k,v := range e.Metadata {
			fmt.Fprintf(stdin, "VDLOG_META_%s=%q\n", strings.ToUpper(k), fmt.Sprint(v))
		}
		err = stdin.Close()
		if err != nil {
			return err
		}
		err = cmd.Run()
		if err != nil {
			return err
		}
		return nil
	}
}

//LogToLocalJournald allows to send messages directly to journald daemon via logger command
func LogToLocalJournald() {
	AddSink("journald local", createJournaldSink("localhost", 514, true, true))
}

//LogToRemoteJournaldViaTCP allows to send messages directly to remote journald daemon via logger command and TCP
func LogToRemoteJournaldViaTCP(server string, port int) {
	AddSink(fmt.Sprintf("journald tcp(%s:%v)", server, port), createJournaldSink(server, port, true, false))
}

//LogToRemoteJournaldViaUDP allows to send messages directly to remote journald daemon via logger command and UDP
func LogToRemoteJournaldViaUDP(server string, port int) {
	AddSink(fmt.Sprintf("journald udp(%s:%v)", server, port), createJournaldSink(server, port, false, false))
}
<|endoftext|>


<commit_before>package entities

import "time"

var (
	timeStamp int64   = time.Date(2012, time.November, 10, 23, 0, 0, 0, time.UTC).UnixNano() / 1e6
	mission   Mission = Mission{
		Color:       Color{22, 22, 22},
		Source:      []int{100, 200},
		Target:      []int{800, 150},
		Type:        "Attack",
		CurrentTime: timeStamp,
		StartTime:   timeStamp,
		TravelTime:  timeStamp,
		Player:      "gophie",
		ShipCount:   5,
	}
	planet Planet = Planet{
		Color:               Color{22, 22, 22},
		Coords:              []int{271, 203},
		IsHome:              false,
		Texture:             3,
		Size:                1,
		LastShipCountUpdate: timeStamp,
		ShipCount:           0,
		MaxShipCount:        0,
		Owner:               "gophie",
	}
)
<commit_msg>Add player in the test data<commit_after>package entities

import "time"

var (
	timeStamp int64   = time.Date(2012, time.November, 10, 23, 0, 0, 0, time.UTC).UnixNano() / 1e6
	mission   Mission = Mission{
		Color:       Color{22, 22, 22},
		Source:      []int{100, 200},
		Target:      []int{800, 150},
		Type:        "Attack",
		CurrentTime: timeStamp,
		StartTime:   timeStamp,
		TravelTime:  timeStamp,
		Player:      "gophie",
		ShipCount:   5,
	}
	planet Planet = Planet{
		Color:               Color{22, 22, 22},
		Coords:              []int{271, 203},
		IsHome:              false,
		Texture:             3,
		Size:                1,
		LastShipCountUpdate: timeStamp,
		ShipCount:           0,
		MaxShipCount:        0,
		Owner:               "gophie",
	}
	player Player = Player{
		username: "gophie",
		Color: Color{22, 22, 22},
		TwitterID: "asdf",
		HomePlanet: "planet.271_203",
		ScreenSize: []int{1, 1},
		ScreenPosition: []int{2, 2},
	}
)
<|endoftext|>


<commit_before>package local

import (
	"bytes"
	"os/exec"
	"strings"
)

// container represents an LXC container with the given name.
type container struct {
	Name string
}

// runLXCCommand runs an LXC command with the given arguments,
// strip outing the usage message.
func runLXCCommand(args ...string) ([]byte, error) {
	output, err := exec.Command(args[0], args...).Output()
	if i := bytes.Index(output, []byte("\nusage: ")); i > 0 {
		output = output[:i]
	}
	return output, err
}

// rootPath returns the LXC container root filesystem path.
func (c *container) rootPath() string {
	return "/var/lib/lxc/" + c.Name + "/rootfs/"
}

// create creates the LXC container.
func (c *container) create() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-create", "-n", c.Name)
}

// start starts the LXC container.
func (c *container) start() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-start", "--daemon", "-n", c.Name)
}

// stop stops the LXC container.
func (c *container) stop() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-stop", "-n", c.Name)
}

// destroy destroys the LXC container.
func (c *container) destroy() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-destroy", "-n", c.Name)
}

// running returns true if the container name is in the
// list of the containers that are running.
func (c *container) running() bool {
	for _, containerName := range list() {
		if containerName == c.Name {
			return true
		}
	}
	return false
}

// list returns a list with the names of containers.
func list() []string {
	output, _ := runLXCCommand("sudo", "lxc-ls")
	return strings.Fields(string(output))
}
<commit_msg>using CombinedOutput instead Output to get errors too<commit_after>package local

import (
	"bytes"
	"os/exec"
	"strings"
)

// container represents an LXC container with the given name.
type container struct {
	Name string
}

// runLXCCommand runs an LXC command with the given arguments,
// strip outing the usage message.
func runLXCCommand(args ...string) ([]byte, error) {
	output, err := exec.Command(args[0], args...).CombinedOutput()
	if i := bytes.Index(bytes.ToLower(output), []byte("\nusage: ")); i > 0 {
		output = output[:i]
	}
	return output, err
}

// rootPath returns the LXC container root filesystem path.
func (c *container) rootPath() string {
	return "/var/lib/lxc/" + c.Name + "/rootfs/"
}

// create creates the LXC container.
func (c *container) create() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-create", "-n", c.Name)
}

// start starts the LXC container.
func (c *container) start() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-start", "--daemon", "-n", c.Name)
}

// stop stops the LXC container.
func (c *container) stop() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-stop", "-n", c.Name)
}

// destroy destroys the LXC container.
func (c *container) destroy() ([]byte, error) {
	return runLXCCommand("sudo", "lxc-destroy", "-n", c.Name)
}

// running returns true if the container name is in the
// list of the containers that are running.
func (c *container) running() bool {
	for _, containerName := range list() {
		if containerName == c.Name {
			return true
		}
	}
	return false
}

// list returns a list with the names of containers.
func list() []string {
	output, _ := runLXCCommand("sudo", "lxc-ls")
	return strings.Fields(string(output))
}
<|endoftext|>


<commit_before>package common

import (
	"errors"
	"github.com/effoeffi/watchtower/Godeps/_workspace/src/github.com/stretchr/testify/assert"
	"testing"
)

func TestErrorBuilder(t *testing.T) {
	var ec ErrorBuilder
	ec.Append(errors.New("uno"))
	ec.Append(errors.New("dos"))
	assert.Equal(t, "uno\ndos", ec.ToError().Error())
}

func TestErrorBuilderNoErrors(t *testing.T) {
	var ec ErrorBuilder
	assert.Nil(t, ec.ToError())
}
<commit_msg>fix test dependency;<commit_after>package common

import (
	"errors"
	"testing"

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

func TestErrorBuilder(t *testing.T) {
	var ec ErrorBuilder
	ec.Append(errors.New("uno"))
	ec.Append(errors.New("dos"))
	assert.Equal(t, "uno\ndos", ec.ToError().Error())
}

func TestErrorBuilderNoErrors(t *testing.T) {
	var ec ErrorBuilder
	assert.Nil(t, ec.ToError())
}
<|endoftext|>


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

package errors

import (
	check "launchpad.net/gocheck"
	"testing"
)

func Test(t *testing.T) { check.TestingT(t) }

type S struct{}

var _ = check.Suite(&S{})

// Helpers
func f(i int) error {
	if i == 0 {
		return Make("message", 0, 10)
	}

	i--
	return f(i)
}

var traceRE = `Trace: message:

 code.google.com/p/biogo/errors.f:
	.*/code.google.com/p/biogo/errors/errors_test.go#L=21
	.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	.*/code.google.com/p/biogo/errors/errors_test.go#L=25

 code.google.com/p/biogo/errors.\(\*S\).TestCaller:
	.*/code.google.com/p/biogo/errors/errors_test.go#L=60

 reflect.Value.call:
	.*/go/src/pkg/reflect/value.go#L=[0-9]+

 reflect.Value.Call:
	.*/go/src/pkg/reflect/value.go#L=[0-9]+

 launchpad.net/gocheck._?func[_·][0-9]+:
	.*/launchpad.net/gocheck/gocheck.go#L=[0-9]+
`

// Tests
func (s *S) TestCaller(c *check.C) {
	err := Make("message", 0, 10, "item")
	c.Check(err.Error(), check.Equals, "message")
	fn, ln := err.FileLine()
	c.Check(fn, check.Matches, "/.*/biogo/errors/errors_test.go")
	c.Check(ln, check.Equals, 53)
	c.Check(err.Package(), check.Equals, "code.google.com/p/biogo/errors.(*S)")
	c.Check(err.Function(), check.Equals, "TestCaller")
	err = f(5).(Error)
	c.Check(err.Tracef(10), check.Matches, traceRE)
}

func (s *S) TestMakeFail(c *check.C) {
	c.Check(func() { Make("message", 0, 0) }, check.Panics, "errors: zero trace depth")
}
<commit_msg>Tighten up test condition and allow drive IDs<commit_after>// Copyright ©2011-2013 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package errors

import (
	check "launchpad.net/gocheck"
	"testing"
)

func Test(t *testing.T) { check.TestingT(t) }

type S struct{}

var _ = check.Suite(&S{})

// Helpers
func f(i int) error {
	if i == 0 {
		return Make("message", 0, 10)
	}

	i--
	return f(i)
}

var traceRE = `Trace: message:

 code.google.com/p/biogo/errors.f:
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=21
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=25

 code.google.com/p/biogo/errors.\(\*S\).TestCaller:
	(?:[A-Z]:)?/.*/code.google.com/p/biogo/errors/errors_test.go#L=60

 reflect.Value.call:
	(?:[A-Z]:)?/.*/go/src/pkg/reflect/value.go#L=[0-9]+

 reflect.Value.Call:
	(?:[A-Z]:)?/.*/go/src/pkg/reflect/value.go#L=[0-9]+

 launchpad.net/gocheck._?func[_·][0-9]+:
	(?:[A-Z]:)?/.*/launchpad.net/gocheck/gocheck.go#L=[0-9]+
`

// Tests
func (s *S) TestCaller(c *check.C) {
	err := Make("message", 0, 10, "item")
	c.Check(err.Error(), check.Equals, "message")
	fn, ln := err.FileLine()
	c.Check(fn, check.Matches, "(?:[A-Z]:)?/.*/biogo/errors/errors_test.go")
	c.Check(ln, check.Equals, 53)
	c.Check(err.Package(), check.Equals, "code.google.com/p/biogo/errors.(*S)")
	c.Check(err.Function(), check.Equals, "TestCaller")
	err = f(5).(Error)
	c.Check(err.Tracef(10), check.Matches, traceRE)
}

func (s *S) TestMakeFail(c *check.C) {
	c.Check(func() { Make("message", 0, 0) }, check.Panics, "errors: zero trace depth")
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"strings"
	//
	"github.com/golangdaddy/tarantula/web"
	"github.com/golangdaddy/tarantula/web/validation"
	"github.com/golangdaddy/tarantula/router/common/openapi"
)

/*
type HandlerSpec struct {
	Method string `json:"method"`
	Endpoint string `json:"endpoint"`
	MockEndpoint string `json:"mockEndpoint"`
	MockPayload interface{} `json:"mockPayload,omitempty"`
	PayloadSchema interface{} `json:"payloadSchema,omitempty"`
	OptionalPayloadSchema interface{} `json:"optionalPayloadSchema,omitempty"`
	ResponseSchema interface{} `json:"responseSchema,omitempty"`
	RouteParams map[string]*validation.Config `json:"routeParams,omitempty"`
	IsFile bool `json:"isFile"`
	FilePath string `json:"filePath,omitempty"`
	Description string `json:"description,omitempty"`
}
*/

func pointerFloat64(f float64) *float64 {
	if f == 0 {
		return nil
	}
	return &f
}

// Builds the handler documentation object.
func (config *Config) BuildOpenAPISpec(req web.RequestInterface) *openapi.APISpec {

	handlers := map[*Handler]*HandlerSpec{}

	// do the legacy spec operation
	config.RLock()
	spec := config.Spec
	for _, handler := range config.Handlers {
		handlers[handler] = handler.Spec(req)
	}
	config.RUnlock()

	for handler, handlerSpec := range handlers {

		// create the object that holds the handler's definition
		pathMethod := &openapi.PathMethod{
			Produces: []string{
				"application/json",
			},
			Parameters: []*openapi.Parameter{},
			Description: handler.Description,
			Responses: openapi.Responses{
				Code200: &openapi.StatusCode{
					Description: "Done OK",
					Schema: openapi.StatusSchema{
						Type: "object",
					},
				},
			},
		}

		k := strings.Replace(
			fmt.Sprintf("%s-%s", handler.Node.FullPath(), handler.Method),
			"/",
			"",
			1,
		)
		definition := &openapi.Definition{
			Type: "object",
			Properties: map[string]openapi.Parameter{},
		}

		// check the type of payload
		switch t := handlerSpec.PayloadSchema.(type) {
			case map[string]*validation.Config:

				// dont make a param object for GET routes etc
				if len(t) == 0 { continue }

				pathMethod.Parameters = append(
					pathMethod.Parameters,
					&openapi.Parameter{
						Required: true,
						Name: "body",
						In: "body",
						Description: handler.Description,
						Schema: &openapi.Schema{
							Ref: "#/definitions/" + k,
						},
					},
				)

				requiredBodyParams := []string{}

				// add body params to the definition properties
				for key, cfg := range t {

					requiredBodyParams = append(
						requiredBodyParams,
						key,
					)

					param := openapi.Parameter{}
					param.Description = cfg.DescriptionValue
					param.Minimum = pointerFloat64(cfg.Min)
					param.Maximum = pointerFloat64(cfg.Max)
					param.Default = cfg.DefaultValue
					param.Format = cfg.Type

					switch param.Format {
					case "bool":
						param.Type = "boolean"
					case "string":
						param.Type = "string"
					case "int64", "int":
						param.Type = "integer"
					case "float32", "float64":
						param.Type = "number"
					}

					definition.Properties[key] = param

				}

				definition.Required = requiredBodyParams

		}

		// only create the definition if it has contents
		if len(definition.Properties) > 0 {
			spec.Definitions[k] = definition
		}

		// route params
		for name, cfg := range handlerSpec.RouteParams {
			param := &openapi.Parameter{}
			param.In = "path"
			param.Name = name
			param.Description = cfg.DescriptionValue
			param.Type = cfg.Type
			minLength := int64(cfg.Min)
			maxLength := int64(cfg.Max)
			param.MinLength = &minLength
			param.MaxLength = &maxLength

			pathMethod.Parameters = append(pathMethod.Parameters, param)
		}

		fullPath := handler.Node.FullPath()
		_, ok := spec.Paths[fullPath]
		if !ok {
			spec.Paths[fullPath] = &openapi.Path{}
		}

		var f *openapi.Path

		switch handler.Method {
		case "GET":
			f = spec.Paths[fullPath]
			f.GET = pathMethod
		case "POST":
			f = spec.Paths[fullPath]
			f.POST = pathMethod
		case "PUT":
			f = spec.Paths[fullPath]
			f.PUT = pathMethod
		case "PATCH":
			f = spec.Paths[fullPath]
			f.PATCH = pathMethod
		case "DELETE":
			f = spec.Paths[fullPath]
			f.DELETE = pathMethod
		case "HEAD":
			f = spec.Paths[fullPath]
			f.HEAD = pathMethod
		case "OPTIONS":
			f = spec.Paths[fullPath]
			f.OPTIONS = pathMethod
		default:
			panic("wtf")
		}

		fmt.Println(fullPath, handler.Method, f)
	}

	return spec
}
<commit_msg>fix openapi spec builder<commit_after>package common

import (
	"fmt"
	"strings"
	//
	"github.com/golangdaddy/tarantula/web"
	"github.com/golangdaddy/tarantula/web/validation"
	"github.com/golangdaddy/tarantula/router/common/openapi"
)

/*
type HandlerSpec struct {
	Method string `json:"method"`
	Endpoint string `json:"endpoint"`
	MockEndpoint string `json:"mockEndpoint"`
	MockPayload interface{} `json:"mockPayload,omitempty"`
	PayloadSchema interface{} `json:"payloadSchema,omitempty"`
	OptionalPayloadSchema interface{} `json:"optionalPayloadSchema,omitempty"`
	ResponseSchema interface{} `json:"responseSchema,omitempty"`
	RouteParams map[string]*validation.Config `json:"routeParams,omitempty"`
	IsFile bool `json:"isFile"`
	FilePath string `json:"filePath,omitempty"`
	Description string `json:"description,omitempty"`
}
*/

func pointerFloat64(f float64) *float64 {
	if f == 0 {
		return nil
	}
	return &f
}

// Builds the handler documentation object.
func (config *Config) BuildOpenAPISpec(req web.RequestInterface) *openapi.APISpec {

	handlers := map[*Handler]*HandlerSpec{}

	// do the legacy spec operation
	config.RLock()
	spec := config.Spec
	for _, handler := range config.Handlers {
		handlers[handler] = handler.Spec(req)
	}
	config.RUnlock()

	for handler, handlerSpec := range handlers {

		// create the object that holds the handler's definition
		pathMethod := &openapi.PathMethod{
			Produces: []string{
				"application/json",
			},
			Parameters: []*openapi.Parameter{},
			Description: handler.Description,
			Responses: openapi.Responses{
				Code200: &openapi.StatusCode{
					Description: "Done OK",
					Schema: openapi.StatusSchema{
						Type: "object",
					},
				},
			},
		}

		k := strings.Replace(
			fmt.Sprintf("%s-%s", handler.Node.FullPath(), handler.Method),
			"/",
			"",
			-1,
		)
		definition := &openapi.Definition{
			Type: "object",
			Properties: map[string]openapi.Parameter{},
		}

		// check the type of payload
		switch t := handlerSpec.PayloadSchema.(type) {
			case map[string]*validation.Config:

				// dont make a param object for GET routes etc
				if len(t) == 0 { continue }

				pathMethod.Parameters = append(
					pathMethod.Parameters,
					&openapi.Parameter{
						Required: true,
						Name: "body",
						In: "body",
						Description: handler.Description,
						Schema: &openapi.Schema{
							Ref: "#/definitions/" + k,
						},
					},
				)

				requiredBodyParams := []string{}

				// add body params to the definition properties
				for key, cfg := range t {

					requiredBodyParams = append(
						requiredBodyParams,
						key,
					)

					param := openapi.Parameter{}
					param.Description = cfg.DescriptionValue
					param.Minimum = pointerFloat64(cfg.Min)
					param.Maximum = pointerFloat64(cfg.Max)
					param.Default = cfg.DefaultValue
					param.Format = cfg.Type

					switch param.Format {
					case "bool":
						param.Type = "boolean"
					case "string":
						param.Type = "string"
					case "int64", "int":
						param.Type = "integer"
					case "float32", "float64":
						param.Type = "number"
					}

					definition.Properties[key] = param

				}

				definition.Required = requiredBodyParams

		}

		// only create the definition if it has contents
		if len(definition.Properties) > 0 {
			spec.Definitions[k] = definition
		}

		// route params
		for name, cfg := range handlerSpec.RouteParams {
			param := &openapi.Parameter{}
			param.In = "path"
			param.Name = name
			param.Description = cfg.DescriptionValue
			param.Type = cfg.Type
			minLength := int64(cfg.Min)
			maxLength := int64(cfg.Max)
			param.MinLength = &minLength
			param.MaxLength = &maxLength

			pathMethod.Parameters = append(pathMethod.Parameters, param)
		}

		fullPath := handler.Node.FullPath()
		_, ok := spec.Paths[fullPath]
		if !ok {
			spec.Paths[fullPath] = &openapi.Path{}
		}

		var f *openapi.Path

		switch handler.Method {
		case "GET":
			f = spec.Paths[fullPath]
			f.GET = pathMethod
		case "POST":
			f = spec.Paths[fullPath]
			f.POST = pathMethod
		case "PUT":
			f = spec.Paths[fullPath]
			f.PUT = pathMethod
		case "PATCH":
			f = spec.Paths[fullPath]
			f.PATCH = pathMethod
		case "DELETE":
			f = spec.Paths[fullPath]
			f.DELETE = pathMethod
		case "HEAD":
			f = spec.Paths[fullPath]
			f.HEAD = pathMethod
		case "OPTIONS":
			f = spec.Paths[fullPath]
			f.OPTIONS = pathMethod
		default:
			panic("wtf")
		}

		fmt.Println(fullPath, handler.Method, f)
	}

	return spec
}
<|endoftext|>


<commit_before>package protocol

import (
	"crypto/ecdsa"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/status-im/markdown"

	"github.com/status-im/status-go/protocol/protobuf"
)

// QuotedMessage contains the original text of the message replied to
type QuotedMessage struct {
	// From is a public key of the author of the message.
	From string `json:"from"`
	Text string `json:"text"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioDurationMs is the audio duration in milliseconds
	AudioDurationMs uint64 `json:"audioDuration,omitempty"`
}

type CommandState int

const (
	CommandStateRequestAddressForTransaction CommandState = iota + 1
	CommandStateRequestAddressForTransactionDeclined
	CommandStateRequestAddressForTransactionAccepted
	CommandStateRequestTransaction
	CommandStateRequestTransactionDeclined
	CommandStateTransactionPending
	CommandStateTransactionSent
)

type CommandParameters struct {
	// ID is the ID of the initial message
	ID string `json:"id"`
	// From is the address we are sending the command from
	From string `json:"from"`
	// Address is the address sent with the command
	Address string `json:"address"`
	// Contract is the contract address for ERC20 tokens
	Contract string `json:"contract"`
	// Value is the value as a string sent
	Value string `json:"value"`
	// TransactionHash is the hash of the transaction
	TransactionHash string `json:"transactionHash"`
	// CommandState is the state of the command
	CommandState CommandState `json:"commandState"`
	// The Signature of the pk-bytes+transaction-hash from the wallet
	// address originating
	Signature []byte `json:"signature"`
}

func (c *CommandParameters) IsTokenTransfer() bool {
	return len(c.Contract) != 0
}

const (
	OutgoingStatusSending = "sending"
	OutgoingStatusSent    = "sent"
)

// Message represents a message record in the database,
// more specifically in user_messages table.
type Message struct {
	protobuf.ChatMessage

	// ID calculated as keccak256(compressedAuthorPubKey, data) where data is unencrypted payload.
	ID string `json:"id"`
	// WhisperTimestamp is a timestamp of a Whisper envelope.
	WhisperTimestamp uint64 `json:"whisperTimestamp"`
	// From is a public key of the author of the message.
	From string `json:"from"`
	// Random 3 words name
	Alias string `json:"alias"`
	// Identicon of the author
	Identicon string `json:"identicon"`
	// The chat id to be stored locally
	LocalChatID string `json:"localChatId"`

	Seen           bool   `json:"seen"`
	OutgoingStatus string `json:"outgoingStatus,omitempty"`

	QuotedMessage *QuotedMessage `json:"quotedMessage"`

	// CommandParameters is the parameters sent with the message
	CommandParameters *CommandParameters `json:"commandParameters"`

	// Computed fields
	// RTL is whether this is a right-to-left message (arabic/hebrew script etc)
	RTL bool `json:"rtl"`
	// ParsedText is the parsed markdown for displaying
	ParsedText []byte `json:"parsedText"`
	// LineCount is the count of newlines in the message
	LineCount int `json:"lineCount"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// ImagePath is the path of the image to be sent
	ImagePath string `json:"imagePath,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioPath is the path of the audio to be sent
	AudioPath string `json:"audioPath,omitempty"`

	// Replace indicates that this is a replacement of a message
	// that has been updated
	Replace   string           `json:"replace,omitempty"`
	SigPubKey *ecdsa.PublicKey `json:"-"`
}

func (m *Message) MarshalJSON() ([]byte, error) {
	type StickerAlias struct {
		Hash string `json:"hash"`
		Pack int32  `json:"pack"`
	}
	item := struct {
		ID                string                           `json:"id"`
		WhisperTimestamp  uint64                           `json:"whisperTimestamp"`
		From              string                           `json:"from"`
		Alias             string                           `json:"alias"`
		Identicon         string                           `json:"identicon"`
		Seen              bool                             `json:"seen"`
		OutgoingStatus    string                           `json:"outgoingStatus,omitempty"`
		QuotedMessage     *QuotedMessage                   `json:"quotedMessage"`
		RTL               bool                             `json:"rtl"`
		ParsedText        json.RawMessage                  `json:"parsedText"`
		LineCount         int                              `json:"lineCount"`
		Text              string                           `json:"text"`
		ChatID            string                           `json:"chatId"`
		LocalChatID       string                           `json:"localChatId"`
		Clock             uint64                           `json:"clock"`
		Replace           string                           `json:"replace"`
		ResponseTo        string                           `json:"responseTo"`
		EnsName           string                           `json:"ensName"`
		Image             string                           `json:"image,omitempty"`
		Audio             string                           `json:"audio,omitempty"`
		AudioDurationMs   uint64                           `json:"audioDurationMs,omitempty"`
		Sticker           *StickerAlias                    `json:"sticker"`
		CommandParameters *CommandParameters               `json:"commandParameters"`
		Timestamp         uint64                           `json:"timestamp"`
		ContentType       protobuf.ChatMessage_ContentType `json:"contentType"`
		MessageType       protobuf.ChatMessage_MessageType `json:"messageType"`
	}{
		ID:                m.ID,
		WhisperTimestamp:  m.WhisperTimestamp,
		From:              m.From,
		Alias:             m.Alias,
		Identicon:         m.Identicon,
		Seen:              m.Seen,
		OutgoingStatus:    m.OutgoingStatus,
		QuotedMessage:     m.QuotedMessage,
		RTL:               m.RTL,
		ParsedText:        m.ParsedText,
		LineCount:         m.LineCount,
		Text:              m.Text,
		Replace:           m.Replace,
		ChatID:            m.ChatId,
		LocalChatID:       m.LocalChatID,
		Clock:             m.Clock,
		ResponseTo:        m.ResponseTo,
		EnsName:           m.EnsName,
		Image:             m.Base64Image,
		Audio:             m.Base64Audio,
		Timestamp:         m.Timestamp,
		ContentType:       m.ContentType,
		MessageType:       m.MessageType,
		CommandParameters: m.CommandParameters,
	}

	if sticker := m.GetSticker(); sticker != nil {
		item.Sticker = &StickerAlias{
			Pack: sticker.Pack,
			Hash: sticker.Hash,
		}
	}

	if audio := m.GetAudio(); audio != nil {
		item.AudioDurationMs = audio.DurationMs
	}

	return json.Marshal(item)
}

func (m *Message) UnmarshalJSON(data []byte) error {
	type Alias Message
	aux := struct {
		*Alias
		ResponseTo      string                           `json:"responseTo"`
		EnsName         string                           `json:"ensName"`
		ChatID          string                           `json:"chatId"`
		Sticker         *protobuf.StickerMessage         `json:"sticker"`
		AudioDurationMs uint64                           `json:"audioDurationMs"`
		ContentType     protobuf.ChatMessage_ContentType `json:"contentType"`
	}{
		Alias: (*Alias)(m),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	if aux.ContentType == protobuf.ChatMessage_STICKER {
		m.Payload = &protobuf.ChatMessage_Sticker{Sticker: aux.Sticker}
	}
	if aux.ContentType == protobuf.ChatMessage_AUDIO {
		m.Payload = &protobuf.ChatMessage_Audio{
			Audio: &protobuf.AudioMessage{DurationMs: aux.AudioDurationMs},
		}
	}
	m.ResponseTo = aux.ResponseTo
	m.EnsName = aux.EnsName
	m.ChatId = aux.ChatID
	m.ContentType = aux.ContentType
	return nil
}

// Check if the first character is Hebrew or Arabic or the RTL character
func isRTL(s string) bool {
	first, _ := utf8.DecodeRuneInString(s)
	return unicode.Is(unicode.Hebrew, first) ||
		unicode.Is(unicode.Arabic, first) ||
		// RTL character
		first == '\u200f'
}

// parseImage check the message contains an image, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseImage() error {
	if m.ContentType != protobuf.ChatMessage_IMAGE {
		return nil
	}
	image := m.GetImage()
	if image == nil {
		return errors.New("image empty")
	}

	payload := image.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getImageMessageMIME(image)

	if err != nil {
		return err
	}

	m.Base64Image = fmt.Sprintf("data:image/%s;base64,%s", mime, encBuf)

	return nil
}

// parseAudio check the message contains an audio, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseAudio() error {
	if m.ContentType != protobuf.ChatMessage_AUDIO {
		return nil
	}
	audio := m.GetAudio()
	if audio == nil {
		return errors.New("audio empty")
	}

	payload := audio.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getAudioMessageMIME(audio)

	if err != nil {
		return err
	}

	m.Base64Audio = fmt.Sprintf("data:audio/%s;base64,%s", mime, encBuf)

	return nil
}

// PrepareContent return the parsed content of the message, the line-count and whether
// is a right-to-left message
func (m *Message) PrepareContent() error {
	parsedText := markdown.Parse([]byte(m.Text), nil)
	jsonParsedText, err := json.Marshal(parsedText)
	if err != nil {
		return err
	}
	m.ParsedText = jsonParsedText
	m.LineCount = strings.Count(m.Text, "\n")
	m.RTL = isRTL(m.Text)
	if err := m.parseImage(); err != nil {
		return err
	}
	return m.parseAudio()
}

func getImageMessageMIME(i *protobuf.ImageMessage) (string, error) {
	switch i.Type {
	case protobuf.ImageMessage_PNG:
		return "png", nil
	case protobuf.ImageMessage_JPEG:
		return "jpeg", nil
	case protobuf.ImageMessage_WEBP:
		return "webp", nil
	case protobuf.ImageMessage_GIF:
		return "gif", nil
	}
	return "", errors.New("image format not supported")
}

func getAudioMessageMIME(i *protobuf.AudioMessage) (string, error) {
	switch i.Type {
	case protobuf.AudioMessage_AAC:
		return "aac", nil
	case protobuf.AudioMessage_AMR:
		return "amr", nil
	}

	return "", errors.New("audio format not supported")
}
<commit_msg>use ms<commit_after>package protocol

import (
	"crypto/ecdsa"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/status-im/markdown"

	"github.com/status-im/status-go/protocol/protobuf"
)

// QuotedMessage contains the original text of the message replied to
type QuotedMessage struct {
	// From is a public key of the author of the message.
	From string `json:"from"`
	Text string `json:"text"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioDurationMs is the audio duration in milliseconds
	AudioDurationMs uint64 `json:"audioDurationMs,omitempty"`
}

type CommandState int

const (
	CommandStateRequestAddressForTransaction CommandState = iota + 1
	CommandStateRequestAddressForTransactionDeclined
	CommandStateRequestAddressForTransactionAccepted
	CommandStateRequestTransaction
	CommandStateRequestTransactionDeclined
	CommandStateTransactionPending
	CommandStateTransactionSent
)

type CommandParameters struct {
	// ID is the ID of the initial message
	ID string `json:"id"`
	// From is the address we are sending the command from
	From string `json:"from"`
	// Address is the address sent with the command
	Address string `json:"address"`
	// Contract is the contract address for ERC20 tokens
	Contract string `json:"contract"`
	// Value is the value as a string sent
	Value string `json:"value"`
	// TransactionHash is the hash of the transaction
	TransactionHash string `json:"transactionHash"`
	// CommandState is the state of the command
	CommandState CommandState `json:"commandState"`
	// The Signature of the pk-bytes+transaction-hash from the wallet
	// address originating
	Signature []byte `json:"signature"`
}

func (c *CommandParameters) IsTokenTransfer() bool {
	return len(c.Contract) != 0
}

const (
	OutgoingStatusSending = "sending"
	OutgoingStatusSent    = "sent"
)

// Message represents a message record in the database,
// more specifically in user_messages table.
type Message struct {
	protobuf.ChatMessage

	// ID calculated as keccak256(compressedAuthorPubKey, data) where data is unencrypted payload.
	ID string `json:"id"`
	// WhisperTimestamp is a timestamp of a Whisper envelope.
	WhisperTimestamp uint64 `json:"whisperTimestamp"`
	// From is a public key of the author of the message.
	From string `json:"from"`
	// Random 3 words name
	Alias string `json:"alias"`
	// Identicon of the author
	Identicon string `json:"identicon"`
	// The chat id to be stored locally
	LocalChatID string `json:"localChatId"`

	Seen           bool   `json:"seen"`
	OutgoingStatus string `json:"outgoingStatus,omitempty"`

	QuotedMessage *QuotedMessage `json:"quotedMessage"`

	// CommandParameters is the parameters sent with the message
	CommandParameters *CommandParameters `json:"commandParameters"`

	// Computed fields
	// RTL is whether this is a right-to-left message (arabic/hebrew script etc)
	RTL bool `json:"rtl"`
	// ParsedText is the parsed markdown for displaying
	ParsedText []byte `json:"parsedText"`
	// LineCount is the count of newlines in the message
	LineCount int `json:"lineCount"`
	// Base64Image is the converted base64 image
	Base64Image string `json:"image,omitempty"`
	// ImagePath is the path of the image to be sent
	ImagePath string `json:"imagePath,omitempty"`
	// Base64Audio is the converted base64 audio
	Base64Audio string `json:"audio,omitempty"`
	// AudioPath is the path of the audio to be sent
	AudioPath string `json:"audioPath,omitempty"`

	// Replace indicates that this is a replacement of a message
	// that has been updated
	Replace   string           `json:"replace,omitempty"`
	SigPubKey *ecdsa.PublicKey `json:"-"`
}

func (m *Message) MarshalJSON() ([]byte, error) {
	type StickerAlias struct {
		Hash string `json:"hash"`
		Pack int32  `json:"pack"`
	}
	item := struct {
		ID                string                           `json:"id"`
		WhisperTimestamp  uint64                           `json:"whisperTimestamp"`
		From              string                           `json:"from"`
		Alias             string                           `json:"alias"`
		Identicon         string                           `json:"identicon"`
		Seen              bool                             `json:"seen"`
		OutgoingStatus    string                           `json:"outgoingStatus,omitempty"`
		QuotedMessage     *QuotedMessage                   `json:"quotedMessage"`
		RTL               bool                             `json:"rtl"`
		ParsedText        json.RawMessage                  `json:"parsedText"`
		LineCount         int                              `json:"lineCount"`
		Text              string                           `json:"text"`
		ChatID            string                           `json:"chatId"`
		LocalChatID       string                           `json:"localChatId"`
		Clock             uint64                           `json:"clock"`
		Replace           string                           `json:"replace"`
		ResponseTo        string                           `json:"responseTo"`
		EnsName           string                           `json:"ensName"`
		Image             string                           `json:"image,omitempty"`
		Audio             string                           `json:"audio,omitempty"`
		AudioDurationMs   uint64                           `json:"audioDurationMs,omitempty"`
		Sticker           *StickerAlias                    `json:"sticker"`
		CommandParameters *CommandParameters               `json:"commandParameters"`
		Timestamp         uint64                           `json:"timestamp"`
		ContentType       protobuf.ChatMessage_ContentType `json:"contentType"`
		MessageType       protobuf.ChatMessage_MessageType `json:"messageType"`
	}{
		ID:                m.ID,
		WhisperTimestamp:  m.WhisperTimestamp,
		From:              m.From,
		Alias:             m.Alias,
		Identicon:         m.Identicon,
		Seen:              m.Seen,
		OutgoingStatus:    m.OutgoingStatus,
		QuotedMessage:     m.QuotedMessage,
		RTL:               m.RTL,
		ParsedText:        m.ParsedText,
		LineCount:         m.LineCount,
		Text:              m.Text,
		Replace:           m.Replace,
		ChatID:            m.ChatId,
		LocalChatID:       m.LocalChatID,
		Clock:             m.Clock,
		ResponseTo:        m.ResponseTo,
		EnsName:           m.EnsName,
		Image:             m.Base64Image,
		Audio:             m.Base64Audio,
		Timestamp:         m.Timestamp,
		ContentType:       m.ContentType,
		MessageType:       m.MessageType,
		CommandParameters: m.CommandParameters,
	}

	if sticker := m.GetSticker(); sticker != nil {
		item.Sticker = &StickerAlias{
			Pack: sticker.Pack,
			Hash: sticker.Hash,
		}
	}

	if audio := m.GetAudio(); audio != nil {
		item.AudioDurationMs = audio.DurationMs
	}

	return json.Marshal(item)
}

func (m *Message) UnmarshalJSON(data []byte) error {
	type Alias Message
	aux := struct {
		*Alias
		ResponseTo      string                           `json:"responseTo"`
		EnsName         string                           `json:"ensName"`
		ChatID          string                           `json:"chatId"`
		Sticker         *protobuf.StickerMessage         `json:"sticker"`
		AudioDurationMs uint64                           `json:"audioDurationMs"`
		ContentType     protobuf.ChatMessage_ContentType `json:"contentType"`
	}{
		Alias: (*Alias)(m),
	}
	if err := json.Unmarshal(data, &aux); err != nil {
		return err
	}
	if aux.ContentType == protobuf.ChatMessage_STICKER {
		m.Payload = &protobuf.ChatMessage_Sticker{Sticker: aux.Sticker}
	}
	if aux.ContentType == protobuf.ChatMessage_AUDIO {
		m.Payload = &protobuf.ChatMessage_Audio{
			Audio: &protobuf.AudioMessage{DurationMs: aux.AudioDurationMs},
		}
	}
	m.ResponseTo = aux.ResponseTo
	m.EnsName = aux.EnsName
	m.ChatId = aux.ChatID
	m.ContentType = aux.ContentType
	return nil
}

// Check if the first character is Hebrew or Arabic or the RTL character
func isRTL(s string) bool {
	first, _ := utf8.DecodeRuneInString(s)
	return unicode.Is(unicode.Hebrew, first) ||
		unicode.Is(unicode.Arabic, first) ||
		// RTL character
		first == '\u200f'
}

// parseImage check the message contains an image, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseImage() error {
	if m.ContentType != protobuf.ChatMessage_IMAGE {
		return nil
	}
	image := m.GetImage()
	if image == nil {
		return errors.New("image empty")
	}

	payload := image.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getImageMessageMIME(image)

	if err != nil {
		return err
	}

	m.Base64Image = fmt.Sprintf("data:image/%s;base64,%s", mime, encBuf)

	return nil
}

// parseAudio check the message contains an audio, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseAudio() error {
	if m.ContentType != protobuf.ChatMessage_AUDIO {
		return nil
	}
	audio := m.GetAudio()
	if audio == nil {
		return errors.New("audio empty")
	}

	payload := audio.Payload

	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(payload))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, payload)

	mime, err := getAudioMessageMIME(audio)

	if err != nil {
		return err
	}

	m.Base64Audio = fmt.Sprintf("data:audio/%s;base64,%s", mime, encBuf)

	return nil
}

// PrepareContent return the parsed content of the message, the line-count and whether
// is a right-to-left message
func (m *Message) PrepareContent() error {
	parsedText := markdown.Parse([]byte(m.Text), nil)
	jsonParsedText, err := json.Marshal(parsedText)
	if err != nil {
		return err
	}
	m.ParsedText = jsonParsedText
	m.LineCount = strings.Count(m.Text, "\n")
	m.RTL = isRTL(m.Text)
	if err := m.parseImage(); err != nil {
		return err
	}
	return m.parseAudio()
}

func getImageMessageMIME(i *protobuf.ImageMessage) (string, error) {
	switch i.Type {
	case protobuf.ImageMessage_PNG:
		return "png", nil
	case protobuf.ImageMessage_JPEG:
		return "jpeg", nil
	case protobuf.ImageMessage_WEBP:
		return "webp", nil
	case protobuf.ImageMessage_GIF:
		return "gif", nil
	}
	return "", errors.New("image format not supported")
}

func getAudioMessageMIME(i *protobuf.AudioMessage) (string, error) {
	switch i.Type {
	case protobuf.AudioMessage_AAC:
		return "aac", nil
	case protobuf.AudioMessage_AMR:
		return "amr", nil
	}

	return "", errors.New("audio format not supported")
}
<|endoftext|>


<commit_before>package providers

import (
	"encoding/json"
	"fmt"
	"github.com/golang/oauth2"
	"net/http"
	"os"
)

type GithubProvider struct {
	GenericProvider
}

type githubUser struct {
	Id    int    `json:"id"`
	Login string `json:"login"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

/*
Github() creates a github oauth client.

The client ID and client secret are taken from the environment variables GITHUB_CLIENT_ID &
GITHUB_CLIENT_SECRET
*/
func Github(baseUrl string) Provider {
	options := &oauth2.Options{
		ClientID:     os.Getenv("GITHUB_CLIENT_ID"),
		ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
		RedirectURL:  baseUrl + "callback/",
		Scopes:       []string{"user"},
	}
	config, _ := oauth2.NewConfig(options, "https://github.com/login/oauth/authorize", "https://github.com/login/oauth/access_token")

	return &GithubProvider{
		GenericProvider: GenericProvider{
			Name:   "github",
			Config: config,
		},
	}
}

func (p *GithubProvider) GetUserInfo(t *oauth2.Transport) (map[string]interface{}, error) {
	req, err := http.NewRequest("GET", "https://api.github.com/user", nil)
	req.Header.Add("Accept", "application/vnd.github.v3+json")
	resp, err := t.RoundTrip(req)

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("github return error.  %d %s", resp.StatusCode, resp.Status)
	}

	// fields we care about include id, login, name, email
	dec := json.NewDecoder(resp.Body)
	var user githubUser
	err = dec.Decode(&user)

	if err != nil {
		return nil, err
	}

	result := map[string]interface{}{
		PROVIDER_EMAIL:    user.Email,
		PROVIDER_USERNAME: user.Login,
		PROVIDER_ID:       user.Id,
		PROVIDER_NAME:     user.Name,
	}
	return result, nil
}
<commit_msg>Limit scope for github<commit_after>package providers

import (
	"encoding/json"
	"fmt"
	"github.com/golang/oauth2"
	"net/http"
	"os"
)

type GithubProvider struct {
	GenericProvider
}

type githubUser struct {
	Id    int    `json:"id"`
	Login string `json:"login"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

/*
Github() creates a github oauth client.

The client ID and client secret are taken from the environment variables GITHUB_CLIENT_ID &
GITHUB_CLIENT_SECRET
*/
func Github(baseUrl string) Provider {
	options := &oauth2.Options{
		ClientID:     os.Getenv("GITHUB_CLIENT_ID"),
		ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
		RedirectURL:  baseUrl + "callback/",
		Scopes:       []string{"user:email"},
	}
	config, _ := oauth2.NewConfig(options, "https://github.com/login/oauth/authorize", "https://github.com/login/oauth/access_token")

	return &GithubProvider{
		GenericProvider: GenericProvider{
			Name:   "github",
			Config: config,
		},
	}
}

func (p *GithubProvider) GetUserInfo(t *oauth2.Transport) (map[string]interface{}, error) {
	req, err := http.NewRequest("GET", "https://api.github.com/user", nil)
	req.Header.Add("Accept", "application/vnd.github.v3+json")
	resp, err := t.RoundTrip(req)

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("github return error.  %d %s", resp.StatusCode, resp.Status)
	}

	// fields we care about include id, login, name, email
	dec := json.NewDecoder(resp.Body)
	var user githubUser
	err = dec.Decode(&user)

	if err != nil {
		return nil, err
	}

	result := map[string]interface{}{
		PROVIDER_EMAIL:    user.Email,
		PROVIDER_USERNAME: user.Login,
		PROVIDER_ID:       user.Id,
		PROVIDER_NAME:     user.Name,
	}
	return result, nil
}
<|endoftext|>


<commit_before>package collectors

import (
	"fmt"
	"strings"
	"time"

	"bosun.org/cmd/scollector/conf"
	"bosun.org/metadata"
	"bosun.org/opentsdb"
)

const (
	ifAlias              = ".1.3.6.1.2.1.31.1.1.1.18"
	ifDescr              = ".1.3.6.1.2.1.2.2.1.2"
	ifHCInBroadcastPkts  = ".1.3.6.1.2.1.31.1.1.1.9"
	ifHCInMulticastPkts  = ".1.3.6.1.2.1.31.1.1.1.8"
	ifHCInUcastPkts      = ".1.3.6.1.2.1.31.1.1.1.7"
	ifHCOutBroadcastPkts = ".1.3.6.1.2.1.31.1.1.1.13"
	ifHCOutMulticastPkts = ".1.3.6.1.2.1.31.1.1.1.12"
	ifHCOutOctets        = ".1.3.6.1.2.1.31.1.1.1.10"
	ifHCOutUcastPkts     = ".1.3.6.1.2.1.31.1.1.1.11"
	ifHCinOctets         = ".1.3.6.1.2.1.31.1.1.1.6"
	ifInDiscards         = ".1.3.6.1.2.1.2.2.1.13"
	ifInErrors           = ".1.3.6.1.2.1.2.2.1.14"
	ifInPauseFrames      = ".1.3.6.1.2.1.10.7.10.1.3"
	ifName               = ".1.3.6.1.2.1.31.1.1.1.1"
	ifOutDiscards        = ".1.3.6.1.2.1.2.2.1.19"
	ifOutErrors          = ".1.3.6.1.2.1.2.2.1.20"
	ifOutPauseFrames     = ".1.3.6.1.2.1.10.7.10.1.4"
)

// SNMPIfaces registers a SNMP Interfaces collector for the given community and host.
func SNMPIfaces(cfg conf.SNMP) {
	collectors = append(collectors, &IntervalCollector{
		F: func() (opentsdb.MultiDataPoint, error) {
			return c_snmp_ifaces(cfg.Community, cfg.Host)
		},
		Interval: time.Second * 30,
		name:     fmt.Sprintf("snmp-ifaces-%s", cfg.Host),
	})
}

func switch_bond(metric, iname string) string {
	if strings.Contains(iname, "port-channel") {
		return "os.net.bond" + strings.TrimPrefix(metric, "os.net")
	}
	return metric
}

func c_snmp_ifaces(community, host string) (opentsdb.MultiDataPoint, error) {
	n, err := snmp_subtree(host, community, ifName)
	if err != nil || len(n) == 0 {
		n, err = snmp_subtree(host, community, ifDescr)
		if err != nil {
			return nil, err
		}
	}
	a, err := snmp_subtree(host, community, ifAlias)
	if err != nil {
		return nil, err
	}
	names := make(map[interface{}]string, len(n))
	aliases := make(map[interface{}]string, len(a))
	for k, v := range n {
		names[k] = fmt.Sprintf("%s", v)
	}
	for k, v := range a {
		// In case clean would come up empty, prevent the point from being removed
		// by setting our own empty case.
		aliases[k], _ = opentsdb.Clean(fmt.Sprintf("%s", v))
		if aliases[k] == "" {
			aliases[k] = "NA"
		}
	}
	var md opentsdb.MultiDataPoint
	add := func(oid, metric, dir string) error {
		m, err := snmp_subtree(host, community, oid)
		if err != nil {
			return err
		}
		for k, v := range m {
			tags := opentsdb.TagSet{
				"host":      host,
				"direction": dir,
				"iface":     fmt.Sprintf("%d", k),
				"iname":     names[k],
			}
			Add(&md, switch_bond(metric, names[k]), v, tags, metadata.Unknown, metadata.None, "")
			metadata.AddMeta("", tags, "alias", aliases[k], false)
		}
		return nil
	}
	oids := []snmpAdd{
		{ifHCInBroadcastPkts, osNetBroadcast, "in"},
		{ifHCInMulticastPkts, osNetMulticast, "in"},
		{ifHCInUcastPkts, osNetUnicast, "in"},
		{ifHCOutBroadcastPkts, osNetBroadcast, "out"},
		{ifHCOutMulticastPkts, osNetMulticast, "out"},
		{ifHCOutOctets, osNetBytes, "out"},
		{ifHCOutUcastPkts, osNetUnicast, "out"},
		{ifHCinOctets, osNetBytes, "in"},
		{ifInDiscards, osNetDropped, "in"},
		{ifInErrors, osNetErrors, "in"},
		{ifOutDiscards, osNetDropped, "out"},
		{ifOutErrors, osNetErrors, "out"},
		{ifInPauseFrames, osNetPauseFrames, "in"},
		{ifOutPauseFrames, osNetPauseFrames, "out"},
	}
	for _, o := range oids {
		if err := add(o.oid, o.metric, o.dir); err != nil {
			return nil, err
		}
	}
	return md, nil
}

type snmpAdd struct {
	oid    string
	metric string
	dir    string
}
<commit_msg>Scollector: calculate os.net.bytes.total metric.<commit_after>package collectors

import (
	"fmt"
	"strings"
	"time"

	"bosun.org/cmd/scollector/conf"
	"bosun.org/metadata"
	"bosun.org/opentsdb"
)

const (
	ifAlias              = ".1.3.6.1.2.1.31.1.1.1.18"
	ifDescr              = ".1.3.6.1.2.1.2.2.1.2"
	ifHCInBroadcastPkts  = ".1.3.6.1.2.1.31.1.1.1.9"
	ifHCInMulticastPkts  = ".1.3.6.1.2.1.31.1.1.1.8"
	ifHCInUcastPkts      = ".1.3.6.1.2.1.31.1.1.1.7"
	ifHCOutBroadcastPkts = ".1.3.6.1.2.1.31.1.1.1.13"
	ifHCOutMulticastPkts = ".1.3.6.1.2.1.31.1.1.1.12"
	ifHCOutOctets        = ".1.3.6.1.2.1.31.1.1.1.10"
	ifHCOutUcastPkts     = ".1.3.6.1.2.1.31.1.1.1.11"
	ifHCinOctets         = ".1.3.6.1.2.1.31.1.1.1.6"
	ifInDiscards         = ".1.3.6.1.2.1.2.2.1.13"
	ifInErrors           = ".1.3.6.1.2.1.2.2.1.14"
	ifInPauseFrames      = ".1.3.6.1.2.1.10.7.10.1.3"
	ifName               = ".1.3.6.1.2.1.31.1.1.1.1"
	ifOutDiscards        = ".1.3.6.1.2.1.2.2.1.19"
	ifOutErrors          = ".1.3.6.1.2.1.2.2.1.20"
	ifOutPauseFrames     = ".1.3.6.1.2.1.10.7.10.1.4"
)

// SNMPIfaces registers a SNMP Interfaces collector for the given community and host.
func SNMPIfaces(cfg conf.SNMP) {
	collectors = append(collectors, &IntervalCollector{
		F: func() (opentsdb.MultiDataPoint, error) {
			return c_snmp_ifaces(cfg.Community, cfg.Host)
		},
		Interval: time.Second * 30,
		name:     fmt.Sprintf("snmp-ifaces-%s", cfg.Host),
	})
}

func switch_bond(metric, iname string) string {
	if isBondInterface(iname) {
		return "os.net.bond" + strings.TrimPrefix(metric, "os.net")
	}
	return metric
}

func isBondInterface(iname string) bool {
	return strings.Contains(iname, "port-channel")
}

func c_snmp_ifaces(community, host string) (opentsdb.MultiDataPoint, error) {
	n, err := snmp_subtree(host, community, ifName)
	if err != nil || len(n) == 0 {
		n, err = snmp_subtree(host, community, ifDescr)
		if err != nil {
			return nil, err
		}
	}
	a, err := snmp_subtree(host, community, ifAlias)
	if err != nil {
		return nil, err
	}
	names := make(map[interface{}]string, len(n))
	aliases := make(map[interface{}]string, len(a))
	for k, v := range n {
		names[k] = fmt.Sprintf("%s", v)
	}
	for k, v := range a {
		// In case clean would come up empty, prevent the point from being removed
		// by setting our own empty case.
		aliases[k], _ = opentsdb.Clean(fmt.Sprintf("%s", v))
		if aliases[k] == "" {
			aliases[k] = "NA"
		}
	}
	var md opentsdb.MultiDataPoint
	add := func(oid, metric, dir string) error {
		m, err := snmp_subtree(host, community, oid)
		if err != nil {
			return err
		}
		var sum int64
		for k, v := range m {
			tags := opentsdb.TagSet{
				"host":      host,
				"direction": dir,
				"iface":     fmt.Sprintf("%d", k),
				"iname":     names[k],
			}
			if iVal, ok := v.(int64); ok && !isBondInterface(names[k]) {
				sum += iVal
			}
			Add(&md, switch_bond(metric, names[k]), v, tags, metadata.Unknown, metadata.None, "")
			metadata.AddMeta("", tags, "alias", aliases[k], false)
		}
		if metric == osNetBytes {
			tags := opentsdb.TagSet{"host": host, "direction": dir}
			Add(&md, osNetBytes+".total", sum, tags, metadata.Counter, metadata.Bytes, "")
		}
		return nil
	}
	oids := []snmpAdd{
		{ifHCInBroadcastPkts, osNetBroadcast, "in"},
		{ifHCInMulticastPkts, osNetMulticast, "in"},
		{ifHCInUcastPkts, osNetUnicast, "in"},
		{ifHCOutBroadcastPkts, osNetBroadcast, "out"},
		{ifHCOutMulticastPkts, osNetMulticast, "out"},
		{ifHCOutOctets, osNetBytes, "out"},
		{ifHCOutUcastPkts, osNetUnicast, "out"},
		{ifHCinOctets, osNetBytes, "in"},
		{ifInDiscards, osNetDropped, "in"},
		{ifInErrors, osNetErrors, "in"},
		{ifOutDiscards, osNetDropped, "out"},
		{ifOutErrors, osNetErrors, "out"},
		{ifInPauseFrames, osNetPauseFrames, "in"},
		{ifOutPauseFrames, osNetPauseFrames, "out"},
	}
	for _, o := range oids {
		if err := add(o.oid, o.metric, o.dir); err != nil {
			return nil, err
		}
	}
	return md, nil
}

type snmpAdd struct {
	oid    string
	metric string
	dir    string
}
<|endoftext|>