<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

// +\build linux

package procfs

import (
	"io/ioutil"
	"regexp"
	"sync"
	"time"

	"github.com/circonus-labs/circonus-agent/internal/builtins/collector"
	"github.com/circonus-labs/circonus-agent/internal/release"
	"github.com/circonus-labs/circonus-agent/internal/tags"
	cgm "github.com/circonus-labs/circonus-gometrics/v3"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
)

// common defines ProcFS metrics common elements
type common struct {
	id              string         // OPT id of the collector (used as metric name prefix)
	pkgID           string         // package prefix used for logging and errors
	procFSPath      string         // OPT procfs mount point path
	file            string         // the file in procfs
	lastEnd         time.Time      // last collection end time
	lastError       string         // last collection error
	lastMetrics     cgm.Metrics    // last metrics collected
	lastRunDuration time.Duration  // last collection duration
	lastStart       time.Time      // last collection start time
	logger          zerolog.Logger // collector logging instance
	metricNameChar  string         // OPT character(s) used as replacement for metricNameRegex
	metricNameRegex *regexp.Regexp // OPT regex for cleaning names, may be overridden in config
	running         bool           // is collector currently running
	runTTL          time.Duration  // OPT ttl for collectors (default is for every request)
	baseTags        tags.Tags
	sync.Mutex
}

// Define stubs to satisfy the collector.Collector interface.
//
// The individual collector implementations must override Collect and Flush.
//
// ID and Inventory are generic and do not need to be overridden unless the
// collector implementation requires it.

// Collect returns collector metrics
func (c *common) Collect() error {
	c.Lock()
	defer c.Unlock()
	return collector.ErrNotImplemented
}

// Flush returns last metrics collected
func (c *common) Flush() cgm.Metrics {
	c.Lock()
	defer c.Unlock()
	if c.lastMetrics == nil {
		c.lastMetrics = cgm.Metrics{}
	}
	return c.lastMetrics
}

// ID returns the id of the instance
func (c *common) ID() string {
	c.Lock()
	defer c.Unlock()
	return c.id
}

// Inventory returns collector stats for /inventory endpoint
func (c *common) Inventory() collector.InventoryStats {
	c.Lock()
	defer c.Unlock()
	return collector.InventoryStats{
		ID:              c.id,
		LastRunStart:    c.lastStart.Format(time.RFC3339Nano),
		LastRunEnd:      c.lastEnd.Format(time.RFC3339Nano),
		LastRunDuration: c.lastRunDuration.String(),
		LastError:       c.lastError,
	}
}

// Logger returns collector's instance of logger
func (c *common) Logger() zerolog.Logger {
	return c.logger
}

// cleanName is used to clean the metric name
func (c *common) cleanName(name string) string {
	// metric names are not dynamic for linux procfs - reintroduce cleaner if
	// procfs sources used return dirty dynamic names.
	//
	// return c.metricNameRegex.ReplaceAllString(name, c.metricNameChar)
	return name
}

// addMetric to internal buffer if metric is active
func (c *common) addMetric(metrics *cgm.Metrics, prefix string, mname, mtype string, mval interface{}, mtags tags.Tags) error {
	if metrics == nil {
		return errors.New("invalid metric submission")
	}

	if mname == "" {
		return errors.New("invalid metric, no name")
	}

	if mtype == "" {
		return errors.New("invalid metric, no type")
	}

	// cleanup the raw metric name, if needed
	mname = c.cleanName(mname)
	metricName := mname
	if prefix != "" {
		metricName = prefix + metricNameSeparator + mname
	}

	var tagList tags.Tags
	tagList = append(tagList, c.baseTags...)
	tagList = append(tagList, tags.Tags{
		tags.Tag{Category: "source", Value: release.NAME},
		tags.Tag{Category: "collector", Value: c.id},
	}...)
	tagList = append(tagList, mtags...)

	metricName = tags.MetricNameWithStreamTags(metricName, tagList)
	(*metrics)[metricName] = cgm.Metric{Type: mtype, Value: mval}

	return nil
}

// setStatus is used in Collect to set the collector status
func (c *common) setStatus(metrics cgm.Metrics, err error) {
	c.Lock()
	if err == nil {
		c.lastError = ""
		c.lastMetrics = metrics
	} else {
		c.lastError = err.Error()
		// on error, ensure metrics are reset
		// do not keep returning a stale set of metrics
		c.lastMetrics = cgm.Metrics{}
	}
	c.lastEnd = time.Now()
	if !c.lastStart.IsZero() {
		c.lastRunDuration = time.Since(c.lastStart)
	}
	c.running = false
	c.Unlock()
}

func (c *common) readFile(file string) ([]byte, error) {
	if file == "" {
		return nil, errors.New("invalid file (empty)")
	}
	return ioutil.ReadFile(file)
}
<commit_msg>upd: refactor common file reader<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

// +\build linux

package procfs

import (
	"bufio"
	"os"
	"regexp"
	"sync"
	"time"

	"github.com/circonus-labs/circonus-agent/internal/builtins/collector"
	"github.com/circonus-labs/circonus-agent/internal/release"
	"github.com/circonus-labs/circonus-agent/internal/tags"
	cgm "github.com/circonus-labs/circonus-gometrics/v3"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
)

// common defines ProcFS metrics common elements
type common struct {
	id              string         // OPT id of the collector (used as metric name prefix)
	pkgID           string         // package prefix used for logging and errors
	procFSPath      string         // OPT procfs mount point path
	file            string         // the file in procfs
	lastEnd         time.Time      // last collection end time
	lastError       string         // last collection error
	lastMetrics     cgm.Metrics    // last metrics collected
	lastRunDuration time.Duration  // last collection duration
	lastStart       time.Time      // last collection start time
	logger          zerolog.Logger // collector logging instance
	metricNameChar  string         // OPT character(s) used as replacement for metricNameRegex
	metricNameRegex *regexp.Regexp // OPT regex for cleaning names, may be overridden in config
	running         bool           // is collector currently running
	runTTL          time.Duration  // OPT ttl for collectors (default is for every request)
	baseTags        tags.Tags
	sync.Mutex
}

// Define stubs to satisfy the collector.Collector interface.
//
// The individual collector implementations must override Collect and Flush.
//
// ID and Inventory are generic and do not need to be overridden unless the
// collector implementation requires it.

// Collect returns collector metrics
func (c *common) Collect() error {
	c.Lock()
	defer c.Unlock()
	return collector.ErrNotImplemented
}

// Flush returns last metrics collected
func (c *common) Flush() cgm.Metrics {
	c.Lock()
	defer c.Unlock()
	if c.lastMetrics == nil {
		c.lastMetrics = cgm.Metrics{}
	}
	return c.lastMetrics
}

// ID returns the id of the instance
func (c *common) ID() string {
	c.Lock()
	defer c.Unlock()
	return c.id
}

// Inventory returns collector stats for /inventory endpoint
func (c *common) Inventory() collector.InventoryStats {
	c.Lock()
	defer c.Unlock()
	return collector.InventoryStats{
		ID:              c.id,
		LastRunStart:    c.lastStart.Format(time.RFC3339Nano),
		LastRunEnd:      c.lastEnd.Format(time.RFC3339Nano),
		LastRunDuration: c.lastRunDuration.String(),
		LastError:       c.lastError,
	}
}

// Logger returns collector's instance of logger
func (c *common) Logger() zerolog.Logger {
	return c.logger
}

// cleanName is used to clean the metric name
func (c *common) cleanName(name string) string {
	// metric names are not dynamic for linux procfs - reintroduce cleaner if
	// procfs sources used return dirty dynamic names.
	//
	// return c.metricNameRegex.ReplaceAllString(name, c.metricNameChar)
	return name
}

// addMetric to internal buffer if metric is active
func (c *common) addMetric(metrics *cgm.Metrics, prefix string, mname, mtype string, mval interface{}, mtags tags.Tags) error {
	if metrics == nil {
		return errors.New("invalid metric submission")
	}

	if mname == "" {
		return errors.New("invalid metric, no name")
	}

	if mtype == "" {
		return errors.New("invalid metric, no type")
	}

	// cleanup the raw metric name, if needed
	mname = c.cleanName(mname)
	metricName := mname
	if prefix != "" {
		metricName = prefix + metricNameSeparator + mname
	}

	var tagList tags.Tags
	tagList = append(tagList, c.baseTags...)
	tagList = append(tagList, tags.Tags{
		tags.Tag{Category: "source", Value: release.NAME},
		tags.Tag{Category: "collector", Value: c.id},
	}...)
	tagList = append(tagList, mtags...)

	metricName = tags.MetricNameWithStreamTags(metricName, tagList)
	(*metrics)[metricName] = cgm.Metric{Type: mtype, Value: mval}

	return nil
}

// setStatus is used in Collect to set the collector status
func (c *common) setStatus(metrics cgm.Metrics, err error) {
	c.Lock()
	if err == nil {
		c.lastError = ""
		c.lastMetrics = metrics
	} else {
		c.lastError = err.Error()
		// on error, ensure metrics are reset
		// do not keep returning a stale set of metrics
		c.lastMetrics = cgm.Metrics{}
	}
	c.lastEnd = time.Now()
	if !c.lastStart.IsZero() {
		c.lastRunDuration = time.Since(c.lastStart)
	}
	c.running = false
	c.Unlock()
}

func (c *common) readFile(file string) ([]string, error) {
	if file == "" {
		return nil, errors.New("invalid file (empty)")
	}

	f, err := os.Open(file)
	if err != nil {
		return nil, errors.Wrapf(err, "opening file (%s)", file)
	}

	var lines []string
	s := bufio.NewScanner(f)
	for s.Scan() {
		lines = append(lines, s.Text())
	}
	if err := s.Err(); err != nil {
		return lines, errors.Wrapf(err, "scanning file (%s) [close:%v]", file, f.Close())
	}

	return lines, f.Close()
}
<|endoftext|>


<commit_before>package cert

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"strings"
	"time"

	"github.com/golang/go/src/pkg/text/template"
)

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
Issuer:     {{.Issuer}}
NotBefore:  {{.NotBefore}}
NotAfter:   {{.NotAfter}}
CommonName: {{.CommonName}}
SANs:       {{.SANs}}
Error:      {{.Error}}

{{end}}
`

const markdownTempl = `DomainName | Issuer | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.Issuer}} | {{.NotBefore}} | {{.NotAfter}} | {{.CommonName}} | {{range .SANs}}{{.}}
{{end}} | {{.Error}}
{{end}}
`

type Certs []*Cert

type Cert struct {
	DomainName string
	Issuer     string
	CommonName string
	SANs       []string
	NotBefore  string
	NotAfter   string
	Error      string
}

func validate(s []string) error {
	if len(s) < 1 {
		return fmt.Errorf("Input at least one domain name.")
	}
	return nil
}

func NewCerts(s []string) (Certs, error) {
	if err := validate(s); err != nil {
		return nil, err
	}

	certs := Certs{}
	for _, d := range s[:] {
		certs = append(certs, NewCert(d))
	}
	return certs, nil
}

func NewAsyncCerts(s []string) (Certs, error) {
	if err := validate(s); err != nil {
		return nil, err
	}

	certs := Certs{}
	ch := make(chan *Cert, len(s))
	for _, d := range s[:] {
		go func(d string) {
			ch <- NewCert(d)
		}(d)
	}

	for range s[:] {
		c := <-ch
		certs = append(certs, c)
	}
	return certs, nil
}

func (certs Certs) String() string {
	var b bytes.Buffer
	t, err := template.New("default").Parse(defaultTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func (certs Certs) Markdown() string {
	var b bytes.Buffer
	t, err := template.New("markdown").Parse(markdownTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs.escapeStar()); err != nil {
		panic(err)
	}
	return b.String()
}

func NewCert(d string) *Cert {
	conn, err := tls.Dial("tcp", d+":443", &tls.Config{})
	if err != nil {
		return &Cert{DomainName: d, Error: err.Error()}
	}
	cert := conn.ConnectionState().PeerCertificates[0]
	conn.Close()
	return &Cert{
		DomainName: d,
		Issuer:     cert.Issuer.Organization[0],
		CommonName: cert.Subject.CommonName,
		SANs:       cert.DNSNames,
		NotBefore:  cert.NotBefore.In(time.Local).Format("2006/01/02 15:04:05"),
		NotAfter:   cert.NotAfter.In(time.Local).Format("2006/01/02 15:04:05"),
		Error:      "",
	}
}

func (certs Certs) escapeStar() Certs {
	for _, cert := range certs {
		for i, san := range cert.SANs {
			cert.SANs[i] = strings.Replace(san, "*", "\\*", -1)
		}
	}
	return certs
}
<commit_msg>Fix import pkg path<commit_after>package cert

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"strings"
	"text/template"
	"time"
)

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
Issuer:     {{.Issuer}}
NotBefore:  {{.NotBefore}}
NotAfter:   {{.NotAfter}}
CommonName: {{.CommonName}}
SANs:       {{.SANs}}
Error:      {{.Error}}

{{end}}
`

const markdownTempl = `DomainName | Issuer | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.Issuer}} | {{.NotBefore}} | {{.NotAfter}} | {{.CommonName}} | {{range .SANs}}{{.}}
{{end}} | {{.Error}}
{{end}}
`

type Certs []*Cert

type Cert struct {
	DomainName string
	Issuer     string
	CommonName string
	SANs       []string
	NotBefore  string
	NotAfter   string
	Error      string
}

func validate(s []string) error {
	if len(s) < 1 {
		return fmt.Errorf("Input at least one domain name.")
	}
	return nil
}

func NewCerts(s []string) (Certs, error) {
	if err := validate(s); err != nil {
		return nil, err
	}

	certs := Certs{}
	for _, d := range s[:] {
		certs = append(certs, NewCert(d))
	}
	return certs, nil
}

func NewAsyncCerts(s []string) (Certs, error) {
	if err := validate(s); err != nil {
		return nil, err
	}

	certs := Certs{}
	ch := make(chan *Cert, len(s))
	for _, d := range s[:] {
		go func(d string) {
			ch <- NewCert(d)
		}(d)
	}

	for range s[:] {
		c := <-ch
		certs = append(certs, c)
	}
	return certs, nil
}

func (certs Certs) String() string {
	var b bytes.Buffer
	t, err := template.New("default").Parse(defaultTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func (certs Certs) Markdown() string {
	var b bytes.Buffer
	t, err := template.New("markdown").Parse(markdownTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs.escapeStar()); err != nil {
		panic(err)
	}
	return b.String()
}

func NewCert(d string) *Cert {
	conn, err := tls.Dial("tcp", d+":443", &tls.Config{})
	if err != nil {
		return &Cert{DomainName: d, Error: err.Error()}
	}
	cert := conn.ConnectionState().PeerCertificates[0]
	conn.Close()
	return &Cert{
		DomainName: d,
		Issuer:     cert.Issuer.Organization[0],
		CommonName: cert.Subject.CommonName,
		SANs:       cert.DNSNames,
		NotBefore:  cert.NotBefore.In(time.Local).Format("2006/01/02 15:04:05"),
		NotAfter:   cert.NotAfter.In(time.Local).Format("2006/01/02 15:04:05"),
		Error:      "",
	}
}

func (certs Certs) escapeStar() Certs {
	for _, cert := range certs {
		for i, san := range cert.SANs {
			cert.SANs[i] = strings.Replace(san, "*", "\\*", -1)
		}
	}
	return certs
}
<|endoftext|>


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

package tls

import (
	"bytes"
	"crypto"
	"crypto/rsa"
	"crypto/subtle"
	"crypto/x509"
	"errors"
	"io"
	"strconv"
)

func (c *Conn) clientHandshake() error {
	finishedHash := newFinishedHash(versionTLS10)

	if c.config == nil {
		c.config = defaultConfig()
	}

	hello := &clientHelloMsg{
		vers:               maxVersion,
		cipherSuites:       c.config.cipherSuites(),
		compressionMethods: []uint8{compressionNone},
		random:             make([]byte, 32),
		ocspStapling:       true,
		serverName:         c.config.ServerName,
		supportedCurves:    []uint16{curveP256, curveP384, curveP521},
		supportedPoints:    []uint8{pointFormatUncompressed},
		nextProtoNeg:       len(c.config.NextProtos) > 0,
	}

	t := uint32(c.config.time().Unix())
	hello.random[0] = byte(t >> 24)
	hello.random[1] = byte(t >> 16)
	hello.random[2] = byte(t >> 8)
	hello.random[3] = byte(t)
	_, err := io.ReadFull(c.config.rand(), hello.random[4:])
	if err != nil {
		c.sendAlert(alertInternalError)
		return errors.New("short read from Rand")
	}

	finishedHash.Write(hello.marshal())
	c.writeRecord(recordTypeHandshake, hello.marshal())

	msg, err := c.readHandshake()
	if err != nil {
		return err
	}
	serverHello, ok := msg.(*serverHelloMsg)
	if !ok {
		return c.sendAlert(alertUnexpectedMessage)
	}
	finishedHash.Write(serverHello.marshal())

	vers, ok := mutualVersion(serverHello.vers)
	if !ok || vers < versionTLS10 {
		// TLS 1.0 is the minimum version supported as a client.
		return c.sendAlert(alertProtocolVersion)
	}
	c.vers = vers
	c.haveVers = true

	if serverHello.compressionMethod != compressionNone {
		return c.sendAlert(alertUnexpectedMessage)
	}

	if !hello.nextProtoNeg && serverHello.nextProtoNeg {
		c.sendAlert(alertHandshakeFailure)
		return errors.New("server advertised unrequested NPN")
	}

	suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
	if suite == nil {
		return c.sendAlert(alertHandshakeFailure)
	}

	msg, err = c.readHandshake()
	if err != nil {
		return err
	}
	certMsg, ok := msg.(*certificateMsg)
	if !ok || len(certMsg.certificates) == 0 {
		return c.sendAlert(alertUnexpectedMessage)
	}
	finishedHash.Write(certMsg.marshal())

	certs := make([]*x509.Certificate, len(certMsg.certificates))
	for i, asn1Data := range certMsg.certificates {
		cert, err := x509.ParseCertificate(asn1Data)
		if err != nil {
			c.sendAlert(alertBadCertificate)
			return errors.New("failed to parse certificate from server: " + err.Error())
		}
		certs[i] = cert
	}

	if !c.config.InsecureSkipVerify {
		opts := x509.VerifyOptions{
			Roots:         c.config.RootCAs,
			CurrentTime:   c.config.time(),
			DNSName:       c.config.ServerName,
			Intermediates: x509.NewCertPool(),
		}

		for i, cert := range certs {
			if i == 0 {
				continue
			}
			opts.Intermediates.AddCert(cert)
		}
		c.verifiedChains, err = certs[0].Verify(opts)
		if err != nil {
			c.sendAlert(alertBadCertificate)
			return err
		}
	}

	if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
		return c.sendAlert(alertUnsupportedCertificate)
	}

	c.peerCertificates = certs

	if serverHello.ocspStapling {
		msg, err = c.readHandshake()
		if err != nil {
			return err
		}
		cs, ok := msg.(*certificateStatusMsg)
		if !ok {
			return c.sendAlert(alertUnexpectedMessage)
		}
		finishedHash.Write(cs.marshal())

		if cs.statusType == statusTypeOCSP {
			c.ocspResponse = cs.response
		}
	}

	msg, err = c.readHandshake()
	if err != nil {
		return err
	}

	keyAgreement := suite.ka()

	skx, ok := msg.(*serverKeyExchangeMsg)
	if ok {
		finishedHash.Write(skx.marshal())
		err = keyAgreement.processServerKeyExchange(c.config, hello, serverHello, certs[0], skx)
		if err != nil {
			c.sendAlert(alertUnexpectedMessage)
			return err
		}

		msg, err = c.readHandshake()
		if err != nil {
			return err
		}
	}

	var certToSend *Certificate
	certReq, ok := msg.(*certificateRequestMsg)
	if ok {
		// RFC 4346 on the certificateAuthorities field:
		// A list of the distinguished names of acceptable certificate
		// authorities. These distinguished names may specify a desired
		// distinguished name for a root CA or for a subordinate CA;
		// thus, this message can be used to describe both known roots
		// and a desired authorization space. If the
		// certificate_authorities list is empty then the client MAY
		// send any certificate of the appropriate
		// ClientCertificateType, unless there is some external
		// arrangement to the contrary.

		finishedHash.Write(certReq.marshal())

		// For now, we only know how to sign challenges with RSA
		rsaAvail := false
		for _, certType := range certReq.certificateTypes {
			if certType == certTypeRSASign {
				rsaAvail = true
				break
			}
		}

		// We need to search our list of client certs for one
		// where SignatureAlgorithm is RSA and the Issuer is in
		// certReq.certificateAuthorities
	findCert:
		for i, cert := range c.config.Certificates {
			if !rsaAvail {
				continue
			}

			leaf := cert.Leaf
			if leaf == nil {
				if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil {
					c.sendAlert(alertInternalError)
					return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
				}
			}

			if leaf.PublicKeyAlgorithm != x509.RSA {
				continue
			}

			if len(certReq.certificateAuthorities) == 0 {
				// they gave us an empty list, so just take the
				// first RSA cert from c.config.Certificates
				certToSend = &cert
				break
			}

			for _, ca := range certReq.certificateAuthorities {
				if bytes.Equal(leaf.RawIssuer, ca) {
					certToSend = &cert
					break findCert
				}
			}
		}

		msg, err = c.readHandshake()
		if err != nil {
			return err
		}
	}

	shd, ok := msg.(*serverHelloDoneMsg)
	if !ok {
		return c.sendAlert(alertUnexpectedMessage)
	}
	finishedHash.Write(shd.marshal())

	if certToSend != nil {
		certMsg = new(certificateMsg)
		certMsg.certificates = certToSend.Certificate
		finishedHash.Write(certMsg.marshal())
		c.writeRecord(recordTypeHandshake, certMsg.marshal())
	}

	preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hello, certs[0])
	if err != nil {
		c.sendAlert(alertInternalError)
		return err
	}
	if ckx != nil {
		finishedHash.Write(ckx.marshal())
		c.writeRecord(recordTypeHandshake, ckx.marshal())
	}

	if certToSend != nil {
		certVerify := new(certificateVerifyMsg)
		digest := make([]byte, 0, 36)
		digest = finishedHash.serverMD5.Sum(digest)
		digest = finishedHash.serverSHA1.Sum(digest)
		signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
		if err != nil {
			return c.sendAlert(alertInternalError)
		}
		certVerify.signature = signed

		finishedHash.Write(certVerify.marshal())
		c.writeRecord(recordTypeHandshake, certVerify.marshal())
	}

	masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
		keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)

	clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
	clientHash := suite.mac(c.vers, clientMAC)
	c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
	c.writeRecord(recordTypeChangeCipherSpec, []byte{1})

	if serverHello.nextProtoNeg {
		nextProto := new(nextProtoMsg)
		proto, fallback := mutualProtocol(c.config.NextProtos, serverHello.nextProtos)
		nextProto.proto = proto
		c.clientProtocol = proto
		c.clientProtocolFallback = fallback

		finishedHash.Write(nextProto.marshal())
		c.writeRecord(recordTypeHandshake, nextProto.marshal())
	}

	finished := new(finishedMsg)
	finished.verifyData = finishedHash.clientSum(masterSecret)
	finishedHash.Write(finished.marshal())
	c.writeRecord(recordTypeHandshake, finished.marshal())

	serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */)
	serverHash := suite.mac(c.vers, serverMAC)
	c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
	c.readRecord(recordTypeChangeCipherSpec)
	if c.err != nil {
		return c.err
	}

	msg, err = c.readHandshake()
	if err != nil {
		return err
	}
	serverFinished, ok := msg.(*finishedMsg)
	if !ok {
		return c.sendAlert(alertUnexpectedMessage)
	}

	verify := finishedHash.serverSum(masterSecret)
	if len(verify) != len(serverFinished.verifyData) ||
		subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
		return c.sendAlert(alertHandshakeFailure)
	}

	c.handshakeComplete = true
	c.cipherSuite = suite.id
	return nil
}

// mutualProtocol finds the mutual Next Protocol Negotiation protocol given the
// set of client and server supported protocols. The set of client supported
// protocols must not be empty. It returns the resulting protocol and flag
// indicating if the fallback case was reached.
func mutualProtocol(clientProtos, serverProtos []string) (string, bool) {
	for _, s := range serverProtos {
		for _, c := range clientProtos {
			if s == c {
				return s, false
			}
		}
	}

	return clientProtos[0], true
}
<commit_msg>crypto/tls: always send a Certificate message if one was requested.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
	"bytes"
	"crypto"
	"crypto/rsa"
	"crypto/subtle"
	"crypto/x509"
	"errors"
	"io"
	"strconv"
)

func (c *Conn) clientHandshake() error {
	finishedHash := newFinishedHash(versionTLS10)

	if c.config == nil {
		c.config = defaultConfig()
	}

	hello := &clientHelloMsg{
		vers:               maxVersion,
		cipherSuites:       c.config.cipherSuites(),
		compressionMethods: []uint8{compressionNone},
		random:             make([]byte, 32),
		ocspStapling:       true,
		serverName:         c.config.ServerName,
		supportedCurves:    []uint16{curveP256, curveP384, curveP521},
		supportedPoints:    []uint8{pointFormatUncompressed},
		nextProtoNeg:       len(c.config.NextProtos) > 0,
	}

	t := uint32(c.config.time().Unix())
	hello.random[0] = byte(t >> 24)
	hello.random[1] = byte(t >> 16)
	hello.random[2] = byte(t >> 8)
	hello.random[3] = byte(t)
	_, err := io.ReadFull(c.config.rand(), hello.random[4:])
	if err != nil {
		c.sendAlert(alertInternalError)
		return errors.New("short read from Rand")
	}

	finishedHash.Write(hello.marshal())
	c.writeRecord(recordTypeHandshake, hello.marshal())

	msg, err := c.readHandshake()
	if err != nil {
		return err
	}
	serverHello, ok := msg.(*serverHelloMsg)
	if !ok {
		return c.sendAlert(alertUnexpectedMessage)
	}
	finishedHash.Write(serverHello.marshal())

	vers, ok := mutualVersion(serverHello.vers)
	if !ok || vers < versionTLS10 {
		// TLS 1.0 is the minimum version supported as a client.
		return c.sendAlert(alertProtocolVersion)
	}
	c.vers = vers
	c.haveVers = true

	if serverHello.compressionMethod != compressionNone {
		return c.sendAlert(alertUnexpectedMessage)
	}

	if !hello.nextProtoNeg && serverHello.nextProtoNeg {
		c.sendAlert(alertHandshakeFailure)
		return errors.New("server advertised unrequested NPN")
	}

	suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite)
	if suite == nil {
		return c.sendAlert(alertHandshakeFailure)
	}

	msg, err = c.readHandshake()
	if err != nil {
		return err
	}
	certMsg, ok := msg.(*certificateMsg)
	if !ok || len(certMsg.certificates) == 0 {
		return c.sendAlert(alertUnexpectedMessage)
	}
	finishedHash.Write(certMsg.marshal())

	certs := make([]*x509.Certificate, len(certMsg.certificates))
	for i, asn1Data := range certMsg.certificates {
		cert, err := x509.ParseCertificate(asn1Data)
		if err != nil {
			c.sendAlert(alertBadCertificate)
			return errors.New("failed to parse certificate from server: " + err.Error())
		}
		certs[i] = cert
	}

	if !c.config.InsecureSkipVerify {
		opts := x509.VerifyOptions{
			Roots:         c.config.RootCAs,
			CurrentTime:   c.config.time(),
			DNSName:       c.config.ServerName,
			Intermediates: x509.NewCertPool(),
		}

		for i, cert := range certs {
			if i == 0 {
				continue
			}
			opts.Intermediates.AddCert(cert)
		}
		c.verifiedChains, err = certs[0].Verify(opts)
		if err != nil {
			c.sendAlert(alertBadCertificate)
			return err
		}
	}

	if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
		return c.sendAlert(alertUnsupportedCertificate)
	}

	c.peerCertificates = certs

	if serverHello.ocspStapling {
		msg, err = c.readHandshake()
		if err != nil {
			return err
		}
		cs, ok := msg.(*certificateStatusMsg)
		if !ok {
			return c.sendAlert(alertUnexpectedMessage)
		}
		finishedHash.Write(cs.marshal())

		if cs.statusType == statusTypeOCSP {
			c.ocspResponse = cs.response
		}
	}

	msg, err = c.readHandshake()
	if err != nil {
		return err
	}

	keyAgreement := suite.ka()

	skx, ok := msg.(*serverKeyExchangeMsg)
	if ok {
		finishedHash.Write(skx.marshal())
		err = keyAgreement.processServerKeyExchange(c.config, hello, serverHello, certs[0], skx)
		if err != nil {
			c.sendAlert(alertUnexpectedMessage)
			return err
		}

		msg, err = c.readHandshake()
		if err != nil {
			return err
		}
	}

	var certToSend *Certificate
	var certRequested bool
	certReq, ok := msg.(*certificateRequestMsg)
	if ok {
		certRequested = true

		// RFC 4346 on the certificateAuthorities field:
		// A list of the distinguished names of acceptable certificate
		// authorities. These distinguished names may specify a desired
		// distinguished name for a root CA or for a subordinate CA;
		// thus, this message can be used to describe both known roots
		// and a desired authorization space. If the
		// certificate_authorities list is empty then the client MAY
		// send any certificate of the appropriate
		// ClientCertificateType, unless there is some external
		// arrangement to the contrary.

		finishedHash.Write(certReq.marshal())

		// For now, we only know how to sign challenges with RSA
		rsaAvail := false
		for _, certType := range certReq.certificateTypes {
			if certType == certTypeRSASign {
				rsaAvail = true
				break
			}
		}

		// We need to search our list of client certs for one
		// where SignatureAlgorithm is RSA and the Issuer is in
		// certReq.certificateAuthorities
	findCert:
		for i, cert := range c.config.Certificates {
			if !rsaAvail {
				continue
			}

			leaf := cert.Leaf
			if leaf == nil {
				if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil {
					c.sendAlert(alertInternalError)
					return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
				}
			}

			if leaf.PublicKeyAlgorithm != x509.RSA {
				continue
			}

			if len(certReq.certificateAuthorities) == 0 {
				// they gave us an empty list, so just take the
				// first RSA cert from c.config.Certificates
				certToSend = &cert
				break
			}

			for _, ca := range certReq.certificateAuthorities {
				if bytes.Equal(leaf.RawIssuer, ca) {
					certToSend = &cert
					break findCert
				}
			}
		}

		msg, err = c.readHandshake()
		if err != nil {
			return err
		}
	}

	shd, ok := msg.(*serverHelloDoneMsg)
	if !ok {
		return c.sendAlert(alertUnexpectedMessage)
	}
	finishedHash.Write(shd.marshal())

	// If the server requested a certificate then we have to send a
	// Certificate message, even if it's empty because we don't have a
	// certificate to send.
	if certRequested {
		certMsg = new(certificateMsg)
		if certToSend != nil {
			certMsg.certificates = certToSend.Certificate
		}
		finishedHash.Write(certMsg.marshal())
		c.writeRecord(recordTypeHandshake, certMsg.marshal())
	}

	preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hello, certs[0])
	if err != nil {
		c.sendAlert(alertInternalError)
		return err
	}
	if ckx != nil {
		finishedHash.Write(ckx.marshal())
		c.writeRecord(recordTypeHandshake, ckx.marshal())
	}

	if certToSend != nil {
		certVerify := new(certificateVerifyMsg)
		digest := make([]byte, 0, 36)
		digest = finishedHash.serverMD5.Sum(digest)
		digest = finishedHash.serverSHA1.Sum(digest)
		signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
		if err != nil {
			return c.sendAlert(alertInternalError)
		}
		certVerify.signature = signed

		finishedHash.Write(certVerify.marshal())
		c.writeRecord(recordTypeHandshake, certVerify.marshal())
	}

	masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
		keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)

	clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
	clientHash := suite.mac(c.vers, clientMAC)
	c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
	c.writeRecord(recordTypeChangeCipherSpec, []byte{1})

	if serverHello.nextProtoNeg {
		nextProto := new(nextProtoMsg)
		proto, fallback := mutualProtocol(c.config.NextProtos, serverHello.nextProtos)
		nextProto.proto = proto
		c.clientProtocol = proto
		c.clientProtocolFallback = fallback

		finishedHash.Write(nextProto.marshal())
		c.writeRecord(recordTypeHandshake, nextProto.marshal())
	}

	finished := new(finishedMsg)
	finished.verifyData = finishedHash.clientSum(masterSecret)
	finishedHash.Write(finished.marshal())
	c.writeRecord(recordTypeHandshake, finished.marshal())

	serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */)
	serverHash := suite.mac(c.vers, serverMAC)
	c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
	c.readRecord(recordTypeChangeCipherSpec)
	if c.err != nil {
		return c.err
	}

	msg, err = c.readHandshake()
	if err != nil {
		return err
	}
	serverFinished, ok := msg.(*finishedMsg)
	if !ok {
		return c.sendAlert(alertUnexpectedMessage)
	}

	verify := finishedHash.serverSum(masterSecret)
	if len(verify) != len(serverFinished.verifyData) ||
		subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
		return c.sendAlert(alertHandshakeFailure)
	}

	c.handshakeComplete = true
	c.cipherSuite = suite.id
	return nil
}

// mutualProtocol finds the mutual Next Protocol Negotiation protocol given the
// set of client and server supported protocols. The set of client supported
// protocols must not be empty. It returns the resulting protocol and flag
// indicating if the fallback case was reached.
func mutualProtocol(clientProtos, serverProtos []string) (string, bool) {
	for _, s := range serverProtos {
		for _, c := range clientProtos {
			if s == c {
				return s, false
			}
		}
	}

	return clientProtos[0], true
}
<|endoftext|>


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

package http_test

import (
	"io/ioutil"
	"net/http"
	"path/filepath"
	"testing"
)

func checker(t *testing.T) func(string, error) {
	return func(call string, err error) {
		if err == nil {
			return
		}
		t.Fatalf("%s: %v", call, err)
	}
}

func TestFileTransport(t *testing.T) {
	check := checker(t)

	dname, err := ioutil.TempDir("", "")
	check("TempDir", err)
	fname := filepath.Join(dname, "foo.txt")
	err = ioutil.WriteFile(fname, []byte("Bar"), 0644)
	check("WriteFile", err)

	tr := &http.Transport{}
	tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname)))
	c := &http.Client{Transport: tr}

	fooURLs := []string{"file:///foo.txt", "file://../foo.txt"}
	for _, urlstr := range fooURLs {
		res, err := c.Get(urlstr)
		check("Get "+urlstr, err)
		if res.StatusCode != 200 {
			t.Errorf("for %s, StatusCode = %d, want 200", urlstr, res.StatusCode)
		}
		if res.ContentLength != -1 {
			t.Errorf("for %s, ContentLength = %d, want -1", urlstr, res.ContentLength)
		}
		if res.Body == nil {
			t.Fatalf("for %s, nil Body", urlstr)
		}
		slurp, err := ioutil.ReadAll(res.Body)
		check("ReadAll "+urlstr, err)
		if string(slurp) != "Bar" {
			t.Errorf("for %s, got content %q, want %q", urlstr, string(slurp), "Bar")
		}
	}

	const badURL = "file://../no-exist.txt"
	res, err := c.Get(badURL)
	check("Get "+badURL, err)
	if res.StatusCode != 404 {
		t.Errorf("for %s, StatusCode = %d, want 404", badURL, res.StatusCode)
	}
}
<commit_msg>net/http: make test remove temporary file and directory<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http_test

import (
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"testing"
)

func checker(t *testing.T) func(string, error) {
	return func(call string, err error) {
		if err == nil {
			return
		}
		t.Fatalf("%s: %v", call, err)
	}
}

func TestFileTransport(t *testing.T) {
	check := checker(t)

	dname, err := ioutil.TempDir("", "")
	check("TempDir", err)
	fname := filepath.Join(dname, "foo.txt")
	err = ioutil.WriteFile(fname, []byte("Bar"), 0644)
	check("WriteFile", err)
	defer os.Remove(dname)
	defer os.Remove(fname)

	tr := &http.Transport{}
	tr.RegisterProtocol("file", http.NewFileTransport(http.Dir(dname)))
	c := &http.Client{Transport: tr}

	fooURLs := []string{"file:///foo.txt", "file://../foo.txt"}
	for _, urlstr := range fooURLs {
		res, err := c.Get(urlstr)
		check("Get "+urlstr, err)
		if res.StatusCode != 200 {
			t.Errorf("for %s, StatusCode = %d, want 200", urlstr, res.StatusCode)
		}
		if res.ContentLength != -1 {
			t.Errorf("for %s, ContentLength = %d, want -1", urlstr, res.ContentLength)
		}
		if res.Body == nil {
			t.Fatalf("for %s, nil Body", urlstr)
		}
		slurp, err := ioutil.ReadAll(res.Body)
		check("ReadAll "+urlstr, err)
		if string(slurp) != "Bar" {
			t.Errorf("for %s, got content %q, want %q", urlstr, string(slurp), "Bar")
		}
	}

	const badURL = "file://../no-exist.txt"
	res, err := c.Get(badURL)
	check("Get "+badURL, err)
	if res.StatusCode != 404 {
		t.Errorf("for %s, StatusCode = %d, want 404", badURL, res.StatusCode)
	}
}
<|endoftext|>


<commit_before>package targetworkloadservice

import (
	"context"
	"encoding/json"

	"k8s.io/apimachinery/pkg/runtime"

	"fmt"

	"strings"

	"sync"

	"reflect"

	"github.com/pkg/errors"
	util "github.com/rancher/rancher/pkg/controllers/managementagent/workload"
	v1 "github.com/rancher/rancher/pkg/generated/norman/core/v1"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
)

// This controller is responsible for monitoring services with targetWorkloadIds,
// locating corresponding pods, and marking them with the label to satisfy service selector

const (
	WorkloadIDLabelPrefix = "workloadID"
)

var workloadServiceUUIDToWorkloadIDs sync.Map

type Controller struct {
	pods           v1.PodInterface
	workloadLister util.CommonController
	podLister      v1.PodLister
	services       v1.ServiceInterface
}

type PodController struct {
	pods           v1.PodInterface
	workloadLister util.CommonController
	serviceLister  v1.ServiceLister
}

func Register(ctx context.Context, workload *config.UserOnlyContext) {
	c := &Controller{
		pods:           workload.Core.Pods(""),
		workloadLister: util.NewWorkloadController(ctx, workload, nil),
		podLister:      workload.Core.Pods("").Controller().Lister(),
		services:       workload.Core.Services(""),
	}
	p := &PodController{
		workloadLister: util.NewWorkloadController(ctx, workload, nil),
		pods:           workload.Core.Pods(""),
		serviceLister:  workload.Core.Services("").Controller().Lister(),
	}
	workload.Core.Services("").AddHandler(ctx, "workloadServiceController", c.sync)
	workload.Core.Pods("").AddHandler(ctx, "podToWorkloadServiceController", p.sync)
}

func (c *Controller) sync(key string, obj *corev1.Service) (runtime.Object, error) {
	if obj == nil || obj.DeletionTimestamp != nil {
		if value, ok := workloadServiceUUIDToWorkloadIDs.Load(key); ok {
			if err := c.updateServiceWorkloadPods(key, value.(map[string]bool)); err != nil {
				return nil, err
			}
		}
		// delete from the workload map
		workloadServiceUUIDToWorkloadIDs.Delete(key)
		return nil, nil
	}

	workloadIDs := getServiceWorkloadIDs(obj)
	// update pods (if needed) with service selector labels
	targetWorkloadIDs, err := c.reconcilePods(key, obj, workloadIDs)
	if err != nil {
		return nil, err
	}

	// if workloadIDs changed, push update for all the pods, so they reconcile the labels
	workloadIDsToUpdate := map[string]bool{}
	oldMap, ok := workloadServiceUUIDToWorkloadIDs.Load(key)
	if ok {
		for workloadID := range oldMap.(map[string]bool) {
			workloadIDsToUpdate[workloadID] = true
		}
	}
	for workloadID := range targetWorkloadIDs {
		workloadIDsToUpdate[workloadID] = true
	}

	if err := c.updateServiceWorkloadPods(key, workloadIDsToUpdate); err != nil {
		return nil, err
	}

	//reset the map
	workloadServiceUUIDToWorkloadIDs.Store(key, targetWorkloadIDs)

	return nil, nil
}

func getServiceWorkloadIDs(obj *corev1.Service) []string {
	var workloadIDs []string
	if obj.Annotations == nil {
		return workloadIDs
	}
	value, ok := obj.Annotations[util.WorkloadAnnotation]
	if !ok || value == "" {
		return workloadIDs
	}
	noop, ok := obj.Annotations[util.WorkloadAnnotatioNoop]
	if ok && noop == "true" {
		return workloadIDs
	}

	err := json.Unmarshal([]byte(value), &workloadIDs)
	if err != nil {
		// just log the error, can't really do anything here.
		logrus.Debugf("Failed to unmarshal targetWorkloadIds, error: %v", err)
	}
	return workloadIDs
}

func (c *Controller) fetchWorkload(workloadID string) (*util.Workload, error) {
	workload, err := c.workloadLister.GetByWorkloadIDRetryAPIIfNotFound(workloadID)
	if err != nil && apierrors.IsNotFound(err) {
		logrus.Warnf("Failed to fetch workload [%s]: [%v]", workloadID, err)
		return nil, nil
	}

	return workload, err
}

func (c *Controller) updateServiceWorkloadPods(key string, workloadIDsToCleanup map[string]bool) error {
	if len(workloadIDsToCleanup) == 0 {
		return nil
	}
	var podsToEnqueue []*corev1.Pod
	var workloadsToCleanup []*util.Workload
	for workloadID := range workloadIDsToCleanup {
		workload, err := c.fetchWorkload(workloadID)
		if err != nil {
			return err
		}
		if workload == nil {
			continue
		}
		pods, err := c.getPodsForWorkload(workload)
		if err != nil {
			return err
		}
		podsToEnqueue = append(podsToEnqueue, pods...)
		workloadsToCleanup = append(workloadsToCleanup, workload)
	}

	for _, pod := range podsToEnqueue {
		c.pods.Controller().Enqueue(pod.Namespace, pod.Name)
	}

	for _, workload := range workloadsToCleanup {
		c.workloadLister.EnqueueWorkload(workload)
	}
	return nil
}

func (c *Controller) reconcilePods(key string, obj *corev1.Service, workloadIDs []string) (map[string]bool, error) {
	if len(workloadIDs) == 0 {
		return nil, nil
	}
	if obj.Spec.Selector == nil {
		obj.Spec.Selector = map[string]string{}
	}
	selectorToAdd := getServiceSelector(obj.Name)
	var toUpdate *corev1.Service
	if _, ok := obj.Spec.Selector[selectorToAdd]; !ok {
		toUpdate = obj.DeepCopy()
		toUpdate.Spec.Selector[selectorToAdd] = "true"
		_, err := c.services.Update(toUpdate)
		if err != nil {
			return nil, err
		}
	}
	return c.updatePods(key, obj, workloadIDs)
}

func (c *Controller) getPodsForWorkload(workload *util.Workload) ([]*corev1.Pod, error) {
	set := labels.Set{}
	for key, val := range workload.SelectorLabels {
		set[key] = val
	}
	workloadSelector := labels.SelectorFromSet(set)
	return c.podLister.List(workload.Namespace, workloadSelector)
}

func (c *Controller) updatePods(serviceName string, obj *corev1.Service, workloadIDs []string) (map[string]bool, error) {
	var podsToUpdate []*corev1.Pod
	targetWorkloadIDs := map[string]bool{}
	for _, workloadID := range workloadIDs {
		workload, err := c.fetchWorkload(workloadID)
		if err != nil {
			return nil, err
		}
		if workload == nil {
			continue
		}

		pods, err := c.getPodsForWorkload(workload)
		if err != nil {
			return nil, err
		}

		// Add workload/deployment to the system map
		targetWorkloadIDs[workloadID] = true

		// Find all the pods satisfying deployments' selectors
		for _, pod := range pods {
			if pod.DeletionTimestamp != nil {
				continue
			}
			for svsSelectorKey, svcSelectorValue := range obj.Spec.Selector {
				if value, ok := pod.Labels[svsSelectorKey]; ok && value == svcSelectorValue {
					continue
				}
				podsToUpdate = append(podsToUpdate, pod)
			}
		}
	}

	// Update the pods with the label
	for _, pod := range podsToUpdate {
		toUpdate := pod.DeepCopy()
		for svcSelectorKey, svcSelectorValue := range obj.Spec.Selector {
			toUpdate.Labels[svcSelectorKey] = svcSelectorValue
		}
		if _, err := c.pods.Update(toUpdate); err != nil {
			return nil, errors.Wrapf(err, "Failed to update pod [%s] with workload service selector [%s]",
				pod.Name, fmt.Sprintf("%s/%s", obj.Namespace, obj.Name))
		}
	}
	return targetWorkloadIDs, nil
}

func getServiceSelector(serviceName string) string {
	return fmt.Sprintf("%s_%s", WorkloadIDLabelPrefix, serviceName)
}

func (c *PodController) sync(key string, obj *corev1.Pod) (runtime.Object, error) {
	if obj == nil || obj.DeletionTimestamp != nil {
		return nil, nil
	}
	// filter out deployments that are match for the pods
	workloads, err := c.workloadLister.GetWorkloadsMatchingLabels(obj.Namespace, obj.Labels)
	if err != nil {
		return nil, err
	}

	workloadServiceUUIDsToAdd := map[string]bool{}
	for _, d := range workloads {
		workloadServiceUUIDToWorkloadIDs.Range(func(k, v interface{}) bool {
			if _, ok := v.(map[string]bool)[d.Key]; ok {
				workloadServiceUUIDsToAdd[k.(string)] = true
			}
			return true
		})
	}

	workloadServicesLabels := map[string]string{}
	for workloadServiceUUID := range workloadServiceUUIDsToAdd {
		parts := strings.Split(workloadServiceUUID, "/")
		workloadService, err := c.serviceLister.Get(parts[0], parts[1])
		if err != nil && !apierrors.IsNotFound(err) {
			return nil, err
		}
		if workloadService == nil {
			logrus.Warnf("Failed to fetch service [%s]: [%v]", workloadServiceUUID, err)
			workloadServiceUUIDToWorkloadIDs.Delete(workloadServiceUUID)
			continue
		}

		for key, value := range workloadService.Spec.Selector {
			workloadServicesLabels[key] = value
		}
	}

	// remove old labels
	labels := map[string]string{}
	for key, value := range obj.Labels {
		if strings.HasPrefix(key, WorkloadIDLabelPrefix) {
			if _, ok := workloadServicesLabels[key]; !ok {
				continue
			}
		}
		labels[key] = value
	}

	// add new labels
	for key, value := range workloadServicesLabels {
		labels[key] = value
	}

	if reflect.DeepEqual(obj.Labels, labels) {
		return nil, nil
	}
	toUpdate := obj.DeepCopy()
	toUpdate.Labels = labels
	_, err = c.pods.Update(toUpdate)
	return nil, err
}
<commit_msg>add WorkloadID selector only if service is created by norman<commit_after>package targetworkloadservice

import (
	"context"
	"encoding/json"

	"k8s.io/apimachinery/pkg/runtime"

	"fmt"

	"strings"

	"sync"

	"reflect"

	"github.com/pkg/errors"
	util "github.com/rancher/rancher/pkg/controllers/managementagent/workload"
	v1 "github.com/rancher/rancher/pkg/generated/norman/core/v1"
	"github.com/rancher/rancher/pkg/types/config"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
)

// This controller is responsible for monitoring services with targetWorkloadIds,
// locating corresponding pods, and marking them with the label to satisfy service selector

const (
	WorkloadIDLabelPrefix = "workloadID"
	creatorLabelKey       = "cattle.io/creator"
	creatorNorman         = "norman"
)

var workloadServiceUUIDToWorkloadIDs sync.Map

type Controller struct {
	pods           v1.PodInterface
	workloadLister util.CommonController
	podLister      v1.PodLister
	services       v1.ServiceInterface
}

type PodController struct {
	pods           v1.PodInterface
	workloadLister util.CommonController
	serviceLister  v1.ServiceLister
}

func Register(ctx context.Context, workload *config.UserOnlyContext) {
	c := &Controller{
		pods:           workload.Core.Pods(""),
		workloadLister: util.NewWorkloadController(ctx, workload, nil),
		podLister:      workload.Core.Pods("").Controller().Lister(),
		services:       workload.Core.Services(""),
	}
	p := &PodController{
		workloadLister: util.NewWorkloadController(ctx, workload, nil),
		pods:           workload.Core.Pods(""),
		serviceLister:  workload.Core.Services("").Controller().Lister(),
	}
	workload.Core.Services("").AddHandler(ctx, "workloadServiceController", c.sync)
	workload.Core.Pods("").AddHandler(ctx, "podToWorkloadServiceController", p.sync)
}

func (c *Controller) sync(key string, obj *corev1.Service) (runtime.Object, error) {
	if obj == nil || obj.DeletionTimestamp != nil {
		if value, ok := workloadServiceUUIDToWorkloadIDs.Load(key); ok {
			if err := c.updateServiceWorkloadPods(key, value.(map[string]bool)); err != nil {
				return nil, err
			}
		}
		// delete from the workload map
		workloadServiceUUIDToWorkloadIDs.Delete(key)
		return nil, nil
	}

	// add WorkloadID selector only if service is created by norman
	if obj.Labels == nil || obj.Labels[creatorLabelKey] != creatorNorman {
		return obj, nil
	}

	workloadIDs := getServiceWorkloadIDs(obj)
	// update pods (if needed) with service selector labels
	targetWorkloadIDs, err := c.reconcilePods(key, obj, workloadIDs)
	if err != nil {
		return nil, err
	}

	// if workloadIDs changed, push update for all the pods, so they reconcile the labels
	workloadIDsToUpdate := map[string]bool{}
	oldMap, ok := workloadServiceUUIDToWorkloadIDs.Load(key)
	if ok {
		for workloadID := range oldMap.(map[string]bool) {
			workloadIDsToUpdate[workloadID] = true
		}
	}
	for workloadID := range targetWorkloadIDs {
		workloadIDsToUpdate[workloadID] = true
	}

	if err := c.updateServiceWorkloadPods(key, workloadIDsToUpdate); err != nil {
		return nil, err
	}

	//reset the map
	workloadServiceUUIDToWorkloadIDs.Store(key, targetWorkloadIDs)

	return nil, nil
}

func getServiceWorkloadIDs(obj *corev1.Service) []string {
	var workloadIDs []string
	if obj.Annotations == nil {
		return workloadIDs
	}
	value, ok := obj.Annotations[util.WorkloadAnnotation]
	if !ok || value == "" {
		return workloadIDs
	}
	noop, ok := obj.Annotations[util.WorkloadAnnotatioNoop]
	if ok && noop == "true" {
		return workloadIDs
	}

	err := json.Unmarshal([]byte(value), &workloadIDs)
	if err != nil {
		// just log the error, can't really do anything here.
		logrus.Debugf("Failed to unmarshal targetWorkloadIds, error: %v", err)
	}
	return workloadIDs
}

func (c *Controller) fetchWorkload(workloadID string) (*util.Workload, error) {
	workload, err := c.workloadLister.GetByWorkloadIDRetryAPIIfNotFound(workloadID)
	if err != nil && apierrors.IsNotFound(err) {
		logrus.Warnf("Failed to fetch workload [%s]: [%v]", workloadID, err)
		return nil, nil
	}

	return workload, err
}

func (c *Controller) updateServiceWorkloadPods(key string, workloadIDsToCleanup map[string]bool) error {
	if len(workloadIDsToCleanup) == 0 {
		return nil
	}
	var podsToEnqueue []*corev1.Pod
	var workloadsToCleanup []*util.Workload
	for workloadID := range workloadIDsToCleanup {
		workload, err := c.fetchWorkload(workloadID)
		if err != nil {
			return err
		}
		if workload == nil {
			continue
		}
		pods, err := c.getPodsForWorkload(workload)
		if err != nil {
			return err
		}
		podsToEnqueue = append(podsToEnqueue, pods...)
		workloadsToCleanup = append(workloadsToCleanup, workload)
	}

	for _, pod := range podsToEnqueue {
		c.pods.Controller().Enqueue(pod.Namespace, pod.Name)
	}

	for _, workload := range workloadsToCleanup {
		c.workloadLister.EnqueueWorkload(workload)
	}
	return nil
}

func (c *Controller) reconcilePods(key string, obj *corev1.Service, workloadIDs []string) (map[string]bool, error) {
	if len(workloadIDs) == 0 {
		return nil, nil
	}
	if obj.Spec.Selector == nil {
		obj.Spec.Selector = map[string]string{}
	}
	selectorToAdd := getServiceSelector(obj.Name)
	var toUpdate *corev1.Service
	if _, ok := obj.Spec.Selector[selectorToAdd]; !ok {
		toUpdate = obj.DeepCopy()
		toUpdate.Spec.Selector[selectorToAdd] = "true"
		_, err := c.services.Update(toUpdate)
		if err != nil {
			return nil, err
		}
	}
	return c.updatePods(key, obj, workloadIDs)
}

func (c *Controller) getPodsForWorkload(workload *util.Workload) ([]*corev1.Pod, error) {
	set := labels.Set{}
	for key, val := range workload.SelectorLabels {
		set[key] = val
	}
	workloadSelector := labels.SelectorFromSet(set)
	return c.podLister.List(workload.Namespace, workloadSelector)
}

func (c *Controller) updatePods(serviceName string, obj *corev1.Service, workloadIDs []string) (map[string]bool, error) {
	var podsToUpdate []*corev1.Pod
	targetWorkloadIDs := map[string]bool{}
	for _, workloadID := range workloadIDs {
		workload, err := c.fetchWorkload(workloadID)
		if err != nil {
			return nil, err
		}
		if workload == nil {
			continue
		}

		pods, err := c.getPodsForWorkload(workload)
		if err != nil {
			return nil, err
		}

		// Add workload/deployment to the system map
		targetWorkloadIDs[workloadID] = true

		// Find all the pods satisfying deployments' selectors
		for _, pod := range pods {
			if pod.DeletionTimestamp != nil {
				continue
			}
			for svsSelectorKey, svcSelectorValue := range obj.Spec.Selector {
				if value, ok := pod.Labels[svsSelectorKey]; ok && value == svcSelectorValue {
					continue
				}
				podsToUpdate = append(podsToUpdate, pod)
			}
		}
	}

	// Update the pods with the label
	for _, pod := range podsToUpdate {
		toUpdate := pod.DeepCopy()
		for svcSelectorKey, svcSelectorValue := range obj.Spec.Selector {
			toUpdate.Labels[svcSelectorKey] = svcSelectorValue
		}
		if _, err := c.pods.Update(toUpdate); err != nil {
			return nil, errors.Wrapf(err, "Failed to update pod [%s] with workload service selector [%s]",
				pod.Name, fmt.Sprintf("%s/%s", obj.Namespace, obj.Name))
		}
	}
	return targetWorkloadIDs, nil
}

func getServiceSelector(serviceName string) string {
	return fmt.Sprintf("%s_%s", WorkloadIDLabelPrefix, serviceName)
}

func (c *PodController) sync(key string, obj *corev1.Pod) (runtime.Object, error) {
	if obj == nil || obj.DeletionTimestamp != nil {
		return nil, nil
	}
	// filter out deployments that are match for the pods
	workloads, err := c.workloadLister.GetWorkloadsMatchingLabels(obj.Namespace, obj.Labels)
	if err != nil {
		return nil, err
	}

	workloadServiceUUIDsToAdd := map[string]bool{}
	for _, d := range workloads {
		workloadServiceUUIDToWorkloadIDs.Range(func(k, v interface{}) bool {
			if _, ok := v.(map[string]bool)[d.Key]; ok {
				workloadServiceUUIDsToAdd[k.(string)] = true
			}
			return true
		})
	}

	workloadServicesLabels := map[string]string{}
	for workloadServiceUUID := range workloadServiceUUIDsToAdd {
		parts := strings.Split(workloadServiceUUID, "/")
		workloadService, err := c.serviceLister.Get(parts[0], parts[1])
		if err != nil && !apierrors.IsNotFound(err) {
			return nil, err
		}
		if workloadService == nil {
			logrus.Warnf("Failed to fetch service [%s]: [%v]", workloadServiceUUID, err)
			workloadServiceUUIDToWorkloadIDs.Delete(workloadServiceUUID)
			continue
		}

		for key, value := range workloadService.Spec.Selector {
			workloadServicesLabels[key] = value
		}
	}

	// remove old labels
	labels := map[string]string{}
	for key, value := range obj.Labels {
		if strings.HasPrefix(key, WorkloadIDLabelPrefix) {
			if _, ok := workloadServicesLabels[key]; !ok {
				continue
			}
		}
		labels[key] = value
	}

	// add new labels
	for key, value := range workloadServicesLabels {
		labels[key] = value
	}

	if reflect.DeepEqual(obj.Labels, labels) {
		return nil, nil
	}
	toUpdate := obj.DeepCopy()
	toUpdate.Labels = labels
	_, err = c.pods.Update(toUpdate)
	return nil, err
}
<|endoftext|>


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

// +build integration

package dockerstatsreceiver

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/exporter/exportertest"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest"

	"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testing/container"
)

type testHost struct {
	component.Host
	t *testing.T
}

// ReportFatalError causes the test to be run to fail.
func (h *testHost) ReportFatalError(err error) {
	h.t.Fatalf("Receiver reported a fatal error: %v", err)
}

var _ component.Host = (*testHost)(nil)

func factory() (component.ReceiverFactory, *Config) {
	f := NewFactory()
	config := f.CreateDefaultConfig().(*Config)
	config.CollectionInterval = 1 * time.Second
	return f, config
}

func paramsAndContext(t *testing.T) (component.ReceiverCreateParams, context.Context, context.CancelFunc) {
	ctx, cancel := context.WithCancel(context.Background())
	logger := zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller()))
	return component.ReceiverCreateParams{Logger: logger}, ctx, cancel
}

func TestDefaultMetricsIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	d := container.New(t)
	d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()
	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	assert.Eventuallyf(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "failed to receive any metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestAllMetricsIntegration(t *testing.T) {
	d := container.New(t)
	d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()
	config.ProvidePerCoreCPUMetrics = true

	params, ctx, cancel := paramsAndContext(t)
	defer cancel()

	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	assert.Eventuallyf(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "failed to receive any metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestMonitoringAddedContainerIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()

	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	d := container.New(t)
	d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	assert.Eventuallyf(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "failed to receive any metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestExcludedImageProducesNoMetricsIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	d := container.New(t)
	d.StartImage("docker.io/library/redis:6.0.3", container.WithPortReady(6379))

	f, config := factory()
	config.ExcludedImages = append(config.ExcludedImages, "*redis*")

	consumer := &exportertest.SinkMetricsExporter{}
	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	assert.Never(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "received undesired metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestRemovedContainerRemovesRecordsIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()
	config.ExcludedImages = append(config.ExcludedImages, "!*nginx*")
	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	d := container.New(t)
	nginx := d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	require.NoError(t, err, "failed creating metrics Receiver")

	desiredAmount := func(numDesired int) func() bool {
		return func() bool {
			// We need the lock to prevent data race warnings for test activity
			r.client.containersLock.Lock()
			defer r.client.containersLock.Unlock()
			return len(r.client.containers) == numDesired
		}
	}

	assert.Eventuallyf(t, desiredAmount(1), 5*time.Second, 1*time.Millisecond, "failed to load container stores")
	containers := r.client.Containers()
	d.RemoveContainer(nginx)
	assert.Eventuallyf(t, desiredAmount(0), 5*time.Second, 1*time.Millisecond, "failed to clear container stores")

	// Confirm missing container paths
	md, err := r.client.FetchContainerStatsAndConvertToMetrics(ctx, containers[0])
	assert.Nil(t, md)
	require.Error(t, err)
	assert.Equal(t, fmt.Sprintf("Error response from daemon: No such container: %s", containers[0].ID), err.Error())

	ctr, ok := r.client.inspectedContainerIsOfInterest(ctx, containers[0].ID)
	assert.False(t, ok)
	assert.Nil(t, ctr)

	assert.NoError(t, r.Shutdown(ctx))
}
<commit_msg>dockerstats: restrict excluded test condition (#1153)<commit_after>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build integration

package dockerstatsreceiver

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/exporter/exportertest"
	"go.opentelemetry.io/collector/translator/conventions"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest"

	"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testing/container"
)

type testHost struct {
	component.Host
	t *testing.T
}

// ReportFatalError causes the test to be run to fail.
func (h *testHost) ReportFatalError(err error) {
	h.t.Fatalf("Receiver reported a fatal error: %v", err)
}

var _ component.Host = (*testHost)(nil)

func factory() (component.ReceiverFactory, *Config) {
	f := NewFactory()
	config := f.CreateDefaultConfig().(*Config)
	config.CollectionInterval = 1 * time.Second
	return f, config
}

func paramsAndContext(t *testing.T) (component.ReceiverCreateParams, context.Context, context.CancelFunc) {
	ctx, cancel := context.WithCancel(context.Background())
	logger := zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller()))
	return component.ReceiverCreateParams{Logger: logger}, ctx, cancel
}

func TestDefaultMetricsIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	d := container.New(t)
	d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()
	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	assert.Eventuallyf(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "failed to receive any metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestAllMetricsIntegration(t *testing.T) {
	d := container.New(t)
	d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()
	config.ProvidePerCoreCPUMetrics = true

	params, ctx, cancel := paramsAndContext(t)
	defer cancel()

	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	assert.Eventuallyf(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "failed to receive any metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestMonitoringAddedContainerIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()

	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	d := container.New(t)
	d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	assert.Eventuallyf(t, func() bool {
		return len(consumer.AllMetrics()) > 0
	}, 5*time.Second, 1*time.Second, "failed to receive any metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestExcludedImageProducesNoMetricsIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	d := container.New(t)
	d.StartImage("docker.io/library/redis:6.0.3", container.WithPortReady(6379))

	f, config := factory()
	config.ExcludedImages = append(config.ExcludedImages, "*redis*")

	consumer := &exportertest.SinkMetricsExporter{}
	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, err, "failed creating metrics Receiver")
	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	assert.Never(t, func() bool {
		if metrics := consumer.AllMetrics(); len(metrics) > 0 {
			for _, metric := range metrics {
				resourceMetrics := metric.ResourceMetrics()
				for i := 0; i < resourceMetrics.Len(); i++ {
					resourceMetric := resourceMetrics.At(i)
					resource := resourceMetric.Resource()
					nameAttr, ok := resource.Attributes().Get(conventions.AttributeContainerImage)
					if ok && !nameAttr.IsNil() {
						if strings.Contains(nameAttr.StringVal(), "redis") {
							return true
						}
					}
				}
			}
		}
		return false
	}, 5*time.Second, 1*time.Second, "received undesired metrics")

	assert.NoError(t, r.Shutdown(ctx))
}

func TestRemovedContainerRemovesRecordsIntegration(t *testing.T) {
	params, ctx, cancel := paramsAndContext(t)
	defer cancel()
	consumer := &exportertest.SinkMetricsExporter{}
	f, config := factory()
	config.ExcludedImages = append(config.ExcludedImages, "!*nginx*")
	receiver, err := f.CreateMetricsReceiver(ctx, params, config, consumer)
	r := receiver.(*Receiver)

	require.NoError(t, r.Start(ctx, &testHost{
		t: t,
	}))

	d := container.New(t)
	nginx := d.StartImage("docker.io/library/nginx:1.17", container.WithPortReady(80))

	require.NoError(t, err, "failed creating metrics Receiver")

	desiredAmount := func(numDesired int) func() bool {
		return func() bool {
			// We need the lock to prevent data race warnings for test activity
			r.client.containersLock.Lock()
			defer r.client.containersLock.Unlock()
			return len(r.client.containers) == numDesired
		}
	}

	assert.Eventuallyf(t, desiredAmount(1), 5*time.Second, 1*time.Millisecond, "failed to load container stores")
	containers := r.client.Containers()
	d.RemoveContainer(nginx)
	assert.Eventuallyf(t, desiredAmount(0), 5*time.Second, 1*time.Millisecond, "failed to clear container stores")

	// Confirm missing container paths
	md, err := r.client.FetchContainerStatsAndConvertToMetrics(ctx, containers[0])
	assert.Nil(t, md)
	require.Error(t, err)
	assert.Equal(t, fmt.Sprintf("Error response from daemon: No such container: %s", containers[0].ID), err.Error())

	ctr, ok := r.client.inspectedContainerIsOfInterest(ctx, containers[0].ID)
	assert.False(t, ok)
	assert.Nil(t, ctr)

	assert.NoError(t, r.Shutdown(ctx))
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/url"
)

// API chat.postMessage: Sends a message to a channel.
func (sl *Slack) ChatPostMessage(channelId string, text string, opt *ChatPostMessageOpt) error {
	uv, err := sl.buildChatPostMessageUrlValues(opt)
	if err != nil {
		return err
	}
	uv.Add("channel", channelId)

	body, err := sl.PostRequest(chatPostMessageApiEndpoint, uv, sl.buildRequestBodyForm(text))
	if err != nil {
		return err
	}
	res := new(ChatPostMessageAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return err
	}
	if !res.Ok {
		return errors.New(res.Error)
	}
	return nil
}

// option type for `chat.postMessage` api
type ChatPostMessageOpt struct {
	AsUser      bool
	Username    string
	Parse       string
	LinkNames   string
	Attachments []*Attachment
	UnfurlLinks string
	UnfurlMedia string
	IconUrl     string
	IconEmoji   string
}

// response type for `chat.postMessage` api
type ChatPostMessageAPIResponse struct {
	BaseAPIResponse
	Channel string `json:"channel"`
	Ts      string `json:"ts"`
}

func (sl *Slack) buildRequestBodyForm(text string) (*bytes.Buffer) {
    return bytes.NewBuffer([]byte("text=" + text))
}

func (sl *Slack) buildChatPostMessageUrlValues(opt *ChatPostMessageOpt) (*url.Values, error) {
	uv := sl.urlValues()
	if opt == nil {
		return uv, nil
	}
	if opt.AsUser {
		uv.Add("as_user", "true")
	} else if opt.Username != "" {
		uv.Add("username", opt.Username)
	}
	if opt.Parse != "" {
		uv.Add("parse", opt.Parse)
	}
	if opt.LinkNames != "" {
		uv.Add("link_names", opt.LinkNames)
	}
	if opt.UnfurlLinks != "" {
		uv.Add("unfurl_links", opt.UnfurlLinks)
	}
	if opt.UnfurlMedia != "" {
		uv.Add("unfurl_media", opt.UnfurlMedia)
	}
	if opt.IconUrl != "" {
		uv.Add("icon_url", opt.IconUrl)
	}
	if opt.IconEmoji != "" {
		uv.Add("icon_emoji", opt.IconEmoji)
	}
	if opt.Attachments != nil && len(opt.Attachments) > 0 {
		ats, err := json.Marshal(opt.Attachments)
		if err != nil {
			return nil, err
		}
		uv.Add("attachments", string(ats))
	}
	return uv, nil
}
<commit_msg>fix encode a message text (#25)<commit_after>package slack

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/url"
)

// API chat.postMessage: Sends a message to a channel.
func (sl *Slack) ChatPostMessage(channelId string, text string, opt *ChatPostMessageOpt) error {
	uv, err := sl.buildChatPostMessageUrlValues(opt)
	if err != nil {
		return err
	}
	uv.Add("channel", channelId)

	body, err := sl.PostRequest(chatPostMessageApiEndpoint, uv, sl.buildRequestBodyForm(text))
	if err != nil {
		return err
	}
	res := new(ChatPostMessageAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return err
	}
	if !res.Ok {
		return errors.New(res.Error)
	}
	return nil
}

// option type for `chat.postMessage` api
type ChatPostMessageOpt struct {
	AsUser      bool
	Username    string
	Parse       string
	LinkNames   string
	Attachments []*Attachment
	UnfurlLinks string
	UnfurlMedia string
	IconUrl     string
	IconEmoji   string
}

// response type for `chat.postMessage` api
type ChatPostMessageAPIResponse struct {
	BaseAPIResponse
	Channel string `json:"channel"`
	Ts      string `json:"ts"`
}

func (sl *Slack) buildRequestBodyForm(text string) *bytes.Buffer {
	return bytes.NewBuffer([]byte("text=" + url.QueryEscape(text)))
}

func (sl *Slack) buildChatPostMessageUrlValues(opt *ChatPostMessageOpt) (*url.Values, error) {
	uv := sl.urlValues()
	if opt == nil {
		return uv, nil
	}
	if opt.AsUser {
		uv.Add("as_user", "true")
	} else if opt.Username != "" {
		uv.Add("username", opt.Username)
	}
	if opt.Parse != "" {
		uv.Add("parse", opt.Parse)
	}
	if opt.LinkNames != "" {
		uv.Add("link_names", opt.LinkNames)
	}
	if opt.UnfurlLinks != "" {
		uv.Add("unfurl_links", opt.UnfurlLinks)
	}
	if opt.UnfurlMedia != "" {
		uv.Add("unfurl_media", opt.UnfurlMedia)
	}
	if opt.IconUrl != "" {
		uv.Add("icon_url", opt.IconUrl)
	}
	if opt.IconEmoji != "" {
		uv.Add("icon_emoji", opt.IconEmoji)
	}
	if opt.Attachments != nil && len(opt.Attachments) > 0 {
		ats, err := json.Marshal(opt.Attachments)
		if err != nil {
			return nil, err
		}
		uv.Add("attachments", string(ats))
	}
	return uv, nil
}
<|endoftext|>


<commit_before>// Package gstrings provides common sense
// string manipulation methods from ruby.

// package main
// import "github.com/wallclockbuilder/gstrings"
//
// func main() {
//   gstrings.Capitalize("abcde")          #=> "ABCDE"
//   gstrings.Reverse("stressed")          #=> "desserts"
//   gstrings.Swapcase("Hello")            #=> "hELLO"
// }

package gstrings

import (
	"strings"
)

// Chop returns a new String with the last character removed.
// If the string ends with \r\n, both characters are removed.
// Applying chop to an empty string returns an empty string.
func Chop(s string) string {
	if s == "" {
		return s
	}
	return chopChars(s)
}

func chopChars(s string) string {
	if strHasWhitespaceSuffix(s) {
		return chopWhitespace(s)
	}
	return chopPrintableChar(s)
}

func chopWhitespace(s string) string {
	return strings.TrimSuffix(s, whitespaceSuffix(s))
}

func strHasWhitespaceSuffix(s string) bool {
	return strings.HasSuffix(s, "\r") ||
		strings.HasSuffix(s, "\n")
}

func chopPrintableChar(s string) string {
	suffix := printableSuffix(s)
	return strings.TrimSuffix(s, suffix)
}

func whitespaceSuffix(s string) string {
	if strings.HasSuffix(s, "\r\n") {
		return "\r\n"
	}
	return simpleWhitespaceSuffix(s)
}

func printableSuffix(s string) string {
	return suffix(s)
}

func suffix(s string) string {
	i := strings.LastIndex(s, "")
	return s[i-1:]
}

func simpleWhitespaceSuffix(s string) string {
	i := strings.LastIndex(s, "") - 1
	return s[i:]
}
<commit_msg>Doc should be one text block<commit_after>// Package gstrings provides common sense
// string manipulation methods from ruby.
// package main
// import "github.com/wallclockbuilder/gstrings"
//
// func main() {
//   gstrings.Capitalize("abcde")          #=> "ABCDE"
//   gstrings.Reverse("stressed")          #=> "desserts"
//   gstrings.Swapcase("Hello")            #=> "hELLO"
// }

package gstrings

import (
	"strings"
)

// Chop returns a new String with the last character removed.
// If the string ends with \r\n, both characters are removed.
// Applying chop to an empty string returns an empty string.
func Chop(s string) string {
	if s == "" {
		return s
	}
	return chopChars(s)
}

func chopChars(s string) string {
	if strHasWhitespaceSuffix(s) {
		return chopWhitespace(s)
	}
	return chopPrintableChar(s)
}

func chopWhitespace(s string) string {
	return strings.TrimSuffix(s, whitespaceSuffix(s))
}

func strHasWhitespaceSuffix(s string) bool {
	return strings.HasSuffix(s, "\r") ||
		strings.HasSuffix(s, "\n")
}

func chopPrintableChar(s string) string {
	suffix := printableSuffix(s)
	return strings.TrimSuffix(s, suffix)
}

func whitespaceSuffix(s string) string {
	if strings.HasSuffix(s, "\r\n") {
		return "\r\n"
	}
	return simpleWhitespaceSuffix(s)
}

func printableSuffix(s string) string {
	return suffix(s)
}

func suffix(s string) string {
	i := strings.LastIndex(s, "")
	return s[i-1:]
}

func simpleWhitespaceSuffix(s string) string {
	i := strings.LastIndex(s, "") - 1
	return s[i:]
}
<|endoftext|>


<commit_before>// Package clac implements an RPN calculator.
package clac

import (
	"errors"
	"math"
	"strconv"
)

var (
	errTooFewArgs    = errors.New("too few arguments")
	errInvalidArg    = errors.New("invalid argument")
	errOutOfRange    = errors.New("argument out of range")
	errNoMoreChanges = errors.New("no more changes")
	errNoHistUpdate  = errors.New("") // for cmds that don't add to history
)

// ParseNum parses a string for an integer or floating point number.
func ParseNum(in string) (float64, error) {
	if i, err := strconv.ParseInt(in, 0, 64); err == nil {
		return float64(i), nil
	}
	num, err := strconv.ParseFloat(in, 64)
	if math.IsNaN(num) {
		return 0, errInvalidArg
	}
	return num, err
}

// Stack represents a stack of floating point numbers.
type Stack []float64

type stackHist struct {
	cur  int
	hist []Stack
}

func newStackHist() *stackHist {
	return &stackHist{hist: []Stack{Stack{}}}
}

func (s *stackHist) undo() bool {
	if s.cur <= 0 {
		return false
	}
	s.cur--
	return true
}

func (s *stackHist) redo() bool {
	if s.cur >= len(s.hist)-1 {
		return false
	}
	s.cur++
	return true
}

func (s *stackHist) push(stack Stack) {
	s.hist = append(s.hist[:s.cur+1], stack)
	s.cur++
}

func (s *stackHist) stack() Stack {
	return s.hist[s.cur]
}

// Clac represents an RPN calculator.
type Clac struct {
	working Stack
	hist    *stackHist
}

// New returns an initialized Clac instance.
func New() *Clac {
	return &Clac{working: Stack{}, hist: newStackHist()}
}

// Stack returns the current stack.
func (c *Clac) Stack() Stack {
	return c.working
}

// Exec executes a clac command, along with necessary bookkeeping
func (c *Clac) Exec(f func() error) error {
	c.updateWorking()
	err := f()
	if err == nil {
		c.hist.push(c.working)
	}
	c.updateWorking()
	if err == errNoHistUpdate {
		return nil
	}
	return err
}

func (c *Clac) updateWorking() {
	c.working = append(Stack{}, c.hist.stack()...)
}

func (c *Clac) checkRange(pos, num int, isEndOK bool) (int, int, error) {
	max := len(c.working)
	if isEndOK {
		max++
	}
	start, end := pos, pos+num-1
	if start < 0 || start > end || start >= max || end >= max {
		return 0, 0, errOutOfRange
	}
	return start, end, nil
}

func (c *Clac) insert(vals []float64, pos int) error {
	idx, _, err := c.checkRange(pos, 1, true)
	if err != nil {
		return err
	}
	c.working = append(c.working[:idx], append(vals, c.working[idx:]...)...)
	return nil
}

func (c *Clac) push(x float64) error {
	return c.insert([]float64{x}, 0)
}

func (c *Clac) remove(pos, num int) ([]float64, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	vals := append([]float64{}, c.working[start:end+1]...)
	c.working = append(c.working[:start], c.working[end+1:]...)
	return vals, nil
}

func (c *Clac) pop() (float64, error) {
	x, err := c.remove(0, 1)
	if err != nil {
		return 0, errTooFewArgs
	}
	return x[0], err
}

func (c *Clac) vals(pos, num int) (Stack, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	return c.working[start : end+1], nil
}

func (c *Clac) dup(pos, num int) error {
	vals, err := c.vals(pos, num)
	if err != nil {
		return err
	}
	return c.insert(vals, 0)
}

func (c *Clac) drop(pos, num int) error {
	_, err := c.remove(pos, num)
	return err
}

func (c *Clac) rotate(pos, num int, isDown bool) error {
	from, to := pos, 0
	if !isDown {
		from, to = 0, pos-num+1
	}
	vals, err := c.remove(from, num)
	if err != nil {
		return err
	}
	return c.insert(vals, to)
}
<commit_msg>Added reset.<commit_after>// Package clac implements an RPN calculator.
package clac

import (
	"errors"
	"math"
	"strconv"
)

var (
	errTooFewArgs    = errors.New("too few arguments")
	errInvalidArg    = errors.New("invalid argument")
	errOutOfRange    = errors.New("argument out of range")
	errNoMoreChanges = errors.New("no more changes")
	errNoHistUpdate  = errors.New("") // for cmds that don't add to history
)

// ParseNum parses a string for an integer or floating point number.
func ParseNum(in string) (float64, error) {
	if i, err := strconv.ParseInt(in, 0, 64); err == nil {
		return float64(i), nil
	}
	num, err := strconv.ParseFloat(in, 64)
	if math.IsNaN(num) {
		return 0, errInvalidArg
	}
	return num, err
}

// Stack represents a stack of floating point numbers.
type Stack []float64

type stackHist struct {
	cur  int
	hist []Stack
}

func newStackHist() *stackHist {
	return &stackHist{hist: []Stack{Stack{}}}
}

func (s *stackHist) undo() bool {
	if s.cur <= 0 {
		return false
	}
	s.cur--
	return true
}

func (s *stackHist) redo() bool {
	if s.cur >= len(s.hist)-1 {
		return false
	}
	s.cur++
	return true
}

func (s *stackHist) push(stack Stack) {
	s.hist = append(s.hist[:s.cur+1], stack)
	s.cur++
}

func (s *stackHist) stack() Stack {
	return s.hist[s.cur]
}

// Clac represents an RPN calculator.
type Clac struct {
	working Stack
	hist    *stackHist
}

// New returns an initialized Clac instance.
func New() *Clac {
	c := &Clac{}
	c.Reset()
	return c
}

// Reset resets clac to its initial state
func (c *Clac) Reset() {
	c.working = Stack{}
	c.hist = newStackHist()
}

// Stack returns the current stack.
func (c *Clac) Stack() Stack {
	return c.working
}

// Exec executes a clac command, along with necessary bookkeeping
func (c *Clac) Exec(f func() error) error {
	c.updateWorking()
	err := f()
	if err == nil {
		c.hist.push(c.working)
	}
	c.updateWorking()
	if err == errNoHistUpdate {
		return nil
	}
	return err
}

func (c *Clac) updateWorking() {
	c.working = append(Stack{}, c.hist.stack()...)
}

func (c *Clac) checkRange(pos, num int, isEndOK bool) (int, int, error) {
	max := len(c.working)
	if isEndOK {
		max++
	}
	start, end := pos, pos+num-1
	if start < 0 || start > end || start >= max || end >= max {
		return 0, 0, errOutOfRange
	}
	return start, end, nil
}

func (c *Clac) insert(vals []float64, pos int) error {
	idx, _, err := c.checkRange(pos, 1, true)
	if err != nil {
		return err
	}
	c.working = append(c.working[:idx], append(vals, c.working[idx:]...)...)
	return nil
}

func (c *Clac) push(x float64) error {
	return c.insert([]float64{x}, 0)
}

func (c *Clac) remove(pos, num int) ([]float64, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	vals := append([]float64{}, c.working[start:end+1]...)
	c.working = append(c.working[:start], c.working[end+1:]...)
	return vals, nil
}

func (c *Clac) pop() (float64, error) {
	x, err := c.remove(0, 1)
	if err != nil {
		return 0, errTooFewArgs
	}
	return x[0], err
}

func (c *Clac) vals(pos, num int) (Stack, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	return c.working[start : end+1], nil
}

func (c *Clac) dup(pos, num int) error {
	vals, err := c.vals(pos, num)
	if err != nil {
		return err
	}
	return c.insert(vals, 0)
}

func (c *Clac) drop(pos, num int) error {
	_, err := c.remove(pos, num)
	return err
}

func (c *Clac) rotate(pos, num int, isDown bool) error {
	from, to := pos, 0
	if !isDown {
		from, to = 0, pos-num+1
	}
	vals, err := c.remove(from, num)
	if err != nil {
		return err
	}
	return c.insert(vals, to)
}
<|endoftext|>


<commit_before>package ramfs

import (
	"io"
	"sync"

	"code.google.com/p/goplan9/plan9"
)

type request struct {
	Fid *Fid
	Tx  *plan9.Fcall
	Rx  *plan9.Fcall
	Err error
}

type conn struct {
	f, x   sync.Mutex
	rwc    io.ReadWriteCloser
	fidnew chan<- (chan *Fid)
	work   chan<- *transaction
	wg     sync.WaitGroup
	err    error
	uid    string
	fidmap map[uint32]*Fid
	log    LogFunc
}

func (c *conn) NewFid() *Fid {
	ch := make(chan *Fid)
	c.fidnew <- ch
	return <-ch
}

func (c *conn) GetFid(num uint32) *Fid {
	c.f.Lock()
	defer c.f.Unlock()

	fid, found := c.fidmap[num]
	if found {
		return fid
	}

	fid = c.NewFid()
	fid.num = num
	fid.uid = c.uid
	c.fidmap[fid.num] = fid
	return fid
}

func (c *conn) DelFid(num uint32) {
	c.f.Lock()
	fid, found := c.fidmap[num]
	if !found {
		panic("fid not found")
	}

	if fid.refCount() == 0 {
		delete(c.fidmap, num)
	}
	c.f.Unlock()
}

func (c *conn) setErr(err error) {
	c.x.Lock()
	if err == nil {
		c.err = err
	}
	c.x.Unlock()
}

func (c *conn) getErr() error {
	c.x.Lock()
	err := c.err
	c.x.Unlock()
	return err
}

func (c *conn) recv() <-chan *request {
	reqout := make(chan *request, 64)

	go func() {
		defer close(reqout)
		var err error
		for {
			req := &request{Rx: &plan9.Fcall{}}
			req.Tx, err = plan9.ReadFcall(c.rwc)
			if err != nil {
				c.setErr(err)
				return
			}
			if c.log != nil {
				c.log("-> %s", req.Tx)
			}
			reqout <- req
		}
	}()

	return reqout
}

func (c *conn) proc(req *request, reqout chan<- *request) {
	defer c.wg.Done()

	switch req.Tx.Type {
	case plan9.Tversion:
		c.f.Lock() // abort all outstanding I/O
		for num := range c.fidmap {
			delete(c.fidmap, num)
		}
		c.f.Unlock()
	case plan9.Tauth:
		// nothing
	default:
		req.Fid = c.GetFid(req.Tx.Fid)
		req.Fid.incRef()
		if req.Tx.Type == plan9.Twalk {
			req.Fid.New = c.GetFid(req.Tx.Newfid)
		}
	}

	txn := &transaction{req, make(chan *request)}
	c.work <- txn
	req = <-txn.ch
	if req.Err != nil {
		req.Rx.Type = plan9.Rerror
		req.Rx.Ename = req.Err.Error()
	} else {
		req.Rx.Type = req.Tx.Type + 1
		req.Rx.Fid = req.Tx.Fid
	}
	req.Rx.Tag = req.Tx.Tag

	switch req.Rx.Type {
	case plan9.Rversion, plan9.Rauth:
		// nothing
	case plan9.Rattach:
		c.f.Lock()
		c.uid = req.Fid.uid
		c.f.Unlock()
		req.Fid.decRef()
		c.DelFid(req.Fid.num)
	case plan9.Rwalk, plan9.Rclunk:
		req.Fid.decRef()
		c.DelFid(req.Fid.num)
	case plan9.Rerror:
		if req.Fid != nil {
			req.Fid.decRef()
		}
	default:
		req.Fid.decRef()
	}

	if c.getErr() == nil {
		reqout <- req
	}
}

func (c *conn) send(reqin <-chan *request) error {
	defer c.rwc.Close()
	reqout := make(chan *request)

	go func() {
		for req := range reqin {
			if c.getErr() == nil {
				c.wg.Add(1)
				go c.proc(req, reqout)
			}
		}
		c.wg.Wait()
		close(reqout)
	}()

	for req := range reqout {
		if c.getErr() == nil {
			if c.log != nil {
				c.log("<- %s", req.Rx)
			}
			err := plan9.WriteFcall(c.rwc, req.Rx)
			if err != nil {
				c.setErr(err)
			}
		}
	}

	return c.getErr()
}
<commit_msg>1398271590<commit_after>package ramfs

import (
	"io"
	"sync"

	"code.google.com/p/goplan9/plan9"
)

type request struct {
	Fid *Fid
	Tx  *plan9.Fcall
	Rx  *plan9.Fcall
	Err error
}

type conn struct {
	f, x   sync.Mutex
	rwc    io.ReadWriteCloser
	fidnew chan<- (chan *Fid)
	work   chan<- *transaction
	wg     sync.WaitGroup
	err    error
	uid    string
	fidmap map[uint32]*Fid
	log    LogFunc
}

func (c *conn) NewFid() *Fid {
	ch := make(chan *Fid)
	c.fidnew <- ch
	return <-ch
}

func (c *conn) GetFid(num uint32) *Fid {
	c.f.Lock()
	defer c.f.Unlock()

	fid, found := c.fidmap[num]
	if found {
		return fid
	}

	fid = c.NewFid()
	fid.num = num
	fid.uid = c.uid
	c.fidmap[fid.num] = fid
	return fid
}

func (c *conn) DelFid(num uint32) {
	c.f.Lock()
	fid, found := c.fidmap[num]
	if !found {
		panic("fid not found")
	}

	if fid.refCount() == 0 {
		delete(c.fidmap, num)
	}
	c.f.Unlock()
}

func (c *conn) setErr(err error) {
	c.x.Lock()
	c.err = err
	c.x.Unlock()
}

func (c *conn) getErr() error {
	c.x.Lock()
	err := c.err
	c.x.Unlock()
	return err
}

func (c *conn) recv() <-chan *request {
	reqout := make(chan *request, 64)

	go func() {
		defer close(reqout)
		var err error
		for {
			req := &request{Rx: &plan9.Fcall{}}
			req.Tx, err = plan9.ReadFcall(c.rwc)
			if err != nil {
				c.setErr(err)
				return
			}
			if c.log != nil {
				c.log("-> %s", req.Tx)
			}
			reqout <- req
		}
	}()

	return reqout
}

func (c *conn) proc(req *request, reqout chan<- *request) {
	defer c.wg.Done()

	switch req.Tx.Type {
	case plan9.Tversion:
		c.f.Lock() // abort all outstanding I/O
		for num := range c.fidmap {
			delete(c.fidmap, num)
		}
		c.f.Unlock()
	case plan9.Tauth:
		// nothing
	default:
		req.Fid = c.GetFid(req.Tx.Fid)
		req.Fid.incRef()
		if req.Tx.Type == plan9.Twalk {
			req.Fid.New = c.GetFid(req.Tx.Newfid)
		}
	}

	txn := &transaction{req, make(chan *request)}
	c.work <- txn
	req = <-txn.ch
	if req.Err != nil {
		req.Rx.Type = plan9.Rerror
		req.Rx.Ename = req.Err.Error()
	} else {
		req.Rx.Type = req.Tx.Type + 1
		req.Rx.Fid = req.Tx.Fid
	}
	req.Rx.Tag = req.Tx.Tag

	switch req.Rx.Type {
	case plan9.Rversion, plan9.Rauth:
		// nothing
	case plan9.Rattach:
		c.f.Lock()
		c.uid = req.Fid.uid
		c.f.Unlock()
		req.Fid.decRef()
		c.DelFid(req.Fid.num)
	case plan9.Rwalk, plan9.Rclunk:
		req.Fid.decRef()
		c.DelFid(req.Fid.num)
	case plan9.Rerror:
		if req.Fid != nil {
			req.Fid.decRef()
		}
	default:
		req.Fid.decRef()
	}

	if c.getErr() == nil {
		reqout <- req
	}
}

func (c *conn) send(reqin <-chan *request) error {
	defer c.rwc.Close()
	reqout := make(chan *request)

	go func() {
		for req := range reqin {
			if c.getErr() == nil {
				c.wg.Add(1)
				go c.proc(req, reqout)
			}
		}
		c.wg.Wait()
		close(reqout)
	}()

	for req := range reqout {
		if c.getErr() == nil {
			if c.log != nil {
				c.log("<- %s", req.Rx)
			}
			err := plan9.WriteFcall(c.rwc, req.Rx)
			if err != nil {
				c.setErr(err)
			}
		}
	}

	return c.getErr()
}
<|endoftext|>