<commit_before>package fi

import (
	"bytes"
	"crypto"
	crypto_rand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"github.com/golang/glog"
	"io"
	"math/big"
	"time"
)

const CertificateId_CA = "ca"

type Certificate struct {
	Subject pkix.Name
	IsCA    bool

	Certificate *x509.Certificate
	PublicKey   crypto.PublicKey
}

func (c *Certificate) UnmarshalJSON(b []byte) error {
	s := ""
	if err := json.Unmarshal(b, &s); err == nil {
		d, err := base64.StdEncoding.DecodeString(s)
		if err != nil {
			return fmt.Errorf("error decoding certificate base64 data: %q", string(b))
		}
		r, err := LoadPEMCertificate(d)
		if err != nil {
			glog.Infof("Invalid certificate data: %q", string(b))
			return fmt.Errorf("error parsing certificate: %v", err)
		}
		*c = *r
		return nil
	}
	return fmt.Errorf("unknown format for Certificate: %q", string(b))
}

func (c *Certificate) MarshalJSON() ([]byte, error) {
	var data bytes.Buffer
	_, err := c.WriteTo(&data)
	if err != nil {
		return nil, fmt.Errorf("error writing SSL certificate: %v", err)
	}
	return json.Marshal(data.String())
}

type CAStore interface {
	Cert(id string) (*Certificate, error)
	PrivateKey(id string) (*PrivateKey, error)

	FindCert(id string) (*Certificate, error)
	FindPrivateKey(id string) (*PrivateKey, error)

	IssueCert(id string, privateKey *PrivateKey, template *x509.Certificate) (*Certificate, error)
	CreatePrivateKey(id string) (*PrivateKey, error)

	List() ([]string, error)
}

func (c *Certificate) AsString() (string, error) {
	// Nicer behaviour because this is called from templates
	if c == nil {
		return "", fmt.Errorf("AsString called on nil Certificate")
	}

	var data bytes.Buffer
	_, err := c.WriteTo(&data)
	if err != nil {
		return "", fmt.Errorf("error writing SSL certificate: %v", err)
	}
	return data.String(), nil
}

type PrivateKey struct {
	Key crypto.PrivateKey
}

func (c *PrivateKey) AsString() (string, error) {
	// Nicer behaviour because this is called from templates
	if c == nil {
		return "", fmt.Errorf("AsString called on nil Certificate")
	}

	var data bytes.Buffer
	_, err := c.WriteTo(&data)
	if err != nil {
		return "", fmt.Errorf("error writing SSL private key: %v", err)
	}
	return data.String(), nil
}

func (k *PrivateKey) UnmarshalJSON(b []byte) (err error) {
	s := ""
	if err = json.Unmarshal(b, &s); err == nil {
		d, err := base64.StdEncoding.DecodeString(s)
		if err != nil {
			return fmt.Errorf("error decoding private key base64 data: %q", string(b))
		}
		key, err := parsePEMPrivateKey(d)
		if err != nil {
			return fmt.Errorf("error parsing private key: %v", err)
		}
		k.Key = key
		return nil
	}
	return fmt.Errorf("unknown format for private key: %q", string(b))
}

func (k *PrivateKey) MarshalJSON() ([]byte, error) {
	var data bytes.Buffer
	_, err := k.WriteTo(&data)
	if err != nil {
		return nil, fmt.Errorf("error writing SSL private key: %v", err)
	}
	return json.Marshal(data.String())
}

var _ io.WriterTo = &PrivateKey{}

func (k *PrivateKey) WriteTo(w io.Writer) (int64, error) {
	var data bytes.Buffer
	var err error

	switch pk := k.Key.(type) {
	case *rsa.PrivateKey:
		err = pem.Encode(w, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)})
	default:
		return 0, fmt.Errorf("unknown private key type: %T", k.Key)
	}

	if err != nil {
		return 0, fmt.Errorf("error writing SSL private key: %v", err)
	}

	return data.WriteTo(w)
}

func LoadPEMCertificate(pemData []byte) (*Certificate, error) {
	cert, err := parsePEMCertificate(pemData)
	if err != nil {
		return nil, err
	}

	c := &Certificate{
		Subject:     cert.Subject,
		Certificate: cert,
		PublicKey:   cert.PublicKey,
		IsCA:        cert.IsCA,
	}
	return c, nil
}

func SignNewCertificate(privateKey *PrivateKey, template *x509.Certificate, signer *x509.Certificate, signerPrivateKey *PrivateKey) (*Certificate, error) {
	if template.PublicKey == nil {
		rsaPrivateKey, ok := privateKey.Key.(*rsa.PrivateKey)
		if ok {
			template.PublicKey = rsaPrivateKey.Public()
		}
	}

	if template.PublicKey == nil {
		return nil, fmt.Errorf("PublicKey not set, and cannot be determined from %T", privateKey)
	}

	now := time.Now()
	if template.NotBefore.IsZero() {
		template.NotBefore = now.Add(time.Hour * -48)
	}

	if template.NotAfter.IsZero() {
		template.NotAfter = now.Add(time.Hour * 10 * 365 * 24)
	}

	if template.SerialNumber == nil {
		serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
		serialNumber, err := crypto_rand.Int(crypto_rand.Reader, serialNumberLimit)
		if err != nil {
			return nil, fmt.Errorf("error generating certificate serial number: %s", err)
		}
		template.SerialNumber = serialNumber
	}
	var parent *x509.Certificate
	if signer != nil {
		parent = signer
	} else {
		parent = template
		signerPrivateKey = privateKey
	}

	if template.KeyUsage == 0 {
		template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
	}

	if template.ExtKeyUsage == nil {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}
	//c.SignatureAlgorithm  = do we want to overrride?

	certificateData, err := x509.CreateCertificate(crypto_rand.Reader, template, parent, template.PublicKey, signerPrivateKey.Key)
	if err != nil {
		return nil, fmt.Errorf("error creating certificate: %v", err)
	}

	c := &Certificate{}
	c.PublicKey = template.PublicKey

	cert, err := x509.ParseCertificate(certificateData)
	if err != nil {
		return nil, fmt.Errorf("error parsing certificate: %v", err)
	}
	c.Certificate = cert

	return c, nil
}

var _ io.WriterTo = &Certificate{}

func (c *Certificate) WriteTo(w io.Writer) (int64, error) {
	var b bytes.Buffer
	err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: c.Certificate.Raw})
	if err != nil {
		return 0, err
	}
	return b.WriteTo(w)
}

func parsePEMCertificate(pemData []byte) (*x509.Certificate, error) {
	for {
		block, rest := pem.Decode(pemData)
		if block == nil {
			return nil, fmt.Errorf("could not parse certificate")
		}

		if block.Type == "CERTIFICATE" {
			glog.V(8).Infof("Parsing pem block: %q", block.Type)
			return x509.ParseCertificate(block.Bytes)
		} else {
			glog.Infof("Ignoring unexpected PEM block: %q", block.Type)
		}

		pemData = rest
	}
}

func parsePEMPrivateKey(pemData []byte) (crypto.PrivateKey, error) {
	for {
		block, rest := pem.Decode(pemData)
		if block == nil {
			return nil, fmt.Errorf("could not parse private key")
		}

		if block.Type == "RSA PRIVATE KEY" {
			glog.V(8).Infof("Parsing pem block: %q", block.Type)
			return x509.ParsePKCS1PrivateKey(block.Bytes)
		} else if block.Type == "PRIVATE KEY" {
			glog.V(8).Infof("Parsing pem block: %q", block.Type)
			k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
			if err != nil {
				return nil, err
			}
			return k.(crypto.PrivateKey), nil
		} else {
			glog.Infof("Ignoring unexpected PEM block: %q", block.Type)
		}

		pemData = rest
	}
}
<commit_msg>upup: support Base64 and raw-string JSON encoding of certs/keys<commit_after>package fi

import (
	"bytes"
	"crypto"
	crypto_rand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"github.com/golang/glog"
	"io"
	"math/big"
	"time"
)

const CertificateId_CA = "ca"

type Certificate struct {
	Subject pkix.Name
	IsCA    bool

	Certificate *x509.Certificate
	PublicKey   crypto.PublicKey
}

func (c *Certificate) UnmarshalJSON(b []byte) error {
	s := ""
	if err := json.Unmarshal(b, &s); err == nil {
		r, err := LoadPEMCertificate([]byte(s))
		if err != nil {
			// Alternative form: Check if base64 encoded
			// TODO: Do we need this?  I think we need this only on nodeup, but maybe we could just not base64-it?
			d, err2 := base64.StdEncoding.DecodeString(s)
			if err2 == nil {
				r2, err2 := LoadPEMCertificate(d)
				if err2 == nil {
					glog.Warningf("used base64 decode of certificate")
					r = r2
					err = nil
				}
			}

			if err != nil {
				glog.Infof("Invalid certificate data: %q", string(b))
				return fmt.Errorf("error parsing certificate: %v", err)
			}
		}
		*c = *r
		return nil
	}
	return fmt.Errorf("unknown format for Certificate: %q", string(b))
}

func (c *Certificate) MarshalJSON() ([]byte, error) {
	var data bytes.Buffer
	_, err := c.WriteTo(&data)
	if err != nil {
		return nil, fmt.Errorf("error writing SSL certificate: %v", err)
	}
	return json.Marshal(data.String())
}

type CAStore interface {
	Cert(id string) (*Certificate, error)
	PrivateKey(id string) (*PrivateKey, error)

	FindCert(id string) (*Certificate, error)
	FindPrivateKey(id string) (*PrivateKey, error)

	IssueCert(id string, privateKey *PrivateKey, template *x509.Certificate) (*Certificate, error)
	CreatePrivateKey(id string) (*PrivateKey, error)

	List() ([]string, error)
}

func (c *Certificate) AsString() (string, error) {
	// Nicer behaviour because this is called from templates
	if c == nil {
		return "", fmt.Errorf("AsString called on nil Certificate")
	}

	var data bytes.Buffer
	_, err := c.WriteTo(&data)
	if err != nil {
		return "", fmt.Errorf("error writing SSL certificate: %v", err)
	}
	return data.String(), nil
}

type PrivateKey struct {
	Key crypto.PrivateKey
}

func (c *PrivateKey) AsString() (string, error) {
	// Nicer behaviour because this is called from templates
	if c == nil {
		return "", fmt.Errorf("AsString called on nil Certificate")
	}

	var data bytes.Buffer
	_, err := c.WriteTo(&data)
	if err != nil {
		return "", fmt.Errorf("error writing SSL private key: %v", err)
	}
	return data.String(), nil
}

func (k *PrivateKey) UnmarshalJSON(b []byte) (err error) {
	s := ""
	if err := json.Unmarshal(b, &s); err == nil {
		r, err := parsePEMPrivateKey([]byte(s))
		if err != nil {
			// Alternative form: Check if base64 encoded
			// TODO: Do we need this?  I think we need this only on nodeup, but maybe we could just not base64-it?
			d, err2 := base64.StdEncoding.DecodeString(s)
			if err2 == nil {
				r2, err2 := parsePEMPrivateKey(d)
				if err2 == nil {
					glog.Warningf("used base64 decode of PrivateKey")
					r = r2
					err = nil
				}
			}

			if err != nil {
				return fmt.Errorf("error parsing private key: %v", err)
			}
		}
		k.Key = r
		return nil
	}

	return fmt.Errorf("unknown format for private key: %q", string(b))
}

func (k *PrivateKey) MarshalJSON() ([]byte, error) {
	var data bytes.Buffer
	_, err := k.WriteTo(&data)
	if err != nil {
		return nil, fmt.Errorf("error writing SSL private key: %v", err)
	}
	return json.Marshal(data.String())
}

var _ io.WriterTo = &PrivateKey{}

func (k *PrivateKey) WriteTo(w io.Writer) (int64, error) {
	var data bytes.Buffer
	var err error

	switch pk := k.Key.(type) {
	case *rsa.PrivateKey:
		err = pem.Encode(w, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)})
	default:
		return 0, fmt.Errorf("unknown private key type: %T", k.Key)
	}

	if err != nil {
		return 0, fmt.Errorf("error writing SSL private key: %v", err)
	}

	return data.WriteTo(w)
}

func LoadPEMCertificate(pemData []byte) (*Certificate, error) {
	cert, err := parsePEMCertificate(pemData)
	if err != nil {
		return nil, err
	}

	c := &Certificate{
		Subject:     cert.Subject,
		Certificate: cert,
		PublicKey:   cert.PublicKey,
		IsCA:        cert.IsCA,
	}
	return c, nil
}

func SignNewCertificate(privateKey *PrivateKey, template *x509.Certificate, signer *x509.Certificate, signerPrivateKey *PrivateKey) (*Certificate, error) {
	if template.PublicKey == nil {
		rsaPrivateKey, ok := privateKey.Key.(*rsa.PrivateKey)
		if ok {
			template.PublicKey = rsaPrivateKey.Public()
		}
	}

	if template.PublicKey == nil {
		return nil, fmt.Errorf("PublicKey not set, and cannot be determined from %T", privateKey)
	}

	now := time.Now()
	if template.NotBefore.IsZero() {
		template.NotBefore = now.Add(time.Hour * -48)
	}

	if template.NotAfter.IsZero() {
		template.NotAfter = now.Add(time.Hour * 10 * 365 * 24)
	}

	if template.SerialNumber == nil {
		serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
		serialNumber, err := crypto_rand.Int(crypto_rand.Reader, serialNumberLimit)
		if err != nil {
			return nil, fmt.Errorf("error generating certificate serial number: %s", err)
		}
		template.SerialNumber = serialNumber
	}
	var parent *x509.Certificate
	if signer != nil {
		parent = signer
	} else {
		parent = template
		signerPrivateKey = privateKey
	}

	if template.KeyUsage == 0 {
		template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
	}

	if template.ExtKeyUsage == nil {
		template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
	}
	//c.SignatureAlgorithm  = do we want to overrride?

	certificateData, err := x509.CreateCertificate(crypto_rand.Reader, template, parent, template.PublicKey, signerPrivateKey.Key)
	if err != nil {
		return nil, fmt.Errorf("error creating certificate: %v", err)
	}

	c := &Certificate{}
	c.PublicKey = template.PublicKey

	cert, err := x509.ParseCertificate(certificateData)
	if err != nil {
		return nil, fmt.Errorf("error parsing certificate: %v", err)
	}
	c.Certificate = cert

	return c, nil
}

var _ io.WriterTo = &Certificate{}

func (c *Certificate) WriteTo(w io.Writer) (int64, error) {
	var b bytes.Buffer
	err := pem.Encode(&b, &pem.Block{Type: "CERTIFICATE", Bytes: c.Certificate.Raw})
	if err != nil {
		return 0, err
	}
	return b.WriteTo(w)
}

func parsePEMCertificate(pemData []byte) (*x509.Certificate, error) {
	for {
		block, rest := pem.Decode(pemData)
		if block == nil {
			return nil, fmt.Errorf("could not parse certificate")
		}

		if block.Type == "CERTIFICATE" {
			glog.V(8).Infof("Parsing pem block: %q", block.Type)
			return x509.ParseCertificate(block.Bytes)
		} else {
			glog.Infof("Ignoring unexpected PEM block: %q", block.Type)
		}

		pemData = rest
	}
}

func parsePEMPrivateKey(pemData []byte) (crypto.PrivateKey, error) {
	for {
		block, rest := pem.Decode(pemData)
		if block == nil {
			return nil, fmt.Errorf("could not parse private key")
		}

		if block.Type == "RSA PRIVATE KEY" {
			glog.V(8).Infof("Parsing pem block: %q", block.Type)
			return x509.ParsePKCS1PrivateKey(block.Bytes)
		} else if block.Type == "PRIVATE KEY" {
			glog.V(8).Infof("Parsing pem block: %q", block.Type)
			k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
			if err != nil {
				return nil, err
			}
			return k.(crypto.PrivateKey), nil
		} else {
			glog.Infof("Ignoring unexpected PEM block: %q", block.Type)
		}

		pemData = rest
	}
}
<|endoftext|>


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

package profiling

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	httpProf "net/http/pprof"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"sync/atomic"

	"github.com/julienschmidt/httprouter"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
)

// Profiler helps setup and manage profiling
type Profiler struct {
	// BindHTTP, if not empty, is the HTTP address to bind to.
	//
	// Can also be configured with "-profile-bind-http" flag.
	BindHTTP string

	// Dir, if set, is the path where profiling data will be written to.
	//
	// Can also be configured with "-profile-output-dir" flag.
	Dir string

	// ProfileCPU, if true, indicates that the profiler should profile the CPU.
	//
	// Requires Dir to be set, since it's where the profiler output is dumped.
	//
	// Can also be set with "-profile-cpu".
	ProfileCPU bool

	// ProfileHeap, if true, indicates that the profiler should profile heap
	// allocations.
	//
	// Requires Dir to be set, since it's where the profiler output is dumped.
	//
	// Can also be set with "-profile-heap".
	ProfileHeap bool

	// Logger, if not nil, will be used to log events and errors. If nil, no
	// logging will be used.
	Logger logging.Logger
	// Clock is the clock instance to use. If nil, the system clock will be used.
	Clock clock.Clock

	// listener is the active listener instance. It is set when Start is called.
	listener net.Listener

	// pathCounter is an atomic counter used to ensure non-conflicting paths.
	pathCounter uint32

	// profilingCPU is true if 'Start' successfully launched CPU profiling.
	profilingCPU bool
}

// AddFlags adds command line flags to common Profiler fields.
func (p *Profiler) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&p.BindHTTP, "profile-bind-http", "",
		"If specified, run a runtime profiler HTTP server bound to this [address][:port].")
	fs.StringVar(&p.Dir, "profile-output-dir", "",
		"If specified, allow generation of profiling artifacts, which will be written here.")
	fs.BoolVar(&p.ProfileCPU, "profile-cpu", false, "If specified, enables CPU profiling.")
	fs.BoolVar(&p.ProfileHeap, "profile-heap", false, "If specified, enables heap profiling.")

}

// Start starts the Profiler's configured operations.  On success, returns a
// function that can be called to shutdown the profiling server.
//
// Calling Stop is not necessary, but will enable end-of-operation profiling
// to be gathered.
func (p *Profiler) Start() error {
	if p.Dir == "" {
		if p.ProfileCPU {
			return errors.New("-profile-cpu requires -profile-output-dir to be set")
		}
		if p.ProfileHeap {
			return errors.New("-profile-heap requires -profile-output-dir to be set")
		}
	}

	if p.ProfileCPU {
		out, err := os.Create(p.generateOutPath("cpu"))
		if err != nil {
			return errors.Annotate(err, "failed to create CPU profile output file").Err()
		}
		pprof.StartCPUProfile(out)
		p.profilingCPU = true
	}

	if p.BindHTTP != "" {
		if err := p.startHTTP(); err != nil {
			return errors.Annotate(err, "failed to start HTTP server").Err()
		}
	}

	return nil
}

func (p *Profiler) startHTTP() error {
	// Register paths: https://golang.org/src/net/http/pprof/pprof.go
	router := httprouter.New()
	router.HandlerFunc("GET", "/debug/pprof/", httpProf.Index)
	router.HandlerFunc("GET", "/debug/pprof/cmdline", httpProf.Cmdline)
	router.HandlerFunc("GET", "/debug/pprof/profile", httpProf.Profile)
	router.HandlerFunc("GET", "/debug/pprof/symbol", httpProf.Symbol)
	router.HandlerFunc("GET", "/debug/pprof/trace", httpProf.Trace)
	for _, p := range pprof.Profiles() {
		name := p.Name()
		router.Handler("GET", fmt.Sprintf("/debug/pprof/%s", name), httpProf.Handler(name))
	}

	// Bind to our profiling port.
	l, err := net.Listen("tcp4", p.BindHTTP)
	if err != nil {
		return errors.Annotate(err, "failed to bind to TCP4 address: %q", p.BindHTTP).Err()
	}

	server := http.Server{
		Handler: http.HandlerFunc(router.ServeHTTP),
	}
	go func() {
		if err := server.Serve(l); err != nil {
			p.getLogger().Errorf("Error serving profile HTTP: %s", err)
		}
	}()
	return nil
}

// Stop stops the Profiler's operations.
func (p *Profiler) Stop() {
	if p.profilingCPU {
		pprof.StopCPUProfile()
		p.profilingCPU = false
	}

	if p.listener != nil {
		if err := p.listener.Close(); err != nil {
			p.getLogger().Warningf("Failed to stop profile HTTP server: %s", err)
		}
		p.listener = nil
	}

	// Take one final snapshot.
	p.DumpSnapshot()
}

// DumpSnapshot dumps a profile snapshot to the configured output directory. If
// no output directory is configured, nothing will happen.
func (p *Profiler) DumpSnapshot() error {
	if p.Dir == "" {
		return nil
	}

	if p.ProfileHeap {
		if err := p.dumpHeapProfile(); err != nil {
			return errors.Annotate(err, "failed to dump heap profile").Err()
		}
	}

	return nil
}

func (p *Profiler) dumpHeapProfile() error {
	fd, err := os.Create(p.generateOutPath("memory"))
	if err != nil {
		return errors.Annotate(err, "failed to create output file").Err()
	}
	defer fd.Close()

	// Get up-to-date statistics.
	runtime.GC()
	if err := pprof.WriteHeapProfile(fd); err != nil {
		return errors.Annotate(err, "failed to write heap profile").Err()
	}
	return nil
}

func (p *Profiler) generateOutPath(base string) string {
	clk := p.Clock
	if clk == nil {
		clk = clock.GetSystemClock()
	}
	now := clk.Now()
	counter := atomic.AddUint32(&p.pathCounter, 1) - 1
	return filepath.Join(p.Dir, fmt.Sprintf("%s_%d_%d.prof", base, now.Unix(), counter))
}

func (p *Profiler) getLogger() logging.Logger {
	if p.Logger != nil {
		return p.Logger
	}
	return logging.Null
}
<commit_msg>profiling: add -profile-heap-frequency flag<commit_after>// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package profiling

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	httpProf "net/http/pprof"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"sync/atomic"
	"time"

	"github.com/julienschmidt/httprouter"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
)

// Profiler helps setup and manage profiling
type Profiler struct {
	// BindHTTP, if not empty, is the HTTP address to bind to.
	//
	// Can also be configured with "-profile-bind-http" flag.
	BindHTTP string

	// Dir, if set, is the path where profiling data will be written to.
	//
	// Can also be configured with "-profile-output-dir" flag.
	Dir string

	// ProfileCPU, if true, indicates that the profiler should profile the CPU.
	//
	// Requires Dir to be set, since it's where the profiler output is dumped.
	//
	// Can also be set with "-profile-cpu".
	ProfileCPU bool

	// ProfileHeap, if true, indicates that the profiler should profile heap
	// allocations.
	//
	// Requires Dir to be set, since it's where the profiler output is dumped.
	//
	// Can also be set with "-profile-heap".
	ProfileHeap bool

	// ProfileHeapFrequency, if set non-zero, instructs the profiler to
	// periodically dump heap profiler snapshots.
	//
	// Requires Dir to be set, since it's where the profiler output is dumped.
	//
	// Can also be set with "-profile-heap-frequency".
	ProfileHeapFrequency time.Duration

	// Logger, if not nil, will be used to log events and errors. If nil, no
	// logging will be used.
	Logger logging.Logger
	// Clock is the clock instance to use. If nil, the system clock will be used.
	Clock clock.Clock

	// listener is the active listener instance. It is set when Start is called.
	listener net.Listener

	// pathCounter is an atomic counter used to ensure non-conflicting paths.
	pathCounter uint32

	// profilingCPU is true if 'Start' successfully launched CPU profiling.
	profilingCPU bool
}

// AddFlags adds command line flags to common Profiler fields.
func (p *Profiler) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&p.BindHTTP, "profile-bind-http", "",
		"If specified, run a runtime profiler HTTP server bound to this [address][:port].")
	fs.StringVar(&p.Dir, "profile-output-dir", "",
		"If specified, allow generation of profiling artifacts, which will be written here.")
	fs.BoolVar(&p.ProfileCPU, "profile-cpu", false, "If specified, enables CPU profiling.")
	fs.BoolVar(&p.ProfileHeap, "profile-heap", false, "If specified, enables heap profiling.")
	fs.DurationVar(&p.ProfileHeapFrequency, "profile-heap-frequency", 0, "If specified non-zero, enables periodic heap profiler snapshots dump.")
}

// Start starts the Profiler's configured operations.  On success, returns a
// function that can be called to shutdown the profiling server.
//
// Calling Stop is not necessary, but will enable end-of-operation profiling
// to be gathered.
func (p *Profiler) Start() error {
	if p.Dir == "" {
		if p.ProfileCPU {
			return errors.New("-profile-cpu requires -profile-output-dir to be set")
		}
		if p.ProfileHeap {
			return errors.New("-profile-heap requires -profile-output-dir to be set")
		}

		if p.ProfileHeapFrequency > 0 {
			return errors.New("-profile-heap-frequency requires -profile-output-dir to be set")
		}
	}

	if p.ProfileHeapFrequency < 0 {
		return errors.New("-profile-heap-frequency should be positive if set")
	}

	if p.ProfileHeapFrequency > 0 && !p.ProfileHeap {
		return errors.New("-profile-heap-frequency requires -profile-heap")
	}

	if p.ProfileCPU {
		out, err := os.Create(p.generateOutPath("cpu"))
		if err != nil {
			return errors.Annotate(err, "failed to create CPU profile output file").Err()
		}
		pprof.StartCPUProfile(out)
		p.profilingCPU = true
	}

	if p.ProfileHeapFrequency > 0 {
		go func() {
			for {
				time.Sleep(p.ProfileHeapFrequency)
				if err := p.dumpHeapProfile(); err != nil {
					p.getLogger().Errorf("Error dump heap profile: %v", err)
				}
			}
		}()
	}

	if p.BindHTTP != "" {
		if err := p.startHTTP(); err != nil {
			return errors.Annotate(err, "failed to start HTTP server").Err()
		}
	}

	return nil
}

func (p *Profiler) startHTTP() error {
	// Register paths: https://golang.org/src/net/http/pprof/pprof.go
	router := httprouter.New()
	router.HandlerFunc("GET", "/debug/pprof/", httpProf.Index)
	router.HandlerFunc("GET", "/debug/pprof/cmdline", httpProf.Cmdline)
	router.HandlerFunc("GET", "/debug/pprof/profile", httpProf.Profile)
	router.HandlerFunc("GET", "/debug/pprof/symbol", httpProf.Symbol)
	router.HandlerFunc("GET", "/debug/pprof/trace", httpProf.Trace)
	for _, p := range pprof.Profiles() {
		name := p.Name()
		router.Handler("GET", fmt.Sprintf("/debug/pprof/%s", name), httpProf.Handler(name))
	}

	// Bind to our profiling port.
	l, err := net.Listen("tcp4", p.BindHTTP)
	if err != nil {
		return errors.Annotate(err, "failed to bind to TCP4 address: %q", p.BindHTTP).Err()
	}

	server := http.Server{
		Handler: http.HandlerFunc(router.ServeHTTP),
	}
	go func() {
		if err := server.Serve(l); err != nil {
			p.getLogger().Errorf("Error serving profile HTTP: %s", err)
		}
	}()
	return nil
}

// Stop stops the Profiler's operations.
func (p *Profiler) Stop() {
	if p.profilingCPU {
		pprof.StopCPUProfile()
		p.profilingCPU = false
	}

	if p.listener != nil {
		if err := p.listener.Close(); err != nil {
			p.getLogger().Warningf("Failed to stop profile HTTP server: %s", err)
		}
		p.listener = nil
	}

	// Take one final snapshot.
	p.DumpSnapshot()
}

// DumpSnapshot dumps a profile snapshot to the configured output directory. If
// no output directory is configured, nothing will happen.
func (p *Profiler) DumpSnapshot() error {
	if p.Dir == "" {
		return nil
	}

	if p.ProfileHeap {
		if err := p.dumpHeapProfile(); err != nil {
			return errors.Annotate(err, "failed to dump heap profile").Err()
		}
	}

	return nil
}

func (p *Profiler) dumpHeapProfile() error {
	fd, err := os.Create(p.generateOutPath("memory"))
	if err != nil {
		return errors.Annotate(err, "failed to create output file").Err()
	}
	defer fd.Close()

	// Get up-to-date statistics.
	runtime.GC()
	if err := pprof.WriteHeapProfile(fd); err != nil {
		return errors.Annotate(err, "failed to write heap profile").Err()
	}
	return nil
}

func (p *Profiler) generateOutPath(base string) string {
	clk := p.Clock
	if clk == nil {
		clk = clock.GetSystemClock()
	}
	now := clk.Now()
	counter := atomic.AddUint32(&p.pathCounter, 1) - 1
	return filepath.Join(p.Dir, fmt.Sprintf("%s_%d_%d.prof", base, now.Unix(), counter))
}

func (p *Profiler) getLogger() logging.Logger {
	if p.Logger != nil {
		return p.Logger
	}
	return logging.Null
}
<|endoftext|>


<commit_before>package main
import (
	"fmt"
	"time"
)

func slow(c chan int){
	time.Sleep(time.Second*5)
	fmt.Println("slow is done")
	c <- 5
}
func fast(c chan int){
	time.Sleep(time.Second)
	fmt.Println("fast is done")
	c <- 1
}

func main() {
	c := make(chan int,2)
	go slow(c)
	go fast(c)
	_,_ = <-c, <-c

}
<commit_msg>working concurrancy exampple<commit_after>package main
import (
	"fmt"
	"time"
)

/*scenario:
start fast and slow run at same time.
As soon as slow finishes, it sends a signal to fast that lets slow go faster.

non sharing version:
	fast -> 1
	slow -> 1 2 3 4 5

sharing version:
	fast -> 1(transfers as soon as 1st second is over)
	slow -> 1 2 (once slow recieves transfer, it goes for only 1 sec instead of 4 more.)

*/
func slow(c chan int, done chan int){
	//keep sleeping (5 sec max)
	for a :=0; a<5;a++{
		fmt.Println(a)

		//recieve from the channel.
		select {
			case <-c:
				fmt.Println(a,">-----forward------>", 999)
				a = 999
				break
			default:
				time.Sleep(time.Second)
		}

	}

	fmt.Println("slow is done")
	done <- 1

}
func fast(c chan int){
	//sleep
	time.Sleep(time.Second*2)

	fmt.Println("fast is done")
	//send a value into the channel
	c <- 9999
}

func main() {
	//make the channel. It will be an int channel.
	c , done:= make(chan int),make(chan int)
	//slow and fast are run at the same time.
	go slow(c,done)
	go fast(c)

	_=<-done

}
<|endoftext|>


<commit_before>//  Copyright 2019 Google 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 service

// logRequest is a server-side pre-defined data structure
type logRequest struct {
	ClientInfo    clientInfo `json:"client_info"`
	LogSource     int64      `json:"log_source"`
	RequestTimeMs int64      `json:"request_time_ms"`
	LogEvent      []logEvent `json:"log_event"`
}

// ClientInfo is a server-side pre-defined data structure
type clientInfo struct {
	// ClientType is defined on server side to clarify which client library is used.
	ClientType string `json:"client_type"`
}

// LogEvent is a server-side pre-defined data structure
type logEvent struct {
	EventTimeMs         int64  `json:"event_time_ms"`
	EventUptimeMs       int64  `json:"event_uptime_ms"`
	SourceExtensionJSON string `json:"source_extension_json"`
}

// logResponse is a server-side pre-defined data structure
type logResponse struct {
	NextRequestWaitMillis int64                `json:"NextRequestWaitMillis,string"`
	LogResponseDetails    []logResponseDetails `json:"LogResponseDetails"`
}

// LogResponseDetails is a server-side pre-defined data structure
type logResponseDetails struct {
	ResponseAction responseAction `json:"ResponseAction"`
}

// ResponseAction is a server-side pre-defined data structure
type responseAction string

const (
	// responseActionUnknown - If the client sees this, it should delete the logRequest (not retry).
	// It may indicate that a new response action was added, which the client
	// doesn't yet understand.  (Deleting rather than retrying will prevent
	// infinite loops.)  The server will do whatever it can to prevent this
	// occurring (by not indicating an action to clients that are behind the
	// requisite version for the action).
	responseActionUnknown responseAction = "RESPONSE_ACTION_UNKNOWN"
	// retryRequestLater - The client should retry the request later, via normal scheduling.
	retryRequestLater responseAction = "RETRY_REQUEST_LATER"
	// deleteRequest - The client should delete the request.  This action will apply for
	// successful requests, and non-retryable requests.
	deleteRequest responseAction = "DELETE_REQUEST"
)

// ComputeImageToolsLogExtension contains all log info, which should be align with sawmill server side configuration.
type ComputeImageToolsLogExtension struct {
	// This id is a random guid for correlation among multiple log lines of a single call
	ID            string       `json:"id"`
	CloudBuildID  string       `json:"cloud_build_id"`
	ToolAction    string       `json:"tool_action"`
	Status        string       `json:"status"`
	ElapsedTimeMs int64        `json:"elapsed_time_ms"`
	EventTimeMs   int64        `json:"event_time_ms"`
	InputParams   *InputParams `json:"input_params,omitempty"`
	OutputInfo    *OutputInfo  `json:"output_info,omitempty"`
}

// InputParams contains the union of all APIs' param info. To simplify logging service, we
// avoid defining different schemas for each API.
type InputParams struct {
	ImageImportParams        *ImageImportParams        `json:"image_import_input_params,omitempty"`
	ImageExportParams        *ImageExportParams        `json:"image_export_input_params,omitempty"`
	InstanceImportParams     *InstanceImportParams     `json:"instance_import_input_params,omitempty"`
	MachineImageImportParams *MachineImageImportParams `json:"machine_image_import_input_params,omitempty"`
	WindowsUpgradeParams     *WindowsUpgradeParams     `json:"windows_upgrade_input_params,omitempty"`
	OnestepImageImportParams *OnestepImageImportParams `json:"onestep_image_import_input_params,omitempty"`
}

// ImageImportParams contains all input params for image import
type ImageImportParams struct {
	*CommonParams

	ImageName          string `json:"image_name,omitempty"`
	DataDisk           bool   `json:"data_disk"`
	OS                 string `json:"os,omitempty"`
	SourceFile         string `json:"source_file,omitempty"`
	SourceImage        string `json:"source_image,omitempty"`
	NoGuestEnvironment bool   `json:"no_guest_environment"`
	Family             string `json:"family,omitempty"`
	Description        string `json:"description,omitempty"`
	NoExternalIP       bool   `json:"no_external_ip"`
	HasKmsKey          bool   `json:"has_kms_key"`
	HasKmsKeyring      bool   `json:"has_kms_keyring"`
	HasKmsLocation     bool   `json:"has_kms_location"`
	HasKmsProject      bool   `json:"has_kms_project"`
	StorageLocation    string `json:"storage_location,omitempty"`
}

// ImageExportParams contains all input params for image export
type ImageExportParams struct {
	*CommonParams

	DestinationURI string `json:"destination_uri,omitempty"`
	SourceImage    string `json:"source_image,omitempty"`
	Format         string `json:"format,omitempty"`
}

// OnestepImageImportParams contains all input params for onestep image import
type OnestepImageImportParams struct {
	*CommonParams

	// Image import params
	ImageName          string `json:"image_name,omitempty"`
	OS                 string `json:"os,omitempty"`
	NoGuestEnvironment bool   `json:"no_guest_environment"`
	Family             string `json:"family,omitempty"`
	Description        string `json:"description,omitempty"`
	NoExternalIP       bool   `json:"no_external_ip"`
	HasKmsKey          bool   `json:"has_kms_key"`
	HasKmsKeyring      bool   `json:"has_kms_keyring"`
	HasKmsLocation     bool   `json:"has_kms_location"`
	HasKmsProject      bool   `json:"has_kms_project"`
	StorageLocation    string `json:"storage_location,omitempty"`

	// AWS related params
	AWSAMIID             string `json:"aws_ami_id,omitempty"`
	AWSAMIExportLocation string `json:"aws_ami_export_location,omitempty"`
	AWSSourceAMIFilePath string `json:"aws_source_ami_file_path,omitempty"`
}

// InstanceImportParams contains all input params for instance import
type InstanceImportParams struct {
	*CommonParams

	InstanceName                string `json:"instance_name,omitempty"`
	OvfGcsPath                  string `json:"ovf_gcs_path,omitempty"`
	CanIPForward                bool   `json:"can_ip_forward"`
	DeletionProtection          bool   `json:"deletion_protection"`
	MachineType                 string `json:"machine_type,omitempty"`
	NetworkInterface            string `json:"network_interface,omitempty"`
	NetworkTier                 string `json:"network_tier,omitempty"`
	PrivateNetworkIP            string `json:"private_network_ip,omitempty"`
	NoExternalIP                bool   `json:"no_external_ip,omitempty"`
	NoRestartOnFailure          bool   `json:"no_restart_on_failure"`
	OS                          string `json:"os,omitempty"`
	ShieldedIntegrityMonitoring bool   `json:"shielded_integrity_monitoring"`
	ShieldedSecureBoot          bool   `json:"shielded_secure_boot"`
	ShieldedVtpm                bool   `json:"shielded_vtpm"`
	Tags                        string `json:"tags,omitempty"`
	HasBootDiskKmsKey           bool   `json:"has_boot_disk_kms_key"`
	HasBootDiskKmsKeyring       bool   `json:"has_boot_disk_kms_keyring"`
	HasBootDiskKmsLocation      bool   `json:"has_boot_disk_kms_location"`
	HasBootDiskKmsProject       bool   `json:"has_boot_disk_kms_project"`
	NoGuestEnvironment          bool   `json:"no_guest_environment"`
	NodeAffinityLabel           string `json:"node_affinity_label,omitempty"`
}

// MachineImageImportParams contains all input params for machine image import
type MachineImageImportParams struct {
	*CommonParams

	MachineImageName            string `json:"machine_image_name,omitempty"`
	OvfGcsPath                  string `json:"ovf_gcs_path,omitempty"`
	CanIPForward                bool   `json:"can_ip_forward"`
	DeletionProtection          bool   `json:"deletion_protection"`
	MachineType                 string `json:"machine_type,omitempty"`
	NetworkInterface            string `json:"network_interface,omitempty"`
	NetworkTier                 string `json:"network_tier,omitempty"`
	PrivateNetworkIP            string `json:"private_network_ip,omitempty"`
	NoExternalIP                bool   `json:"no_external_ip,omitempty"`
	NoRestartOnFailure          bool   `json:"no_restart_on_failure"`
	OS                          string `json:"os,omitempty"`
	ShieldedIntegrityMonitoring bool   `json:"shielded_integrity_monitoring"`
	ShieldedSecureBoot          bool   `json:"shielded_secure_boot"`
	ShieldedVtpm                bool   `json:"shielded_vtpm"`
	Tags                        string `json:"tags,omitempty"`
	HasBootDiskKmsKey           bool   `json:"has_boot_disk_kms_key"`
	HasBootDiskKmsKeyring       bool   `json:"has_boot_disk_kms_keyring"`
	HasBootDiskKmsLocation      bool   `json:"has_boot_disk_kms_location"`
	HasBootDiskKmsProject       bool   `json:"has_boot_disk_kms_project"`
	NoGuestEnvironment          bool   `json:"no_guest_environment"`
	NodeAffinityLabel           string `json:"node_affinity_label,omitempty"`
	Hostname                    string `json:"hostname,omitempty"`
	MachineImageStorageLocation string `json:"machine_image_storage_location,omitempty"`
}

// CommonParams is only used to organize the code without impacting hierarchy of data
type CommonParams struct {
	ClientID                string `json:"client_id,omitempty"`
	ClientVersion           string `json:"client_version,omitempty"`
	Network                 string `json:"network,omitempty"`
	Subnet                  string `json:"subnet,omitempty"`
	Zone                    string `json:"zone,omitempty"`
	Timeout                 string `json:"timeout,omitempty"`
	Project                 string `json:"project,omitempty"`
	ObfuscatedProject       string `json:"obfuscated_project,omitempty"`
	Labels                  string `json:"labels,omitempty"`
	ScratchBucketGcsPath    string `json:"scratch_bucket_gcs_path,omitempty"`
	Oauth                   string `json:"oauth,omitempty"`
	ComputeEndpointOverride string `json:"compute_endpoint_override,omitempty"`
	DisableGcsLogging       bool   `json:"disable_gcs_logging"`
	DisableCloudLogging     bool   `json:"disable_cloud_logging"`
	DisableStdoutLogging    bool   `json:"disable_stdout_logging"`
}

// WindowsUpgradeParams contains all input params for windows upgrade
type WindowsUpgradeParams struct {
	*CommonParams

	SourceOS               string `json:"source_os,omitempty"`
	TargetOS               string `json:"target_os,omitempty"`
	Instance               string `json:"instance,omitempty"`
	CreateMachineBackup    bool   `json:"create_machine_backup"`
	AutoRollback           bool   `json:"auto_rollback"`
	UseStagingInstallMedia bool   `json:"use_staging_install_media"`
}

// OutputInfo contains output values from the tools execution
type OutputInfo struct {
	// Size of import/export sources (image or file)
	SourcesSizeGb []int64 `json:"sources_size_gb,omitempty"`
	// Size of import/export targets (image or file)
	TargetsSizeGb []int64 `json:"targets_size_gb,omitempty"`
	// Failure message of the command
	FailureMessage string `json:"failure_message,omitempty"`
	// Failure message of the command without privacy info
	FailureMessageWithoutPrivacyInfo string `json:"failure_message_without_privacy_info,omitempty"`
	// ImportFileFormat shows what is the actual image format of the imported file
	ImportFileFormat string `json:"import_file_format,omitempty"`
	// Serial output from worker instances; only populated
	// if workflow failed.
	SerialOutputs []string `json:"serial_outputs,omitempty"`
	// Inflation type (qemu, API, etc)
	InflationType string `json:"inflation_type,omitempty"`
	// Inflation time (seconds)
	InflationTime []int64 `json:"inflation_time_ms,omitempty"`
	// Inflation time (seconds) of the shadow disk
	ShadowInflationTime []int64 `json:"shadow_inflation_time_ms,omitempty"`
	// Shadow disk match result for shadow disk inflater
	ShadowDiskMatchResult string `json:"shadow_disk_match_result,omitempty"`
	// Indicates whether UEFI_COMPATIBLE was added to the image's guestOSFeatures, either due to inspection or user request
	IsUEFICompatibleImage bool `json:"is_uefi_compatible_image,omitempty"`
	// Indicates whether the image is auto-detected to be UEFI compatible
	IsUEFIDetected bool `json:"is_uefi_detected,omitempty"`
	// InspectionResults contains metadata determined using automated inspection
	InspectionResults InspectionResults `json:"inspection_results,omitempty"`
}

// InspectionResults contains metadata determined using automated inspection
type InspectionResults struct {
	// UEFIBootable indicates whether the disk is bootable with UEFI.
	UEFIBootable bool `json:"uefi_bootable,omitempty"`

	// BIOSBootable indicates whether the disk is bootable with BIOS.
	BIOSBootable bool `json:"bios_bootable,omitempty"`

	// RootFS indicates the file system type of the partition containing
	// the root directory ("/").
	RootFS string `json:"root_fs,omitempty"`
}

func (l *Logger) updateParams(projectPointer *string) {
	l.mutex.Lock()
	defer l.mutex.Unlock()

	if projectPointer == nil {
		return
	}

	project := *projectPointer
	obfuscatedProject := Hash(project)

	if l.Params.ImageImportParams != nil {
		l.Params.ImageImportParams.CommonParams.Project = project
		l.Params.ImageImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.ImageExportParams != nil {
		l.Params.ImageExportParams.CommonParams.Project = project
		l.Params.ImageExportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.InstanceImportParams != nil {
		l.Params.InstanceImportParams.CommonParams.Project = project
		l.Params.InstanceImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.MachineImageImportParams != nil {
		l.Params.MachineImageImportParams.CommonParams.Project = project
		l.Params.MachineImageImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.OnestepImageImportParams != nil {
		l.Params.OnestepImageImportParams.CommonParams.Project = project
		l.Params.OnestepImageImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
}
<commit_msg>[log] switch fields order (#1403)<commit_after>//  Copyright 2019 Google 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 service

// logRequest is a server-side pre-defined data structure
type logRequest struct {
	ClientInfo    clientInfo `json:"client_info"`
	LogSource     int64      `json:"log_source"`
	RequestTimeMs int64      `json:"request_time_ms"`
	LogEvent      []logEvent `json:"log_event"`
}

// ClientInfo is a server-side pre-defined data structure
type clientInfo struct {
	// ClientType is defined on server side to clarify which client library is used.
	ClientType string `json:"client_type"`
}

// LogEvent is a server-side pre-defined data structure
type logEvent struct {
	EventTimeMs         int64  `json:"event_time_ms"`
	EventUptimeMs       int64  `json:"event_uptime_ms"`
	SourceExtensionJSON string `json:"source_extension_json"`
}

// logResponse is a server-side pre-defined data structure
type logResponse struct {
	NextRequestWaitMillis int64                `json:"NextRequestWaitMillis,string"`
	LogResponseDetails    []logResponseDetails `json:"LogResponseDetails"`
}

// LogResponseDetails is a server-side pre-defined data structure
type logResponseDetails struct {
	ResponseAction responseAction `json:"ResponseAction"`
}

// ResponseAction is a server-side pre-defined data structure
type responseAction string

const (
	// responseActionUnknown - If the client sees this, it should delete the logRequest (not retry).
	// It may indicate that a new response action was added, which the client
	// doesn't yet understand.  (Deleting rather than retrying will prevent
	// infinite loops.)  The server will do whatever it can to prevent this
	// occurring (by not indicating an action to clients that are behind the
	// requisite version for the action).
	responseActionUnknown responseAction = "RESPONSE_ACTION_UNKNOWN"
	// retryRequestLater - The client should retry the request later, via normal scheduling.
	retryRequestLater responseAction = "RETRY_REQUEST_LATER"
	// deleteRequest - The client should delete the request.  This action will apply for
	// successful requests, and non-retryable requests.
	deleteRequest responseAction = "DELETE_REQUEST"
)

// ComputeImageToolsLogExtension contains all log info, which should be align with sawmill server side configuration.
type ComputeImageToolsLogExtension struct {
	// This id is a random guid for correlation among multiple log lines of a single call
	ID            string       `json:"id"`
	CloudBuildID  string       `json:"cloud_build_id"`
	ToolAction    string       `json:"tool_action"`
	Status        string       `json:"status"`
	ElapsedTimeMs int64        `json:"elapsed_time_ms"`
	EventTimeMs   int64        `json:"event_time_ms"`
	InputParams   *InputParams `json:"input_params,omitempty"`
	OutputInfo    *OutputInfo  `json:"output_info,omitempty"`
}

// InputParams contains the union of all APIs' param info. To simplify logging service, we
// avoid defining different schemas for each API.
type InputParams struct {
	ImageImportParams        *ImageImportParams        `json:"image_import_input_params,omitempty"`
	ImageExportParams        *ImageExportParams        `json:"image_export_input_params,omitempty"`
	InstanceImportParams     *InstanceImportParams     `json:"instance_import_input_params,omitempty"`
	MachineImageImportParams *MachineImageImportParams `json:"machine_image_import_input_params,omitempty"`
	WindowsUpgradeParams     *WindowsUpgradeParams     `json:"windows_upgrade_input_params,omitempty"`
	OnestepImageImportParams *OnestepImageImportParams `json:"onestep_image_import_input_params,omitempty"`
}

// ImageImportParams contains all input params for image import
type ImageImportParams struct {
	*CommonParams

	ImageName          string `json:"image_name,omitempty"`
	DataDisk           bool   `json:"data_disk"`
	OS                 string `json:"os,omitempty"`
	SourceFile         string `json:"source_file,omitempty"`
	SourceImage        string `json:"source_image,omitempty"`
	NoGuestEnvironment bool   `json:"no_guest_environment"`
	Family             string `json:"family,omitempty"`
	Description        string `json:"description,omitempty"`
	NoExternalIP       bool   `json:"no_external_ip"`
	HasKmsKey          bool   `json:"has_kms_key"`
	HasKmsKeyring      bool   `json:"has_kms_keyring"`
	HasKmsLocation     bool   `json:"has_kms_location"`
	HasKmsProject      bool   `json:"has_kms_project"`
	StorageLocation    string `json:"storage_location,omitempty"`
}

// ImageExportParams contains all input params for image export
type ImageExportParams struct {
	*CommonParams

	DestinationURI string `json:"destination_uri,omitempty"`
	SourceImage    string `json:"source_image,omitempty"`
	Format         string `json:"format,omitempty"`
}

// OnestepImageImportParams contains all input params for onestep image import
type OnestepImageImportParams struct {
	*CommonParams

	// Image import params
	ImageName          string `json:"image_name,omitempty"`
	OS                 string `json:"os,omitempty"`
	NoGuestEnvironment bool   `json:"no_guest_environment"`
	Family             string `json:"family,omitempty"`
	Description        string `json:"description,omitempty"`
	NoExternalIP       bool   `json:"no_external_ip"`
	HasKmsKey          bool   `json:"has_kms_key"`
	HasKmsKeyring      bool   `json:"has_kms_keyring"`
	HasKmsLocation     bool   `json:"has_kms_location"`
	HasKmsProject      bool   `json:"has_kms_project"`
	StorageLocation    string `json:"storage_location,omitempty"`

	// AWS related params
	AWSAMIID             string `json:"aws_ami_id,omitempty"`
	AWSAMIExportLocation string `json:"aws_ami_export_location,omitempty"`
	AWSSourceAMIFilePath string `json:"aws_source_ami_file_path,omitempty"`
}

// InstanceImportParams contains all input params for instance import
type InstanceImportParams struct {
	*CommonParams

	InstanceName                string `json:"instance_name,omitempty"`
	OvfGcsPath                  string `json:"ovf_gcs_path,omitempty"`
	CanIPForward                bool   `json:"can_ip_forward"`
	DeletionProtection          bool   `json:"deletion_protection"`
	MachineType                 string `json:"machine_type,omitempty"`
	NetworkInterface            string `json:"network_interface,omitempty"`
	NetworkTier                 string `json:"network_tier,omitempty"`
	PrivateNetworkIP            string `json:"private_network_ip,omitempty"`
	NoExternalIP                bool   `json:"no_external_ip,omitempty"`
	NoRestartOnFailure          bool   `json:"no_restart_on_failure"`
	OS                          string `json:"os,omitempty"`
	ShieldedIntegrityMonitoring bool   `json:"shielded_integrity_monitoring"`
	ShieldedSecureBoot          bool   `json:"shielded_secure_boot"`
	ShieldedVtpm                bool   `json:"shielded_vtpm"`
	Tags                        string `json:"tags,omitempty"`
	HasBootDiskKmsKey           bool   `json:"has_boot_disk_kms_key"`
	HasBootDiskKmsKeyring       bool   `json:"has_boot_disk_kms_keyring"`
	HasBootDiskKmsLocation      bool   `json:"has_boot_disk_kms_location"`
	HasBootDiskKmsProject       bool   `json:"has_boot_disk_kms_project"`
	NoGuestEnvironment          bool   `json:"no_guest_environment"`
	NodeAffinityLabel           string `json:"node_affinity_label,omitempty"`
}

// MachineImageImportParams contains all input params for machine image import
type MachineImageImportParams struct {
	*CommonParams

	MachineImageName            string `json:"machine_image_name,omitempty"`
	OvfGcsPath                  string `json:"ovf_gcs_path,omitempty"`
	CanIPForward                bool   `json:"can_ip_forward"`
	DeletionProtection          bool   `json:"deletion_protection"`
	MachineType                 string `json:"machine_type,omitempty"`
	NetworkInterface            string `json:"network_interface,omitempty"`
	NetworkTier                 string `json:"network_tier,omitempty"`
	PrivateNetworkIP            string `json:"private_network_ip,omitempty"`
	NoExternalIP                bool   `json:"no_external_ip,omitempty"`
	NoRestartOnFailure          bool   `json:"no_restart_on_failure"`
	OS                          string `json:"os,omitempty"`
	ShieldedIntegrityMonitoring bool   `json:"shielded_integrity_monitoring"`
	ShieldedSecureBoot          bool   `json:"shielded_secure_boot"`
	ShieldedVtpm                bool   `json:"shielded_vtpm"`
	Tags                        string `json:"tags,omitempty"`
	HasBootDiskKmsKey           bool   `json:"has_boot_disk_kms_key"`
	HasBootDiskKmsKeyring       bool   `json:"has_boot_disk_kms_keyring"`
	HasBootDiskKmsLocation      bool   `json:"has_boot_disk_kms_location"`
	HasBootDiskKmsProject       bool   `json:"has_boot_disk_kms_project"`
	NoGuestEnvironment          bool   `json:"no_guest_environment"`
	NodeAffinityLabel           string `json:"node_affinity_label,omitempty"`
	Hostname                    string `json:"hostname,omitempty"`
	MachineImageStorageLocation string `json:"machine_image_storage_location,omitempty"`
}

// CommonParams is only used to organize the code without impacting hierarchy of data
type CommonParams struct {
	ClientID                string `json:"client_id,omitempty"`
	ClientVersion           string `json:"client_version,omitempty"`
	Network                 string `json:"network,omitempty"`
	Subnet                  string `json:"subnet,omitempty"`
	Zone                    string `json:"zone,omitempty"`
	Timeout                 string `json:"timeout,omitempty"`
	Project                 string `json:"project,omitempty"`
	ObfuscatedProject       string `json:"obfuscated_project,omitempty"`
	Labels                  string `json:"labels,omitempty"`
	ScratchBucketGcsPath    string `json:"scratch_bucket_gcs_path,omitempty"`
	Oauth                   string `json:"oauth,omitempty"`
	ComputeEndpointOverride string `json:"compute_endpoint_override,omitempty"`
	DisableGcsLogging       bool   `json:"disable_gcs_logging"`
	DisableCloudLogging     bool   `json:"disable_cloud_logging"`
	DisableStdoutLogging    bool   `json:"disable_stdout_logging"`
}

// WindowsUpgradeParams contains all input params for windows upgrade
type WindowsUpgradeParams struct {
	*CommonParams

	SourceOS               string `json:"source_os,omitempty"`
	TargetOS               string `json:"target_os,omitempty"`
	Instance               string `json:"instance,omitempty"`
	CreateMachineBackup    bool   `json:"create_machine_backup"`
	AutoRollback           bool   `json:"auto_rollback"`
	UseStagingInstallMedia bool   `json:"use_staging_install_media"`
}

// OutputInfo contains output values from the tools execution
type OutputInfo struct {
	// Size of import/export sources (image or file)
	SourcesSizeGb []int64 `json:"sources_size_gb,omitempty"`
	// Size of import/export targets (image or file)
	TargetsSizeGb []int64 `json:"targets_size_gb,omitempty"`
	// Failure message of the command
	FailureMessage string `json:"failure_message,omitempty"`
	// Failure message of the command without privacy info
	FailureMessageWithoutPrivacyInfo string `json:"failure_message_without_privacy_info,omitempty"`
	// ImportFileFormat shows what is the actual image format of the imported file
	ImportFileFormat string `json:"import_file_format,omitempty"`
	// Serial output from worker instances; only populated
	// if workflow failed.
	SerialOutputs []string `json:"serial_outputs,omitempty"`
	// Inflation type (qemu, API, etc)
	InflationType string `json:"inflation_type,omitempty"`
	// Inflation time (seconds)
	InflationTime []int64 `json:"inflation_time_ms,omitempty"`
	// Inflation time (seconds) of the shadow disk
	ShadowInflationTime []int64 `json:"shadow_inflation_time_ms,omitempty"`
	// Shadow disk match result for shadow disk inflater
	ShadowDiskMatchResult string `json:"shadow_disk_match_result,omitempty"`
	// Indicates whether UEFI_COMPATIBLE was added to the image's guestOSFeatures, either due to inspection or user request
	IsUEFICompatibleImage bool `json:"is_uefi_compatible_image,omitempty"`
	// Indicates whether the image is auto-detected to be UEFI compatible
	IsUEFIDetected bool `json:"is_uefi_detected,omitempty"`
	// InspectionResults contains metadata determined using automated inspection
	InspectionResults InspectionResults `json:"inspection_results,omitempty"`
}

// InspectionResults contains metadata determined using automated inspection
type InspectionResults struct {
	// BIOSBootable indicates whether the disk is bootable with BIOS.
	BIOSBootable bool `json:"bios_bootable,omitempty"`

	// UEFIBootable indicates whether the disk is bootable with UEFI.
	UEFIBootable bool `json:"uefi_bootable,omitempty"`

	// RootFS indicates the file system type of the partition containing
	// the root directory ("/").
	RootFS string `json:"root_fs,omitempty"`
}

func (l *Logger) updateParams(projectPointer *string) {
	l.mutex.Lock()
	defer l.mutex.Unlock()

	if projectPointer == nil {
		return
	}

	project := *projectPointer
	obfuscatedProject := Hash(project)

	if l.Params.ImageImportParams != nil {
		l.Params.ImageImportParams.CommonParams.Project = project
		l.Params.ImageImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.ImageExportParams != nil {
		l.Params.ImageExportParams.CommonParams.Project = project
		l.Params.ImageExportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.InstanceImportParams != nil {
		l.Params.InstanceImportParams.CommonParams.Project = project
		l.Params.InstanceImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.MachineImageImportParams != nil {
		l.Params.MachineImageImportParams.CommonParams.Project = project
		l.Params.MachineImageImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
	if l.Params.OnestepImageImportParams != nil {
		l.Params.OnestepImageImportParams.CommonParams.Project = project
		l.Params.OnestepImageImportParams.CommonParams.ObfuscatedProject = obfuscatedProject
	}
}
<|endoftext|>


<commit_before>package app

import (
	"sync"

	"github.com/docker/engine-api/types"
	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
	"github.com/nsf/termbox-go"
)

type commandToExecute struct {
	command   docker.Command
	container types.Container
}
type containersScreenEventHandler struct {
	dry                  *Dry
	screen               *ui.Screen
	keyboardQueueForView chan termbox.Event
	closeView            chan struct{}
}

func (h containersScreenEventHandler) handle(renderChan chan<- struct{}, event termbox.Event) bool {
	focus := true
	dry := h.dry
	screen := h.screen
	cursor := screen.Cursor
	cursorPos := cursor.Position()
	//Controls if the event has been handled by the first switch statement
	handled := true
	switch event.Key {
	case termbox.KeyArrowUp: //cursor up
		cursor.ScrollCursorUp()
	case termbox.KeyArrowDown: // cursor down
		cursor.ScrollCursorDown()
	case termbox.KeyF1: //sort
		dry.Sort()
	case termbox.KeyF2: //show all containers
		cursor.Reset()
		dry.ToggleShowAllContainers()
	case termbox.KeyF5: // refresh
		dry.Refresh()
	case termbox.KeyF9: // docker events
		dry.ShowDockerEvents()
		focus = false
		go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
	case termbox.KeyF10: // docker info
		dry.ShowInfo()
		focus = false
		go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
	case termbox.KeyCtrlE: //remove all stopped
		dry.RemoveAllStoppedContainers()
	case termbox.KeyCtrlK: //kill
		dry.KillAt(cursorPos)
	case termbox.KeyCtrlR: //start
		dry.RestartContainerAt(cursorPos)
	case termbox.KeyCtrlT: //stop
		dry.StopContainerAt(cursorPos)
	case termbox.KeyEnter: //inspect
		if cursorPos >= 0 {
			focus = false
			go showContainerOptions(h, dry, screen, h.keyboardQueueForView, h.closeView)
		}
	default: //Not handled
		handled = false
	}
	if !handled {
		switch event.Ch {
		case 's', 'S': //stats
			if cursorPos >= 0 {
				container, err := dry.ContainerAt(cursorPos)
				focus = false
				if err == nil {
					h.handleCommand(commandToExecute{
						docker.STATS,
						container,
					})
				} else {
					ui.ShowErrorMessage(screen, h.keyboardQueueForView, h.closeView, err)
				}
			}
		case 'i', 'I': //inspect
			if cursorPos >= 0 {
				focus = false
				container, err := dry.ContainerAt(cursorPos)
				if err == nil {
					h.handleCommand(commandToExecute{
						docker.INSPECT,
						container,
					})
				} else {
					ui.ShowErrorMessage(screen, h.keyboardQueueForView, h.closeView, err)
				}
			}
		case 'l', 'L': //logs
			if cursorPos >= 0 {
				focus = false
				container, err := dry.ContainerAt(cursorPos)
				if err == nil {
					h.handleCommand(commandToExecute{
						docker.LOGS,
						container,
					})
				} else {
					ui.ShowErrorMessage(screen, h.keyboardQueueForView, h.closeView, err)
				}
			}
		case '?', 'h', 'H': //help
			focus = false
			dry.ShowHelp()
			go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
		case '2':
			cursor.Reset()
			dry.ShowImages()
		case '3':
			cursor.Reset()
			dry.ShowNetworks()
		case 'e', 'E': //remove
			if cursorPos >= 0 {
				dry.RmAt(cursorPos)
				cursor.ScrollCursorDown()
			}
		}
	}
	if focus {
		renderChan <- struct{}{}
	}
	return focus
}

func (h containersScreenEventHandler) handleCommand(command commandToExecute) {
	focus := true
	dry := h.dry
	screen := h.screen

	id := command.container.ID

	switch command.command {
	case docker.KILL:
		dry.Kill(id)
	case docker.RESTART:
		dry.RestartContainer(id)
	case docker.STOP:
		dry.StopContainer(id)
	case docker.LOGS:
		if logs, err := dry.Logs(id); err == nil {
			focus = false
			go appui.Stream(screen, logs, h.keyboardQueueForView, h.closeView)
		}
	case docker.STATS:
		focus = false
		go statsScreen(command.container, screen, dry, h.keyboardQueueForView, h.closeView)
	case docker.INSPECT:
		dry.Inspect(id)
		focus = false
		go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
	}
	if focus {
		h.closeView <- struct{}{}
	}
}

//statsScreen shows container stats on the screen
//TODO move to appui
func statsScreen(container types.Container, screen *ui.Screen, dry *Dry, keyboardQueue chan termbox.Event, closeView chan<- struct{}) {
	closeViewOnExit := true
	screen.Clear()

	defer func() {
		if closeViewOnExit {
			closeView <- struct{}{}
		}
	}()

	if !docker.IsContainerRunning(container) {
		return
	}

	stats, done, err := dry.Stats(container.ID)
	if err != nil {
		closeViewOnExit = false
		ui.ShowErrorMessage(screen, keyboardQueue, closeView, err)
		return
	}
	info, infoLines := appui.NewContainerInfo(container)
	screen.Render(1, info)

	var mutex = &sync.Mutex{}
	screen.Flush()

loop:
	for {
		select {
		case event := <-keyboardQueue:
			switch event.Type {
			case termbox.EventKey:
				if event.Key == termbox.KeyEsc {
					//the lock is acquired before breaking the loop
					mutex.Lock()
					stats = nil
				}
			}
		case s := <-stats:
			{
				mutex.Lock()
				screen.RenderBufferer(
					appui.NewDockerStatsBufferer(
						s, 0, infoLines+3, screen.Height, screen.Width)...)
				screen.Flush()
				mutex.Unlock()
			}
		}
		if stats == nil {
			break loop
		}
	}
	//cleanup before exiting, the screen is cleared and the lock released
	screen.Clear()
	screen.Sync()
	mutex.Unlock()
	close(done)
}

//statsScreen shows container stats on the screen
func showContainerOptions(h containersScreenEventHandler, dry *Dry, screen *ui.Screen, keyboardQueue chan termbox.Event, closeView chan<- struct{}) {

	//TODO handle error
	container, _ := dry.ContainerAt(screen.Cursor.Position())
	screen.Clear()
	screen.Sync()
	screen.Cursor.Reset()

	info, infoLines := appui.NewContainerInfo(container)
	screen.RenderLineWithBackGround(0, screen.Height-1, commandsMenuBar, ui.MenuBarBackgroundColor)
	screen.Render(1, info)
	l := appui.NewContainerCommands(container,
		0,
		infoLines+1,
		screen.Height-appui.MainScreenFooterSize-infoLines-1,
		screen.Width)
	commandsLen := len(l.Commands)
	refreshChan := make(chan struct{}, 1)
	var command docker.CommandDescription
	refreshChan <- struct{}{}

	go func() {
		for {
			_, ok := <-refreshChan
			if ok {
				markSelectedCommand(l.Commands, screen.Cursor.Position())
				screen.RenderBufferer(l.List)
				screen.Flush()
			} else {
				return
			}
		}
	}()

loop:
	for {
		select {
		case event := <-keyboardQueue:
			switch event.Type {
			case termbox.EventKey:
				if event.Key == termbox.KeyEsc {
					close(refreshChan)
					break loop
				} else if event.Key == termbox.KeyArrowUp { //cursor up
					if screen.Cursor.Position() > 0 {
						screen.Cursor.ScrollCursorUp()
						refreshChan <- struct{}{}
					}
				} else if event.Key == termbox.KeyArrowDown { // cursor down
					if screen.Cursor.Position() < commandsLen-1 {
						screen.Cursor.ScrollCursorDown()
						refreshChan <- struct{}{}
					}
				} else if event.Key == termbox.KeyEnter { // execute command
					command = docker.ContainerCommands[screen.Cursor.Position()]
					close(refreshChan)
					break loop
				}
			}
		}
	}

	screen.Clear()
	screen.Sync()
	screen.Cursor.Reset()

	if (docker.CommandDescription{}) != command {
		h.handleCommand(
			commandToExecute{
				command.Command,
				container,
			})
	} else {
		//view is closed here if there is not a command to execute
		closeView <- struct{}{}
	}
}

//adds an arrow character before the command description on the given index
func markSelectedCommand(commands []string, index int) {
	copy(commands, docker.CommandDescriptions)
	commands[index] = replaceAtIndex(
		commands[index],
		appui.RightArrow,
		0)
}

func replaceAtIndex(str string, replacement string, index int) string {
	return str[:index] + replacement + str[index+1:]
}
<commit_msg>Handler error retrieving a container before showing container options<commit_after>package app

import (
	"sync"

	"github.com/docker/engine-api/types"
	"github.com/moncho/dry/appui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
	"github.com/nsf/termbox-go"
)

type commandToExecute struct {
	command   docker.Command
	container types.Container
}
type containersScreenEventHandler struct {
	dry                  *Dry
	screen               *ui.Screen
	keyboardQueueForView chan termbox.Event
	closeView            chan struct{}
}

func (h containersScreenEventHandler) handle(renderChan chan<- struct{}, event termbox.Event) bool {
	focus := true
	dry := h.dry
	screen := h.screen
	cursor := screen.Cursor
	cursorPos := cursor.Position()
	//Controls if the event has been handled by the first switch statement
	handled := true
	switch event.Key {
	case termbox.KeyArrowUp: //cursor up
		cursor.ScrollCursorUp()
	case termbox.KeyArrowDown: // cursor down
		cursor.ScrollCursorDown()
	case termbox.KeyF1: //sort
		dry.Sort()
	case termbox.KeyF2: //show all containers
		cursor.Reset()
		dry.ToggleShowAllContainers()
	case termbox.KeyF5: // refresh
		dry.Refresh()
	case termbox.KeyF9: // docker events
		dry.ShowDockerEvents()
		focus = false
		go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
	case termbox.KeyF10: // docker info
		dry.ShowInfo()
		focus = false
		go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
	case termbox.KeyCtrlE: //remove all stopped
		dry.RemoveAllStoppedContainers()
	case termbox.KeyCtrlK: //kill
		dry.KillAt(cursorPos)
	case termbox.KeyCtrlR: //start
		dry.RestartContainerAt(cursorPos)
	case termbox.KeyCtrlT: //stop
		dry.StopContainerAt(cursorPos)
	case termbox.KeyEnter: //inspect
		focus = false
		go showContainerOptions(h, dry, screen, h.keyboardQueueForView, h.closeView)
	default: //Not handled
		handled = false
	}
	if !handled {
		switch event.Ch {
		case 's', 'S': //stats
			if cursorPos >= 0 {
				container, err := dry.ContainerAt(cursorPos)
				focus = false
				if err == nil {
					h.handleCommand(commandToExecute{
						docker.STATS,
						container,
					})
				} else {
					ui.ShowErrorMessage(screen, h.keyboardQueueForView, h.closeView, err)
				}
			}
		case 'i', 'I': //inspect
			if cursorPos >= 0 {
				focus = false
				container, err := dry.ContainerAt(cursorPos)
				if err == nil {
					h.handleCommand(commandToExecute{
						docker.INSPECT,
						container,
					})
				} else {
					ui.ShowErrorMessage(screen, h.keyboardQueueForView, h.closeView, err)
				}
			}
		case 'l', 'L': //logs
			if cursorPos >= 0 {
				focus = false
				container, err := dry.ContainerAt(cursorPos)
				if err == nil {
					h.handleCommand(commandToExecute{
						docker.LOGS,
						container,
					})
				} else {
					ui.ShowErrorMessage(screen, h.keyboardQueueForView, h.closeView, err)
				}
			}
		case '?', 'h', 'H': //help
			focus = false
			dry.ShowHelp()
			go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
		case '2':
			cursor.Reset()
			dry.ShowImages()
		case '3':
			cursor.Reset()
			dry.ShowNetworks()
		case 'e', 'E': //remove
			if cursorPos >= 0 {
				dry.RmAt(cursorPos)
				cursor.ScrollCursorDown()
			}
		}
	}
	if focus {
		renderChan <- struct{}{}
	}
	return focus
}

func (h containersScreenEventHandler) handleCommand(command commandToExecute) {
	focus := true
	dry := h.dry
	screen := h.screen

	id := command.container.ID

	switch command.command {
	case docker.KILL:
		dry.Kill(id)
	case docker.RESTART:
		dry.RestartContainer(id)
	case docker.STOP:
		dry.StopContainer(id)
	case docker.LOGS:
		if logs, err := dry.Logs(id); err == nil {
			focus = false
			go appui.Stream(screen, logs, h.keyboardQueueForView, h.closeView)
		}
	case docker.STATS:
		focus = false
		go statsScreen(command.container, screen, dry, h.keyboardQueueForView, h.closeView)
	case docker.INSPECT:
		dry.Inspect(id)
		focus = false
		go appui.Less(renderDry(dry), screen, h.keyboardQueueForView, h.closeView)
	}
	if focus {
		h.closeView <- struct{}{}
	}
}

//statsScreen shows container stats on the screen
//TODO move to appui
func statsScreen(container types.Container, screen *ui.Screen, dry *Dry, keyboardQueue chan termbox.Event, closeView chan<- struct{}) {
	closeViewOnExit := true
	screen.Clear()

	defer func() {
		if closeViewOnExit {
			closeView <- struct{}{}
		}
	}()

	if !docker.IsContainerRunning(container) {
		return
	}

	stats, done, err := dry.Stats(container.ID)
	if err != nil {
		closeViewOnExit = false
		ui.ShowErrorMessage(screen, keyboardQueue, closeView, err)
		return
	}
	info, infoLines := appui.NewContainerInfo(container)
	screen.Render(1, info)

	var mutex = &sync.Mutex{}
	screen.Flush()

loop:
	for {
		select {
		case event := <-keyboardQueue:
			switch event.Type {
			case termbox.EventKey:
				if event.Key == termbox.KeyEsc {
					//the lock is acquired before breaking the loop
					mutex.Lock()
					stats = nil
				}
			}
		case s := <-stats:
			{
				mutex.Lock()
				screen.RenderBufferer(
					appui.NewDockerStatsBufferer(
						s, 0, infoLines+3, screen.Height, screen.Width)...)
				screen.Flush()
				mutex.Unlock()
			}
		}
		if stats == nil {
			break loop
		}
	}
	//cleanup before exiting, the screen is cleared and the lock released
	screen.Clear()
	screen.Sync()
	mutex.Unlock()
	close(done)
}

//statsScreen shows container stats on the screen
func showContainerOptions(h containersScreenEventHandler, dry *Dry, screen *ui.Screen, keyboardQueue chan termbox.Event, closeView chan<- struct{}) {

	//TODO handle error
	container, err := dry.ContainerAt(screen.Cursor.Position())
	if err == nil {
		screen.Clear()
		screen.Sync()
		screen.Cursor.Reset()

		info, infoLines := appui.NewContainerInfo(container)
		screen.RenderLineWithBackGround(0, screen.Height-1, commandsMenuBar, ui.MenuBarBackgroundColor)
		screen.Render(1, info)
		l := appui.NewContainerCommands(container,
			0,
			infoLines+1,
			screen.Height-appui.MainScreenFooterSize-infoLines-1,
			screen.Width)
		commandsLen := len(l.Commands)
		refreshChan := make(chan struct{}, 1)
		var command docker.CommandDescription
		refreshChan <- struct{}{}

		go func() {
			for {
				_, ok := <-refreshChan
				if ok {
					markSelectedCommand(l.Commands, screen.Cursor.Position())
					screen.RenderBufferer(l.List)
					screen.Flush()
				} else {
					return
				}
			}
		}()

	loop:
		for {
			select {
			case event := <-keyboardQueue:
				switch event.Type {
				case termbox.EventKey:
					if event.Key == termbox.KeyEsc {
						close(refreshChan)
						break loop
					} else if event.Key == termbox.KeyArrowUp { //cursor up
						if screen.Cursor.Position() > 0 {
							screen.Cursor.ScrollCursorUp()
							refreshChan <- struct{}{}
						}
					} else if event.Key == termbox.KeyArrowDown { // cursor down
						if screen.Cursor.Position() < commandsLen-1 {
							screen.Cursor.ScrollCursorDown()
							refreshChan <- struct{}{}
						}
					} else if event.Key == termbox.KeyEnter { // execute command
						command = docker.ContainerCommands[screen.Cursor.Position()]
						close(refreshChan)
						break loop
					}
				}
			}
		}

		screen.Clear()
		screen.Sync()
		screen.Cursor.Reset()

		if (docker.CommandDescription{}) != command {
			h.handleCommand(
				commandToExecute{
					command.Command,
					container,
				})
		} else {
			//view is closed here if there is not a command to execute
			closeView <- struct{}{}
		}
	} else {
		//view is closed here if there is not a command to execute
		closeView <- struct{}{}
	}
}

//adds an arrow character before the command description on the given index
func markSelectedCommand(commands []string, index int) {
	copy(commands, docker.CommandDescriptions)
	commands[index] = replaceAtIndex(
		commands[index],
		appui.RightArrow,
		0)
}

func replaceAtIndex(str string, replacement string, index int) string {
	return str[:index] + replacement + str[index+1:]
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cert

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"math"
	"math/big"
	"net"
	"time"
)

const (
	rsaKeySize   = 2048
	duration365d = time.Hour * 24 * 365
)

// Config contains the basic fields required for creating a certificate
type Config struct {
	CommonName   string
	Organization []string
	AltNames     AltNames
	Usages       []x509.ExtKeyUsage
}

// AltNames contains the domain names and IP addresses that will be added
// to the API Server's x509 certificate SubAltNames field. The values will
// be passed directly to the x509.Certificate object.
type AltNames struct {
	DNSNames []string
	IPs      []net.IP
}

// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
	return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}

// NewSelfSignedCACert creates a CA certificate
func NewSelfSignedCACert(cfg Config, key *rsa.PrivateKey) (*x509.Certificate, error) {
	now := time.Now()
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		NotBefore:             now.UTC(),
		NotAfter:              now.Add(duration365d * 10).UTC(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg Config, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
	serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
	if err != nil {
		return nil, err
	}
	if len(cfg.CommonName) == 0 {
		return nil, errors.New("must specify a CommonName")
	}
	if len(cfg.Usages) == 0 {
		return nil, errors.New("must specify at least one ExtKeyUsage")
	}

	certTmpl := x509.Certificate{
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		DNSNames:     cfg.AltNames.DNSNames,
		IPAddresses:  cfg.AltNames.IPs,
		SerialNumber: serial,
		NotBefore:    caCert.NotBefore,
		NotAfter:     time.Now().Add(duration365d).UTC(),
		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  cfg.Usages,
	}
	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// MakeEllipticPrivateKeyPEM creates an ECDSA private key
func MakeEllipticPrivateKeyPEM() ([]byte, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
	if err != nil {
		return nil, err
	}

	derBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	privateKeyPemBlock := &pem.Block{
		Type:  ECPrivateKeyBlockType,
		Bytes: derBytes,
	}
	return pem.EncodeToMemory(privateKeyPemBlock), nil
}

// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name
// You may also specify additional subject alt names (either ip or dns names) for the certificate
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
	template.DNSNames = append(template.DNSNames, alternateDNS...)

	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return nil, nil, err
	}

	// Generate cert
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
		return nil, nil, err
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		return nil, nil, err
	}

	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}

// FormatBytesCert receives byte array certificate and formats in human-readable format
func FormatBytesCert(cert []byte) (string, error) {
	block, _ := pem.Decode(cert)
	c, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse certificate [%v]", err)
	}
	return FormatCert(c), nil
}

// FormatCert receives certificate and formats in human-readable format
func FormatCert(c *x509.Certificate) string {
	var ips []string
	for _, ip := range c.IPAddresses {
		ips = append(ips, ip.String())
	}
	altNames := append(ips, c.DNSNames...)
	res := fmt.Sprintf(
		"Issuer: CN=%s | Subject: CN=%s | CA: %t\n",
		c.Issuer.CommonName, c.Subject.CommonName, c.IsCA,
	)
	res += fmt.Sprintf("Not before: %s Not After: %s", c.NotBefore, c.NotAfter)
	if len(altNames) > 0 {
		res += fmt.Sprintf("\nAlternate Names: %v", altNames)
	}
	return res
}
<commit_msg>Split self-signed cert and CA<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cert

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"math"
	"math/big"
	"net"
	"time"
)

const (
	rsaKeySize   = 2048
	duration365d = time.Hour * 24 * 365
)

// Config contains the basic fields required for creating a certificate
type Config struct {
	CommonName   string
	Organization []string
	AltNames     AltNames
	Usages       []x509.ExtKeyUsage
}

// AltNames contains the domain names and IP addresses that will be added
// to the API Server's x509 certificate SubAltNames field. The values will
// be passed directly to the x509.Certificate object.
type AltNames struct {
	DNSNames []string
	IPs      []net.IP
}

// NewPrivateKey creates an RSA private key
func NewPrivateKey() (*rsa.PrivateKey, error) {
	return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}

// NewSelfSignedCACert creates a CA certificate
func NewSelfSignedCACert(cfg Config, key *rsa.PrivateKey) (*x509.Certificate, error) {
	now := time.Now()
	tmpl := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(0),
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		NotBefore:             now.UTC(),
		NotAfter:              now.Add(duration365d * 10).UTC(),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg Config, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
	serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
	if err != nil {
		return nil, err
	}
	if len(cfg.CommonName) == 0 {
		return nil, errors.New("must specify a CommonName")
	}
	if len(cfg.Usages) == 0 {
		return nil, errors.New("must specify at least one ExtKeyUsage")
	}

	certTmpl := x509.Certificate{
		Subject: pkix.Name{
			CommonName:   cfg.CommonName,
			Organization: cfg.Organization,
		},
		DNSNames:     cfg.AltNames.DNSNames,
		IPAddresses:  cfg.AltNames.IPs,
		SerialNumber: serial,
		NotBefore:    caCert.NotBefore,
		NotAfter:     time.Now().Add(duration365d).UTC(),
		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  cfg.Usages,
	}
	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
	if err != nil {
		return nil, err
	}
	return x509.ParseCertificate(certDERBytes)
}

// MakeEllipticPrivateKeyPEM creates an ECDSA private key
func MakeEllipticPrivateKeyPEM() ([]byte, error) {
	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
	if err != nil {
		return nil, err
	}

	derBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		return nil, err
	}

	privateKeyPemBlock := &pem.Block{
		Type:  ECPrivateKeyBlockType,
		Bytes: derBytes,
	}
	return pem.EncodeToMemory(privateKeyPemBlock), nil
}

// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
// Host may be an IP or a DNS name
// You may also specify additional subject alt names (either ip or dns names) for the certificate
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
	caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	caTemplate := x509.Certificate{
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA: true,
	}

	caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	caCertificate, err := x509.ParseCertificate(caDERBytes)
	if err != nil {
		return nil, nil, err
	}

	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
	if err != nil {
		return nil, nil, err
	}

	template := x509.Certificate{
		SerialNumber: big.NewInt(2),
		Subject: pkix.Name{
			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(time.Hour * 24 * 365),

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
	template.DNSNames = append(template.DNSNames, alternateDNS...)

	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
	if err != nil {
		return nil, nil, err
	}

	// Generate cert, followed by ca
	certBuffer := bytes.Buffer{}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
		return nil, nil, err
	}
	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
		return nil, nil, err
	}

	// Generate key
	keyBuffer := bytes.Buffer{}
	if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
		return nil, nil, err
	}

	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
}

// FormatBytesCert receives byte array certificate and formats in human-readable format
func FormatBytesCert(cert []byte) (string, error) {
	block, _ := pem.Decode(cert)
	c, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse certificate [%v]", err)
	}
	return FormatCert(c), nil
}

// FormatCert receives certificate and formats in human-readable format
func FormatCert(c *x509.Certificate) string {
	var ips []string
	for _, ip := range c.IPAddresses {
		ips = append(ips, ip.String())
	}
	altNames := append(ips, c.DNSNames...)
	res := fmt.Sprintf(
		"Issuer: CN=%s | Subject: CN=%s | CA: %t\n",
		c.Issuer.CommonName, c.Subject.CommonName, c.IsCA,
	)
	res += fmt.Sprintf("Not before: %s Not After: %s", c.NotBefore, c.NotAfter)
	if len(altNames) > 0 {
		res += fmt.Sprintf("\nAlternate Names: %v", altNames)
	}
	return res
}
<|endoftext|>


<commit_before>package utils

import (
	"errors"
	"os"
	"path/filepath"
	"strings"
)

const sgitPrefix = "/.git/sgit"

func GetGitRootDir() (string, error) {
	out, err := Execute("git", "rev-parse", "--show-toplevel")
	gitRootDir := strings.TrimSpace(out)

	if err != nil {
		return gitRootDir, err
	}

	if exists, err := pathExists(gitRootDir); exists {
		return gitRootDir, err
	} else if err != nil {
		return gitRootDir, err
	} else {
		err := errors.New("Could not locate git repo.\n")
		return gitRootDir, err
	}
}

func NavToGitRootDir() error {
	gitRootDir, err := GetGitRootDir()
	if err != nil {
		return err
	}
	return os.Chdir(gitRootDir)
}

func MakeSgitRootDir() error {
	sgitRootDir, err := GetSgitRootDir()
	if err != nil {
		return err
	}
	return os.Mkdir(sgitRootDir, 0777)
}

func GetSgitRootDir() (string, error) {
	rootDir, err := GetGitRootDir()
	return filepath.Join(rootDir, sgitPrefix), err
}

func GetBranch() (string, error) {
	branch, err := Execute("git", "rev-parse", "--abbrev-ref", "HEAD")
	return strings.TrimSpace(branch), err
}

func pathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, err
	}
	return !os.IsNotExist(err), err
}
<commit_msg>Clean up errors<commit_after>package utils

import (
	"errors"
	"os"
	"path/filepath"
	"strings"
)

const sgitPrefix = "/.git/sgit"

func GetGitRootDir() (string, error) {
	out, err := Execute("git", "rev-parse", "--show-toplevel")
	gitRootDir := strings.TrimSpace(out)

	if err != nil {
		return gitRootDir, err
	}

	if exists, err := pathExists(gitRootDir); exists {
		return gitRootDir, err
	} else if err != nil {
		return gitRootDir, err
	} else {
		err := errors.New("Not a git repository")
		return gitRootDir, err
	}
}

func NavToGitRootDir() error {
	gitRootDir, err := GetGitRootDir()
	if err != nil {
		return err
	}
	return os.Chdir(gitRootDir)
}

func MakeSgitRootDir() error {
	sgitRootDir, err := GetSgitRootDir()
	if err != nil {
		return err
	}
	return os.Mkdir(sgitRootDir, 0777)
}

func GetSgitRootDir() (string, error) {
	rootDir, err := GetGitRootDir()
	return filepath.Join(rootDir, sgitPrefix), err
}

func GetBranch() (string, error) {
	branch, err := Execute("git", "rev-parse", "--abbrev-ref", "HEAD")
	return strings.TrimSpace(branch), err
}

func pathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, err
	}
	return !os.IsNotExist(err), err
}
<|endoftext|>


<commit_before>package utils

import (
	. "github.com/russross/blackfriday"
	"log"
	"regexp"
	"strings"
)

// 封装Markdown转换为Html的逻辑

const (
	//http://maruku.rubyforge.org/maruku.html#toc-generation
	TOC_MARKUP = "[toc]"
)

var (
	TOC_TITLE = "
文章导航:
"
)

var navRegex = regexp.MustCompile(`(?ismU)<nav>(.*)</nav>`)

func MarkdownToHtml(content string) (str string) {
	defer func() {
		e := recover()
		if e != nil {
			str = content
			log.Println("Render Markdown ERR:", e)
		}
	}()

	htmlFlags := 0

	if strings.Contains(content, TOC_MARKUP) {
		htmlFlags |= HTML_TOC
	}

	htmlFlags |= HTML_USE_XHTML
	htmlFlags |= HTML_USE_SMARTYPANTS
	htmlFlags |= HTML_SMARTYPANTS_FRACTIONS
	htmlFlags |= HTML_SMARTYPANTS_LATEX_DASHES
	renderer := HtmlRenderer(htmlFlags, "", "")

	// set up the parser
	extensions := 0
	extensions |= EXTENSION_NO_INTRA_EMPHASIS
	extensions |= EXTENSION_TABLES
	extensions |= EXTENSION_FENCED_CODE
	extensions |= EXTENSION_AUTOLINK
	extensions |= EXTENSION_STRIKETHROUGH
	extensions |= EXTENSION_SPACE_HEADERS

	str = string(Markdown([]byte(content), renderer, extensions))

	if htmlFlags&HTML_TOC != 0 {
		found := navRegex.FindIndex([]byte(str))
		if len(found) > 0 {
			toc := str[found[0]:found[1]]
			toc = TOC_TITLE + toc
			str = str[found[1]:]
			str = strings.Replace(str, TOC_MARKUP, toc, -1)
		}
	}
	return str
}
<commit_msg>fix theme<commit_after>package utils

import (
	. "github.com/russross/blackfriday"
	"log"
	"regexp"
	"strings"
)

// 封装Markdown转换为Html的逻辑

const (
	//http://maruku.rubyforge.org/maruku.html#toc-generation
	TOC_MARKUP = "[toc]"
)

var (
	TOC_TITLE = "
文章导航:
"
)

var navRegex = regexp.MustCompile(`(?ismU)<nav>(.*)</nav>`)
var checkboxRegex = regexp.MustCompile(``)

func MarkdownToHtml(content string) (str string) {
	defer func() {
		e := recover()
		if e != nil {
			str = content
			log.Println("Render Markdown ERR:", e)
		}
	}()

	htmlFlags := 0

	if strings.Contains(content, TOC_MARKUP) {
		htmlFlags |= HTML_TOC
	}

	htmlFlags |= HTML_USE_XHTML
	htmlFlags |= HTML_USE_SMARTYPANTS
	htmlFlags |= HTML_SMARTYPANTS_FRACTIONS
	htmlFlags |= HTML_SMARTYPANTS_LATEX_DASHES
	renderer := HtmlRenderer(htmlFlags, "", "")

	// set up the parser
	extensions := 0
	extensions |= EXTENSION_NO_INTRA_EMPHASIS
	extensions |= EXTENSION_TABLES
	extensions |= EXTENSION_FENCED_CODE
	extensions |= EXTENSION_AUTOLINK
	extensions |= EXTENSION_STRIKETHROUGH
	extensions |= EXTENSION_SPACE_HEADERS
	extensions |= EXTENSION_HARD_LINE_BREAK
	extensions |= EXTENSION_FOOTNOTES

	str = string(Markdown([]byte(content), renderer, extensions))

	if htmlFlags&HTML_TOC != 0 {
		found := navRegex.FindIndex([]byte(str))
		if len(found) > 0 {
			toc := str[found[0]:found[1]]
			toc = TOC_TITLE + toc
			str = str[found[1]:]
			str = strings.Replace(str, TOC_MARKUP, toc, -1)
		}
	}
	return str
}
<|endoftext|>