<commit_before>package xmpp

import (
	"crypto/tls"
	"encoding/xml"
	"errors"
	"fmt"
	"log"
)

// Config structure used to create a new XMPP client connection.
type ClientConfig struct {
	// Don't upgrade the connection to TLS, even if the server supports it. If
	// the server *requires* TLS then this option is ignored.
	NoTLS bool

	// Skip verification of the server's certificate chain. Probably only
	// useful during development.
	InsecureSkipVerify bool
}

// Create a client XMPP over the stream.
func NewClientXMPP(stream *Stream, jid JID, password string, config *ClientConfig) (*XMPP, error) {

	if config == nil {
		config = &ClientConfig{}
	}

	for {

		if err := startClient(stream, jid); err != nil {
			return nil, err
		}

		// Read features.
		f := new(features)
		if err := stream.Decode(f, nil); err != nil {
			return nil, err
		}

		// TLS?
		if f.StartTLS != nil && (f.StartTLS.Required != nil || !config.NoTLS) {
			log.Println("Start TLS")
			if err := startTLS(stream, config); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Authentication
		if f.Mechanisms != nil {
			log.Println("Authenticating")
			if err := authenticate(stream, f.Mechanisms.Mechanisms, jid.Node, password); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Bind resource.
		if f.Bind != nil {
			log.Println("Binding resource.")
			boundJID, err := bindResource(stream, jid)
			if err != nil {
				return nil, err
			}
			jid = boundJID
		}

		// Session.
		if f.Session != nil {
			log.Println("Establishing session.")
			if err := establishSession(stream, jid.Domain); err != nil {
				return nil, err
			}
		}

		break
	}

	return newXMPP(jid, stream), nil
}

func startClient(stream *Stream, jid JID) error {

	start := xml.StartElement{
		xml.Name{"stream", "stream"},
		[]xml.Attr{
			xml.Attr{xml.Name{"", "xmlns"}, nsClient},
			xml.Attr{xml.Name{"xmlns", "stream"}, nsStreams},
			xml.Attr{xml.Name{"", "from"}, jid.Full()},
			xml.Attr{xml.Name{"", "to"}, jid.Domain},
			xml.Attr{xml.Name{"", "version"}, "1.0"},
		},
	}

	rstart, err := stream.SendStart(&start)
	if err != nil {
		return err
	}
	if rstart.Name != (xml.Name{nsStreams, "stream"}) {
		return fmt.Errorf("unexpected start element: %s", rstart.Name)
	}
	return nil
}

func startTLS(stream *Stream, config *ClientConfig) error {

	if err := stream.Send(&tlsStart{}); err != nil {
		return err
	}

	p := tlsProceed{}
	if err := stream.Decode(&p, nil); err != nil {
		return err
	}

	tlsConfig := tls.Config{InsecureSkipVerify: config.InsecureSkipVerify, ServerName: stream.config.ConnectionDomain}
	return stream.UpgradeTLS(&tlsConfig)
}

type tlsStart struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
}

type tlsProceed struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
}

func authenticate(stream *Stream, mechanisms []string, user, password string) error {
	for _, handler := range authHandlers {
		if !stringSliceContains(mechanisms, handler.Mechanism) {
			continue
		}
		if err := handler.Fn(stream, user, password); err == nil {
			log.Printf("Authentication (%s) successful", handler.Mechanism)
			return nil
		}
	}
	return errors.New("No supported SASL mechanism found.")
}

type authHandler struct {
	Mechanism string
	Fn        func(*Stream, string, string) error
}

var authHandlers = []authHandler{
	{"PLAIN", authenticatePlain},
}

func authenticatePlain(stream *Stream, user, password string) error {
	auth := saslAuth{Mechanism: "PLAIN", Text: saslEncodePlain(user, password)}
	if err := stream.Send(&auth); err != nil {
		return err
	}
	return authenticateResponse(stream)
}

func authenticateResponse(stream *Stream) error {
	se, err := stream.Next()
	if err != nil {
		return err
	}

	switch se.Name.Local {
	case "success":
		if err := stream.Skip(); err != nil {
			return err
		}
		return nil
	case "failure":
		f := new(saslFailure)
		if err := stream.Decode(f, se); err != nil {
			return err
		}
		return fmt.Errorf("Authentication failed: %s", f.Reason.Local)
	default:
		return fmt.Errorf("Unexpected: %s", se.Name)
	}
}

type saslAuth struct {
	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
	Mechanism string   `xml:"mechanism,attr"`
	Text      string   `xml:",chardata"`
}

func bindResource(stream *Stream, jid JID) (JID, error) {

	req := Iq{Id: UUID4(), Type: "set"}
	if jid.Resource == "" {
		req.PayloadEncode(bindIq{})
	} else {
		req.PayloadEncode(bindIq{Resource: jid.Resource})
	}
	if err := stream.Send(req); err != nil {
		return JID{}, err
	}

	resp := Iq{}
	err := stream.Decode(&resp, nil)
	if err != nil {
		return JID{}, err
	}
	bindResp := bindIq{}
	resp.PayloadDecode(&bindResp)

	boundJID, err := ParseJID(bindResp.JID)
	return boundJID, nil
}

type bindIq struct {
	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Resource string   `xml:"resource,omitempty"`
	JID      string   `xml:"jid,omitempty"`
}

func establishSession(stream *Stream, domain string) error {

	req := Iq{Id: UUID4(), Type: "set", To: domain}
	req.PayloadEncode(&session{})
	if err := stream.Send(req); err != nil {
		return err
	}

	resp := Iq{}
	if err := stream.Decode(&resp, nil); err != nil {
		return err
	} else if resp.Error != nil {
		return resp.Error
	}

	return nil
}

func stringSliceContains(l []string, m string) bool {
	for _, i := range l {
		if i == m {
			return true
		}
	}
	return false
}

type features struct {
	XMLName    xml.Name     `xml:"http://etherx.jabber.org/streams features"`
	StartTLS   *tlsStartTLS `xml:"starttls"`
	Mechanisms *mechanisms  `xml:"mechanisms"`
	Bind       *bind        `xml:"bind"`
	Session    *session     `xml:"session"`
}

type session struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-session session"`
}

type bind struct {
	XMLName  xml.Name  `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Required *required `xml:"required"`
}

type mechanisms struct {
	XMLName    xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
	Mechanisms []string `xml:"mechanism"`
}

type tlsStartTLS struct {
	XMLName  xml.Name  `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
	Required *required `xml:"required"`
}

type required struct{}

type saslFailure struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
	Reason  xml.Name `xml:",any"`
}

// BUG(matt): authentication incorrectly reports, "No supported SASL mechanism
// found", for authentication attemtps that fail due to invalid credentials.
<commit_msg>Improve error message format.<commit_after>package xmpp

import (
	"crypto/tls"
	"encoding/xml"
	"errors"
	"fmt"
	"log"
)

// Config structure used to create a new XMPP client connection.
type ClientConfig struct {
	// Don't upgrade the connection to TLS, even if the server supports it. If
	// the server *requires* TLS then this option is ignored.
	NoTLS bool

	// Skip verification of the server's certificate chain. Probably only
	// useful during development.
	InsecureSkipVerify bool
}

// Create a client XMPP over the stream.
func NewClientXMPP(stream *Stream, jid JID, password string, config *ClientConfig) (*XMPP, error) {

	if config == nil {
		config = &ClientConfig{}
	}

	for {

		if err := startClient(stream, jid); err != nil {
			return nil, err
		}

		// Read features.
		f := new(features)
		if err := stream.Decode(f, nil); err != nil {
			return nil, err
		}

		// TLS?
		if f.StartTLS != nil && (f.StartTLS.Required != nil || !config.NoTLS) {
			log.Println("Start TLS")
			if err := startTLS(stream, config); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Authentication
		if f.Mechanisms != nil {
			log.Println("Authenticating")
			if err := authenticate(stream, f.Mechanisms.Mechanisms, jid.Node, password); err != nil {
				return nil, err
			}
			continue // Restart
		}

		// Bind resource.
		if f.Bind != nil {
			log.Println("Binding resource.")
			boundJID, err := bindResource(stream, jid)
			if err != nil {
				return nil, err
			}
			jid = boundJID
		}

		// Session.
		if f.Session != nil {
			log.Println("Establishing session.")
			if err := establishSession(stream, jid.Domain); err != nil {
				return nil, err
			}
		}

		break
	}

	return newXMPP(jid, stream), nil
}

func startClient(stream *Stream, jid JID) error {

	start := xml.StartElement{
		xml.Name{"stream", "stream"},
		[]xml.Attr{
			xml.Attr{xml.Name{"", "xmlns"}, nsClient},
			xml.Attr{xml.Name{"xmlns", "stream"}, nsStreams},
			xml.Attr{xml.Name{"", "from"}, jid.Full()},
			xml.Attr{xml.Name{"", "to"}, jid.Domain},
			xml.Attr{xml.Name{"", "version"}, "1.0"},
		},
	}

	rstart, err := stream.SendStart(&start)
	if err != nil {
		return err
	}
	if rstart.Name != (xml.Name{nsStreams, "stream"}) {
		return fmt.Errorf("unexpected start element: %s", rstart.Name)
	}
	return nil
}

func startTLS(stream *Stream, config *ClientConfig) error {

	if err := stream.Send(&tlsStart{}); err != nil {
		return err
	}

	p := tlsProceed{}
	if err := stream.Decode(&p, nil); err != nil {
		return err
	}

	tlsConfig := tls.Config{InsecureSkipVerify: config.InsecureSkipVerify, ServerName: stream.config.ConnectionDomain}
	return stream.UpgradeTLS(&tlsConfig)
}

type tlsStart struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
}

type tlsProceed struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
}

func authenticate(stream *Stream, mechanisms []string, user, password string) error {
	for _, handler := range authHandlers {
		if !stringSliceContains(mechanisms, handler.Mechanism) {
			continue
		}
		if err := handler.Fn(stream, user, password); err == nil {
			log.Printf("Authentication (%s) successful", handler.Mechanism)
			return nil
		}
	}
	return errors.New("no supported SASL mechanism found")
}

type authHandler struct {
	Mechanism string
	Fn        func(*Stream, string, string) error
}

var authHandlers = []authHandler{
	{"PLAIN", authenticatePlain},
}

func authenticatePlain(stream *Stream, user, password string) error {
	auth := saslAuth{Mechanism: "PLAIN", Text: saslEncodePlain(user, password)}
	if err := stream.Send(&auth); err != nil {
		return err
	}
	return authenticateResponse(stream)
}

func authenticateResponse(stream *Stream) error {
	se, err := stream.Next()
	if err != nil {
		return err
	}

	switch se.Name.Local {
	case "success":
		if err := stream.Skip(); err != nil {
			return err
		}
		return nil
	case "failure":
		f := new(saslFailure)
		if err := stream.Decode(f, se); err != nil {
			return err
		}
		return fmt.Errorf("Authentication failed: %s", f.Reason.Local)
	default:
		return fmt.Errorf("Unexpected: %s", se.Name)
	}
}

type saslAuth struct {
	XMLName   xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl auth"`
	Mechanism string   `xml:"mechanism,attr"`
	Text      string   `xml:",chardata"`
}

func bindResource(stream *Stream, jid JID) (JID, error) {

	req := Iq{Id: UUID4(), Type: "set"}
	if jid.Resource == "" {
		req.PayloadEncode(bindIq{})
	} else {
		req.PayloadEncode(bindIq{Resource: jid.Resource})
	}
	if err := stream.Send(req); err != nil {
		return JID{}, err
	}

	resp := Iq{}
	err := stream.Decode(&resp, nil)
	if err != nil {
		return JID{}, err
	}
	bindResp := bindIq{}
	resp.PayloadDecode(&bindResp)

	boundJID, err := ParseJID(bindResp.JID)
	return boundJID, nil
}

type bindIq struct {
	XMLName  xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Resource string   `xml:"resource,omitempty"`
	JID      string   `xml:"jid,omitempty"`
}

func establishSession(stream *Stream, domain string) error {

	req := Iq{Id: UUID4(), Type: "set", To: domain}
	req.PayloadEncode(&session{})
	if err := stream.Send(req); err != nil {
		return err
	}

	resp := Iq{}
	if err := stream.Decode(&resp, nil); err != nil {
		return err
	} else if resp.Error != nil {
		return resp.Error
	}

	return nil
}

func stringSliceContains(l []string, m string) bool {
	for _, i := range l {
		if i == m {
			return true
		}
	}
	return false
}

type features struct {
	XMLName    xml.Name     `xml:"http://etherx.jabber.org/streams features"`
	StartTLS   *tlsStartTLS `xml:"starttls"`
	Mechanisms *mechanisms  `xml:"mechanisms"`
	Bind       *bind        `xml:"bind"`
	Session    *session     `xml:"session"`
}

type session struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-session session"`
}

type bind struct {
	XMLName  xml.Name  `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
	Required *required `xml:"required"`
}

type mechanisms struct {
	XMLName    xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
	Mechanisms []string `xml:"mechanism"`
}

type tlsStartTLS struct {
	XMLName  xml.Name  `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
	Required *required `xml:"required"`
}

type required struct{}

type saslFailure struct {
	XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl failure"`
	Reason  xml.Name `xml:",any"`
}

// BUG(matt): authentication incorrectly reports, "No supported SASL mechanism
// found", for authentication attemtps that fail due to invalid credentials.
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"testing"
)

type MockRoleGetter struct {
	r Roles
}

func NewMockRoleGetter(r []string) RoleGetter {
	return &MockRoleGetter{r: Roles(r)}
}

func (m *MockRoleGetter) Roles() Roles {
	return m.r.Dedup()
}

func ExampleRoleGetter() {
	roles := []string{
		"mock3", "mock2", "mock1", "mock2", "mock4", "mock1",
	}
	m := NewMockRoleGetter(roles)
	for _, r := range m.Roles() {
		fmt.Println(r)
	}
	// Output:
	// mock1
	// mock2
	// mock3
	// mock4
}

func TestEmptyRoleGetter(t *testing.T) {
	m := NewMockRoleGetter([]string{})
	r := m.Roles()

	t.Logf("Empty role result: %v", r)
	if len(r) != 0 {
		t.Errorf("Found unexpected roles from empty input")
	}
}

func TestNilRoleGetter(t *testing.T) {
	m := NewMockRoleGetter(nil)
	r := m.Roles()

	t.Logf("Nil role result: %v", r)
	if len(r) != 0 {
		t.Errorf("Found unexpected roles from nil input")
	}
}
<commit_msg>add some more tests for role_getter<commit_after>package lib

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws/session"
	"testing"
)

type MockRoleGetter struct {
	r Roles
}

func NewMockRoleGetter(r []string) RoleGetter {
	return &MockRoleGetter{r: Roles(r)}
}

func (m *MockRoleGetter) Roles() Roles {
	return m.r.Dedup()
}

func ExampleRoleGetter() {
	roles := []string{
		"mock3", "mock2", "mock1", "mock2", "mock4", "mock1",
	}
	m := NewMockRoleGetter(roles)
	for _, r := range m.Roles() {
		fmt.Println(r)
	}
	// Output:
	// mock1
	// mock2
	// mock3
	// mock4
}

func TestEmptyRoleGetter(t *testing.T) {
	m := NewMockRoleGetter([]string{})
	r := m.Roles()

	t.Logf("Empty role result: %v", r)
	if len(r) != 0 {
		t.Errorf("Found unexpected roles from empty input")
	}
}

func TestNilRoleGetter(t *testing.T) {
	m := NewMockRoleGetter(nil)
	r := m.Roles()

	t.Logf("Nil role result: %v", r)
	if len(r) != 0 {
		t.Errorf("Found unexpected roles from nil input")
	}
}

func TestNewAwsRoleGetterDefault(t *testing.T) {
	defer func() {
		if x := recover(); x != nil {
			t.Errorf("panic() from default NewAwsRoleGetter()")
		}
	}()
	s, err := session.NewSessionWithOptions(session.Options{})
	if err != nil {
		t.Errorf("Error creating AWS session: %v", err)
	}
	NewAwsRoleGetter(s, "u", new(RoleGetterOptions))
}

func TestNewAwsRoleGetterNilOpts(t *testing.T) {
	defer func() {
		if x := recover(); x != nil {
			t.Logf("Got expected panic() from default NewAwsRoleGetter()")
		} else {
			t.Errorf("Did not see expected panic() with nil options")
		}
	}()
	s, err := session.NewSessionWithOptions(session.Options{})
	if err != nil {
		t.Errorf("Error creating AWS session: %v", err)
	}
	NewAwsRoleGetter(s, "u", nil)
}
<|endoftext|>


<commit_before>package db

// CertInfo is here to pass the certificates content
// from the database around
type CertInfo struct {
	ID          int
	Fingerprint string
	Type        int
	Name        string
	Certificate string
}

// CertificatesGet returns all certificates from the DB as CertBaseInfo objects.
func (c *Cluster) CertificatesGet() (certs []*CertInfo, err error) {
	err = c.Transaction(func(tx *ClusterTx) error {
		rows, err := tx.tx.Query(
			"SELECT id, fingerprint, type, name, certificate FROM certificates",
		)
		if err != nil {
			return err
		}

		defer rows.Close()

		for rows.Next() {
			cert := new(CertInfo)
			rows.Scan(
				&cert.ID,
				&cert.Fingerprint,
				&cert.Type,
				&cert.Name,
				&cert.Certificate,
			)
			certs = append(certs, cert)
		}

		return rows.Err()
	})
	if err != nil {
		return certs, err
	}

	return certs, nil
}

// CertificateGet gets an CertBaseInfo object from the database.
// The argument fingerprint will be queried with a LIKE query, means you can
// pass a shortform and will get the full fingerprint.
// There can never be more than one image with a given fingerprint, as it is
// enforced by a UNIQUE constraint in the schema.
func (c *Cluster) CertificateGet(fingerprint string) (cert *CertInfo, err error) {
	cert = new(CertInfo)

	inargs := []interface{}{fingerprint + "%"}
	outfmt := []interface{}{
		&cert.ID,
		&cert.Fingerprint,
		&cert.Type,
		&cert.Name,
		&cert.Certificate,
	}

	query := `
		SELECT
			id, fingerprint, type, name, certificate
		FROM
			certificates
		WHERE fingerprint LIKE ?`

	if err = dbQueryRowScan(c.db, query, inargs, outfmt); err != nil {
		return nil, err
	}

	return cert, err
}

// CertSave stores a CertBaseInfo object in the db,
// it will ignore the ID field from the CertInfo.
func (c *Cluster) CertSave(cert *CertInfo) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		stmt, err := tx.tx.Prepare(`
			INSERT INTO certificates (
				fingerprint,
				type,
				name,
				certificate
			) VALUES (?, ?, ?, ?)`,
		)
		if err != nil {
			return err
		}
		defer stmt.Close()
		_, err = stmt.Exec(
			cert.Fingerprint,
			cert.Type,
			cert.Name,
			cert.Certificate,
		)
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

// CertDelete deletes a certificate from the db.
func (c *Cluster) CertDelete(fingerprint string) error {
	err := exec(c.db, "DELETE FROM certificates WHERE fingerprint=?", fingerprint)
	if err != nil {
		return err
	}

	return nil
}

func (c *Cluster) CertUpdate(fingerprint string, certName string, certType int) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		_, err := tx.tx.Exec("UPDATE certificates SET name=?, type=? WHERE fingerprint=?", certName, certType, fingerprint)
		return err
	})
	return err
}
<commit_msg>Fix lints in lxd/db/certificates.go<commit_after>package db

// CertInfo is here to pass the certificates content
// from the database around
type CertInfo struct {
	ID          int
	Fingerprint string
	Type        int
	Name        string
	Certificate string
}

// CertificatesGet returns all certificates from the DB as CertBaseInfo objects.
func (c *Cluster) CertificatesGet() (certs []*CertInfo, err error) {
	err = c.Transaction(func(tx *ClusterTx) error {
		rows, err := tx.tx.Query(
			"SELECT id, fingerprint, type, name, certificate FROM certificates",
		)
		if err != nil {
			return err
		}

		defer rows.Close()

		for rows.Next() {
			cert := new(CertInfo)
			rows.Scan(
				&cert.ID,
				&cert.Fingerprint,
				&cert.Type,
				&cert.Name,
				&cert.Certificate,
			)
			certs = append(certs, cert)
		}

		return rows.Err()
	})
	if err != nil {
		return certs, err
	}

	return certs, nil
}

// CertificateGet gets an CertBaseInfo object from the database.
// The argument fingerprint will be queried with a LIKE query, means you can
// pass a shortform and will get the full fingerprint.
// There can never be more than one image with a given fingerprint, as it is
// enforced by a UNIQUE constraint in the schema.
func (c *Cluster) CertificateGet(fingerprint string) (cert *CertInfo, err error) {
	cert = new(CertInfo)

	inargs := []interface{}{fingerprint + "%"}
	outfmt := []interface{}{
		&cert.ID,
		&cert.Fingerprint,
		&cert.Type,
		&cert.Name,
		&cert.Certificate,
	}

	query := `
		SELECT
			id, fingerprint, type, name, certificate
		FROM
			certificates
		WHERE fingerprint LIKE ?`

	if err = dbQueryRowScan(c.db, query, inargs, outfmt); err != nil {
		return nil, err
	}

	return cert, err
}

// CertSave stores a CertBaseInfo object in the db,
// it will ignore the ID field from the CertInfo.
func (c *Cluster) CertSave(cert *CertInfo) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		stmt, err := tx.tx.Prepare(`
			INSERT INTO certificates (
				fingerprint,
				type,
				name,
				certificate
			) VALUES (?, ?, ?, ?)`,
		)
		if err != nil {
			return err
		}
		defer stmt.Close()
		_, err = stmt.Exec(
			cert.Fingerprint,
			cert.Type,
			cert.Name,
			cert.Certificate,
		)
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

// CertDelete deletes a certificate from the db.
func (c *Cluster) CertDelete(fingerprint string) error {
	err := exec(c.db, "DELETE FROM certificates WHERE fingerprint=?", fingerprint)
	if err != nil {
		return err
	}

	return nil
}

// CertUpdate updates the certificate with the given fingerprint.
func (c *Cluster) CertUpdate(fingerprint string, certName string, certType int) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		_, err := tx.tx.Exec("UPDATE certificates SET name=?, type=? WHERE fingerprint=?", certName, certType, fingerprint)
		return err
	})
	return err
}
<|endoftext|>


<commit_before>package subprocess

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"
)

// Process struct. Has ability to set runtime arguments
type Process struct {
	exitCode int64 `yaml:"-"`
	exitErr  error `yaml:"-"`

	chExit     chan struct{} `yaml:"-"`
	hasMonitor bool          `yaml:"-"`

	Name   string   `yaml:"name"`
	Args   []string `yaml:"args,flow"`
	Pid    int64    `yaml:"pid"`
	Stdout string   `yaml:"stdout"`
	Stderr string   `yaml:"stderr"`
}

// GetPid returns the pid for the given process object
func (p *Process) GetPid() (int64, error) {
	pr, _ := os.FindProcess(int(p.Pid))
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		return p.Pid, nil
	}

	return 0, ErrNotRunning
}

// Stop will stop the given process object
func (p *Process) Stop() error {
	pr, _ := os.FindProcess(int(p.Pid))

	// Check if process exists.
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		err = pr.Kill()
		if err == nil {
			if p.hasMonitor {
				<-p.chExit
			}

			return nil // Killed successfully.
		}
	}

	// Check if either the existence check or the kill resulted in an already finished error.
	if strings.Contains(err.Error(), "process already finished") {
		if p.hasMonitor {
			<-p.chExit
		}

		return ErrNotRunning
	}

	return errors.Wrapf(err, "Could not kill process")
}

// Start will start the given process object
func (p *Process) Start() error {
	cmd := exec.Command(p.Name, p.Args...)
	cmd.Stdin = nil
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Setsid = true

	// Setup output capture.
	if p.Stdout != "" {
		out, err := os.Create(p.Stdout)
		if err != nil {
			return errors.Wrapf(err, "Unable to open stdout file")
		}
		defer out.Close()
		cmd.Stdout = out
	}

	if p.Stderr == p.Stdout {
		cmd.Stderr = cmd.Stdout
	} else if p.Stderr != "" {
		out, err := os.Create(p.Stderr)
		if err != nil {
			return errors.Wrapf(err, "Unable to open stderr file")
		}
		defer out.Close()
		cmd.Stderr = out
	}

	// Start the process.
	err := cmd.Start()
	if err != nil {
		return errors.Wrapf(err, "Unable to start process")
	}

	p.Pid = int64(cmd.Process.Pid)

	// Reset exitCode/exitErr
	p.exitCode = 0
	p.exitErr = nil

	// Spawn a goroutine waiting for it to exit.
	p.chExit = make(chan struct{})
	p.hasMonitor = true
	go func() {
		procstate, err := cmd.Process.Wait()
		if err != nil {
			p.exitCode = -1
			p.exitErr = err
			close(p.chExit)
			return
		}

		exitcode := int64(procstate.ExitCode())
		p.exitCode = exitcode
		close(p.chExit)
	}()

	return nil
}

// Restart stop and starts the given process object
func (p *Process) Restart() error {
	err := p.Stop()
	if err != nil {
		return errors.Wrapf(err, "Unable to stop process")
	}

	err = p.Start()
	if err != nil {
		return errors.Wrapf(err, "Unable to start process")
	}

	return nil
}

// Reload sends the SIGHUP signal to the given process object
func (p *Process) Reload() error {
	pr, _ := os.FindProcess(int(p.Pid))
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		err = pr.Signal(syscall.SIGHUP)
		if err != nil {
			return errors.Wrapf(err, "Could not reload process")
		}
		return nil
	} else if strings.Contains(err.Error(), "process already finished") {
		return ErrNotRunning
	}

	return errors.Wrapf(err, "Could not reload process")
}

// Save will save the given process object to a YAML file. Can be imported at a later point.
func (p *Process) Save(path string) error {
	dat, err := yaml.Marshal(p)
	if err != nil {
		return errors.Wrapf(err, "Unable to serialize process struct to YAML")
	}

	err = ioutil.WriteFile(path, dat, 0644)
	if err != nil {
		return errors.Wrapf(err, "Unable to write to file '%s'", path)
	}

	return nil
}

// Signal will send a signal to the given process object given a signal value
func (p *Process) Signal(signal int64) error {
	pr, _ := os.FindProcess(int(p.Pid))
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		err = pr.Signal(syscall.Signal(signal))
		if err != nil {
			return errors.Wrapf(err, "Could not signal process")
		}
		return nil
	} else if strings.Contains(err.Error(), "process already finished") {
		return ErrNotRunning
	}

	return errors.Wrapf(err, "Could not signal process")
}

// Wait will wait for the given process object exit code
func (p *Process) Wait() (int64, error) {
	if !p.hasMonitor {
		return -1, fmt.Errorf("Unable to wait on process we didn't spawn")
	}

	<-p.chExit
	return p.exitCode, p.exitErr
}
<commit_msg>shared/subprocess: Add AppArmor support<commit_after>package subprocess

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd/shared"
)

// Process struct. Has ability to set runtime arguments
type Process struct {
	exitCode int64 `yaml:"-"`
	exitErr  error `yaml:"-"`

	chExit     chan struct{} `yaml:"-"`
	hasMonitor bool          `yaml:"-"`

	Name     string   `yaml:"name"`
	Args     []string `yaml:"args,flow"`
	Apparmor string   `yaml:"apparmor"`
	Pid      int64    `yaml:"pid"`
	Stdout   string   `yaml:"stdout"`
	Stderr   string   `yaml:"stderr"`
}

func (p *Process) hasApparmor() bool {
	_, err := exec.LookPath("aa-exec")
	if err != nil {
		return false
	}

	if !shared.PathExists("/sys/kernel/security/apparmor") {
		return false
	}

	return true
}

// GetPid returns the pid for the given process object
func (p *Process) GetPid() (int64, error) {
	pr, _ := os.FindProcess(int(p.Pid))
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		return p.Pid, nil
	}

	return 0, ErrNotRunning
}

// SetApparmor allows setting the AppArmor profile.
func (p *Process) SetApparmor(profile string) {
	p.Apparmor = profile
}

// Stop will stop the given process object
func (p *Process) Stop() error {
	pr, _ := os.FindProcess(int(p.Pid))

	// Check if process exists.
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		err = pr.Kill()
		if err == nil {
			if p.hasMonitor {
				<-p.chExit
			}

			return nil // Killed successfully.
		}
	}

	// Check if either the existence check or the kill resulted in an already finished error.
	if strings.Contains(err.Error(), "process already finished") {
		if p.hasMonitor {
			<-p.chExit
		}

		return ErrNotRunning
	}

	return errors.Wrapf(err, "Could not kill process")
}

// Start will start the given process object
func (p *Process) Start() error {
	var cmd *exec.Cmd

	if p.Apparmor != "" && p.hasApparmor() {
		cmd = exec.Command("aa-exec", append([]string{"-p", p.Apparmor, p.Name}, p.Args...)...)
	} else {
		cmd = exec.Command(p.Name, p.Args...)
	}
	cmd.Stdin = nil
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Setsid = true

	// Setup output capture.
	if p.Stdout != "" {
		out, err := os.Create(p.Stdout)
		if err != nil {
			return errors.Wrapf(err, "Unable to open stdout file")
		}
		defer out.Close()
		cmd.Stdout = out
	}

	if p.Stderr == p.Stdout {
		cmd.Stderr = cmd.Stdout
	} else if p.Stderr != "" {
		out, err := os.Create(p.Stderr)
		if err != nil {
			return errors.Wrapf(err, "Unable to open stderr file")
		}
		defer out.Close()
		cmd.Stderr = out
	}

	// Start the process.
	err := cmd.Start()
	if err != nil {
		return errors.Wrapf(err, "Unable to start process")
	}

	p.Pid = int64(cmd.Process.Pid)

	// Reset exitCode/exitErr
	p.exitCode = 0
	p.exitErr = nil

	// Spawn a goroutine waiting for it to exit.
	p.chExit = make(chan struct{})
	p.hasMonitor = true
	go func() {
		procstate, err := cmd.Process.Wait()
		if err != nil {
			p.exitCode = -1
			p.exitErr = err
			close(p.chExit)
			return
		}

		exitcode := int64(procstate.ExitCode())
		p.exitCode = exitcode
		close(p.chExit)
	}()

	return nil
}

// Restart stop and starts the given process object
func (p *Process) Restart() error {
	err := p.Stop()
	if err != nil {
		return errors.Wrapf(err, "Unable to stop process")
	}

	err = p.Start()
	if err != nil {
		return errors.Wrapf(err, "Unable to start process")
	}

	return nil
}

// Reload sends the SIGHUP signal to the given process object
func (p *Process) Reload() error {
	pr, _ := os.FindProcess(int(p.Pid))
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		err = pr.Signal(syscall.SIGHUP)
		if err != nil {
			return errors.Wrapf(err, "Could not reload process")
		}
		return nil
	} else if strings.Contains(err.Error(), "process already finished") {
		return ErrNotRunning
	}

	return errors.Wrapf(err, "Could not reload process")
}

// Save will save the given process object to a YAML file. Can be imported at a later point.
func (p *Process) Save(path string) error {
	dat, err := yaml.Marshal(p)
	if err != nil {
		return errors.Wrapf(err, "Unable to serialize process struct to YAML")
	}

	err = ioutil.WriteFile(path, dat, 0644)
	if err != nil {
		return errors.Wrapf(err, "Unable to write to file '%s'", path)
	}

	return nil
}

// Signal will send a signal to the given process object given a signal value
func (p *Process) Signal(signal int64) error {
	pr, _ := os.FindProcess(int(p.Pid))
	err := pr.Signal(syscall.Signal(0))
	if err == nil {
		err = pr.Signal(syscall.Signal(signal))
		if err != nil {
			return errors.Wrapf(err, "Could not signal process")
		}
		return nil
	} else if strings.Contains(err.Error(), "process already finished") {
		return ErrNotRunning
	}

	return errors.Wrapf(err, "Could not signal process")
}

// Wait will wait for the given process object exit code
func (p *Process) Wait() (int64, error) {
	if !p.hasMonitor {
		return -1, fmt.Errorf("Unable to wait on process we didn't spawn")
	}

	<-p.chExit
	return p.exitCode, p.exitErr
}
<|endoftext|>


<commit_before>package etcdserver

import (
	"net/url"
	"strings"
	"time"

	"github.com/coreos/etcd/embed"
	"github.com/coreos/etcd/pkg/osutil"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/go-semver/semver"
	"github.com/golang/glog"

	configapi "github.com/openshift/origin/pkg/cmd/server/api"
)

const defaultName = "openshift.local"

// RunEtcd starts an etcd server and runs it forever
func RunEtcd(etcdServerConfig *configapi.EtcdConfig) {
	cfg := embed.NewConfig()
	cfg.Debug = true
	cfg.Name = defaultName
	cfg.Dir = etcdServerConfig.StorageDir

	clientTLS := true
	cfg.ClientTLSInfo.CAFile = etcdServerConfig.ServingInfo.ClientCA
	cfg.ClientTLSInfo.CertFile = etcdServerConfig.ServingInfo.ServerCert.CertFile
	cfg.ClientTLSInfo.KeyFile = etcdServerConfig.ServingInfo.ServerCert.KeyFile
	cfg.ClientTLSInfo.ClientCertAuth = len(cfg.ClientTLSInfo.CAFile) > 0
	u, err := types.NewURLs(addressToURLs(etcdServerConfig.ServingInfo.BindAddress, clientTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd peer URLs: %v", err)
	}
	cfg.LCUrls = []url.URL(u)

	peerTLS := true
	cfg.PeerTLSInfo.CAFile = etcdServerConfig.PeerServingInfo.ClientCA
	cfg.PeerTLSInfo.CertFile = etcdServerConfig.PeerServingInfo.ServerCert.CertFile
	cfg.PeerTLSInfo.KeyFile = etcdServerConfig.PeerServingInfo.ServerCert.KeyFile
	cfg.PeerTLSInfo.ClientCertAuth = len(cfg.PeerTLSInfo.CAFile) > 0
	u, err = types.NewURLs(addressToURLs(etcdServerConfig.PeerServingInfo.BindAddress, peerTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd peer URLs: %v", err)
	}
	cfg.LPUrls = []url.URL(u)

	u, err = types.NewURLs(addressToURLs(etcdServerConfig.Address, clientTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd announce client URLs: %v", err)
	}
	cfg.ACUrls = []url.URL(u)

	u, err = types.NewURLs(addressToURLs(etcdServerConfig.PeerAddress, peerTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd announce peer URLs: %v", err)
	}
	cfg.APUrls = []url.URL(u)

	cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)

	osutil.HandleInterrupts()

	e, err := embed.StartEtcd(cfg)
	if err != nil {
		glog.Fatalf("Unable to start etcd: %v", err)
	}

	ready := make(chan struct{})
	go func() {
		defer e.Close()

		select {
		case <-e.Server.ReadyNotify():
			// embedded servers must negotiate to reach v3 mode, this ensures we loop until that happens
			glog.V(4).Infof("Waiting for etcd to reach cluster version 3.0.0")
			for min := semver.Must(semver.NewVersion("3.0.0")); e.Server.ClusterVersion() == nil || e.Server.ClusterVersion().LessThan(*min); {
				time.Sleep(25 * time.Millisecond)
			}
			close(ready)
			glog.Infof("Started etcd at %s", etcdServerConfig.Address)
		case <-time.After(60 * time.Second):
			glog.Warning("etcd took too long to start, stopped")
			e.Server.Stop() // trigger a shutdown
		}
		glog.Fatalf("etcd has returned an error: %v", <-e.Err())
	}()
	<-ready
}

// addressToURLs turns a host:port comma delimited list into an array valid
// URL strings with the appropriate prefix for the TLS mode.
func addressToURLs(addr string, isTLS bool) []string {
	addrs := strings.Split(addr, ",")
	for i := range addrs {
		if isTLS {
			addrs[i] = "https://" + addrs[i]
		} else {
			addrs[i] = "http://" + addrs[i]
		}
	}
	return addrs
}
<commit_msg>Allow unix domain sockets to be passed to etcd start<commit_after>package etcdserver

import (
	"net/url"
	"strings"
	"time"

	"github.com/coreos/etcd/embed"
	"github.com/coreos/etcd/pkg/osutil"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/go-semver/semver"
	"github.com/golang/glog"

	configapi "github.com/openshift/origin/pkg/cmd/server/api"
)

const defaultName = "openshift.local"

// RunEtcd starts an etcd server and runs it forever
func RunEtcd(etcdServerConfig *configapi.EtcdConfig) {
	cfg := embed.NewConfig()
	cfg.Debug = true
	cfg.Name = defaultName
	cfg.Dir = etcdServerConfig.StorageDir

	clientTLS := true
	cfg.ClientTLSInfo.CAFile = etcdServerConfig.ServingInfo.ClientCA
	cfg.ClientTLSInfo.CertFile = etcdServerConfig.ServingInfo.ServerCert.CertFile
	cfg.ClientTLSInfo.KeyFile = etcdServerConfig.ServingInfo.ServerCert.KeyFile
	cfg.ClientTLSInfo.ClientCertAuth = len(cfg.ClientTLSInfo.CAFile) > 0
	u, err := types.NewURLs(addressToURLs(etcdServerConfig.ServingInfo.BindAddress, clientTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd peer URLs: %v", err)
	}
	cfg.LCUrls = []url.URL(u)

	peerTLS := true
	cfg.PeerTLSInfo.CAFile = etcdServerConfig.PeerServingInfo.ClientCA
	cfg.PeerTLSInfo.CertFile = etcdServerConfig.PeerServingInfo.ServerCert.CertFile
	cfg.PeerTLSInfo.KeyFile = etcdServerConfig.PeerServingInfo.ServerCert.KeyFile
	cfg.PeerTLSInfo.ClientCertAuth = len(cfg.PeerTLSInfo.CAFile) > 0
	u, err = types.NewURLs(addressToURLs(etcdServerConfig.PeerServingInfo.BindAddress, peerTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd peer URLs: %v", err)
	}
	cfg.LPUrls = []url.URL(u)

	u, err = types.NewURLs(addressToURLs(etcdServerConfig.Address, clientTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd announce client URLs: %v", err)
	}
	cfg.ACUrls = []url.URL(u)

	u, err = types.NewURLs(addressToURLs(etcdServerConfig.PeerAddress, peerTLS))
	if err != nil {
		glog.Fatalf("Unable to build etcd announce peer URLs: %v", err)
	}
	cfg.APUrls = []url.URL(u)

	cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)

	osutil.HandleInterrupts()

	e, err := embed.StartEtcd(cfg)
	if err != nil {
		glog.Fatalf("Unable to start etcd: %v", err)
	}

	ready := make(chan struct{})
	go func() {
		defer e.Close()

		select {
		case <-e.Server.ReadyNotify():
			// embedded servers must negotiate to reach v3 mode, this ensures we loop until that happens
			glog.V(4).Infof("Waiting for etcd to reach cluster version 3.0.0")
			for min := semver.Must(semver.NewVersion("3.0.0")); e.Server.ClusterVersion() == nil || e.Server.ClusterVersion().LessThan(*min); {
				time.Sleep(25 * time.Millisecond)
			}
			close(ready)
			glog.Infof("Started etcd at %s", etcdServerConfig.Address)
		case <-time.After(60 * time.Second):
			glog.Warning("etcd took too long to start, stopped")
			e.Server.Stop() // trigger a shutdown
		}
		glog.Fatalf("etcd has returned an error: %v", <-e.Err())
	}()
	<-ready
}

// addressToURLs turns a host:port comma delimited list into an array valid
// URL strings with the appropriate prefix for the TLS mode.
func addressToURLs(addr string, isTLS bool) []string {
	addrs := strings.Split(addr, ",")
	for i := range addrs {
		if strings.HasPrefix(addrs[i], "unix://") || strings.HasPrefix(addrs[i], "unixs://") {
			continue
		}
		if isTLS {
			addrs[i] = "https://" + addrs[i]
		} else {
			addrs[i] = "http://" + addrs[i]
		}
	}
	return addrs
}
<|endoftext|>


<commit_before>package failure_test

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"

	"github.com/slok/ragnarok/attack"
	"github.com/slok/ragnarok/clock"
	"github.com/slok/ragnarok/failure"
	mattack "github.com/slok/ragnarok/mocks/attack"
	mclock "github.com/slok/ragnarok/mocks/clock"
)

func TestNewSystemFailure(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{
				"attack1": attack.Opts{
					"size": 524288000,
				},
			},
			{
				"attack1": attack.Opts{
					"size": 100,
				},
			},
			{
				"attack2": nil,
			},
			{
				"attack3": attack.Opts{
					"target":   "myTarget",
					"quantity": 10,
					"pace":     "10m",
					"rest":     "30s",
				},
			},
		},
	}
	at1 := &mattack.Attacker{}
	at2 := &mattack.Attacker{}
	at3 := &mattack.Attacker{}
	at4 := &mattack.Attacker{}
	// Mock registry.
	reg := &mattack.Registry{}
	reg.On("New", "attack1", d.Attacks[0]["attack1"]).Return(at1, nil)
	reg.On("New", "attack1", d.Attacks[1]["attack1"]).Return(at2, nil)
	reg.On("New", "attack2", d.Attacks[2]["attack2"]).Return(at3, nil)
	reg.On("New", "attack3", d.Attacks[3]["attack3"]).Return(at4, nil)

	// Test.
	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		assert.NotNil(f, "A succesful creation shoudln't be an error")
		assert.Equal(failure.Created, f.State)
		reg.AssertExpectations(t)
	}

}

func TestNewSystemFailureError(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{
				"attack1": attack.Opts{
					"size": 524288000,
				},
			},
			{
				"attack2": nil,
			},
			{
				"attack3": nil,
			},
		},
	}

	// Mock registry.
	reg := &mattack.Registry{}
	reg.On("New", "attack1", d.Attacks[0]["attack1"]).Return(nil, nil)
	reg.On("New", "attack2", d.Attacks[1]["attack2"]).Return(nil, errors.New("error test"))

	// Test.
	_, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	reg.AssertNotCalled(t, "New", "attack3", d.Attacks[2]["attack3"])
	if assert.Error(err) {
		reg.AssertExpectations(t)
	}

}

func TestNewSystemFailureMultipleAttacksOnBlock(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{
				"attack1": attack.Opts{
					"size": 524288000,
				},
				"attack2": nil,
			},
			{
				"attack3": nil,
			},
		},
	}

	// Mock registry.
	reg := &mattack.Registry{}
	reg.AssertNotCalled(t, "New")
	// Test.
	_, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.Error(err) {
		reg.AssertExpectations(t)
	}
}

func TestSystemFailureFailState(t *testing.T) {
	assert := assert.New(t)
	expectedErr := fmt.Errorf("invalid state. The only valid state for execution is: %s", failure.Created)
	tests := []struct {
		state         failure.State
		expectedErr   error
		expectedState failure.State
	}{
		{
			state:         failure.Created,
			expectedErr:   nil,
			expectedState: failure.Executing,
		},
		{
			state:         failure.Executing,
			expectedErr:   expectedErr,
			expectedState: failure.Executing,
		},
		{
			state:         failure.Reverted,
			expectedErr:   expectedErr,
			expectedState: failure.Reverted,
		},
		{
			state:         failure.Error,
			expectedErr:   expectedErr,
			expectedState: failure.Error,
		},
		{
			state:         failure.ErrorReverting,
			expectedErr:   expectedErr,
			expectedState: failure.ErrorReverting,
		},
		{
			state:         failure.Unknown,
			expectedErr:   expectedErr,
			expectedState: failure.Unknown,
		},
	}

	for _, test := range tests {
		f, err := failure.NewSystemFailure(failure.Definition{}, nil, nil)
		f.State = test.state
		if assert.NoError(err) {
			err = f.Fail()
			assert.Equal(test.expectedErr, err)
			assert.Equal(test.expectedState, f.State, "Expected state should be '%s', got: '%s'", test.expectedState, f.State)
		}
	}
}

func TestSystemFailureAttacksOK(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at := &mattack.Attacker{}
	at.On("Apply", ctxMatcher).Times(3).Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		if assert.NoError(f.Fail()) {
			assert.Equal(failure.Executing, f.State)
			at.AssertExpectations(t)
		}
	}
}

func TestSystemFailureAttacksOKRevertOK(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at := &mattack.Attacker{}
	at.On("Apply", ctxMatcher).Times(3).Return(nil)
	at.On("Revert").Times(3).Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		if assert.NoError(f.Fail()) {
			assert.Equal(failure.Executing, f.State)
			if assert.NoError(f.Revert()) {
				assert.Equal(failure.Reverted, f.State)
				at.AssertExpectations(t)
			}
		}
	}
}

func TestSystemFailureFailAttacksErrorAutoRevertOK(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1, at2, at3 := &mattack.Attacker{}, &mattack.Attacker{}, &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(errors.New("error1"))
	at2.On("Apply", ctxMatcher).Once().Return(nil)
	at3.On("Apply", ctxMatcher).Once().Return(errors.New("error3"))
	// the only one that needs to be reverted is the 2nd one because is the only one that applied correctly
	at2.On("Revert").Once().Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at2, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at3, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		err = f.Fail()
		if assert.Error(err) {
			assert.Equal(errors.New("error aplying failure"), err)
			assert.Equal(failure.Error, f.State)
			at1.AssertExpectations(t)
			at2.AssertExpectations(t)
			at3.AssertExpectations(t)
		}
	}
}

func TestSystemFailureFailAttacksErrorAutoRevertError(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1, at2, at3 := &mattack.Attacker{}, &mattack.Attacker{}, &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(nil)
	at2.On("Apply", ctxMatcher).Once().Return(errors.New("error2"))
	at3.On("Apply", ctxMatcher).Once().Return(nil)
	at1.On("Revert").Once().Return(nil)
	at3.On("Revert").Once().Return(errors.New("revert_error3"))

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at2, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at3, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		err = f.Fail()
		if assert.Error(err) {
			assert.Equal(errors.New("error aplying failure & error when trying to revert the applied ones"), err)
			assert.Equal(failure.ErrorReverting, f.State)
			at1.AssertExpectations(t)
			at2.AssertExpectations(t)
			at3.AssertExpectations(t)
		}
	}
}

func TestSystemFailureFailAttacksFinishWithTimeout(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1 := &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(nil)
	reverted := make(chan struct{})
	at1.On("Revert").Once().Return(nil).Run(func(args mock.Arguments) {
		reverted <- struct{}{}
	})

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)

	// Mock clock
	cl := &mclock.Clock{}
	cl.On("After", d.Timeout).Return(time.After(0))
	cl.On("Now").Return(time.Now())

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, cl)
	if assert.NoError(err) {
		err = f.Fail()
		// Wait until clock timeout to check revert called
		select {
		case <-clock.After(5 * time.Millisecond):
			require.Fail("Timeout calling revert after a timeout")
		case <-reverted:
			at1.AssertExpectations(t)
		}

	}
}

func TestSystemFailureFailAttacksFinishForced(t *testing.T) {
	assert := assert.New(t)

	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1 := &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(nil)
	at1.On("Revert").Once().Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)

	// Mock clock
	cl := &mclock.Clock{}
	cl.On("After", d.Timeout).Return(time.After(9999 * time.Hour)) // Never
	cl.On("Now").Return(time.Now())

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, cl)
	if assert.NoError(err) {
		err = f.Fail()
		f.Revert()
		at1.AssertExpectations(t)
	}
}
<commit_msg>mend<commit_after>package failure_test

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"

	"github.com/slok/ragnarok/attack"
	"github.com/slok/ragnarok/clock"
	"github.com/slok/ragnarok/failure"
	mattack "github.com/slok/ragnarok/mocks/attack"
	mclock "github.com/slok/ragnarok/mocks/clock"
	"github.com/slok/ragnarok/types"
)

func TestNewSystemFailure(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{
				"attack1": attack.Opts{
					"size": 524288000,
				},
			},
			{
				"attack1": attack.Opts{
					"size": 100,
				},
			},
			{
				"attack2": nil,
			},
			{
				"attack3": attack.Opts{
					"target":   "myTarget",
					"quantity": 10,
					"pace":     "10m",
					"rest":     "30s",
				},
			},
		},
	}
	at1 := &mattack.Attacker{}
	at2 := &mattack.Attacker{}
	at3 := &mattack.Attacker{}
	at4 := &mattack.Attacker{}
	// Mock registry.
	reg := &mattack.Registry{}
	reg.On("New", "attack1", d.Attacks[0]["attack1"]).Return(at1, nil)
	reg.On("New", "attack1", d.Attacks[1]["attack1"]).Return(at2, nil)
	reg.On("New", "attack2", d.Attacks[2]["attack2"]).Return(at3, nil)
	reg.On("New", "attack3", d.Attacks[3]["attack3"]).Return(at4, nil)

	// Test.
	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		assert.NotNil(f, "A succesful creation shoudln't be an error")
		assert.Equal(types.EnabledFailureState, f.State)
		reg.AssertExpectations(t)
	}

}

func TestNewSystemFailureError(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{
				"attack1": attack.Opts{
					"size": 524288000,
				},
			},
			{
				"attack2": nil,
			},
			{
				"attack3": nil,
			},
		},
	}

	// Mock registry.
	reg := &mattack.Registry{}
	reg.On("New", "attack1", d.Attacks[0]["attack1"]).Return(nil, nil)
	reg.On("New", "attack2", d.Attacks[1]["attack2"]).Return(nil, errors.New("error test"))

	// Test.
	_, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	reg.AssertNotCalled(t, "New", "attack3", d.Attacks[2]["attack3"])
	if assert.Error(err) {
		reg.AssertExpectations(t)
	}

}

func TestNewSystemFailureMultipleAttacksOnBlock(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{
				"attack1": attack.Opts{
					"size": 524288000,
				},
				"attack2": nil,
			},
			{
				"attack3": nil,
			},
		},
	}

	// Mock registry.
	reg := &mattack.Registry{}
	reg.AssertNotCalled(t, "New")
	// Test.
	_, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.Error(err) {
		reg.AssertExpectations(t)
	}
}

func TestSystemFailureFailState(t *testing.T) {
	assert := assert.New(t)
	expectedErr := fmt.Errorf("invalid state. The only valid state for execution is: %s", types.EnabledFailureState)
	tests := []struct {
		state         types.FailureState
		expectedErr   error
		expectedState types.FailureState
	}{
		{
			state:         types.EnabledFailureState,
			expectedErr:   nil,
			expectedState: types.ExecutingFailureState,
		},
		{
			state:         types.ExecutingFailureState,
			expectedErr:   expectedErr,
			expectedState: types.ExecutingFailureState,
		},
		{
			state:         types.DisabledFailureState,
			expectedErr:   expectedErr,
			expectedState: types.DisabledFailureState,
		},
		{
			state:         types.ErroredFailureState,
			expectedErr:   expectedErr,
			expectedState: types.ErroredFailureState,
		},
		{
			state:         types.ErroredRevertingFailureState,
			expectedErr:   expectedErr,
			expectedState: types.ErroredRevertingFailureState,
		},
		{
			state:         types.UnknownFailureState,
			expectedErr:   expectedErr,
			expectedState: types.UnknownFailureState,
		},
	}

	for _, test := range tests {
		f, err := failure.NewSystemFailure(failure.Definition{}, nil, nil)
		f.State = test.state
		if assert.NoError(err) {
			err = f.Fail()
			assert.Equal(test.expectedErr, err)
			assert.Equal(test.expectedState, f.State, "Expected state should be '%s', got: '%s'", test.expectedState, f.State)
		}
	}
}

func TestSystemFailureAttacksOK(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at := &mattack.Attacker{}
	at.On("Apply", ctxMatcher).Times(3).Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		if assert.NoError(f.Fail()) {
			assert.Equal(types.ExecutingFailureState, f.State)
			at.AssertExpectations(t)
		}
	}
}

func TestSystemFailureAttacksOKRevertOK(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at := &mattack.Attacker{}
	at.On("Apply", ctxMatcher).Times(3).Return(nil)
	at.On("Revert").Times(3).Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		if assert.NoError(f.Fail()) {
			assert.Equal(types.ExecutingFailureState, f.State)
			if assert.NoError(f.Revert()) {
				assert.Equal(types.DisabledFailureState, f.State)
				at.AssertExpectations(t)
			}
		}
	}
}

func TestSystemFailureFailAttacksErrorAutoRevertOK(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1, at2, at3 := &mattack.Attacker{}, &mattack.Attacker{}, &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(errors.New("error1"))
	at2.On("Apply", ctxMatcher).Once().Return(nil)
	at3.On("Apply", ctxMatcher).Once().Return(errors.New("error3"))
	// the only one that needs to be reverted is the 2nd one because is the only one that applied correctly
	at2.On("Revert").Once().Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at2, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at3, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		err = f.Fail()
		if assert.Error(err) {
			assert.Equal(errors.New("error aplying failure"), err)
			assert.Equal(types.ErroredFailureState, f.State)
			at1.AssertExpectations(t)
			at2.AssertExpectations(t)
			at3.AssertExpectations(t)
		}
	}
}

func TestSystemFailureFailAttacksErrorAutoRevertError(t *testing.T) {
	assert := assert.New(t)
	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
			{"attack2": attack.Opts{}},
			{"attack3": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1, at2, at3 := &mattack.Attacker{}, &mattack.Attacker{}, &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(nil)
	at2.On("Apply", ctxMatcher).Once().Return(errors.New("error2"))
	at3.On("Apply", ctxMatcher).Once().Return(nil)
	at1.On("Revert").Once().Return(nil)
	at3.On("Revert").Once().Return(errors.New("revert_error3"))

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)
	reg.On("New", "attack2", attack.Opts{}).Return(at2, nil)
	reg.On("New", "attack3", attack.Opts{}).Return(at3, nil)

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, nil)
	if assert.NoError(err) {
		err = f.Fail()
		if assert.Error(err) {
			assert.Equal(errors.New("error aplying failure & error when trying to revert the applied ones"), err)
			assert.Equal(types.ErroredRevertingFailureState, f.State)
			at1.AssertExpectations(t)
			at2.AssertExpectations(t)
			at3.AssertExpectations(t)
		}
	}
}

func TestSystemFailureFailAttacksFinishWithTimeout(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1 := &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(nil)
	reverted := make(chan struct{})
	at1.On("Revert").Once().Return(nil).Run(func(args mock.Arguments) {
		reverted <- struct{}{}
	})

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)

	// Mock clock
	cl := &mclock.Clock{}
	cl.On("After", d.Timeout).Return(time.After(0))
	cl.On("Now").Return(time.Now())

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, cl)
	if assert.NoError(err) {
		err = f.Fail()
		// Wait until clock timeout to check revert called
		select {
		case <-clock.After(5 * time.Millisecond):
			require.Fail("Timeout calling revert after a timeout")
		case <-reverted:
			at1.AssertExpectations(t)
		}

	}
}

func TestSystemFailureFailAttacksFinishForced(t *testing.T) {
	assert := assert.New(t)

	d := failure.Definition{
		Timeout: 1 * time.Hour,
		Attacks: []failure.AttackMap{
			{"attack1": attack.Opts{}},
		},
	}

	// Mock attackers
	ctxMatcher := mock.MatchedBy(func(ctx context.Context) bool { return true })
	at1 := &mattack.Attacker{}
	at1.On("Apply", ctxMatcher).Once().Return(nil)
	at1.On("Revert").Once().Return(nil)

	// Mock Registry
	reg := &mattack.Registry{}
	reg.On("New", "attack1", attack.Opts{}).Return(at1, nil)

	// Mock clock
	cl := &mclock.Clock{}
	cl.On("After", d.Timeout).Return(time.After(9999 * time.Hour)) // Never
	cl.On("Now").Return(time.Now())

	f, err := failure.NewSystemFailureFromReg(d, reg, nil, cl)
	if assert.NoError(err) {
		err = f.Fail()
		f.Revert()
		at1.AssertExpectations(t)
	}
}
<|endoftext|>


<commit_before>package pathutil

import (
	"bufio"
	"io"
	"io/ioutil"
	"os"

	"github.com/pkg/errors"
)

type ReadSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

// OpenReader retun ReadSeekCloser interface
//
// for example:
//	path, _ := New("/bla/bla")
//	r, err := path.OpenReader()
//	if err != nil {
//		panic(err)
//	}
//	defer r.Close()
//
func (path PathImpl) OpenReader() (ReadSeekCloser, error) {
	file, err := os.Open(path.Canonpath())
	if err != nil {
		return nil, errors.Wrapf(err, "OpenReader on path %s fail (%+v)", path, path)
	}

	return file, nil
}

// OpenWriter retun *os.File as new file (like `>>`)
//
// for example:
//	path, _ := NewFilePath(FilePathOpt{})
//	file, err := path.OpenWriter()
//	if err != nil {
//		panic(err)
//	}
//	defer func(){
//		file.Close()
//		file.Sync()
//	}()
//
//  writer.Write(some_bytes)
//
func (path PathImpl) OpenWriter() (*os.File, error) {
	return path.openWriterFlag(os.O_RDWR | os.O_CREATE)
}

// OpenWriterAppend create new writer, similar as `OpenWriter` but append (like `>`)
func (path PathImpl) OpenWriterAppend() (*os.File, error) {
	return path.openWriterFlag(os.O_APPEND | os.O_CREATE | os.O_WRONLY)
}

func (path PathImpl) openWriterFlag(flag int) (*os.File, error) {
	file, err := os.OpenFile(path.String(), flag, 0775)
	if err != nil {
		return nil, err
	}

	return file, err
}

// Slurp read all file
// like ioutil.ReadFile
func (path PathImpl) Slurp() (string, error) {
	bytes, err := path.SlurpBytes()
	if err != nil {
		return "", err
	}

	return string(bytes[:]), nil
}

func (path PathImpl) SlurpBytes() ([]byte, error) {
	return ioutil.ReadFile(path.String())
}

// Spew write string to file
func (path PathImpl) Spew(content string) (err error) {
	file, err := path.OpenWriter()
	if err != nil {
		return err
	}

	defer func() {
		if errClose := file.Close(); errClose != nil {
			err = errClose
		}
	}()

	_, err = file.WriteString(content)
	return err
}

// SpewBytes write bytes to file
func (path PathImpl) SpewBytes(bytes []byte) (err error) {
	file, err := path.OpenWriter()
	if err != nil {
		return err
	}

	defer func() {
		if errClose := file.Close(); errClose != nil {
			err = errClose
		}
	}()

	_, err = file.Write(bytes)
	return err
}

// LinesWalker read lines in file and call LinesFunc with line parameter
//
// for example:
//	lines := make([]string, 0)
//
//	linesFuncError := path.LinesWalker(func(line string) {
//		lines = append(lines, line)
//	})
func (path PathImpl) LinesWalker(linesFunc LinesFunc) (err error) {
	reader, err := path.OpenReader()
	if err != nil {
		return err
	}
	defer func() {
		if errClose := reader.Close(); errClose != nil {
			err = errClose
		}
	}()

	scanner := bufio.NewScanner(reader)

	for scanner.Scan() {
		linesFunc(scanner.Text())
	}

	return scanner.Err()
}

// Read all lines and return as array of lines
func (path PathImpl) Lines() ([]string, error) {
	lines := make([]string, 0)

	linesFuncError := path.LinesWalker(func(line string) {
		lines = append(lines, line)
	})

	return lines, linesFuncError
}

// CopyFrom copy stream from reader to path
// (file after copy close and sync)
func (path PathImpl) CopyFrom(reader io.Reader) (copyied int64, err error) {
	file, err := path.OpenWriter()
	if err != nil {
		return 0, err
	}

	defer func() {
		if errSync := file.Sync(); errSync != nil {
			err = errSync
		} else if errClose := file.Close(); errClose != nil {
			err = errClose
		}

	}()

	copyied, err = io.Copy(file, reader)

	return copyied, err
}
<commit_msg>ioutil.ReadFile -> os.ReadFile<commit_after>package pathutil

import (
	"bufio"
	"io"
	"os"

	"github.com/pkg/errors"
)

type ReadSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

// OpenReader retun ReadSeekCloser interface
//
// for example:
//
//	path, _ := New("/bla/bla")
//	r, err := path.OpenReader()
//	if err != nil {
//		panic(err)
//	}
//	defer r.Close()
func (path PathImpl) OpenReader() (ReadSeekCloser, error) {
	file, err := os.Open(path.Canonpath())
	if err != nil {
		return nil, errors.Wrapf(err, "OpenReader on path %s fail (%+v)", path, path)
	}

	return file, nil
}

// OpenWriter retun *os.File as new file (like `>>`)
//
// for example:
//
//		path, _ := NewFilePath(FilePathOpt{})
//		file, err := path.OpenWriter()
//		if err != nil {
//			panic(err)
//		}
//		defer func(){
//			file.Close()
//			file.Sync()
//		}()
//
//	 writer.Write(some_bytes)
func (path PathImpl) OpenWriter() (*os.File, error) {
	return path.openWriterFlag(os.O_RDWR | os.O_CREATE)
}

// OpenWriterAppend create new writer, similar as `OpenWriter` but append (like `>`)
func (path PathImpl) OpenWriterAppend() (*os.File, error) {
	return path.openWriterFlag(os.O_APPEND | os.O_CREATE | os.O_WRONLY)
}

func (path PathImpl) openWriterFlag(flag int) (*os.File, error) {
	file, err := os.OpenFile(path.String(), flag, 0775)
	if err != nil {
		return nil, err
	}

	return file, err
}

// Slurp read the whole file and return content as string
func (path PathImpl) Slurp() (string, error) {
	bytes, err := path.SlurpBytes()
	if err != nil {
		return "", err
	}

	return string(bytes[:]), nil
}

// SlurpBytes reads the whole file and return content slice of bytes
// like os.ReadFile
func (path PathImpl) SlurpBytes() ([]byte, error) {
	return os.ReadFile(path.String())
}

// Spew write string to file
func (path PathImpl) Spew(content string) (err error) {
	file, err := path.OpenWriter()
	if err != nil {
		return err
	}

	defer func() {
		if errClose := file.Close(); errClose != nil {
			err = errClose
		}
	}()

	_, err = file.WriteString(content)
	return err
}

// SpewBytes write bytes to file
func (path PathImpl) SpewBytes(bytes []byte) (err error) {
	file, err := path.OpenWriter()
	if err != nil {
		return err
	}

	defer func() {
		if errClose := file.Close(); errClose != nil {
			err = errClose
		}
	}()

	_, err = file.Write(bytes)
	return err
}

// LinesWalker read lines in file and call LinesFunc with line parameter
//
// for example:
//
//	lines := make([]string, 0)
//
//	linesFuncError := path.LinesWalker(func(line string) {
//		lines = append(lines, line)
//	})
func (path PathImpl) LinesWalker(linesFunc LinesFunc) (err error) {
	reader, err := path.OpenReader()
	if err != nil {
		return err
	}
	defer func() {
		if errClose := reader.Close(); errClose != nil {
			err = errClose
		}
	}()

	scanner := bufio.NewScanner(reader)

	for scanner.Scan() {
		linesFunc(scanner.Text())
	}

	return scanner.Err()
}

// Read all lines and return as array of lines
func (path PathImpl) Lines() ([]string, error) {
	lines := make([]string, 0)

	linesFuncError := path.LinesWalker(func(line string) {
		lines = append(lines, line)
	})

	return lines, linesFuncError
}

// CopyFrom copy stream from reader to path
// (file after copy close and sync)
func (path PathImpl) CopyFrom(reader io.Reader) (copyied int64, err error) {
	file, err := path.OpenWriter()
	if err != nil {
		return 0, err
	}

	defer func() {
		if errSync := file.Sync(); errSync != nil {
			err = errSync
		} else if errClose := file.Close(); errClose != nil {
			err = errClose
		}

	}()

	copyied, err = io.Copy(file, reader)

	return copyied, err
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
	"strconv"
)

func TestEncode(t *testing.T) {
	for _, c := range []struct {
		in, want string
	}{
		{"1", "b"},
		{"26", "A"},
		{"51", "Z"},
		{"61", "9"},
	}{
		n, _:= strconv.Atoi(c.in)
		got := encode(n, base, a)
		if got != c.want {
			t.Errorf("Encode(%q) == %q, want %q", c.in, got, c.want)
		}
	}

}

func TestReverse(t *testing.T) {
	for _, c := range []struct {
		in, want string
	} {
		{ "44$4", "4$44"},
		{"caruso", "osurac"},
		{"Hello, 世界", "界世 ,olleH"},
	} {
		got := reverse(c.in)
		if got != c.want {
			t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}<commit_msg>updated tests for shortUrlShortner<commit_after>package main

import (
	"testing"
	"strconv"
)

func TestEncode(t *testing.T) {
	for _, c := range []struct {
		in, want string
	}{
		{"1", "b"},
		{"26", "A"},
		{"51", "Z"},
		{"61", "9"},
		{"147", "cx"},
	}{
		n, _:= strconv.Atoi(c.in)
		got := encode(n, base, a)
		if got != c.want {
			t.Errorf("Encode(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

func TestDecode(t *testing.T) {
	for _, c:= range []struct {
		in, want string
	} {
		{"b", "1"},
		{"A", "26"},
		{ "Z", "51"},
		{ "9", "61"},
		{ "cx", "147"},
	}{
		n := c.in
		got := decode(n, a)
		if strconv.Itoa(got) != c.want {
			t.Errorf("Encode(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

func TestReverse(t *testing.T) {
	for _, c := range []struct {
		in, want string
	} {
		{ "44$4", "4$44"},
		{"caruso", "osurac"},
		{"Hello, 世界", "界世 ,olleH"},
	} {
		got := reverse(c.in)
		if got != c.want {
			t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}<|endoftext|>


<commit_before>// Copyright 2014 Simon Zimmermann. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package swift

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strconv"
)

const shardCount = 8 << 7

type sharder [shardCount]string

func (s sharder) shard(v string) string {
	src := md5.Sum([]byte(v))
	dst := make([]byte, hex.EncodedLen(len(src)))
	hex.Encode(dst, src[:])
	return s[int(s.num(dst))]
}

func (s sharder) num(digest []byte) uint32 {
	return s.sum32(digest) % uint32(shardCount)
}

func (s sharder) sum32(digest []byte) uint32 {
	vv, _ := strconv.ParseUint(string(digest[:4]), 16, 32)
	return uint32(vv)
}

func newSharder() (s sharder) {
	for i := range s {
		s[i] = fmt.Sprintf("%0.2x", i)
	}
	return s
}
<commit_msg>swift: less code<commit_after>// Copyright 2014 Simon Zimmermann. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package swift

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strconv"
)

const shardCount = 8 << 7

type sharder [shardCount]string

func (s sharder) shard(v string) string {
	src := md5.Sum([]byte(v))
	str := hex.EncodeToString(src[:])
	return s[int(s.num(str))]
}

func (s sharder) num(digest string) uint32 {
	return s.sum32(digest) % uint32(shardCount)
}

func (s sharder) sum32(digest string) uint32 {
	vv, _ := strconv.ParseUint(digest[:4], 16, 32)
	return uint32(vv)
}

func newSharder() (s sharder) {
	for i := range s {
		s[i] = fmt.Sprintf("%0.2x", i)
	}
	return s
}
<|endoftext|>


<commit_before>// Swift interface
package swift

import (
	"errors"
	"fmt"
	"io"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/ncw/rclone/fs"
	"github.com/ncw/swift"
)

// Register with Fs
func init() {
	fs.Register(&fs.FsInfo{
		Name:  "swift",
		NewFs: NewFs,
		Options: []fs.Option{{
			Name: "user",
			Help: "User name to log in.",
		}, {
			Name: "key",
			Help: "API key or password.",
		}, {
			Name: "auth",
			Help: "Authentication URL for server.",
			Examples: []fs.OptionExample{{
				Help:  "Rackspace US",
				Value: "https://auth.api.rackspacecloud.com/v1.0",
			}, {
				Help:  "Rackspace UK",
				Value: "https://lon.auth.api.rackspacecloud.com/v1.0",
			}, {
				Help:  "Rackspace v2",
				Value: "https://identity.api.rackspacecloud.com/v2.0",
			}, {
				Help:  "Memset Memstore UK",
				Value: "https://auth.storage.memset.com/v1.0",
			}, {
				Help:  "Memset Memstore UK v2",
				Value: "https://auth.storage.memset.com/v2.0",
			}},
		},
		// snet     = flag.Bool("swift-snet", false, "Use internal service network") // FIXME not implemented
		},
	})
}

// FsSwift represents a remote swift server
type FsSwift struct {
	c         swift.Connection // the connection to the swift server
	container string           // the container we are working on
	root      string           // the path we are working on if any
}

// FsObjectSwift describes a swift object
//
// Will definitely have info but maybe not meta
type FsObjectSwift struct {
	swift  *FsSwift        // what this object is part of
	remote string          // The remote path
	info   swift.Object    // Info from the swift object if known
	meta   *swift.Metadata // The object metadata if known
}

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

// String converts this FsSwift to a string
func (f *FsSwift) String() string {
	if f.root == "" {
		return fmt.Sprintf("Swift container %s", f.container)
	}
	return fmt.Sprintf("Swift container %s path %s", f.container, f.root)
}

// Pattern to match a swift path
var matcher = regexp.MustCompile(`^([^/]*)(.*)$`)

// parseParse parses a swift 'url'
func parsePath(path string) (container, directory string, err error) {
	parts := matcher.FindStringSubmatch(path)
	if parts == nil {
		err = fmt.Errorf("Couldn't find container in swift path %q", path)
	} else {
		container, directory = parts[1], parts[2]
		directory = strings.Trim(directory, "/")
	}
	return
}

// swiftConnection makes a connection to swift
func swiftConnection(name string) (*swift.Connection, error) {
	userName := fs.ConfigFile.MustValue(name, "user")
	if userName == "" {
		return nil, errors.New("user not found")
	}
	apiKey := fs.ConfigFile.MustValue(name, "key")
	if apiKey == "" {
		return nil, errors.New("key not found")
	}
	authUrl := fs.ConfigFile.MustValue(name, "auth")
	if authUrl == "" {
		return nil, errors.New("auth not found")
	}
	c := &swift.Connection{
		UserName:  userName,
		ApiKey:    apiKey,
		AuthUrl:   authUrl,
		UserAgent: fs.UserAgent,
	}
	err := c.Authenticate()
	if err != nil {
		return nil, err
	}
	return c, nil
}

// NewFs contstructs an FsSwift from the path, container:path
func NewFs(name, root string) (fs.Fs, error) {
	container, directory, err := parsePath(root)
	if err != nil {
		return nil, err
	}
	c, err := swiftConnection(name)
	if err != nil {
		return nil, err
	}
	f := &FsSwift{
		c:         *c,
		container: container,
		root:      directory,
	}
	if f.root != "" {
		f.root += "/"
		// Check to see if the object exists
		_, _, err = f.c.Object(container, directory)
		if err == nil {
			remote := path.Base(directory)
			f.root = path.Dir(directory)
			if f.root == "." {
				f.root = ""
			} else {
				f.root += "/"
			}
			obj := f.NewFsObject(remote)
			// return a Fs Limited to this object
			return fs.NewLimited(f, obj), nil
		}
	}
	return f, nil
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsSwift) NewFsObjectWithInfo(remote string, info *swift.Object) fs.Object {
	fs := &FsObjectSwift{
		swift:  f,
		remote: remote,
	}
	if info != nil {
		// Set info but not meta
		fs.info = *info
	} else {
		err := fs.readMetaData() // reads info and meta, returning an error
		if err != nil {
			// logged already FsDebug("Failed to read info: %s", err)
			return nil
		}
	}
	return fs
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsSwift) NewFsObject(remote string) fs.Object {
	return f.NewFsObjectWithInfo(remote, nil)
}

// list the objects into the function supplied
//
// If directories is set it only sends directories
func (f *FsSwift) list(directories bool, fn func(string, *swift.Object)) {
	// Options for ObjectsWalk
	opts := swift.ObjectsOpts{
		Prefix: f.root,
		Limit:  256,
	}
	if directories {
		opts.Delimiter = '/'
	}
	rootLength := len(f.root)
	err := f.c.ObjectsWalk(f.container, &opts, func(opts *swift.ObjectsOpts) (interface{}, error) {
		objects, err := f.c.Objects(f.container, opts)
		if err == nil {
			for i := range objects {
				object := &objects[i]
				// FIXME if there are no directories, swift gives back the files for some reason!
				if directories && !strings.HasSuffix(object.Name, "/") {
					continue
				}
				if !strings.HasPrefix(object.Name, f.root) {
					fs.Log(f, "Odd name received %q", object.Name)
					continue
				}
				remote := object.Name[rootLength:]
				fn(remote, object)
			}
		}
		return objects, err
	})
	if err != nil {
		fs.Stats.Error()
		fs.Log(f, "Couldn't read container %q: %s", f.container, err)
	}
}

// Walk the path returning a channel of FsObjects
func (f *FsSwift) List() fs.ObjectsChan {
	out := make(fs.ObjectsChan, fs.Config.Checkers)
	if f.container == "" {
		// Return no objects at top level list
		close(out)
		fs.Stats.Error()
		fs.Log(f, "Can't list objects at root - choose a container using lsd")
	} else {
		// List the objects
		go func() {
			defer close(out)
			f.list(false, func(remote string, object *swift.Object) {
				if fs := f.NewFsObjectWithInfo(remote, object); fs != nil {
					out <- fs
				}
			})
		}()
	}
	return out
}

// Lists the containers
func (f *FsSwift) ListDir() fs.DirChan {
	out := make(fs.DirChan, fs.Config.Checkers)
	if f.container == "" {
		// List the containers
		go func() {
			defer close(out)
			containers, err := f.c.ContainersAll(nil)
			if err != nil {
				fs.Stats.Error()
				fs.Log(f, "Couldn't list containers: %v", err)
			} else {
				for _, container := range containers {
					out <- &fs.Dir{
						Name:  container.Name,
						Bytes: container.Bytes,
						Count: container.Count,
					}
				}
			}
		}()
	} else {
		// List the directories in the path in the container
		go func() {
			defer close(out)
			f.list(true, func(remote string, object *swift.Object) {
				out <- &fs.Dir{
					Name:  remote,
					Bytes: object.Bytes,
					Count: 0,
				}
			})
		}()
	}
	return out
}

// Put the object into the container
//
// Copy the reader in to the new object which is returned
//
// The new object may have been created if an error is returned
func (f *FsSwift) Put(in io.Reader, remote string, modTime time.Time, size int64) (fs.Object, error) {
	// Temporary FsObject under construction
	fs := &FsObjectSwift{swift: f, remote: remote}
	return fs, fs.Update(in, modTime, size)
}

// Mkdir creates the container if it doesn't exist
func (f *FsSwift) Mkdir() error {
	return f.c.ContainerCreate(f.container, nil)
}

// Rmdir deletes the container
//
// Returns an error if it isn't empty
func (f *FsSwift) Rmdir() error {
	return f.c.ContainerDelete(f.container)
}

// Return the precision
func (fs *FsSwift) Precision() time.Duration {
	return time.Nanosecond
}

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

// Return the parent Fs
func (o *FsObjectSwift) Fs() fs.Fs {
	return o.swift
}

// Return a string version
func (o *FsObjectSwift) String() string {
	if o == nil {
		return "<nil>"
	}
	return o.remote
}

// Return the remote path
func (o *FsObjectSwift) Remote() string {
	return o.remote
}

// Md5sum returns the Md5sum of an object returning a lowercase hex string
func (o *FsObjectSwift) Md5sum() (string, error) {
	return strings.ToLower(o.info.Hash), nil
}

// Size returns the size of an object in bytes
func (o *FsObjectSwift) Size() int64 {
	return o.info.Bytes
}

// readMetaData gets the metadata if it hasn't already been fetched
//
// it also sets the info
func (o *FsObjectSwift) readMetaData() (err error) {
	if o.meta != nil {
		return nil
	}
	info, h, err := o.swift.c.Object(o.swift.container, o.swift.root+o.remote)
	if err != nil {
		fs.Debug(o, "Failed to read info: %s", err)
		return err
	}
	meta := h.ObjectMetadata()
	o.info = info
	o.meta = &meta
	return nil
}

// ModTime returns the modification time of the object
//
//
// It attempts to read the objects mtime and if that isn't present the
// LastModified returned in the http headers
func (o *FsObjectSwift) ModTime() time.Time {
	err := o.readMetaData()
	if err != nil {
		// fs.Log(o, "Failed to read metadata: %s", err)
		return o.info.LastModified
	}
	modTime, err := o.meta.GetModTime()
	if err != nil {
		// fs.Log(o, "Failed to read mtime from object: %s", err)
		return o.info.LastModified
	}
	return modTime
}

// Sets the modification time of the local fs object
func (o *FsObjectSwift) SetModTime(modTime time.Time) {
	err := o.readMetaData()
	if err != nil {
		fs.Stats.Error()
		fs.Log(o, "Failed to read metadata: %s", err)
		return
	}
	o.meta.SetModTime(modTime)
	err = o.swift.c.ObjectUpdate(o.swift.container, o.swift.root+o.remote, o.meta.ObjectHeaders())
	if err != nil {
		fs.Stats.Error()
		fs.Log(o, "Failed to update remote mtime: %s", err)
	}
}

// Is this object storable
func (o *FsObjectSwift) Storable() bool {
	return true
}

// Open an object for read
func (o *FsObjectSwift) Open() (in io.ReadCloser, err error) {
	in, _, err = o.swift.c.ObjectOpen(o.swift.container, o.swift.root+o.remote, true, nil)
	return
}

// Update the object with the contents of the io.Reader, modTime and size
//
// The new object may have been created if an error is returned
func (o *FsObjectSwift) Update(in io.Reader, modTime time.Time, size int64) error {
	// Set the mtime
	m := swift.Metadata{}
	m.SetModTime(modTime)
	_, err := o.swift.c.ObjectPut(o.swift.container, o.swift.root+o.remote, in, true, "", "", m.ObjectHeaders())
	if err != nil {
		return err
	}
	// Read the metadata from the newly created object
	o.meta = nil // wipe old metadata
	err = o.readMetaData()
	return err
}

// Remove an object
func (o *FsObjectSwift) Remove() error {
	return o.swift.c.ObjectDelete(o.swift.container, o.swift.root+o.remote)
}

// Check the interfaces are satisfied
var _ fs.Fs = &FsSwift{}
var _ fs.Object = &FsObjectSwift{}
<commit_msg>swift: return directories without / in ListDir<commit_after>// Swift interface
package swift

import (
	"errors"
	"fmt"
	"io"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/ncw/rclone/fs"
	"github.com/ncw/swift"
)

// Register with Fs
func init() {
	fs.Register(&fs.FsInfo{
		Name:  "swift",
		NewFs: NewFs,
		Options: []fs.Option{{
			Name: "user",
			Help: "User name to log in.",
		}, {
			Name: "key",
			Help: "API key or password.",
		}, {
			Name: "auth",
			Help: "Authentication URL for server.",
			Examples: []fs.OptionExample{{
				Help:  "Rackspace US",
				Value: "https://auth.api.rackspacecloud.com/v1.0",
			}, {
				Help:  "Rackspace UK",
				Value: "https://lon.auth.api.rackspacecloud.com/v1.0",
			}, {
				Help:  "Rackspace v2",
				Value: "https://identity.api.rackspacecloud.com/v2.0",
			}, {
				Help:  "Memset Memstore UK",
				Value: "https://auth.storage.memset.com/v1.0",
			}, {
				Help:  "Memset Memstore UK v2",
				Value: "https://auth.storage.memset.com/v2.0",
			}},
		},
		// snet     = flag.Bool("swift-snet", false, "Use internal service network") // FIXME not implemented
		},
	})
}

// FsSwift represents a remote swift server
type FsSwift struct {
	c         swift.Connection // the connection to the swift server
	container string           // the container we are working on
	root      string           // the path we are working on if any
}

// FsObjectSwift describes a swift object
//
// Will definitely have info but maybe not meta
type FsObjectSwift struct {
	swift  *FsSwift        // what this object is part of
	remote string          // The remote path
	info   swift.Object    // Info from the swift object if known
	meta   *swift.Metadata // The object metadata if known
}

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

// String converts this FsSwift to a string
func (f *FsSwift) String() string {
	if f.root == "" {
		return fmt.Sprintf("Swift container %s", f.container)
	}
	return fmt.Sprintf("Swift container %s path %s", f.container, f.root)
}

// Pattern to match a swift path
var matcher = regexp.MustCompile(`^([^/]*)(.*)$`)

// parseParse parses a swift 'url'
func parsePath(path string) (container, directory string, err error) {
	parts := matcher.FindStringSubmatch(path)
	if parts == nil {
		err = fmt.Errorf("Couldn't find container in swift path %q", path)
	} else {
		container, directory = parts[1], parts[2]
		directory = strings.Trim(directory, "/")
	}
	return
}

// swiftConnection makes a connection to swift
func swiftConnection(name string) (*swift.Connection, error) {
	userName := fs.ConfigFile.MustValue(name, "user")
	if userName == "" {
		return nil, errors.New("user not found")
	}
	apiKey := fs.ConfigFile.MustValue(name, "key")
	if apiKey == "" {
		return nil, errors.New("key not found")
	}
	authUrl := fs.ConfigFile.MustValue(name, "auth")
	if authUrl == "" {
		return nil, errors.New("auth not found")
	}
	c := &swift.Connection{
		UserName:  userName,
		ApiKey:    apiKey,
		AuthUrl:   authUrl,
		UserAgent: fs.UserAgent,
	}
	err := c.Authenticate()
	if err != nil {
		return nil, err
	}
	return c, nil
}

// NewFs contstructs an FsSwift from the path, container:path
func NewFs(name, root string) (fs.Fs, error) {
	container, directory, err := parsePath(root)
	if err != nil {
		return nil, err
	}
	c, err := swiftConnection(name)
	if err != nil {
		return nil, err
	}
	f := &FsSwift{
		c:         *c,
		container: container,
		root:      directory,
	}
	if f.root != "" {
		f.root += "/"
		// Check to see if the object exists
		_, _, err = f.c.Object(container, directory)
		if err == nil {
			remote := path.Base(directory)
			f.root = path.Dir(directory)
			if f.root == "." {
				f.root = ""
			} else {
				f.root += "/"
			}
			obj := f.NewFsObject(remote)
			// return a Fs Limited to this object
			return fs.NewLimited(f, obj), nil
		}
	}
	return f, nil
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsSwift) NewFsObjectWithInfo(remote string, info *swift.Object) fs.Object {
	fs := &FsObjectSwift{
		swift:  f,
		remote: remote,
	}
	if info != nil {
		// Set info but not meta
		fs.info = *info
	} else {
		err := fs.readMetaData() // reads info and meta, returning an error
		if err != nil {
			// logged already FsDebug("Failed to read info: %s", err)
			return nil
		}
	}
	return fs
}

// Return an FsObject from a path
//
// May return nil if an error occurred
func (f *FsSwift) NewFsObject(remote string) fs.Object {
	return f.NewFsObjectWithInfo(remote, nil)
}

// list the objects into the function supplied
//
// If directories is set it only sends directories
func (f *FsSwift) list(directories bool, fn func(string, *swift.Object)) {
	// Options for ObjectsWalk
	opts := swift.ObjectsOpts{
		Prefix: f.root,
		Limit:  256,
	}
	if directories {
		opts.Delimiter = '/'
	}
	rootLength := len(f.root)
	err := f.c.ObjectsWalk(f.container, &opts, func(opts *swift.ObjectsOpts) (interface{}, error) {
		objects, err := f.c.Objects(f.container, opts)
		if err == nil {
			for i := range objects {
				object := &objects[i]
				// FIXME if there are no directories, swift gives back the files for some reason!
				if directories {
					if !strings.HasSuffix(object.Name, "/") {
						continue
					}
					object.Name = object.Name[:len(object.Name)-1]
				}
				if !strings.HasPrefix(object.Name, f.root) {
					fs.Log(f, "Odd name received %q", object.Name)
					continue
				}
				remote := object.Name[rootLength:]
				fn(remote, object)
			}
		}
		return objects, err
	})
	if err != nil {
		fs.Stats.Error()
		fs.Log(f, "Couldn't read container %q: %s", f.container, err)
	}
}

// Walk the path returning a channel of FsObjects
func (f *FsSwift) List() fs.ObjectsChan {
	out := make(fs.ObjectsChan, fs.Config.Checkers)
	if f.container == "" {
		// Return no objects at top level list
		close(out)
		fs.Stats.Error()
		fs.Log(f, "Can't list objects at root - choose a container using lsd")
	} else {
		// List the objects
		go func() {
			defer close(out)
			f.list(false, func(remote string, object *swift.Object) {
				if fs := f.NewFsObjectWithInfo(remote, object); fs != nil {
					out <- fs
				}
			})
		}()
	}
	return out
}

// Lists the containers
func (f *FsSwift) ListDir() fs.DirChan {
	out := make(fs.DirChan, fs.Config.Checkers)
	if f.container == "" {
		// List the containers
		go func() {
			defer close(out)
			containers, err := f.c.ContainersAll(nil)
			if err != nil {
				fs.Stats.Error()
				fs.Log(f, "Couldn't list containers: %v", err)
			} else {
				for _, container := range containers {
					out <- &fs.Dir{
						Name:  container.Name,
						Bytes: container.Bytes,
						Count: container.Count,
					}
				}
			}
		}()
	} else {
		// List the directories in the path in the container
		go func() {
			defer close(out)
			f.list(true, func(remote string, object *swift.Object) {
				out <- &fs.Dir{
					Name:  remote,
					Bytes: object.Bytes,
					Count: 0,
				}
			})
		}()
	}
	return out
}

// Put the object into the container
//
// Copy the reader in to the new object which is returned
//
// The new object may have been created if an error is returned
func (f *FsSwift) Put(in io.Reader, remote string, modTime time.Time, size int64) (fs.Object, error) {
	// Temporary FsObject under construction
	fs := &FsObjectSwift{swift: f, remote: remote}
	return fs, fs.Update(in, modTime, size)
}

// Mkdir creates the container if it doesn't exist
func (f *FsSwift) Mkdir() error {
	return f.c.ContainerCreate(f.container, nil)
}

// Rmdir deletes the container
//
// Returns an error if it isn't empty
func (f *FsSwift) Rmdir() error {
	return f.c.ContainerDelete(f.container)
}

// Return the precision
func (fs *FsSwift) Precision() time.Duration {
	return time.Nanosecond
}

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

// Return the parent Fs
func (o *FsObjectSwift) Fs() fs.Fs {
	return o.swift
}

// Return a string version
func (o *FsObjectSwift) String() string {
	if o == nil {
		return "<nil>"
	}
	return o.remote
}

// Return the remote path
func (o *FsObjectSwift) Remote() string {
	return o.remote
}

// Md5sum returns the Md5sum of an object returning a lowercase hex string
func (o *FsObjectSwift) Md5sum() (string, error) {
	return strings.ToLower(o.info.Hash), nil
}

// Size returns the size of an object in bytes
func (o *FsObjectSwift) Size() int64 {
	return o.info.Bytes
}

// readMetaData gets the metadata if it hasn't already been fetched
//
// it also sets the info
func (o *FsObjectSwift) readMetaData() (err error) {
	if o.meta != nil {
		return nil
	}
	info, h, err := o.swift.c.Object(o.swift.container, o.swift.root+o.remote)
	if err != nil {
		fs.Debug(o, "Failed to read info: %s", err)
		return err
	}
	meta := h.ObjectMetadata()
	o.info = info
	o.meta = &meta
	return nil
}

// ModTime returns the modification time of the object
//
//
// It attempts to read the objects mtime and if that isn't present the
// LastModified returned in the http headers
func (o *FsObjectSwift) ModTime() time.Time {
	err := o.readMetaData()
	if err != nil {
		// fs.Log(o, "Failed to read metadata: %s", err)
		return o.info.LastModified
	}
	modTime, err := o.meta.GetModTime()
	if err != nil {
		// fs.Log(o, "Failed to read mtime from object: %s", err)
		return o.info.LastModified
	}
	return modTime
}

// Sets the modification time of the local fs object
func (o *FsObjectSwift) SetModTime(modTime time.Time) {
	err := o.readMetaData()
	if err != nil {
		fs.Stats.Error()
		fs.Log(o, "Failed to read metadata: %s", err)
		return
	}
	o.meta.SetModTime(modTime)
	err = o.swift.c.ObjectUpdate(o.swift.container, o.swift.root+o.remote, o.meta.ObjectHeaders())
	if err != nil {
		fs.Stats.Error()
		fs.Log(o, "Failed to update remote mtime: %s", err)
	}
}

// Is this object storable
func (o *FsObjectSwift) Storable() bool {
	return true
}

// Open an object for read
func (o *FsObjectSwift) Open() (in io.ReadCloser, err error) {
	in, _, err = o.swift.c.ObjectOpen(o.swift.container, o.swift.root+o.remote, true, nil)
	return
}

// Update the object with the contents of the io.Reader, modTime and size
//
// The new object may have been created if an error is returned
func (o *FsObjectSwift) Update(in io.Reader, modTime time.Time, size int64) error {
	// Set the mtime
	m := swift.Metadata{}
	m.SetModTime(modTime)
	_, err := o.swift.c.ObjectPut(o.swift.container, o.swift.root+o.remote, in, true, "", "", m.ObjectHeaders())
	if err != nil {
		return err
	}
	// Read the metadata from the newly created object
	o.meta = nil // wipe old metadata
	err = o.readMetaData()
	return err
}

// Remove an object
func (o *FsObjectSwift) Remove() error {
	return o.swift.c.ObjectDelete(o.swift.container, o.swift.root+o.remote)
}

// Check the interfaces are satisfied
var _ fs.Fs = &FsSwift{}
var _ fs.Object = &FsObjectSwift{}
<|endoftext|>