<commit_before>package proxy

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/url"

	kubeutils "github.com/gravitational/teleport/lib/kube/utils"
	"github.com/gravitational/trace"

	"github.com/sirupsen/logrus"
	authzapi "k8s.io/api/authorization/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	authztypes "k8s.io/client-go/kubernetes/typed/authorization/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/transport"

	// Load kubeconfig auth plugins for gcp and azure.
	// Without this, users can't provide a kubeconfig using those.
	//
	// Note: we don't want to load _all_ plugins. This is a balance between
	// support for popular hosting providers and minimizing attack surface.
	_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

// kubeCreds contain authentication-related fields from kubeconfig.
//
// TODO(awly): make this an interface, one implementation for local k8s cluster
// and another for a remote teleport cluster.
type kubeCreds struct {
	// tlsConfig contains (m)TLS configuration.
	tlsConfig *tls.Config
	// transportConfig contains HTTPS-related configuration.
	// Note: use wrapTransport method if working with http.RoundTrippers.
	transportConfig *transport.Config
	// targetAddr is a kubernetes API address.
	targetAddr string
	kubeClient *kubernetes.Clientset
}

var skipSelfPermissionCheck bool

// TestOnlySkipSelfPermissionCheck sets whether or not to skip checking k8s
// impersonation permissions granted to this instance.
//
// Used in CI integration tests, where we intentionally scope down permissions
// from what a normal prod instance should have.
func TestOnlySkipSelfPermissionCheck(skip bool) {
	skipSelfPermissionCheck = skip
}

// getKubeCreds fetches the kubernetes API credentials.
//
// There are 2 possible sources of credentials:
// - pod service account credentials: files in hardcoded paths when running
//   inside of a k8s pod; this is used when kubeClusterName is set
// - kubeconfig: a file with a set of k8s endpoints and credentials mapped to
//   them this is used when kubeconfigPath is set
//
// serviceType changes the loading behavior:
// - LegacyProxyService:
//   - if loading from kubeconfig, only "current-context" is returned; the
//     returned map key matches tpClusterName
//   - if no credentials are loaded, no error is returned
//   - permission self-test failures are only logged
// - ProxyService:
//   - no credentials are loaded and no error is returned
// - KubeService:
//   - if loading from kubeconfig, all contexts are returned
//   - if no credentials are loaded, returns an error
//   - permission self-test failures cause an error to be returned
func getKubeCreds(ctx context.Context, log logrus.FieldLogger, tpClusterName, kubeClusterName, kubeconfigPath string, serviceType KubeServiceType) (map[string]*kubeCreds, error) {
	log.
		WithField("kubeconfigPath", kubeconfigPath).
		WithField("kubeClusterName", kubeClusterName).
		WithField("serviceType", serviceType).
		Debug("Reading Kubernetes creds.")

	// Proxy service should never have creds, forwards to kube service
	if serviceType == ProxyService {
		return map[string]*kubeCreds{}, nil
	}

	// Load kubeconfig or local pod credentials.
	loadAll := serviceType == KubeService
	cfg, err := kubeutils.GetKubeConfig(kubeconfigPath, loadAll, kubeClusterName)
	if err != nil && !trace.IsNotFound(err) {
		return nil, trace.Wrap(err)
	}

	if trace.IsNotFound(err) || len(cfg.Contexts) == 0 {
		switch serviceType {
		case KubeService:
			return nil, trace.BadParameter("no Kubernetes credentials found; Kubernetes_service requires either a valid kubeconfig_path or to run inside of a Kubernetes pod")
		case LegacyProxyService:
			log.Debugf("Could not load Kubernetes credentials. This proxy will still handle Kubernetes requests for trusted teleport clusters or Kubernetes nodes in this teleport cluster")
		}
		return map[string]*kubeCreds{}, nil
	}

	if serviceType == LegacyProxyService {
		// Hack for legacy proxy service - register a k8s cluster named after
		// the teleport cluster name to route legacy requests.
		//
		// Also, remove all other contexts. Multiple kubeconfig entries are
		// only supported for kubernetes_service.
		if currentContext, ok := cfg.Contexts[cfg.CurrentContext]; ok {
			cfg.Contexts = map[string]*rest.Config{
				tpClusterName: currentContext,
			}
		} else {
			return nil, trace.BadParameter("no Kubernetes current-context found; Kubernetes proxy service requires either a valid kubeconfig_path with a current-context or to run inside of a Kubernetes pod")
		}
	}

	res := make(map[string]*kubeCreds, len(cfg.Contexts))
	// Convert kubeconfig contexts into kubeCreds.
	for cluster, clientCfg := range cfg.Contexts {
		log := log.WithField("cluster", cluster)
		log.Debug("Checking Kubernetes impersonation permissions.")
		client, err := kubernetes.NewForConfig(clientCfg)
		if err != nil {
			return nil, trace.Wrap(err, "failed to generate Kubernetes client for cluster %q", cluster)
		}
		// For each loaded cluster, check impersonation permissions. This
		// failure is only critical for newKubeService.
		if err := checkImpersonationPermissions(ctx, client.AuthorizationV1().SelfSubjectAccessReviews()); err != nil {
			// kubernetes_service must have valid RBAC permissions, otherwise
			// it's pointless.
			// proxy_service can run without them (e.g. a root proxy).
			if serviceType == KubeService {
				return nil, trace.Wrap(err)
			}
			log.WithError(err).Warning("Failed to test the necessary Kubernetes permissions. This teleport instance will still handle Kubernetes requests towards other Kubernetes clusters")
			// We used to recommend users to set a dummy kubeconfig on root
			// proxies to get kubernetes support working for leaf clusters:
			// https://community.goteleport.com/t/enabling-teleport-to-act-as-a-kubernetes-proxy-for-trusted-leaf-clusters/418
			//
			// Since this is no longer necessary, recommend them to clean up
			// via logs.
			if kubeconfigPath != "" {
				log.Info("If this is a proxy and you provided a dummy kubeconfig_path, you can remove it from teleport.yaml to get rid of this warning")
			}
		} else {
			log.Debug("Have all necessary Kubernetes impersonation permissions.")
		}

		targetAddr, err := parseKubeHost(clientCfg.Host)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		tlsConfig, err := rest.TLSConfigFor(clientCfg)
		if err != nil {
			return nil, trace.Wrap(err, "failed to generate TLS config from kubeconfig: %v", err)
		}
		transportConfig, err := clientCfg.TransportConfig()
		if err != nil {
			return nil, trace.Wrap(err, "failed to generate transport config from kubeconfig: %v", err)
		}

		log.Debug("Initialized Kubernetes credentials")
		res[cluster] = &kubeCreds{
			tlsConfig:       tlsConfig,
			transportConfig: transportConfig,
			targetAddr:      targetAddr,
			kubeClient:      client,
		}
	}
	return res, nil
}

// parseKubeHost parses and formats kubernetes hostname
// to host:port format, if no port it set,
// it assumes default HTTPS port
func parseKubeHost(host string) (string, error) {
	u, err := url.Parse(host)
	if err != nil {
		return "", trace.Wrap(err, "failed to parse Kubernetes host: %v", err)
	}
	if _, _, err := net.SplitHostPort(u.Host); err != nil {
		// add default HTTPS port
		return fmt.Sprintf("%v:443", u.Host), nil
	}
	return u.Host, nil
}

func (c *kubeCreds) wrapTransport(rt http.RoundTripper) (http.RoundTripper, error) {
	if c == nil {
		return rt, nil
	}
	return transport.HTTPWrappersForConfig(c.transportConfig, rt)
}

func checkImpersonationPermissions(ctx context.Context, sarClient authztypes.SelfSubjectAccessReviewInterface) error {
	if skipSelfPermissionCheck {
		return nil
	}

	for _, resource := range []string{"users", "groups", "serviceaccounts"} {
		resp, err := sarClient.Create(ctx, &authzapi.SelfSubjectAccessReview{
			Spec: authzapi.SelfSubjectAccessReviewSpec{
				ResourceAttributes: &authzapi.ResourceAttributes{
					Verb:     "impersonate",
					Resource: resource,
				},
			},
		}, metav1.CreateOptions{})
		if err != nil {
			return trace.Wrap(err, "failed to verify impersonation permissions for Kubernetes: %v; this may be due to missing the SelfSubjectAccessReview permission on the ClusterRole used by the proxy; please make sure that proxy has all the necessary permissions: https://goteleport.com/teleport/docs/kubernetes-ssh/#impersonation", err)
		}
		if !resp.Status.Allowed {
			return trace.AccessDenied("proxy can't impersonate Kubernetes %s at the cluster level; please make sure that proxy has all the necessary permissions: https://goteleport.com/teleport/docs/kubernetes-ssh/#impersonation", resource)
		}
	}
	return nil
}
<commit_msg>config: Change mentions of kubeconfig_path -> kubeconfig_file (#7646)<commit_after>package proxy

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/url"

	kubeutils "github.com/gravitational/teleport/lib/kube/utils"
	"github.com/gravitational/trace"

	"github.com/sirupsen/logrus"
	authzapi "k8s.io/api/authorization/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	authztypes "k8s.io/client-go/kubernetes/typed/authorization/v1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/transport"

	// Load kubeconfig auth plugins for gcp and azure.
	// Without this, users can't provide a kubeconfig using those.
	//
	// Note: we don't want to load _all_ plugins. This is a balance between
	// support for popular hosting providers and minimizing attack surface.
	_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

// kubeCreds contain authentication-related fields from kubeconfig.
//
// TODO(awly): make this an interface, one implementation for local k8s cluster
// and another for a remote teleport cluster.
type kubeCreds struct {
	// tlsConfig contains (m)TLS configuration.
	tlsConfig *tls.Config
	// transportConfig contains HTTPS-related configuration.
	// Note: use wrapTransport method if working with http.RoundTrippers.
	transportConfig *transport.Config
	// targetAddr is a kubernetes API address.
	targetAddr string
	kubeClient *kubernetes.Clientset
}

var skipSelfPermissionCheck bool

// TestOnlySkipSelfPermissionCheck sets whether or not to skip checking k8s
// impersonation permissions granted to this instance.
//
// Used in CI integration tests, where we intentionally scope down permissions
// from what a normal prod instance should have.
func TestOnlySkipSelfPermissionCheck(skip bool) {
	skipSelfPermissionCheck = skip
}

// getKubeCreds fetches the kubernetes API credentials.
//
// There are 2 possible sources of credentials:
// - pod service account credentials: files in hardcoded paths when running
//   inside of a k8s pod; this is used when kubeClusterName is set
// - kubeconfig: a file with a set of k8s endpoints and credentials mapped to
//   them this is used when kubeconfigPath is set
//
// serviceType changes the loading behavior:
// - LegacyProxyService:
//   - if loading from kubeconfig, only "current-context" is returned; the
//     returned map key matches tpClusterName
//   - if no credentials are loaded, no error is returned
//   - permission self-test failures are only logged
// - ProxyService:
//   - no credentials are loaded and no error is returned
// - KubeService:
//   - if loading from kubeconfig, all contexts are returned
//   - if no credentials are loaded, returns an error
//   - permission self-test failures cause an error to be returned
func getKubeCreds(ctx context.Context, log logrus.FieldLogger, tpClusterName, kubeClusterName, kubeconfigPath string, serviceType KubeServiceType) (map[string]*kubeCreds, error) {
	log.
		WithField("kubeconfigPath", kubeconfigPath).
		WithField("kubeClusterName", kubeClusterName).
		WithField("serviceType", serviceType).
		Debug("Reading Kubernetes creds.")

	// Proxy service should never have creds, forwards to kube service
	if serviceType == ProxyService {
		return map[string]*kubeCreds{}, nil
	}

	// Load kubeconfig or local pod credentials.
	loadAll := serviceType == KubeService
	cfg, err := kubeutils.GetKubeConfig(kubeconfigPath, loadAll, kubeClusterName)
	if err != nil && !trace.IsNotFound(err) {
		return nil, trace.Wrap(err)
	}

	if trace.IsNotFound(err) || len(cfg.Contexts) == 0 {
		switch serviceType {
		case KubeService:
			return nil, trace.BadParameter("no Kubernetes credentials found; Kubernetes_service requires either a valid kubeconfig_file or to run inside of a Kubernetes pod")
		case LegacyProxyService:
			log.Debugf("Could not load Kubernetes credentials. This proxy will still handle Kubernetes requests for trusted teleport clusters or Kubernetes nodes in this teleport cluster")
		}
		return map[string]*kubeCreds{}, nil
	}

	if serviceType == LegacyProxyService {
		// Hack for legacy proxy service - register a k8s cluster named after
		// the teleport cluster name to route legacy requests.
		//
		// Also, remove all other contexts. Multiple kubeconfig entries are
		// only supported for kubernetes_service.
		if currentContext, ok := cfg.Contexts[cfg.CurrentContext]; ok {
			cfg.Contexts = map[string]*rest.Config{
				tpClusterName: currentContext,
			}
		} else {
			return nil, trace.BadParameter("no Kubernetes current-context found; Kubernetes proxy service requires either a valid kubeconfig_file with a current-context or to run inside of a Kubernetes pod")
		}
	}

	res := make(map[string]*kubeCreds, len(cfg.Contexts))
	// Convert kubeconfig contexts into kubeCreds.
	for cluster, clientCfg := range cfg.Contexts {
		log := log.WithField("cluster", cluster)
		log.Debug("Checking Kubernetes impersonation permissions.")
		client, err := kubernetes.NewForConfig(clientCfg)
		if err != nil {
			return nil, trace.Wrap(err, "failed to generate Kubernetes client for cluster %q", cluster)
		}
		// For each loaded cluster, check impersonation permissions. This
		// failure is only critical for newKubeService.
		if err := checkImpersonationPermissions(ctx, client.AuthorizationV1().SelfSubjectAccessReviews()); err != nil {
			// kubernetes_service must have valid RBAC permissions, otherwise
			// it's pointless.
			// proxy_service can run without them (e.g. a root proxy).
			if serviceType == KubeService {
				return nil, trace.Wrap(err)
			}
			log.WithError(err).Warning("Failed to test the necessary Kubernetes permissions. This teleport instance will still handle Kubernetes requests towards other Kubernetes clusters")
			// We used to recommend users to set a dummy kubeconfig on root
			// proxies to get kubernetes support working for leaf clusters:
			// https://community.goteleport.com/t/enabling-teleport-to-act-as-a-kubernetes-proxy-for-trusted-leaf-clusters/418
			//
			// Since this is no longer necessary, recommend them to clean up
			// via logs.
			if kubeconfigPath != "" {
				log.Info("If this is a proxy and you provided a dummy kubeconfig_file, you can remove it from teleport.yaml to get rid of this warning")
			}
		} else {
			log.Debug("Have all necessary Kubernetes impersonation permissions.")
		}

		targetAddr, err := parseKubeHost(clientCfg.Host)
		if err != nil {
			return nil, trace.Wrap(err)
		}
		tlsConfig, err := rest.TLSConfigFor(clientCfg)
		if err != nil {
			return nil, trace.Wrap(err, "failed to generate TLS config from kubeconfig: %v", err)
		}
		transportConfig, err := clientCfg.TransportConfig()
		if err != nil {
			return nil, trace.Wrap(err, "failed to generate transport config from kubeconfig: %v", err)
		}

		log.Debug("Initialized Kubernetes credentials")
		res[cluster] = &kubeCreds{
			tlsConfig:       tlsConfig,
			transportConfig: transportConfig,
			targetAddr:      targetAddr,
			kubeClient:      client,
		}
	}
	return res, nil
}

// parseKubeHost parses and formats kubernetes hostname
// to host:port format, if no port it set,
// it assumes default HTTPS port
func parseKubeHost(host string) (string, error) {
	u, err := url.Parse(host)
	if err != nil {
		return "", trace.Wrap(err, "failed to parse Kubernetes host: %v", err)
	}
	if _, _, err := net.SplitHostPort(u.Host); err != nil {
		// add default HTTPS port
		return fmt.Sprintf("%v:443", u.Host), nil
	}
	return u.Host, nil
}

func (c *kubeCreds) wrapTransport(rt http.RoundTripper) (http.RoundTripper, error) {
	if c == nil {
		return rt, nil
	}
	return transport.HTTPWrappersForConfig(c.transportConfig, rt)
}

func checkImpersonationPermissions(ctx context.Context, sarClient authztypes.SelfSubjectAccessReviewInterface) error {
	if skipSelfPermissionCheck {
		return nil
	}

	for _, resource := range []string{"users", "groups", "serviceaccounts"} {
		resp, err := sarClient.Create(ctx, &authzapi.SelfSubjectAccessReview{
			Spec: authzapi.SelfSubjectAccessReviewSpec{
				ResourceAttributes: &authzapi.ResourceAttributes{
					Verb:     "impersonate",
					Resource: resource,
				},
			},
		}, metav1.CreateOptions{})
		if err != nil {
			return trace.Wrap(err, "failed to verify impersonation permissions for Kubernetes: %v; this may be due to missing the SelfSubjectAccessReview permission on the ClusterRole used by the proxy; please make sure that proxy has all the necessary permissions: https://goteleport.com/teleport/docs/kubernetes-ssh/#impersonation", err)
		}
		if !resp.Status.Allowed {
			return trace.AccessDenied("proxy can't impersonate Kubernetes %s at the cluster level; please make sure that proxy has all the necessary permissions: https://goteleport.com/teleport/docs/kubernetes-ssh/#impersonation", resource)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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 session

import (
	"sync"
	"time"

	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/soap"
	"golang.org/x/net/context"
)

type keepAlive struct {
	sync.Mutex

	roundTripper    soap.RoundTripper
	idleTime        time.Duration
	notifyRequest   chan struct{}
	notifyStop      chan struct{}
	notifyWaitGroup sync.WaitGroup

	// keepAlive executes a request in the background with the purpose of
	// keeping the session active. The response for this request is discarded.
	keepAlive func(soap.RoundTripper)
}

func defaultKeepAlive(roundTripper soap.RoundTripper) {
	methods.GetServiceContent(context.Background(), roundTripper)
}

// KeepAlive wraps the specified soap.RoundTripper and executes a meaningless
// API request in the background after the RoundTripper has been idle for the
// specified amount of idle time. The keep alive process only starts once a
// user logs in and runs until the user logs out again.
func KeepAlive(roundTripper soap.RoundTripper, idleTime time.Duration) soap.RoundTripper {
	k := &keepAlive{
		roundTripper:  roundTripper,
		idleTime:      idleTime,
		notifyRequest: make(chan struct{}),
	}

	k.keepAlive = defaultKeepAlive

	return k
}

func (k *keepAlive) start() {
	k.Lock()
	defer k.Unlock()

	if k.notifyStop != nil {
		return
	}

	// This channel must be closed to terminate idle timer.
	k.notifyStop = make(chan struct{})
	k.notifyWaitGroup.Add(1)

	go func() {
		defer k.notifyWaitGroup.Done()

		for t := time.NewTimer(k.idleTime); ; {
			select {
			case <-k.notifyStop:
				return
			case <-k.notifyRequest:
				t.Reset(k.idleTime)
			case <-t.C:
				k.keepAlive(k.roundTripper)
				t = time.NewTimer(k.idleTime)
			}
		}
	}()
}

func (k *keepAlive) stop() {
	k.Lock()
	defer k.Unlock()

	if k.notifyStop != nil {
		close(k.notifyStop)
		k.notifyWaitGroup.Wait()
		k.notifyStop = nil
	}
}

func (k *keepAlive) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
	err := k.roundTripper.RoundTrip(ctx, req, res)
	if err != nil {
		return err
	}

	// Start ticker on login, stop ticker on logout.
	switch req.(type) {
	case *methods.LoginBody:
	case *methods.LoginExtensionByCertificateBody:
		k.start()
	case *methods.LogoutBody:
		k.stop()
	}

	return nil
}
<commit_msg>Fix KeepAlive<commit_after>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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 session

import (
	"sync"
	"time"

	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/soap"
	"golang.org/x/net/context"
)

type keepAlive struct {
	sync.Mutex

	roundTripper    soap.RoundTripper
	idleTime        time.Duration
	notifyRequest   chan struct{}
	notifyStop      chan struct{}
	notifyWaitGroup sync.WaitGroup

	// keepAlive executes a request in the background with the purpose of
	// keeping the session active. The response for this request is discarded.
	keepAlive func(soap.RoundTripper)
}

func defaultKeepAlive(roundTripper soap.RoundTripper) {
	methods.GetServiceContent(context.Background(), roundTripper)
}

// KeepAlive wraps the specified soap.RoundTripper and executes a meaningless
// API request in the background after the RoundTripper has been idle for the
// specified amount of idle time. The keep alive process only starts once a
// user logs in and runs until the user logs out again.
func KeepAlive(roundTripper soap.RoundTripper, idleTime time.Duration) soap.RoundTripper {
	k := &keepAlive{
		roundTripper:  roundTripper,
		idleTime:      idleTime,
		notifyRequest: make(chan struct{}),
	}

	k.keepAlive = defaultKeepAlive

	return k
}

func (k *keepAlive) start() {
	k.Lock()
	defer k.Unlock()

	if k.notifyStop != nil {
		return
	}

	// This channel must be closed to terminate idle timer.
	k.notifyStop = make(chan struct{})
	k.notifyWaitGroup.Add(1)

	go func() {
		defer k.notifyWaitGroup.Done()

		for t := time.NewTimer(k.idleTime); ; {
			select {
			case <-k.notifyStop:
				return
			case <-k.notifyRequest:
				t.Reset(k.idleTime)
			case <-t.C:
				k.keepAlive(k.roundTripper)
				t = time.NewTimer(k.idleTime)
			}
		}
	}()
}

func (k *keepAlive) stop() {
	k.Lock()
	defer k.Unlock()

	if k.notifyStop != nil {
		close(k.notifyStop)
		k.notifyWaitGroup.Wait()
		k.notifyStop = nil
	}
}

func (k *keepAlive) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
	err := k.roundTripper.RoundTrip(ctx, req, res)
	if err != nil {
		return err
	}

	// Start ticker on login, stop ticker on logout.
	switch req.(type) {
	case *methods.LoginBody, *methods.LoginExtensionByCertificateBody:
		k.start()
	case *methods.LogoutBody:
		k.stop()
	}

	return nil
}
<|endoftext|>


<commit_before>package solidproxy

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"math/big"
	"net/http"
	"strings"
	"time"
)

// Agent is a structure which contains all the information necessary for WebID-TLS auth.
type Agent struct {
	WebID   string
	Profile string
	Cert    *tls.Certificate
	Key     *rsa.PrivateKey
	Log     *log.Logger
}

var (
	err            error
	subjectAltName = []int{2, 5, 29, 17}
	notBefore      = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	notAfter       = time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
	rsaBits        = 2048
)

// NewAgent creates a new agent object using an existing WebID (URI)
func NewAgent(uri string) (*Agent, error) {
	agent := &Agent{
		Log: InitLogger(false),
	}
	if len(uri) == 0 {
		return agent, errors.New("You must provide an URI for the agent's WebID")
	}
	agent.WebID = uri
	return agent, nil
}

// NewAgentLocal creates a new agent object together with a full profile and certificate
func NewAgentLocal(uri string, bits ...int) (*Agent, error) {
	agent, err := NewAgent(uri)
	if err != nil {
		return agent, err
	}

	keyLen := rsaBits
	if len(bits) > 0 {
		keyLen = bits[0]
	}

	// Create a new keypair
	privKey, E, N, err := NewRSAKey(keyLen)
	if err != nil {
		return agent, err
	}
	agent.Key = privKey

	agent.Profile = NewAgentProfile(E, N)
	agent.Cert, err = NewRSAcert(uri, "Solid Agent Cooper", privKey)
	return agent, err
}

// NewRSAKey creates a new RSA key pair
func NewRSAKey(bits int) (*rsa.PrivateKey, string, string, error) {
	var e, n string
	p, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return p, e, n, err
	}
	e = fmt.Sprintf("%d", p.PublicKey.E)
	n = fmt.Sprintf("%x", p.PublicKey.N)

	return p, e, n, err
}

// Handler function handles requests for the agent's WebID profile document
func (agent *Agent) Handler(w http.ResponseWriter, req *http.Request) {
	agent.Log.Printf("New request for agent WebID from: %+v\n", req.RemoteAddr)
	w.Header().Set("Content-Type", "text/turtle")
	w.WriteHeader(200)
	w.Write([]byte(agent.Profile))
}

// NewAgentProfile returns a new WebID profile document for the agent
func NewAgentProfile(exp string, mod string) string {
	return `@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
<>
    a <http://xmlns.com/foaf/0.1/PersonalProfileDocument> ;
    <http://xmlns.com/foaf/0.1/primaryTopic> <#me> .

<#me>
    a <http://xmlns.com/foaf/0.1/Agent> ;
    <http://xmlns.com/foaf/0.1/title> "Special agent" ;
    <http://xmlns.com/foaf/0.1/name> "Cooper" ;
    <http://xmlns.com/foaf/0.1/nick> "Coop" ;
    <http://www.w3.org/ns/auth/cert#key> <#key> .

<#key>
    a <http://www.w3.org/ns/auth/cert#RSAPublicKey> ;
    <http://www.w3.org/ns/auth/cert#exponent> "` + exp + `"^^<http://www.w3.org/2001/XMLSchema#int> ;
    <http://www.w3.org/ns/auth/cert#modulus> "` + mod + `"^^<http://www.w3.org/2001/XMLSchema#hexBinary> .
`
}

// NewRSAcert creates a new RSA x509 self-signed certificate to use for
// WebID-TLS authentication
func NewRSAcert(uri string, name string, priv *rsa.PrivateKey) (*tls.Certificate, error) {
	// Create the certificate
	uri = "URI: " + uri
	template := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(42),
		Subject: pkix.Name{
			CommonName:   name,
			Organization: []string{"WebID"},
			// Country:      []string{"US"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		BasicConstraintsValid: true,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}
	rawValues := []asn1.RawValue{
		{Class: 2, Tag: 6, Bytes: []byte(uri)},
	}
	values, err := asn1.Marshal(rawValues)
	if err != nil {
		return nil, err
	}
	template.ExtraExtensions = []pkix.Extension{{Id: subjectAltName, Value: values}}

	keyPEM := bytes.NewBuffer(nil)
	err = pem.Encode(keyPEM, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
	if err != nil {
		return nil, err
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, err
	}
	certPEM := bytes.NewBuffer(nil)
	err = pem.Encode(certPEM, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if err != nil {
		return nil, err
	}

	cert, err := tls.X509KeyPair(certPEM.Bytes(), keyPEM.Bytes())
	if err != nil {
		return nil, err
	}

	return &cert, nil
}

func extractSANValue(cert *x509.Certificate) (string, error) {
	for _, x := range cert.Extensions {
		if x.Id.Equal(subjectAltName) {
			v := asn1.RawValue{}
			_, err = asn1.Unmarshal(x.Value, &v)
			if err != nil {
				return "", err
			}
			sanURI := string(v.Bytes[2:])
			if strings.HasPrefix(sanURI, "URI:") {
				sanURI = strings.TrimSpace(sanURI[4:])
			}
			return sanURI, nil

		}
	}
	return "", nil
}

// WebIDFromBytes takes a certificate and extracts the subjectAlternativeName value from it.
func WebIDFromBytes(cert []byte) (string, error) {
	parsed, err := x509.ParseCertificate(cert)
	if err != nil {
		return "", err
	}

	return extractSANValue(parsed)
}

// WebIDFromCert returns a WebID value (URI) from a given tls.Certificate
func WebIDFromCert(cert *tls.Certificate) (string, error) {
	parsed, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return "", err
	}

	return extractSANValue(parsed)
}

// WebIDFromReq takes an http.Request object and extracts the WebID value (URI) from it
func WebIDFromReq(req *http.Request) (string, error) {
	t := req.TLS

	if t == nil || !t.HandshakeComplete {
		return "", errors.New("Not a TLS connection or TLS handshake failed")
	}

	if len(t.PeerCertificates) < 1 {
		return "", errors.New("No client certificate found in the TLS request!")
	}

	return extractSANValue(t.PeerCertificates[0])
}
<commit_msg>prepare WebID-RSA support<commit_after>package solidproxy

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/pem"
	"errors"
	"fmt"
	"log"
	"math/big"
	"net/http"
	"strings"
	"time"
)

// Agent is a structure which contains all the information necessary for WebID-TLS auth.
type Agent struct {
	WebID   string
	Profile string
	Cert    *tls.Certificate
	Key     *rsa.PrivateKey
	Log     *log.Logger
}

// DigestAuthentication structure
type RSAAuthentication struct {
	Type, Source, Username, Realm, Nonce, URI, QOP, NC, CNonce, Response, Opaque, Algorithm string
}

// DigestAuthorization structure
type RSAAuthorization struct {
	Type, Source, Username, Nonce, Signature string
}

var (
	err            error
	subjectAltName = []int{2, 5, 29, 17}
	notBefore      = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	notAfter       = time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
	rsaBits        = 2048
)

// NewAgent creates a new agent object using an existing WebID (URI)
func NewAgent(uri string) (*Agent, error) {
	agent := &Agent{
		Log: InitLogger(false),
	}
	if len(uri) == 0 {
		return agent, errors.New("You must provide an URI for the agent's WebID")
	}
	agent.WebID = uri
	return agent, nil
}

// NewAgentLocal creates a new agent object together with a full profile and certificate
func NewAgentLocal(uri string, bits ...int) (*Agent, error) {
	agent, err := NewAgent(uri)
	if err != nil {
		return agent, err
	}

	keyLen := rsaBits
	if len(bits) > 0 {
		keyLen = bits[0]
	}

	// Create a new keypair
	privKey, E, N, err := NewRSAKey(keyLen)
	if err != nil {
		return agent, err
	}
	agent.Key = privKey

	agent.Profile = NewAgentProfile(E, N)
	agent.Cert, err = NewRSAcert(uri, "Solid Agent Cooper", privKey)
	return agent, err
}

// NewRSAKey creates a new RSA key pair
func NewRSAKey(bits int) (*rsa.PrivateKey, string, string, error) {
	var e, n string
	p, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return p, e, n, err
	}
	e = fmt.Sprintf("%d", p.PublicKey.E)
	n = fmt.Sprintf("%x", p.PublicKey.N)

	return p, e, n, err
}

// Handler function handles requests for the agent's WebID profile document
func (agent *Agent) Handler(w http.ResponseWriter, req *http.Request) {
	agent.Log.Printf("New request for agent WebID from: %+v\n", req.RemoteAddr)
	w.Header().Set("Content-Type", "text/turtle")
	w.WriteHeader(200)
	w.Write([]byte(agent.Profile))
}

// NewAgentProfile returns a new WebID profile document for the agent
func NewAgentProfile(exp string, mod string) string {
	return `@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
<>
    a <http://xmlns.com/foaf/0.1/PersonalProfileDocument> ;
    <http://xmlns.com/foaf/0.1/primaryTopic> <#me> .

<#me>
    a <http://xmlns.com/foaf/0.1/Agent> ;
    <http://xmlns.com/foaf/0.1/title> "Special agent" ;
    <http://xmlns.com/foaf/0.1/name> "Cooper" ;
    <http://xmlns.com/foaf/0.1/nick> "Coop" ;
    <http://www.w3.org/ns/auth/cert#key> <#key> .

<#key>
    a <http://www.w3.org/ns/auth/cert#RSAPublicKey> ;
    <http://www.w3.org/ns/auth/cert#exponent> "` + exp + `"^^<http://www.w3.org/2001/XMLSchema#int> ;
    <http://www.w3.org/ns/auth/cert#modulus> "` + mod + `"^^<http://www.w3.org/2001/XMLSchema#hexBinary> .
`
}

// NewRSAcert creates a new RSA x509 self-signed certificate to use for
// WebID-TLS authentication
func NewRSAcert(uri string, name string, priv *rsa.PrivateKey) (*tls.Certificate, error) {
	// Create the certificate
	uri = "URI: " + uri
	template := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(42),
		Subject: pkix.Name{
			CommonName:   name,
			Organization: []string{"WebID"},
			// Country:      []string{"US"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		BasicConstraintsValid: true,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}
	rawValues := []asn1.RawValue{
		{Class: 2, Tag: 6, Bytes: []byte(uri)},
	}
	values, err := asn1.Marshal(rawValues)
	if err != nil {
		return nil, err
	}
	template.ExtraExtensions = []pkix.Extension{{Id: subjectAltName, Value: values}}

	keyPEM := bytes.NewBuffer(nil)
	err = pem.Encode(keyPEM, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
	if err != nil {
		return nil, err
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, err
	}
	certPEM := bytes.NewBuffer(nil)
	err = pem.Encode(certPEM, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if err != nil {
		return nil, err
	}

	cert, err := tls.X509KeyPair(certPEM.Bytes(), keyPEM.Bytes())
	if err != nil {
		return nil, err
	}

	return &cert, nil
}

func extractSANValue(cert *x509.Certificate) (string, error) {
	for _, x := range cert.Extensions {
		if x.Id.Equal(subjectAltName) {
			v := asn1.RawValue{}
			_, err = asn1.Unmarshal(x.Value, &v)
			if err != nil {
				return "", err
			}
			sanURI := string(v.Bytes[2:])
			if strings.HasPrefix(sanURI, "URI:") {
				sanURI = strings.TrimSpace(sanURI[4:])
			}
			return sanURI, nil

		}
	}
	return "", nil
}

// WebIDFromBytes takes a certificate and extracts the subjectAlternativeName value from it.
func WebIDFromBytes(cert []byte) (string, error) {
	parsed, err := x509.ParseCertificate(cert)
	if err != nil {
		return "", err
	}

	return extractSANValue(parsed)
}

// WebIDFromCert returns a WebID value (URI) from a given tls.Certificate
func WebIDFromCert(cert *tls.Certificate) (string, error) {
	parsed, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return "", err
	}

	return extractSANValue(parsed)
}

// WebIDFromReq takes an http.Request object and extracts the WebID value (URI) from it
func WebIDFromReq(req *http.Request) (string, error) {
	t := req.TLS

	if t == nil || !t.HandshakeComplete {
		return "", errors.New("Not a TLS connection or TLS handshake failed")
	}

	if len(t.PeerCertificates) < 1 {
		return "", errors.New("No client certificate found in the TLS request!")
	}

	return extractSANValue(t.PeerCertificates[0])
}

// ParseRSAAuthenticateHeader parses an Authenticate header and returns an RSAAuthentication object
func ParseRSAAuthenticateHeader(header string) (*RSAAuthentication, error) {
	var auth RSAAuthentication

	if len(header) == 0 {
		return auth, errors.New("Cannot parse WWW-Authenticate header: no header present")
	}

	opts := make(map[string]string)
	parts := strings.SplitN(header, " ", 2)
	opts["type"] = parts[0]
	parts = strings.Split(parts[1], ",")

	for _, part := range parts {
		vals := strings.SplitN(strings.TrimSpace(part), "=", 2)
		key := vals[0]
		val := strings.Replace(vals[1], "\"", "", -1)
		opts[key] = val
	}

	auth = &RSAAuthentication{
		opts["type"],
		opts["source"],
		opts["username"],
		opts["realm"],
		opts["nonce"],
		opts["uri"],
		opts["qop"],
		opts["nc"],
		opts["qnonce"],
		opts["response"],
		opts["opaque"],
		opts["algorithm"],
	}
	return auth, nil
}

// ParseRSAAuthorizationHeader parses an Authorization header and returns an RSAAuthorization object
func ParseRSAAuthorizationHeader(header string) (*RSAAuthorization, error) {
	auth := RSAAuthorization{}

	if len(header) == 0 {
		return &auth, errors.New("Cannot parse Authorization header: no header present")
	}

	opts := make(map[string]string)
	parts := strings.SplitN(header, " ", 2)
	opts["type"] = parts[0]
	if opts["type"] != "WebID-RSA" {
		return &auth, errors.New("Not a Digest authorization header. Got " + opts["type"])
	}

	parts = strings.Split(parts[1], ",")

	for _, part := range parts {
		vals := strings.SplitN(strings.TrimSpace(part), "=", 2)
		key := vals[0]
		val := strings.Replace(vals[1], "\"", "", -1)
		opts[key] = val
	}

	auth = DigestAuthorization{
		opts["type"],
		opts["source"],
		opts["username"],
		opts["nonce"],
		opts["sig"],
	}
	return &auth, nil
}
<|endoftext|>


<commit_before>package driver

import (
	"bufio"
	"fmt"
	"strings"

	"github.com/estesp/dockerbench/utils"
	log "github.com/sirupsen/logrus"
)

// RuncDriver is an implementation of the driver interface for Runc.
// IMPORTANT: This implementation does not protect instance metadata for thread safely.
// At this time there is no understood use case for multi-threaded use of this implementation.
type RuncDriver struct {
	runcBinary string
}

// RuncContainer is an implementation of the container metadata needed for runc
type RuncContainer struct {
	name       string
	bundlePath string
	detached   bool
	state      string
	pid        string
	trace      bool
}

// NewRuncDriver creates an instance of the runc driver, providing a path to runc
func NewRuncDriver(binaryPath string) (Driver, error) {
	resolvedBinPath, err := utils.ResolveBinary(binaryPath)
	if err != nil {
		return &RuncDriver{}, err
	}
	driver := &RuncDriver{
		runcBinary: resolvedBinPath,
	}
	return driver, nil
}

// newRuncContainer creates the metadata object of a runc-specific container with
// bundle, name, and any required additional information
func newRuncContainer(name, bundlepath string, detached bool, trace bool) Container {
	return &RuncContainer{
		name:       name,
		bundlePath: bundlepath,
		detached:   detached,
		trace:      trace,
	}
}

// Name returns the name of the container
func (c *RuncContainer) Name() string {
	return c.name
}

// Detached returns whether the container should be started in detached mode
func (c *RuncContainer) Detached() bool {
	return c.detached
}

// Trace returns whether the container should be started with tracing enabled
func (c *RuncContainer) Trace() bool {
	return c.trace
}

// Image returns the bundle path that runc will use
func (c *RuncContainer) Image() string {
	return c.bundlePath
}

// Pid returns the process ID in cases where this container instance is
// wrapping a potentially running container
func (c *RuncContainer) Pid() string {
	return c.pid
}

// State returns the queried state of the container (if available)
func (c *RuncContainer) State() string {
	return c.state
}

// Type returns a driver.Type to indentify the driver implementation
func (r *RuncDriver) Type() Type {
	return Runc
}

// Info returns
func (r *RuncDriver) Info() (string, error) {
	info := "runc driver (binary: " + r.runcBinary + ")\n"
	versionInfo, err := utils.ExecCmd(r.runcBinary, "--v")
	if err != nil {
		return "", fmt.Errorf("Error trying to retrieve runc version info: %v", err)
	}
	return info + versionInfo, nil
}

// Create will create a container instance matching the specific needs
// of a driver
func (r *RuncDriver) Create(name, image string, detached bool, trace bool) (Container, error) {
	return newRuncContainer(name, image, detached, trace), nil
}

// Clean will clean the environment; removing any remaining containers in the runc metadata
func (r *RuncDriver) Clean() error {
	var tries int
	out, err := utils.ExecCmd(r.runcBinary, "list")
	if err != nil {
		return fmt.Errorf("Error getting runc list output: (err: %v) output: %s", err, out)
	}
	// try up to 3 times to handle any remaining containers in the runc list
	containers := parseRuncList(out)
	log.Infof("Attempting to cleanup runc containers/metadata; %d listed", len(containers))
	for len(containers) > 0 && tries < 3 {
		log.Infof("runc cleanup: Pass #%d", tries+1)
		for _, ctr := range containers {
			switch ctr.State() {
			case "running":
				log.Infof("Attempting stop and remove on container %q", ctr.Name())
				r.Stop(ctr)
				r.Remove(ctr)
			case "paused":
				log.Infof("Attempting unpause and removal of container %q", ctr.Name())
				r.Unpause(ctr)
				r.Remove(ctr)
			case "stopped":
				log.Infof("Attempting remove of container %q", ctr.Name())
				r.Remove(ctr)
			default:
				log.Warnf("Unknown state %q for ctr %q", ctr.State(), ctr.Name())
			}
		}
		tries++
		out, err := utils.ExecCmd(r.runcBinary, "list")
		if err != nil {
			return fmt.Errorf("Error getting runc list output: %v", err)
		}
		containers = parseRuncList(out)
	}
	log.Infof("runc cleanup complete.")
	return nil
}

// Run will execute a container using the driver. Note that if the container is specified to
// run detached, but the config.json for the bundle specifies a "tty" allocation, this
// runc invocation will fail due to the fact we cannot detach without providing a "--console"
// device to runc. Detached daemon/server bundles should not need a tty; stdin/out/err of
// the container will be ignored given this is for benchmarking not validating container
// operation.
func (r *RuncDriver) Run(ctr Container) (string, int, error) {
	var (
		detached string
		trace    string
	)
	if ctr.Detached() {
		detached = "--detach"
	}
	if ctr.Trace() {
		trace = fmt.Sprintf("--trace /tmp/%s.trace ", ctr.Name())
	}

	args := fmt.Sprintf("%srun %s --bundle %s %s", trace, detached, ctr.Image(), ctr.Name())
	// the "NoOut" variant of ExecTimedCmd ignores stdin/out/err (sets them to /dev/null)
	return utils.ExecTimedCmdNoOut(r.runcBinary, args)
}

// Stop will stop/kill a container
func (r *RuncDriver) Stop(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "kill "+ctr.Name()+" KILL")
}

// Remove will remove a container
func (r *RuncDriver) Remove(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "delete "+ctr.Name())
}

// Pause will pause a container
func (r *RuncDriver) Pause(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "pause "+ctr.Name())
}

// Unpause will unpause/resume a container
func (r *RuncDriver) Unpause(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "resume "+ctr.Name())
}

// take the output of "runc list" and parse into container instances
func parseRuncList(listOutput string) []*RuncContainer {
	var results []*RuncContainer
	reader := strings.NewReader(listOutput)
	scan := bufio.NewScanner(reader)

	for scan.Scan() {
		line := scan.Text()
		if strings.HasPrefix(line, "ID ") {
			// skip header line
			continue
		}
		parts := strings.Fields(line)
		if len(parts) < 4 {
			// not sure what this is, but it ain't a container
			log.Warnf("runc list parsing found invalid line: %q", line)
			continue
		}
		ctr := &RuncContainer{
			name:       parts[0],
			bundlePath: parts[3],
			pid:        parts[1],
			state:      parts[2],
		}
		results = append(results, ctr)
	}
	return results
}
<commit_msg>only clean up containers we created<commit_after>package driver

import (
	"bufio"
	"fmt"
	"strings"

	"github.com/estesp/dockerbench/utils"
	log "github.com/sirupsen/logrus"
)

// RuncDriver is an implementation of the driver interface for Runc.
// IMPORTANT: This implementation does not protect instance metadata for thread safely.
// At this time there is no understood use case for multi-threaded use of this implementation.
type RuncDriver struct {
	runcBinary string
}

// RuncContainer is an implementation of the container metadata needed for runc
type RuncContainer struct {
	name       string
	bundlePath string
	detached   bool
	state      string
	pid        string
	trace      bool
}

// NewRuncDriver creates an instance of the runc driver, providing a path to runc
func NewRuncDriver(binaryPath string) (Driver, error) {
	resolvedBinPath, err := utils.ResolveBinary(binaryPath)
	if err != nil {
		return &RuncDriver{}, err
	}
	driver := &RuncDriver{
		runcBinary: resolvedBinPath,
	}
	return driver, nil
}

// newRuncContainer creates the metadata object of a runc-specific container with
// bundle, name, and any required additional information
func newRuncContainer(name, bundlepath string, detached bool, trace bool) Container {
	return &RuncContainer{
		name:       name,
		bundlePath: bundlepath,
		detached:   detached,
		trace:      trace,
	}
}

// Name returns the name of the container
func (c *RuncContainer) Name() string {
	return c.name
}

// Detached returns whether the container should be started in detached mode
func (c *RuncContainer) Detached() bool {
	return c.detached
}

// Trace returns whether the container should be started with tracing enabled
func (c *RuncContainer) Trace() bool {
	return c.trace
}

// Image returns the bundle path that runc will use
func (c *RuncContainer) Image() string {
	return c.bundlePath
}

// Pid returns the process ID in cases where this container instance is
// wrapping a potentially running container
func (c *RuncContainer) Pid() string {
	return c.pid
}

// State returns the queried state of the container (if available)
func (c *RuncContainer) State() string {
	return c.state
}

// Type returns a driver.Type to indentify the driver implementation
func (r *RuncDriver) Type() Type {
	return Runc
}

// Info returns
func (r *RuncDriver) Info() (string, error) {
	info := "runc driver (binary: " + r.runcBinary + ")\n"
	versionInfo, err := utils.ExecCmd(r.runcBinary, "--v")
	if err != nil {
		return "", fmt.Errorf("Error trying to retrieve runc version info: %v", err)
	}
	return info + versionInfo, nil
}

// Create will create a container instance matching the specific needs
// of a driver
func (r *RuncDriver) Create(name, image string, detached bool, trace bool) (Container, error) {
	return newRuncContainer(name, image, detached, trace), nil
}

// Clean will clean the environment; removing any remaining containers in the runc metadata
func (r *RuncDriver) Clean() error {
	var tries int
	out, err := utils.ExecCmd(r.runcBinary, "list")
	if err != nil {
		return fmt.Errorf("Error getting runc list output: (err: %v) output: %s", err, out)
	}
	// try up to 3 times to handle any remaining containers in the runc list
	containers := parseRuncList(out)
	log.Infof("Attempting to cleanup runc containers/metadata; %d listed", len(containers))
	for len(containers) > 0 && tries < 3 {
		log.Infof("runc cleanup: Pass #%d", tries+1)
		for _, ctr := range containers {
			switch ctr.State() {
			case "running":
				log.Infof("Attempting stop and remove on container %q", ctr.Name())
				r.Stop(ctr)
				r.Remove(ctr)
			case "paused":
				log.Infof("Attempting unpause and removal of container %q", ctr.Name())
				r.Unpause(ctr)
				r.Remove(ctr)
			case "stopped":
				log.Infof("Attempting remove of container %q", ctr.Name())
				r.Remove(ctr)
			default:
				log.Warnf("Unknown state %q for ctr %q", ctr.State(), ctr.Name())
			}
		}
		tries++
		out, err := utils.ExecCmd(r.runcBinary, "list")
		if err != nil {
			return fmt.Errorf("Error getting runc list output: %v", err)
		}
		containers = parseRuncList(out)
	}
	log.Infof("runc cleanup complete.")
	return nil
}

// Run will execute a container using the driver. Note that if the container is specified to
// run detached, but the config.json for the bundle specifies a "tty" allocation, this
// runc invocation will fail due to the fact we cannot detach without providing a "--console"
// device to runc. Detached daemon/server bundles should not need a tty; stdin/out/err of
// the container will be ignored given this is for benchmarking not validating container
// operation.
func (r *RuncDriver) Run(ctr Container) (string, int, error) {
	var (
		detached string
		trace    string
	)
	if ctr.Detached() {
		detached = "--detach"
	}
	if ctr.Trace() {
		trace = fmt.Sprintf("--trace /tmp/%s.trace ", ctr.Name())
	}

	args := fmt.Sprintf("%srun %s --bundle %s %s", trace, detached, ctr.Image(), ctr.Name())
	// the "NoOut" variant of ExecTimedCmd ignores stdin/out/err (sets them to /dev/null)
	return utils.ExecTimedCmdNoOut(r.runcBinary, args)
}

// Stop will stop/kill a container
func (r *RuncDriver) Stop(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "kill "+ctr.Name()+" KILL")
}

// Remove will remove a container
func (r *RuncDriver) Remove(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "delete "+ctr.Name())
}

// Pause will pause a container
func (r *RuncDriver) Pause(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "pause "+ctr.Name())
}

// Unpause will unpause/resume a container
func (r *RuncDriver) Unpause(ctr Container) (string, int, error) {
	return utils.ExecTimedCmd(r.runcBinary, "resume "+ctr.Name())
}

// take the output of "runc list" and parse into container instances
func parseRuncList(listOutput string) []*RuncContainer {
	var results []*RuncContainer
	reader := strings.NewReader(listOutput)
	scan := bufio.NewScanner(reader)

	for scan.Scan() {
		line := scan.Text()
		if strings.HasPrefix(line, "ID ") {
			// skip header line
			continue
		}
		parts := strings.Fields(line)
		if len(parts) < 4 {
			// not sure what this is, but it ain't a container
			log.Warnf("runc list parsing found invalid line: %q", line)
			continue
		}
		// don't delete containers that aren't part of our benchmark run!
		if !strings.Contains(parts[0], "bb-") {
			continue
		}
		ctr := &RuncContainer{
			name:       parts[0],
			bundlePath: parts[3],
			pid:        parts[1],
			state:      parts[2],
		}
		results = append(results, ctr)
	}
	return results
}
<|endoftext|>


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

// Rudimentary logging package. Defines a type, Logger, with simple
// methods for formatting output to one or two destinations. Also has
// predefined Loggers accessible through helper functions Stdout[f],
// Stderr[f], Exit[f], and Crash[f], which are easier to use than creating
// a Logger manually.
// Exit exits when written to.
// Crash causes a crash when written to.
package log

import (
	"fmt";
	"io";
	"os";
	"time";
)

// These flags define the properties of the Logger and the output they produce.
const (
	// Flags
	Lok = iota;
	Lexit;	// terminate execution when written
	Lcrash;	// crash (panic) when written
	// Bits or'ed together to control what's printed. There is no control over the
	// order they appear (the order listed here) or the format they present (as
	// described in the comments).  A colon appears after these items:
	//	2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
	Ldate = 1 << iota;	// the date: 2009/0123
	Ltime;	// the time: 01:23:23
	Lmicroseconds;	// microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile;	// full file name and line number: /a/b/c/d.go:23
	Lshortfile;	// final file name element and line number: d.go:23. overrides Llongfile
	lAllBits = Ldate | Ltime | Lmicroseconds | Llongfile | Lshortfile;
)

// Logger represents an active logging object.
type Logger struct {
	out0	io.Write;	// first destination for output
	out1	io.Write;	// second destination for output; may be nil
	prefix string;	// prefix to write at beginning of each line
	flag int;	// properties
}

// NewLogger creates a new Logger.   The out0 and out1 variables set the
// destinations to which log data will be written; out1 may be nil.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func NewLogger(out0, out1 io.Write, prefix string, flag int) *Logger {
	return &Logger{out0, out1, prefix, flag}
}

var (
	stdout = NewLogger(os.Stdout, nil, "", Lok|Ldate|Ltime);
	stderr = NewLogger(os.Stderr, nil, "", Lok|Ldate|Ltime);
	exit = NewLogger(os.Stderr, nil, "", Lexit|Ldate|Ltime);
	crash = NewLogger(os.Stderr, nil, "", Lcrash|Ldate|Ltime);
)

var shortnames = make(map[string] string)	// cache of short names to avoid allocation.

// Cheap integer to fixed-width decimal ASCII.  Use a negative width to avoid zero-padding
func itoa(i int, wid int) string {
	var u uint = uint(i);
	if u == 0 && wid <= 1 {
		return "0"
	}

	// Assemble decimal in reverse order.
	var b [32]byte;
	bp := len(b);
	for ; u > 0 || wid > 0; u /= 10 {
		bp--;
		wid--;
		b[bp] = byte(u%10) + '0';
	}

	return string(b[bp:len(b)])
}

func (l *Logger) formatHeader(ns int64, calldepth int) string {
	h := l.prefix;
	if l.flag & (Ldate | Ltime | Lmicroseconds) != 0 {
		t := time.SecondsToLocalTime(ns/1e9);
		if l.flag & (Ldate) != 0 {
			h += itoa(int(t.Year), 4) + "/" + itoa(t.Month, 2) + itoa(t.Day, 2) + " "
		}
		if l.flag & (Ltime | Lmicroseconds) != 0 {
			h += itoa(t.Hour, 2) + ":" + itoa(t.Minute, 2) + ":" + itoa(t.Second, 2);
			if l.flag & Lmicroseconds != 0 {
				h += "." + itoa(int(ns % 1e9)/1e3, 6);
			}
			h += " ";
		}
	}
	if l.flag & (Lshortfile | Llongfile) != 0 {
		pc, file, line, ok := sys.Caller(calldepth);
		if ok {
			if l.flag & Lshortfile != 0 {
				short, ok := shortnames[file];
				if !ok {
					short = file;
					for i := len(file) - 1; i > 0; i-- {
						if file[i] == '/' {
							short = file[i+1:len(file)];
							break;
						}
					}
					shortnames[file] = short;
				}
				file = short;
			}
		} else {
			file = "???";
			line = 0;
		}
		h += file + ":" + itoa(line, -1) + ": ";
	}
	return h;
}

// Output writes the output for a logging event.  The string s contains the text to print after
// the time stamp;  calldepth is used to recover the PC.  It is provided for generality, although
// at the moment on all pre-defined paths it will be 2.
func (l *Logger) Output(calldepth int, s string) {
	now := time.Nanoseconds();	// get this early.
	newline := "\n";
	if len(s) > 0 && s[len(s)-1] == '\n' {
		newline = ""
	}
	s = l.formatHeader(now, calldepth+1) + s + newline;
	io.WriteString(l.out0, s);
	if l.out1 != nil {
		io.WriteString(l.out1, s);
	}
	switch l.flag & ^lAllBits {
	case Lcrash:
		panic("log: fatal error");
	case Lexit:
		sys.Exit(1);
	}
}

// Logf is analogous to Printf() for a Logger.
func (l *Logger) Logf(format string, v ...) {
	l.Output(2, fmt.Sprintf(format, v))
}

// Log is analogouts to Print() for a Logger.
func (l *Logger) Log(v ...) {
	l.Output(2, fmt.Sprintln(v))
}

// Stdout is a helper function for easy logging to stdout. It is analogous to Print().
func Stdout(v ...) {
	stdout.Output(2, fmt.Sprint(v))
}

// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr).
func Stderr(v ...) {
	stdout.Output(2, fmt.Sprintln(v))
}

// Stdoutf is a helper functions for easy formatted logging to stdout. It is analogous to Printf().
func Stdoutf(format string, v ...) {
	stdout.Output(2, fmt.Sprintf(format, v))
}

// Stderrf is a helper function for easy formatted logging to stderr. It is analogous to Fprintf(os.Stderr).
func Stderrf(format string, v ...) {
	stderr.Output(2, fmt.Sprintf(format, v))
}

// Exit is equivalent to Stderr() followed by a call to sys.Exit(1).
func Exit(v ...) {
	exit.Output(2, fmt.Sprintln(v))
}

// Exitf is equivalent to Stderrf() followed by a call to sys.Exit(1).
func Exitf(format string, v ...) {
	exit.Output(2, fmt.Sprintf(format, v))
}

// Crash is equivalent to Stderrf() followed by a call to panic().
func Crash(v ...) {
	crash.Output(2, fmt.Sprintln(v))
}

// Crashf is equivalent to Stderrf() followed by a call to panic().
func Crashf(format string, v ...) {
	crash.Output(2, fmt.Sprintf(format, v))
}
<commit_msg>log.Stderr should actually go to stderr.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Rudimentary logging package. Defines a type, Logger, with simple
// methods for formatting output to one or two destinations. Also has
// predefined Loggers accessible through helper functions Stdout[f],
// Stderr[f], Exit[f], and Crash[f], which are easier to use than creating
// a Logger manually.
// Exit exits when written to.
// Crash causes a crash when written to.
package log

import (
	"fmt";
	"io";
	"os";
	"time";
)

// These flags define the properties of the Logger and the output they produce.
const (
	// Flags
	Lok = iota;
	Lexit;	// terminate execution when written
	Lcrash;	// crash (panic) when written
	// Bits or'ed together to control what's printed. There is no control over the
	// order they appear (the order listed here) or the format they present (as
	// described in the comments).  A colon appears after these items:
	//	2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
	Ldate = 1 << iota;	// the date: 2009/0123
	Ltime;	// the time: 01:23:23
	Lmicroseconds;	// microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile;	// full file name and line number: /a/b/c/d.go:23
	Lshortfile;	// final file name element and line number: d.go:23. overrides Llongfile
	lAllBits = Ldate | Ltime | Lmicroseconds | Llongfile | Lshortfile;
)

// Logger represents an active logging object.
type Logger struct {
	out0	io.Write;	// first destination for output
	out1	io.Write;	// second destination for output; may be nil
	prefix string;	// prefix to write at beginning of each line
	flag int;	// properties
}

// NewLogger creates a new Logger.   The out0 and out1 variables set the
// destinations to which log data will be written; out1 may be nil.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func NewLogger(out0, out1 io.Write, prefix string, flag int) *Logger {
	return &Logger{out0, out1, prefix, flag}
}

var (
	stdout = NewLogger(os.Stdout, nil, "", Lok|Ldate|Ltime);
	stderr = NewLogger(os.Stderr, nil, "", Lok|Ldate|Ltime);
	exit = NewLogger(os.Stderr, nil, "", Lexit|Ldate|Ltime);
	crash = NewLogger(os.Stderr, nil, "", Lcrash|Ldate|Ltime);
)

var shortnames = make(map[string] string)	// cache of short names to avoid allocation.

// Cheap integer to fixed-width decimal ASCII.  Use a negative width to avoid zero-padding
func itoa(i int, wid int) string {
	var u uint = uint(i);
	if u == 0 && wid <= 1 {
		return "0"
	}

	// Assemble decimal in reverse order.
	var b [32]byte;
	bp := len(b);
	for ; u > 0 || wid > 0; u /= 10 {
		bp--;
		wid--;
		b[bp] = byte(u%10) + '0';
	}

	return string(b[bp:len(b)])
}

func (l *Logger) formatHeader(ns int64, calldepth int) string {
	h := l.prefix;
	if l.flag & (Ldate | Ltime | Lmicroseconds) != 0 {
		t := time.SecondsToLocalTime(ns/1e9);
		if l.flag & (Ldate) != 0 {
			h += itoa(int(t.Year), 4) + "/" + itoa(t.Month, 2) + itoa(t.Day, 2) + " "
		}
		if l.flag & (Ltime | Lmicroseconds) != 0 {
			h += itoa(t.Hour, 2) + ":" + itoa(t.Minute, 2) + ":" + itoa(t.Second, 2);
			if l.flag & Lmicroseconds != 0 {
				h += "." + itoa(int(ns % 1e9)/1e3, 6);
			}
			h += " ";
		}
	}
	if l.flag & (Lshortfile | Llongfile) != 0 {
		pc, file, line, ok := sys.Caller(calldepth);
		if ok {
			if l.flag & Lshortfile != 0 {
				short, ok := shortnames[file];
				if !ok {
					short = file;
					for i := len(file) - 1; i > 0; i-- {
						if file[i] == '/' {
							short = file[i+1:len(file)];
							break;
						}
					}
					shortnames[file] = short;
				}
				file = short;
			}
		} else {
			file = "???";
			line = 0;
		}
		h += file + ":" + itoa(line, -1) + ": ";
	}
	return h;
}

// Output writes the output for a logging event.  The string s contains the text to print after
// the time stamp;  calldepth is used to recover the PC.  It is provided for generality, although
// at the moment on all pre-defined paths it will be 2.
func (l *Logger) Output(calldepth int, s string) {
	now := time.Nanoseconds();	// get this early.
	newline := "\n";
	if len(s) > 0 && s[len(s)-1] == '\n' {
		newline = ""
	}
	s = l.formatHeader(now, calldepth+1) + s + newline;
	io.WriteString(l.out0, s);
	if l.out1 != nil {
		io.WriteString(l.out1, s);
	}
	switch l.flag & ^lAllBits {
	case Lcrash:
		panic("log: fatal error");
	case Lexit:
		sys.Exit(1);
	}
}

// Logf is analogous to Printf() for a Logger.
func (l *Logger) Logf(format string, v ...) {
	l.Output(2, fmt.Sprintf(format, v))
}

// Log is analogouts to Print() for a Logger.
func (l *Logger) Log(v ...) {
	l.Output(2, fmt.Sprintln(v))
}

// Stdout is a helper function for easy logging to stdout. It is analogous to Print().
func Stdout(v ...) {
	stdout.Output(2, fmt.Sprint(v))
}

// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr).
func Stderr(v ...) {
	stderr.Output(2, fmt.Sprintln(v))
}

// Stdoutf is a helper functions for easy formatted logging to stdout. It is analogous to Printf().
func Stdoutf(format string, v ...) {
	stdout.Output(2, fmt.Sprintf(format, v))
}

// Stderrf is a helper function for easy formatted logging to stderr. It is analogous to Fprintf(os.Stderr).
func Stderrf(format string, v ...) {
	stderr.Output(2, fmt.Sprintf(format, v))
}

// Exit is equivalent to Stderr() followed by a call to sys.Exit(1).
func Exit(v ...) {
	exit.Output(2, fmt.Sprintln(v))
}

// Exitf is equivalent to Stderrf() followed by a call to sys.Exit(1).
func Exitf(format string, v ...) {
	exit.Output(2, fmt.Sprintf(format, v))
}

// Crash is equivalent to Stderrf() followed by a call to panic().
func Crash(v ...) {
	crash.Output(2, fmt.Sprintln(v))
}

// Crashf is equivalent to Stderrf() followed by a call to panic().
func Crashf(format string, v ...) {
	crash.Output(2, fmt.Sprintf(format, v))
}
<|endoftext|>


<commit_before>// Copyright 2018 Drone.IO Inc.
//
// 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 drone

type (
	// User represents a user account.
	User struct {
		ID        int64  `json:"id"`
		Login     string `json:"login"`
		Email     string `json:"email"`
		Avatar    string `json:"avatar_url"`
		Active    bool   `json:"active"`
		Admin     bool   `json:"admin"`
		Machine   bool   `json:"machine"`
		Syncing   bool   `json:"syncing"`
		Synced    int64  `json:"synced"`
		Created   int64  `json:"created"`
		Updated   int64  `json:"updated"`
		LastLogin int64  `json:"last_login"`
		Token     string `json:"token"`
	}

	// UserPatch defines a user patch request.
	UserPatch struct {
		Active  *bool   `json:"active,omitempty"`
		Admin   *bool   `json:"admin,omitempty"`
		Machine *bool   `json:"machine,omitempty"`
		Token   *string `json:"token,omitempty"`
	}

	// Repo represents a repository.
	Repo struct {
		ID          int64  `json:"id"`
		UID         string `json:"uid"`
		UserID      int64  `json:"user_id"`
		Namespace   string `json:"namespace"`
		Name        string `json:"name"`
		Slug        string `json:"slug"`
		SCM         string `json:"scm"`
		HTTPURL     string `json:"git_http_url"`
		SSHURL      string `json:"git_ssh_url"`
		Link        string `json:"link"`
		Branch      string `json:"default_branch"`
		Private     bool   `json:"private"`
		Visibility  string `json:"visibility"`
		Active      bool   `json:"active"`
		Config      string `json:"config_path"`
		Trusted     bool   `json:"trusted"`
		Protected   bool   `json:"protected"`
		IgnoreForks bool   `json:"ignore_forks"`
		IgnorePulls bool   `json:"ignore_pull_requests"`
		CancelPulls bool   `json:"auto_cancel_pull_requests"`
		CancelPush  bool   `json:"auto_cancel_pushes"`
		Timeout     int64  `json:"timeout"`
		Counter     int64  `json:"counter"`
		Synced      int64  `json:"synced"`
		Created     int64  `json:"created"`
		Updated     int64  `json:"updated"`
		Version     int64  `json:"version"`
		Signer      string `json:"signer,omitempty"`
		Secret      string `json:"secret,omitempty"`
		Build       Build  `json:"build,omitempty"`
	}

	// RepoPatch defines a repository patch request.
	RepoPatch struct {
		Config      *string `json:"config_path,omitempty"`
		Protected   *bool   `json:"protected,omitempty"`
		Trusted     *bool   `json:"trusted,omitempty"`
		Timeout     *int64  `json:"timeout,omitempty"`
		Visibility  *string `json:"visibility,omitempty"`
		IgnoreForks *bool   `json:"ignore_forks"`
		IgnorePulls *bool   `json:"ignore_pull_requests"`
		CancelPulls *bool   `json:"auto_cancel_pull_requests"`
		CancelPush  *bool   `json:"auto_cancel_pushes"`
		Counter     *int64  `json:"counter,omitempty"`
	}

	// Build defines a build object.
	Build struct {
		ID           int64             `json:"id"`
		RepoID       int64             `json:"repo_id"`
		Trigger      string            `json:"trigger"`
		Number       int64             `json:"number"`
		Parent       int64             `json:"parent,omitempty"`
		Status       string            `json:"status"`
		Error        string            `json:"error,omitempty"`
		Event        string            `json:"event"`
		Action       string            `json:"action"`
		Link         string            `json:"link"`
		Timestamp    int64             `json:"timestamp"`
		Title        string            `json:"title,omitempty"`
		Message      string            `json:"message"`
		Before       string            `json:"before"`
		After        string            `json:"after"`
		Ref          string            `json:"ref"`
		Fork         string            `json:"source_repo"`
		Source       string            `json:"source"`
		Target       string            `json:"target"`
		Author       string            `json:"author_login"`
		AuthorName   string            `json:"author_name"`
		AuthorEmail  string            `json:"author_email"`
		AuthorAvatar string            `json:"author_avatar"`
		Sender       string            `json:"sender"`
		Params       map[string]string `json:"params,omitempty"`
		Cron         string            `json:"cron,omitempty"`
		Deploy       string            `json:"deploy_to,omitempty"`
		DeployID     int64             `json:"deploy_id,omitempty"`
		Started      int64             `json:"started"`
		Finished     int64             `json:"finished"`
		Created      int64             `json:"created"`
		Updated      int64             `json:"updated"`
		Version      int64             `json:"version"`
		Stages       []*Stage          `json:"stages,omitempty"`
	}

	// Stage represents a stage of build execution.
	Stage struct {
		ID        int64             `json:"id"`
		BuildID   int64             `json:"build_id"`
		Number    int               `json:"number"`
		Name      string            `json:"name"`
		Kind      string            `json:"kind,omitempty"`
		Type      string            `json:"type,omitempty"`
		Status    string            `json:"status"`
		Error     string            `json:"error,omitempty"`
		ErrIgnore bool              `json:"errignore"`
		ExitCode  int               `json:"exit_code"`
		Machine   string            `json:"machine,omitempty"`
		OS        string            `json:"os"`
		Arch      string            `json:"arch"`
		Variant   string            `json:"variant,omitempty"`
		Kernel    string            `json:"kernel,omitempty"`
		Limit     int               `json:"limit,omitempty"`
		Started   int64             `json:"started"`
		Stopped   int64             `json:"stopped"`
		Created   int64             `json:"created"`
		Updated   int64             `json:"updated"`
		Version   int64             `json:"version"`
		OnSuccess bool              `json:"on_success"`
		OnFailure bool              `json:"on_failure"`
		DependsOn []string          `json:"depends_on,omitempty"`
		Labels    map[string]string `json:"labels,omitempty"`
		Steps     []*Step           `json:"steps,omitempty"`
	}

	// Step represents an individual step in the stage.
	Step struct {
		ID        int64  `json:"id"`
		StageID   int64  `json:"step_id"`
		Number    int    `json:"number"`
		Name      string `json:"name"`
		Status    string `json:"status"`
		Error     string `json:"error,omitempty"`
		ErrIgnore bool   `json:"errignore,omitempty"`
		ExitCode  int    `json:"exit_code"`
		Started   int64  `json:"started,omitempty"`
		Stopped   int64  `json:"stopped,omitempty"`
		Version   int64  `json:"version"`
	}

	// Registry represents a docker registry with credentials.
	// DEPRECATED
	Registry struct {
		Address  string `json:"address"`
		Username string `json:"username"`
		Password string `json:"password,omitempty"`
		Email    string `json:"email"`
		Token    string `json:"token"`
		Policy   string `json:"policy,omitempty"`
	}

	// Secret represents a secret variable, such as a password or token.
	Secret struct {
		Namespace       string `json:"namespace,omitempty"`
		Name            string `json:"name,omitempty"`
		Data            string `json:"data,omitempty"`
		PullRequest     bool   `json:"pull_request,omitempty"`
		PullRequestPush bool   `json:"pull_request_push,omitempty"`

		// Deprecated.
		Pull bool `json:"pull,omitempty"`
		Fork bool `json:"fork,omitempty"`
	}

	// Server represents a server node.
	Server struct {
		ID       string `json:"id"`
		Provider string `json:"provider"`
		State    string `json:"state"`
		Name     string `json:"name"`
		Image    string `json:"image"`
		Region   string `json:"region"`
		Size     string `json:"size"`
		Address  string `json:"address"`
		Capacity int    `json:"capacity"`
		Secret   string `json:"secret"`
		Error    string `json:"error"`
		CAKey    []byte `json:"ca_key"`
		CACert   []byte `json:"ca_cert"`
		TLSKey   []byte `json:"tls_key"`
		TLSCert  []byte `json:"tls_cert"`
		Created  int64  `json:"created"`
		Updated  int64  `json:"updated"`
		Started  int64  `json:"started"`
		Stopped  int64  `json:"stopped"`
	}

	// Cron represents a cron job.
	Cron struct {
		ID       int64  `json:"id"`
		RepoID   int64  `json:"repo_id"`
		Name     string `json:"name"`
		Expr     string `json:"expr"`
		Next     int64  `json:"next"`
		Prev     int64  `json:"prev"`
		Event    string `json:"event"`
		Branch   string `json:"branch"`
		Target   string `json:"target"`
		Disabled bool   `json:"disabled"`
		Created  int64  `json:"created"`
		Updated  int64  `json:"updated"`
	}

	// CronPatch defines a cron patch request.
	CronPatch struct {
		Event    *string `json:"event"`
		Branch   *string `json:"branch"`
		Target   *string `json:"target"`
		Disabled *bool   `json:"disabled"`
	}

	// Line represents a line of container logs.
	Line struct {
		Number    int    `json:"pos"`
		Message   string `json:"out"`
		Timestamp int64  `json:"time"`
	}

	// Config represents a config file.
	Config struct {
		Data string `json:"data"`
		Kind string `json:"kind"`
	}

	// Version provides system version details.
	Version struct {
		Source  string `json:"source,omitempty"`
		Version string `json:"version,omitempty"`
		Commit  string `json:"commit,omitempty"`
	}

	// System stores system information.
	System struct {
		Proto   string `json:"proto,omitempty"`
		Host    string `json:"host,omitempty"`
		Link    string `json:"link,omitempty"`
		Version string `json:"version,omitempty"`
	}

	// Node provides node details.
	Node struct {
		ID        int64             `json:"id"`
		UID       string            `json:"uid"`
		Provider  string            `json:"provider"`
		State     string            `json:"state"`
		Name      string            `json:"name"`
		Image     string            `json:"image"`
		Region    string            `json:"region"`
		Size      string            `json:"size"`
		OS        string            `json:"os"`
		Arch      string            `json:"arch"`
		Kernel    string            `json:"kernel"`
		Variant   string            `json:"variant"`
		Address   string            `json:"address"`
		Capacity  int               `json:"capacity"`
		Filters   []string          `json:"filters"`
		Labels    map[string]string `json:"labels"`
		Error     string            `json:"error"`
		CAKey     []byte            `json:"ca_key"`
		CACert    []byte            `json:"ca_cert"`
		TLSKey    []byte            `json:"tls_key"`
		TLSCert   []byte            `json:"tls_cert"`
		TLSName   string            `json:"tls_name"`
		Paused    bool              `json:"paused"`
		Protected bool              `json:"protected"`
		Created   int64             `json:"created"`
		Updated   int64             `json:"updated"`
	}

	// NodePatch defines a node patch request.
	NodePatch struct {
		UID       *string            `json:"uid"`
		Provider  *string            `json:"provider"`
		State     *string            `json:"state"`
		Image     *string            `json:"image"`
		Region    *string            `json:"region"`
		Size      *string            `json:"size"`
		Address   *string            `json:"address"`
		Capacity  *int               `json:"capacity"`
		Filters   *[]string          `json:"filters"`
		Labels    *map[string]string `json:"labels"`
		Error     *string            `json:"error"`
		CAKey     *[]byte            `json:"ca_key"`
		CACert    *[]byte            `json:"ca_cert"`
		TLSKey    *[]byte            `json:"tls_key"`
		TLSCert   *[]byte            `json:"tls_cert"`
		Paused    *bool              `json:"paused"`
		Protected *bool              `json:"protected"`
	}

	// Netrc contains login and initialization information used
	// by an automated login process.
	Netrc struct {
		Machine  string `json:"machine"`
		Login    string `json:"login"`
		Password string `json:"password"`
	}
)

// Error represents a json-encoded API error.
type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (e *Error) Error() string {
	return e.Message
}
<commit_msg>added debug flag to Build<commit_after>// Copyright 2018 Drone.IO Inc.
//
// 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 drone

type (
	// User represents a user account.
	User struct {
		ID        int64  `json:"id"`
		Login     string `json:"login"`
		Email     string `json:"email"`
		Avatar    string `json:"avatar_url"`
		Active    bool   `json:"active"`
		Admin     bool   `json:"admin"`
		Machine   bool   `json:"machine"`
		Syncing   bool   `json:"syncing"`
		Synced    int64  `json:"synced"`
		Created   int64  `json:"created"`
		Updated   int64  `json:"updated"`
		LastLogin int64  `json:"last_login"`
		Token     string `json:"token"`
	}

	// UserPatch defines a user patch request.
	UserPatch struct {
		Active  *bool   `json:"active,omitempty"`
		Admin   *bool   `json:"admin,omitempty"`
		Machine *bool   `json:"machine,omitempty"`
		Token   *string `json:"token,omitempty"`
	}

	// Repo represents a repository.
	Repo struct {
		ID          int64  `json:"id"`
		UID         string `json:"uid"`
		UserID      int64  `json:"user_id"`
		Namespace   string `json:"namespace"`
		Name        string `json:"name"`
		Slug        string `json:"slug"`
		SCM         string `json:"scm"`
		HTTPURL     string `json:"git_http_url"`
		SSHURL      string `json:"git_ssh_url"`
		Link        string `json:"link"`
		Branch      string `json:"default_branch"`
		Private     bool   `json:"private"`
		Visibility  string `json:"visibility"`
		Active      bool   `json:"active"`
		Config      string `json:"config_path"`
		Trusted     bool   `json:"trusted"`
		Protected   bool   `json:"protected"`
		IgnoreForks bool   `json:"ignore_forks"`
		IgnorePulls bool   `json:"ignore_pull_requests"`
		CancelPulls bool   `json:"auto_cancel_pull_requests"`
		CancelPush  bool   `json:"auto_cancel_pushes"`
		Timeout     int64  `json:"timeout"`
		Counter     int64  `json:"counter"`
		Synced      int64  `json:"synced"`
		Created     int64  `json:"created"`
		Updated     int64  `json:"updated"`
		Version     int64  `json:"version"`
		Signer      string `json:"signer,omitempty"`
		Secret      string `json:"secret,omitempty"`
		Build       Build  `json:"build,omitempty"`
	}

	// RepoPatch defines a repository patch request.
	RepoPatch struct {
		Config      *string `json:"config_path,omitempty"`
		Protected   *bool   `json:"protected,omitempty"`
		Trusted     *bool   `json:"trusted,omitempty"`
		Timeout     *int64  `json:"timeout,omitempty"`
		Visibility  *string `json:"visibility,omitempty"`
		IgnoreForks *bool   `json:"ignore_forks"`
		IgnorePulls *bool   `json:"ignore_pull_requests"`
		CancelPulls *bool   `json:"auto_cancel_pull_requests"`
		CancelPush  *bool   `json:"auto_cancel_pushes"`
		Counter     *int64  `json:"counter,omitempty"`
	}

	// Build defines a build object.
	Build struct {
		ID           int64             `json:"id"`
		RepoID       int64             `json:"repo_id"`
		Trigger      string            `json:"trigger"`
		Number       int64             `json:"number"`
		Parent       int64             `json:"parent,omitempty"`
		Status       string            `json:"status"`
		Error        string            `json:"error,omitempty"`
		Event        string            `json:"event"`
		Action       string            `json:"action"`
		Link         string            `json:"link"`
		Timestamp    int64             `json:"timestamp"`
		Title        string            `json:"title,omitempty"`
		Message      string            `json:"message"`
		Before       string            `json:"before"`
		After        string            `json:"after"`
		Ref          string            `json:"ref"`
		Fork         string            `json:"source_repo"`
		Source       string            `json:"source"`
		Target       string            `json:"target"`
		Author       string            `json:"author_login"`
		AuthorName   string            `json:"author_name"`
		AuthorEmail  string            `json:"author_email"`
		AuthorAvatar string            `json:"author_avatar"`
		Sender       string            `json:"sender"`
		Params       map[string]string `json:"params,omitempty"`
		Cron         string            `json:"cron,omitempty"`
		Deploy       string            `json:"deploy_to,omitempty"`
		DeployID     int64             `json:"deploy_id,omitempty"`
		Debug        bool              `json:"debug"`
		Started      int64             `json:"started"`
		Finished     int64             `json:"finished"`
		Created      int64             `json:"created"`
		Updated      int64             `json:"updated"`
		Version      int64             `json:"version"`
		Stages       []*Stage          `json:"stages,omitempty"`
	}

	// Stage represents a stage of build execution.
	Stage struct {
		ID        int64             `json:"id"`
		BuildID   int64             `json:"build_id"`
		Number    int               `json:"number"`
		Name      string            `json:"name"`
		Kind      string            `json:"kind,omitempty"`
		Type      string            `json:"type,omitempty"`
		Status    string            `json:"status"`
		Error     string            `json:"error,omitempty"`
		ErrIgnore bool              `json:"errignore"`
		ExitCode  int               `json:"exit_code"`
		Machine   string            `json:"machine,omitempty"`
		OS        string            `json:"os"`
		Arch      string            `json:"arch"`
		Variant   string            `json:"variant,omitempty"`
		Kernel    string            `json:"kernel,omitempty"`
		Limit     int               `json:"limit,omitempty"`
		Started   int64             `json:"started"`
		Stopped   int64             `json:"stopped"`
		Created   int64             `json:"created"`
		Updated   int64             `json:"updated"`
		Version   int64             `json:"version"`
		OnSuccess bool              `json:"on_success"`
		OnFailure bool              `json:"on_failure"`
		DependsOn []string          `json:"depends_on,omitempty"`
		Labels    map[string]string `json:"labels,omitempty"`
		Steps     []*Step           `json:"steps,omitempty"`
	}

	// Step represents an individual step in the stage.
	Step struct {
		ID        int64  `json:"id"`
		StageID   int64  `json:"step_id"`
		Number    int    `json:"number"`
		Name      string `json:"name"`
		Status    string `json:"status"`
		Error     string `json:"error,omitempty"`
		ErrIgnore bool   `json:"errignore,omitempty"`
		ExitCode  int    `json:"exit_code"`
		Started   int64  `json:"started,omitempty"`
		Stopped   int64  `json:"stopped,omitempty"`
		Version   int64  `json:"version"`
	}

	// Registry represents a docker registry with credentials.
	// DEPRECATED
	Registry struct {
		Address  string `json:"address"`
		Username string `json:"username"`
		Password string `json:"password,omitempty"`
		Email    string `json:"email"`
		Token    string `json:"token"`
		Policy   string `json:"policy,omitempty"`
	}

	// Secret represents a secret variable, such as a password or token.
	Secret struct {
		Namespace       string `json:"namespace,omitempty"`
		Name            string `json:"name,omitempty"`
		Data            string `json:"data,omitempty"`
		PullRequest     bool   `json:"pull_request,omitempty"`
		PullRequestPush bool   `json:"pull_request_push,omitempty"`

		// Deprecated.
		Pull bool `json:"pull,omitempty"`
		Fork bool `json:"fork,omitempty"`
	}

	// Server represents a server node.
	Server struct {
		ID       string `json:"id"`
		Provider string `json:"provider"`
		State    string `json:"state"`
		Name     string `json:"name"`
		Image    string `json:"image"`
		Region   string `json:"region"`
		Size     string `json:"size"`
		Address  string `json:"address"`
		Capacity int    `json:"capacity"`
		Secret   string `json:"secret"`
		Error    string `json:"error"`
		CAKey    []byte `json:"ca_key"`
		CACert   []byte `json:"ca_cert"`
		TLSKey   []byte `json:"tls_key"`
		TLSCert  []byte `json:"tls_cert"`
		Created  int64  `json:"created"`
		Updated  int64  `json:"updated"`
		Started  int64  `json:"started"`
		Stopped  int64  `json:"stopped"`
	}

	// Cron represents a cron job.
	Cron struct {
		ID       int64  `json:"id"`
		RepoID   int64  `json:"repo_id"`
		Name     string `json:"name"`
		Expr     string `json:"expr"`
		Next     int64  `json:"next"`
		Prev     int64  `json:"prev"`
		Event    string `json:"event"`
		Branch   string `json:"branch"`
		Target   string `json:"target"`
		Disabled bool   `json:"disabled"`
		Created  int64  `json:"created"`
		Updated  int64  `json:"updated"`
	}

	// CronPatch defines a cron patch request.
	CronPatch struct {
		Event    *string `json:"event"`
		Branch   *string `json:"branch"`
		Target   *string `json:"target"`
		Disabled *bool   `json:"disabled"`
	}

	// Line represents a line of container logs.
	Line struct {
		Number    int    `json:"pos"`
		Message   string `json:"out"`
		Timestamp int64  `json:"time"`
	}

	// Config represents a config file.
	Config struct {
		Data string `json:"data"`
		Kind string `json:"kind"`
	}

	// Version provides system version details.
	Version struct {
		Source  string `json:"source,omitempty"`
		Version string `json:"version,omitempty"`
		Commit  string `json:"commit,omitempty"`
	}

	// System stores system information.
	System struct {
		Proto   string `json:"proto,omitempty"`
		Host    string `json:"host,omitempty"`
		Link    string `json:"link,omitempty"`
		Version string `json:"version,omitempty"`
	}

	// Node provides node details.
	Node struct {
		ID        int64             `json:"id"`
		UID       string            `json:"uid"`
		Provider  string            `json:"provider"`
		State     string            `json:"state"`
		Name      string            `json:"name"`
		Image     string            `json:"image"`
		Region    string            `json:"region"`
		Size      string            `json:"size"`
		OS        string            `json:"os"`
		Arch      string            `json:"arch"`
		Kernel    string            `json:"kernel"`
		Variant   string            `json:"variant"`
		Address   string            `json:"address"`
		Capacity  int               `json:"capacity"`
		Filters   []string          `json:"filters"`
		Labels    map[string]string `json:"labels"`
		Error     string            `json:"error"`
		CAKey     []byte            `json:"ca_key"`
		CACert    []byte            `json:"ca_cert"`
		TLSKey    []byte            `json:"tls_key"`
		TLSCert   []byte            `json:"tls_cert"`
		TLSName   string            `json:"tls_name"`
		Paused    bool              `json:"paused"`
		Protected bool              `json:"protected"`
		Created   int64             `json:"created"`
		Updated   int64             `json:"updated"`
	}

	// NodePatch defines a node patch request.
	NodePatch struct {
		UID       *string            `json:"uid"`
		Provider  *string            `json:"provider"`
		State     *string            `json:"state"`
		Image     *string            `json:"image"`
		Region    *string            `json:"region"`
		Size      *string            `json:"size"`
		Address   *string            `json:"address"`
		Capacity  *int               `json:"capacity"`
		Filters   *[]string          `json:"filters"`
		Labels    *map[string]string `json:"labels"`
		Error     *string            `json:"error"`
		CAKey     *[]byte            `json:"ca_key"`
		CACert    *[]byte            `json:"ca_cert"`
		TLSKey    *[]byte            `json:"tls_key"`
		TLSCert   *[]byte            `json:"tls_cert"`
		Paused    *bool              `json:"paused"`
		Protected *bool              `json:"protected"`
	}

	// Netrc contains login and initialization information used
	// by an automated login process.
	Netrc struct {
		Machine  string `json:"machine"`
		Login    string `json:"login"`
		Password string `json:"password"`
	}
)

// Error represents a json-encoded API error.
type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (e *Error) Error() string {
	return e.Message
}
<|endoftext|>