<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.

// The code is the original Go implementation of rand.Prime optimized for
// generating safe (Sophie Germain) primes.
// A safe prime is a prime number of the form 2p + 1, where p is also a prime.

package paillier

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"math/big"
	"sync"
	"time"
)

// smallPrimes is a list of small, prime numbers that allows us to rapidly
// exclude some fraction of composite candidates when searching for a random
// prime. This list is truncated at the point where smallPrimesProduct exceeds
// a uint64. It does not include two because we ensure that the candidates are
// odd by construction.
var smallPrimes = []uint8{
	3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
}

// smallPrimesProduct is the product of the values in smallPrimes and allows us
// to reduce a candidate prime by this number and then determine whether it's
// coprime to all the elements of smallPrimes without further big.Int
// operations.
var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365)

// GenerateSafePrime tries to find a safe prime concurrently.
// The returned result is a safe prime `p` and prime `q` such that `p=2q+1`.
// Concurrency level can be controlled with the `concurrencyLevel` parameter.
// If a safe prime could not be found in the specified `timeout`, the error
// is returned. Also, if at least one search process failed, error is returned
// as well.
//
// How fast we generate a prime number is mostly a matter of luck and it depends
// on how lucky we are with drawing the first bytes.
// With today's multicore processors, we can execute the process on multiple
// cores concurrently, accept the first valid result and cancel the rest of
// work. This way, with the same finding algorithm, we can get the result
// faster.
//
// Concurrency level should be set depending on what `bitLen` of prime is
// expected. For example, as of today, on a typical workstation, for 512-bit
// safe prime, `concurrencyLevel` should be set to `1` as generating the prime
// of this length is a matter of milliseconds for a single core.
// For 1024-bit safe prime, `concurrencyLevel` should be usually set to at least
// `2` and for 2048-bit safe prime, `concurrencyLevel` must be set to at least
// `4` to get the result in a reasonable time.
//
// This function generates safe primes of at least 6 `bitLen`. For every
// generated safe prime, the two most significant bits are always set to `1`
// - we don't want the generated number to be too small.
func GenerateSafePrime(
	bitLen int,
	concurrencyLevel int,
	timeout time.Duration,
) (*big.Int, *big.Int, error) {
	if bitLen < 6 {
		return nil, nil, errors.New("safe prime size must be at least 6 bits")
	}

	primeChan := make(chan safePrime, 1)
	errChan := make(chan error, 1)

	defer close(primeChan)
	defer close(errChan)

	mutex := &sync.Mutex{}

	ctx, cancel := context.WithCancel(context.Background())

	for i := 0; i < concurrencyLevel; i++ {
		runGenPrimeRoutine(ctx, primeChan, errChan, mutex, rand.Reader, bitLen)
	}

	// Cancel after the specified timeout.
	go func() {
		time.Sleep(timeout)
		mutex.Lock()
		cancel()
		mutex.Unlock()
	}()

	select {
	case result := <-primeChan:
		mutex.Lock()
		cancel()
		mutex.Unlock()
		return result.p, result.q, nil
	case err := <-errChan:
		mutex.Lock()
		cancel()
		mutex.Unlock()
		return nil, nil, err
	case <-ctx.Done():
		return nil, nil, fmt.Errorf("generator timed out after %v", timeout)
	}
}

type safePrime struct {
	p *big.Int // p = 2q + 1
	q *big.Int
}

// Starts a Goroutine searching for a safe prime of the specified `pBitLen`.
// If succeeds, writes prime `p` and prime `q` such that `p = 2q+1` to the
// `primeChan`. Prime `p` has a bit length equal to `pBitLen` and prime `q` has
// a bit length equal to `pBitLen-1`.
//
// The algorithm is as follows:
// 1. Generate a random odd number `q` of length `pBitLen-1` with two the most
//    significant bytes set to `1`.
// 2. Execute preliminary primality test on `q` checking whether it is coprime
//    to all the elements of `smallPrimes`. It allows to eliminate trivial
//    cases quickly, when `q` is obviously no prime, without running an
//    expensive final primality tests.
//    If `q` is coprime to all of the `smallPrimes`, then go to the point 3.
//    If not, add `2` and try again. Do it at most 10 times.
// 3. Check the potentially prime `q`, whether `q = 1 (mod 3)`. This will
//    happen for 50% of cases.
//    If it is, then `p = 2q+1` will be a multiple of 3, so it will be obviously
//    not a prime number. In this case, add `2` and try again. Do it at most 10
//    times. If `q != 1 (mod 3)`, go to the point 4.
// 4. Now we know `q` is potentially prime and `p = 2q+1` is not a multiple of
//    3. We execute a preliminary primality test on `p`, checking whether
//    it is coprime to all the elements of `smallPrimes` just like we did for
//    `q` in point 2. If `p` is not coprime to at least one element of the
//    `smallPrimes`, then go back to point 1.
//    If `p` is coprime to all the elements of `smallPrimes`, go to point 5.
// 5. At this point, we know `q` is potentially prime, and `p=q+1` is also
//    potentially prime. We need to execute a final primality test for `q`.
//    We apply Miller-Rabin and Baillie-PSW tests. If they succeeds, it means
//    that `q` is prime with a very high probability. Knowing `q` is prime,
//    we use Pocklington's criterion to prove the primality of `p=2q+1`, that
//    is, we execute Fermat primality test to base 2 checking whether
//    `2^{p-1} = 1 (mod p)`. It's significantly faster than running full
//    Miller-Rabin and Baillie-PSW for `p`.
//    If `q` and `p` are found to be prime, return them as a result. If not, go
//    back to the point 1.
func runGenPrimeRoutine(
	ctx context.Context,
	primeChan chan safePrime,
	errChan chan error,
	mutex *sync.Mutex,
	rand io.Reader,
	pBitLen int,
) {
	qBitLen := pBitLen - 1
	b := uint(qBitLen % 8)
	if b == 0 {
		b = 8
	}

	bytes := make([]byte, (qBitLen+7)/8)
	p := new(big.Int)
	q := new(big.Int)

	bigMod := new(big.Int)

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
				_, err := io.ReadFull(rand, bytes)
				if err != nil {
					errChan <- err
					return
				}

				// Clear bits in the first byte to make sure the candidate has
				// a size <= bits.
				bytes[0] &= uint8(int(1<<b) - 1)
				// Don't let the value be too small, i.e, set the most
				// significant two bits.
				// Setting the top two bits, rather than just the top bit,
				// means that when two of these values are multiplied together,
				// the result isn't ever one bit short.
				if b >= 2 {
					bytes[0] |= 3 << (b - 2)
				} else {
					// Here b==1, because b cannot be zero.
					bytes[0] |= 1
					if len(bytes) > 1 {
						bytes[1] |= 0x80
					}
				}
				// Make the value odd since an even number this large certainly
				// isn't prime.
				bytes[len(bytes)-1] |= 1

				q.SetBytes(bytes)

				p.Mul(q, big.NewInt(2))
				p.Add(p, big.NewInt(1))

				// Calculate the value mod the product of smallPrimes. If it's
				// a multiple of any of these primes we add two until it isn't.
				// The probability of overflowing is minimal and can be ignored
				// because we still perform Miller-Rabin tests on the result.
				bigMod.Mod(q, smallPrimesProduct)
				mod := bigMod.Uint64()

			NextDelta:
				for delta := uint64(0); delta < 1<<20; delta += 2 {
					m := mod + delta
					for _, prime := range smallPrimes {
						if m%uint64(prime) == 0 && (qBitLen > 6 || m != uint64(prime)) {
							continue NextDelta
						}
					}

					if delta > 0 {
						bigMod.SetUint64(delta)
						q.Add(q, bigMod)
					}

					qMod3 := new(big.Int).Mod(q, big.NewInt(3))
					if qMod3.Cmp(big.NewInt(1)) == 0 {
						continue NextDelta
					}

					// p = 2q+1
					p.Mul(q, big.NewInt(2))
					p.Add(p, big.NewInt(1))
					if !isPrimeCandidate(p) {
						continue NextDelta
					}

					break
				}

				// There is a tiny possibility that, by adding delta, we caused
				// the number to be one bit too long. Thus we check BitLen
				// here.
				if q.ProbablyPrime(20) &&
					isPocklingtonCriterionSatisfied(p) &&
					q.BitLen() == qBitLen {

					mutex.Lock()
					if ctx.Err() == nil {
						primeChan <- safePrime{p, q}
					}
					mutex.Unlock()

					return
				}
			}
		}
	}()
}

func isPocklingtonCriterionSatisfied(p *big.Int) bool {
	return new(big.Int).Exp(
		big.NewInt(2),
		new(big.Int).Sub(p, big.NewInt(1)),
		p,
	).Cmp(big.NewInt(1)) == 0
}

func isPrimeCandidate(number *big.Int) bool {
	bigMod := new(big.Int)
	bigMod.Mod(number, smallPrimesProduct)
	m := bigMod.Uint64()

	for _, prime := range smallPrimes {
		if m%uint64(prime) == 0 && m != uint64(prime) {
			return false
		}
	}

	return true
}
<commit_msg>Wait for all goroutines to finish<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.

// The code is the original Go implementation of rand.Prime optimized for
// generating safe (Sophie Germain) primes.
// A safe prime is a prime number of the form 2p + 1, where p is also a prime.

package paillier

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"math/big"
	"sync"
	"time"
)

// smallPrimes is a list of small, prime numbers that allows us to rapidly
// exclude some fraction of composite candidates when searching for a random
// prime. This list is truncated at the point where smallPrimesProduct exceeds
// a uint64. It does not include two because we ensure that the candidates are
// odd by construction.
var smallPrimes = []uint8{
	3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53,
}

// smallPrimesProduct is the product of the values in smallPrimes and allows us
// to reduce a candidate prime by this number and then determine whether it's
// coprime to all the elements of smallPrimes without further big.Int
// operations.
var smallPrimesProduct = new(big.Int).SetUint64(16294579238595022365)

// GenerateSafePrime tries to find a safe prime concurrently.
// The returned result is a safe prime `p` and prime `q` such that `p=2q+1`.
// Concurrency level can be controlled with the `concurrencyLevel` parameter.
// If a safe prime could not be found in the specified `timeout`, the error
// is returned. Also, if at least one search process failed, error is returned
// as well.
//
// How fast we generate a prime number is mostly a matter of luck and it depends
// on how lucky we are with drawing the first bytes.
// With today's multicore processors, we can execute the process on multiple
// cores concurrently, accept the first valid result and cancel the rest of
// work. This way, with the same finding algorithm, we can get the result
// faster.
//
// Concurrency level should be set depending on what `bitLen` of prime is
// expected. For example, as of today, on a typical workstation, for 512-bit
// safe prime, `concurrencyLevel` should be set to `1` as generating the prime
// of this length is a matter of milliseconds for a single core.
// For 1024-bit safe prime, `concurrencyLevel` should be usually set to at least
// `2` and for 2048-bit safe prime, `concurrencyLevel` must be set to at least
// `4` to get the result in a reasonable time.
//
// This function generates safe primes of at least 6 `bitLen`. For every
// generated safe prime, the two most significant bits are always set to `1`
// - we don't want the generated number to be too small.
func GenerateSafePrime(
	bitLen int,
	concurrencyLevel int,
	timeout time.Duration,
) (p *big.Int, q *big.Int, err error) {
	if bitLen < 6 {
		return nil, nil, errors.New("safe prime size must be at least 6 bits")
	}

	primeChan := make(chan safePrime, 1)
	errChan := make(chan error, 1)

	defer close(primeChan)
	defer close(errChan)

	mutex := &sync.Mutex{}
	waitGroup := &sync.WaitGroup{}
	waitGroup.Add(concurrencyLevel)

	ctx, cancel := context.WithCancel(context.Background())

	for i := 0; i < concurrencyLevel; i++ {
		runGenPrimeRoutine(
			ctx, primeChan, errChan, mutex, waitGroup, rand.Reader, bitLen,
		)
	}

	// Cancel after the specified timeout.
	go func() {
		time.Sleep(timeout)
		mutex.Lock()
		cancel()
		mutex.Unlock()
	}()

	select {
	case result := <-primeChan:
		mutex.Lock()
		cancel()
		mutex.Unlock()
		p, q, err = result.p, result.q, nil
	case err := <-errChan:
		mutex.Lock()
		cancel()
		mutex.Unlock()
		p, q, err = nil, nil, err
	case <-ctx.Done():
		p, q, err = nil, nil, fmt.Errorf("generator timed out after %v", timeout)
	}

	waitGroup.Wait()
	return
}

type safePrime struct {
	p *big.Int // p = 2q + 1
	q *big.Int
}

// Starts a Goroutine searching for a safe prime of the specified `pBitLen`.
// If succeeds, writes prime `p` and prime `q` such that `p = 2q+1` to the
// `primeChan`. Prime `p` has a bit length equal to `pBitLen` and prime `q` has
// a bit length equal to `pBitLen-1`.
//
// The algorithm is as follows:
// 1. Generate a random odd number `q` of length `pBitLen-1` with two the most
//    significant bytes set to `1`.
// 2. Execute preliminary primality test on `q` checking whether it is coprime
//    to all the elements of `smallPrimes`. It allows to eliminate trivial
//    cases quickly, when `q` is obviously no prime, without running an
//    expensive final primality tests.
//    If `q` is coprime to all of the `smallPrimes`, then go to the point 3.
//    If not, add `2` and try again. Do it at most 10 times.
// 3. Check the potentially prime `q`, whether `q = 1 (mod 3)`. This will
//    happen for 50% of cases.
//    If it is, then `p = 2q+1` will be a multiple of 3, so it will be obviously
//    not a prime number. In this case, add `2` and try again. Do it at most 10
//    times. If `q != 1 (mod 3)`, go to the point 4.
// 4. Now we know `q` is potentially prime and `p = 2q+1` is not a multiple of
//    3. We execute a preliminary primality test on `p`, checking whether
//    it is coprime to all the elements of `smallPrimes` just like we did for
//    `q` in point 2. If `p` is not coprime to at least one element of the
//    `smallPrimes`, then go back to point 1.
//    If `p` is coprime to all the elements of `smallPrimes`, go to point 5.
// 5. At this point, we know `q` is potentially prime, and `p=q+1` is also
//    potentially prime. We need to execute a final primality test for `q`.
//    We apply Miller-Rabin and Baillie-PSW tests. If they succeeds, it means
//    that `q` is prime with a very high probability. Knowing `q` is prime,
//    we use Pocklington's criterion to prove the primality of `p=2q+1`, that
//    is, we execute Fermat primality test to base 2 checking whether
//    `2^{p-1} = 1 (mod p)`. It's significantly faster than running full
//    Miller-Rabin and Baillie-PSW for `p`.
//    If `q` and `p` are found to be prime, return them as a result. If not, go
//    back to the point 1.
func runGenPrimeRoutine(
	ctx context.Context,
	primeChan chan safePrime,
	errChan chan error,
	mutex *sync.Mutex,
	waitGroup *sync.WaitGroup,
	rand io.Reader,
	pBitLen int,
) {
	qBitLen := pBitLen - 1
	b := uint(qBitLen % 8)
	if b == 0 {
		b = 8
	}

	bytes := make([]byte, (qBitLen+7)/8)
	p := new(big.Int)
	q := new(big.Int)

	bigMod := new(big.Int)

	go func() {
		for {
			select {
			case <-ctx.Done():
				waitGroup.Done()
				return
			default:
				_, err := io.ReadFull(rand, bytes)
				if err != nil {
					errChan <- err
					return
				}

				// Clear bits in the first byte to make sure the candidate has
				// a size <= bits.
				bytes[0] &= uint8(int(1<<b) - 1)
				// Don't let the value be too small, i.e, set the most
				// significant two bits.
				// Setting the top two bits, rather than just the top bit,
				// means that when two of these values are multiplied together,
				// the result isn't ever one bit short.
				if b >= 2 {
					bytes[0] |= 3 << (b - 2)
				} else {
					// Here b==1, because b cannot be zero.
					bytes[0] |= 1
					if len(bytes) > 1 {
						bytes[1] |= 0x80
					}
				}
				// Make the value odd since an even number this large certainly
				// isn't prime.
				bytes[len(bytes)-1] |= 1

				q.SetBytes(bytes)

				p.Mul(q, big.NewInt(2))
				p.Add(p, big.NewInt(1))

				// Calculate the value mod the product of smallPrimes. If it's
				// a multiple of any of these primes we add two until it isn't.
				// The probability of overflowing is minimal and can be ignored
				// because we still perform Miller-Rabin tests on the result.
				bigMod.Mod(q, smallPrimesProduct)
				mod := bigMod.Uint64()

			NextDelta:
				for delta := uint64(0); delta < 1<<20; delta += 2 {
					m := mod + delta
					for _, prime := range smallPrimes {
						if m%uint64(prime) == 0 && (qBitLen > 6 || m != uint64(prime)) {
							continue NextDelta
						}
					}

					if delta > 0 {
						bigMod.SetUint64(delta)
						q.Add(q, bigMod)
					}

					qMod3 := new(big.Int).Mod(q, big.NewInt(3))
					if qMod3.Cmp(big.NewInt(1)) == 0 {
						continue NextDelta
					}

					// p = 2q+1
					p.Mul(q, big.NewInt(2))
					p.Add(p, big.NewInt(1))
					if !isPrimeCandidate(p) {
						continue NextDelta
					}

					break
				}

				// There is a tiny possibility that, by adding delta, we caused
				// the number to be one bit too long. Thus we check BitLen
				// here.
				if q.ProbablyPrime(20) &&
					isPocklingtonCriterionSatisfied(p) &&
					q.BitLen() == qBitLen {

					mutex.Lock()
					if ctx.Err() == nil {
						primeChan <- safePrime{p, q}
					}
					mutex.Unlock()

					waitGroup.Done()
					return
				}
			}
		}
	}()
}

func isPocklingtonCriterionSatisfied(p *big.Int) bool {
	return new(big.Int).Exp(
		big.NewInt(2),
		new(big.Int).Sub(p, big.NewInt(1)),
		p,
	).Cmp(big.NewInt(1)) == 0
}

func isPrimeCandidate(number *big.Int) bool {
	bigMod := new(big.Int)
	bigMod.Mod(number, smallPrimesProduct)
	m := bigMod.Uint64()

	for _, prime := range smallPrimes {
		if m%uint64(prime) == 0 && m != uint64(prime) {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package zfs_test

import (
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"
	"time"

	zfs "github.com/mistifyio/go-zfs/v3"
)

func sleep(delay int) {
	time.Sleep(time.Duration(delay) * time.Second)
}

func pow2(x int) int64 {
	return int64(math.Pow(2, float64(x)))
}

// https://github.com/benbjohnson/testing
// assert fails the test if the condition is false.
func _assert(t *testing.T, condition bool, msg string, v ...interface{}) {
	t.Helper()

	if !condition {
		_, file, line, _ := runtime.Caller(2)
		fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
		t.FailNow()
	}
}

func assert(t *testing.T, condition bool, msg string, v ...interface{}) {
	t.Helper()
	_assert(t, condition, msg, v...)
}

// ok fails the test if an err is not nil.
func ok(t *testing.T, err error) {
	t.Helper()
	_assert(t, err == nil, "unexpected error: %v", err)
}

// nok fails the test if an err is nil.
func nok(t *testing.T, err error) {
	t.Helper()
	_assert(t, err != nil, "expected error, got nil")
}

// equals fails the test if exp is not equal to act.
func equals(t *testing.T, exp, act interface{}) {
	t.Helper()
	_assert(t, reflect.DeepEqual(exp, act), "exp: %#v\n\ngot: %#v", exp, act)
}

func zpoolTest(t *testing.T, fn func()) {
	t.Helper()

	tempfiles := make([]string, 3)
	for i := range tempfiles {
		f, _ := ioutil.TempFile("/tmp/", "zfs-")
		err := f.Truncate(pow2(30))
		f.Close()
		ok(t, err)
		tempfiles[i] = f.Name()
		defer os.Remove(f.Name()) // nolint:revive // its ok to defer to end of func
	}

	pool, err := zfs.CreateZpool("test", nil, tempfiles...)
	ok(t, err)
	defer pool.Destroy()
	ok(t, err)
	fn()
}

func TestDatasets(t *testing.T) {
	t.Helper()

	zpoolTest(t, func() {
		_, err := zfs.Datasets("")
		ok(t, err)

		ds, err := zfs.GetDataset("test")
		ok(t, err)
		equals(t, zfs.DatasetFilesystem, ds.Type)
		equals(t, "", ds.Origin)
		if runtime.GOOS != "solaris" {
			assert(t, ds.Logicalused != 0, "Logicalused is not greater than 0")
		}
	})
}

func TestDatasetGetProperty(t *testing.T) {
	zpoolTest(t, func() {
		ds, err := zfs.GetDataset("test")
		ok(t, err)

		prop, err := ds.GetProperty("foobarbaz")
		nok(t, err)
		equals(t, "", prop)

		prop, err = ds.GetProperty("compression")
		ok(t, err)
		equals(t, "off", prop)
	})
}

func TestSnapshots(t *testing.T) {
	zpoolTest(t, func() {
		snapshots, err := zfs.Snapshots("")
		ok(t, err)

		for _, snapshot := range snapshots {
			equals(t, zfs.DatasetSnapshot, snapshot.Type)
		}
	})
}

func TestFilesystems(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/filesystem-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestCreateFilesystemWithProperties(t *testing.T) {
	zpoolTest(t, func() {
		props := map[string]string{
			"compression": "lz4",
		}

		f, err := zfs.CreateFilesystem("test/filesystem-test", props)
		ok(t, err)

		equals(t, "lz4", f.Compression)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestVolumes(t *testing.T) {
	zpoolTest(t, func() {
		v, err := zfs.CreateVolume("test/volume-test", uint64(pow2(23)), nil)
		ok(t, err)

		// volumes are sometimes "busy" if you try to manipulate them right away
		sleep(1)

		equals(t, zfs.DatasetVolume, v.Type)
		volumes, err := zfs.Volumes("")
		ok(t, err)

		for _, volume := range volumes {
			equals(t, zfs.DatasetVolume, volume.Type)
		}

		ok(t, v.Destroy(zfs.DestroyDefault))
	})
}

func TestSnapshot(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s, err := f.Snapshot("test", false)
		ok(t, err)

		equals(t, zfs.DatasetSnapshot, s.Type)

		equals(t, "test/snapshot-test@test", s.Name)

		ok(t, s.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestClone(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s, err := f.Snapshot("test", false)
		ok(t, err)

		equals(t, zfs.DatasetSnapshot, s.Type)
		equals(t, "test/snapshot-test@test", s.Name)

		c, err := s.Clone("test/clone-test", nil)
		ok(t, err)

		equals(t, zfs.DatasetFilesystem, c.Type)

		ok(t, c.Destroy(zfs.DestroyDefault))

		ok(t, s.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestSendSnapshot(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s, err := f.Snapshot("test", false)
		ok(t, err)

		file, _ := ioutil.TempFile("/tmp/", "zfs-")
		defer file.Close()
		err = file.Truncate(pow2(30))
		ok(t, err)
		defer os.Remove(file.Name())

		err = s.SendSnapshot(file)
		ok(t, err)

		ok(t, s.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestChildren(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		s, err := f.Snapshot("test", false)
		ok(t, err)

		equals(t, zfs.DatasetSnapshot, s.Type)
		equals(t, "test/snapshot-test@test", s.Name)

		children, err := f.Children(0)
		ok(t, err)

		equals(t, 1, len(children))
		equals(t, "test/snapshot-test@test", children[0].Name)

		ok(t, s.Destroy(zfs.DestroyDefault))
		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestListZpool(t *testing.T) {
	zpoolTest(t, func() {
		pools, err := zfs.ListZpools()
		ok(t, err)
		for _, pool := range pools {
			if pool.Name == "test" {
				equals(t, "test", pool.Name)
				return
			}
		}
		t.Fatal("Failed to find test pool")
	})
}

func TestRollback(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s1, err := f.Snapshot("test", false)
		ok(t, err)

		_, err = f.Snapshot("test2", false)
		ok(t, err)

		s3, err := f.Snapshot("test3", false)
		ok(t, err)

		err = s3.Rollback(false)
		ok(t, err)

		err = s1.Rollback(false)
		assert(t, err != nil, "should error when rolling back beyond most recent without destroyMoreRecent = true")

		err = s1.Rollback(true)
		ok(t, err)

		ok(t, s1.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestDiff(t *testing.T) {
	zpoolTest(t, func() {
		fs, err := zfs.CreateFilesystem("test/origin", nil)
		ok(t, err)

		linkedFile, err := os.Create(filepath.Join(fs.Mountpoint, "linked"))
		ok(t, err)

		movedFile, err := os.Create(filepath.Join(fs.Mountpoint, "file"))
		ok(t, err)

		snapshot, err := fs.Snapshot("snapshot", false)
		ok(t, err)

		unicodeFile, err := os.Create(filepath.Join(fs.Mountpoint, "i ❤ unicode"))
		ok(t, err)

		err = os.Rename(movedFile.Name(), movedFile.Name()+"-new")
		ok(t, err)

		err = os.Link(linkedFile.Name(), linkedFile.Name()+"_hard")
		ok(t, err)

		inodeChanges, err := fs.Diff(snapshot.Name)
		ok(t, err)
		equals(t, 4, len(inodeChanges))

		unicodePath := "/test/origin/i\x040\x1c2\x135\x144\x040unicode"
		wants := map[string]*zfs.InodeChange{
			"/test/origin/linked": {
				Type:                 zfs.File,
				Change:               zfs.Modified,
				ReferenceCountChange: 1,
			},
			"/test/origin/file": {
				Type:    zfs.File,
				Change:  zfs.Renamed,
				NewPath: "/test/origin/file-new",
			},
			"/test/origin/i ❤ unicode": {
				Path:   "❤❤ unicode ❤❤",
				Type:   zfs.File,
				Change: zfs.Created,
			},
			unicodePath: {
				Path:   "❤❤ unicode ❤❤",
				Type:   zfs.File,
				Change: zfs.Created,
			},
			"/test/origin/": {
				Type:   zfs.Directory,
				Change: zfs.Modified,
			},
		}
		for _, change := range inodeChanges {
			want := wants[change.Path]
			want.Path = change.Path
			delete(wants, change.Path)

			equals(t, want, change)
		}

		equals(t, 1, len(wants))
		for _, want := range wants {
			equals(t, "❤❤ unicode ❤❤", want.Path)
		}

		ok(t, movedFile.Close())
		ok(t, unicodeFile.Close())
		ok(t, linkedFile.Close())
		ok(t, snapshot.Destroy(zfs.DestroyForceUmount))
		ok(t, fs.Destroy(zfs.DestroyForceUmount))
	})
}
<commit_msg>test: Use t.Logf instead of fmt.Printf<commit_after>package zfs_test

import (
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"
	"time"

	zfs "github.com/mistifyio/go-zfs/v3"
)

func sleep(delay int) {
	time.Sleep(time.Duration(delay) * time.Second)
}

func pow2(x int) int64 {
	return int64(math.Pow(2, float64(x)))
}

// https://github.com/benbjohnson/testing
// assert fails the test if the condition is false.
func _assert(t *testing.T, condition bool, msg string, v ...interface{}) {
	t.Helper()

	if !condition {
		_, file, line, _ := runtime.Caller(2)
		t.Logf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
		t.FailNow()
	}
}

func assert(t *testing.T, condition bool, msg string, v ...interface{}) {
	t.Helper()
	_assert(t, condition, msg, v...)
}

// ok fails the test if an err is not nil.
func ok(t *testing.T, err error) {
	t.Helper()
	_assert(t, err == nil, "unexpected error: %v", err)
}

// nok fails the test if an err is nil.
func nok(t *testing.T, err error) {
	t.Helper()
	_assert(t, err != nil, "expected error, got nil")
}

// equals fails the test if exp is not equal to act.
func equals(t *testing.T, exp, act interface{}) {
	t.Helper()
	_assert(t, reflect.DeepEqual(exp, act), "exp: %#v\n\ngot: %#v", exp, act)
}

func zpoolTest(t *testing.T, fn func()) {
	t.Helper()

	tempfiles := make([]string, 3)
	for i := range tempfiles {
		f, _ := ioutil.TempFile("/tmp/", "zfs-")
		err := f.Truncate(pow2(30))
		f.Close()
		ok(t, err)
		tempfiles[i] = f.Name()
		defer os.Remove(f.Name()) // nolint:revive // its ok to defer to end of func
	}

	pool, err := zfs.CreateZpool("test", nil, tempfiles...)
	ok(t, err)
	defer pool.Destroy()
	ok(t, err)
	fn()
}

func TestDatasets(t *testing.T) {
	t.Helper()

	zpoolTest(t, func() {
		_, err := zfs.Datasets("")
		ok(t, err)

		ds, err := zfs.GetDataset("test")
		ok(t, err)
		equals(t, zfs.DatasetFilesystem, ds.Type)
		equals(t, "", ds.Origin)
		if runtime.GOOS != "solaris" {
			assert(t, ds.Logicalused != 0, "Logicalused is not greater than 0")
		}
	})
}

func TestDatasetGetProperty(t *testing.T) {
	zpoolTest(t, func() {
		ds, err := zfs.GetDataset("test")
		ok(t, err)

		prop, err := ds.GetProperty("foobarbaz")
		nok(t, err)
		equals(t, "", prop)

		prop, err = ds.GetProperty("compression")
		ok(t, err)
		equals(t, "off", prop)
	})
}

func TestSnapshots(t *testing.T) {
	zpoolTest(t, func() {
		snapshots, err := zfs.Snapshots("")
		ok(t, err)

		for _, snapshot := range snapshots {
			equals(t, zfs.DatasetSnapshot, snapshot.Type)
		}
	})
}

func TestFilesystems(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/filesystem-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestCreateFilesystemWithProperties(t *testing.T) {
	zpoolTest(t, func() {
		props := map[string]string{
			"compression": "lz4",
		}

		f, err := zfs.CreateFilesystem("test/filesystem-test", props)
		ok(t, err)

		equals(t, "lz4", f.Compression)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestVolumes(t *testing.T) {
	zpoolTest(t, func() {
		v, err := zfs.CreateVolume("test/volume-test", uint64(pow2(23)), nil)
		ok(t, err)

		// volumes are sometimes "busy" if you try to manipulate them right away
		sleep(1)

		equals(t, zfs.DatasetVolume, v.Type)
		volumes, err := zfs.Volumes("")
		ok(t, err)

		for _, volume := range volumes {
			equals(t, zfs.DatasetVolume, volume.Type)
		}

		ok(t, v.Destroy(zfs.DestroyDefault))
	})
}

func TestSnapshot(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s, err := f.Snapshot("test", false)
		ok(t, err)

		equals(t, zfs.DatasetSnapshot, s.Type)

		equals(t, "test/snapshot-test@test", s.Name)

		ok(t, s.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestClone(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s, err := f.Snapshot("test", false)
		ok(t, err)

		equals(t, zfs.DatasetSnapshot, s.Type)
		equals(t, "test/snapshot-test@test", s.Name)

		c, err := s.Clone("test/clone-test", nil)
		ok(t, err)

		equals(t, zfs.DatasetFilesystem, c.Type)

		ok(t, c.Destroy(zfs.DestroyDefault))

		ok(t, s.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestSendSnapshot(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s, err := f.Snapshot("test", false)
		ok(t, err)

		file, _ := ioutil.TempFile("/tmp/", "zfs-")
		defer file.Close()
		err = file.Truncate(pow2(30))
		ok(t, err)
		defer os.Remove(file.Name())

		err = s.SendSnapshot(file)
		ok(t, err)

		ok(t, s.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestChildren(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		s, err := f.Snapshot("test", false)
		ok(t, err)

		equals(t, zfs.DatasetSnapshot, s.Type)
		equals(t, "test/snapshot-test@test", s.Name)

		children, err := f.Children(0)
		ok(t, err)

		equals(t, 1, len(children))
		equals(t, "test/snapshot-test@test", children[0].Name)

		ok(t, s.Destroy(zfs.DestroyDefault))
		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestListZpool(t *testing.T) {
	zpoolTest(t, func() {
		pools, err := zfs.ListZpools()
		ok(t, err)
		for _, pool := range pools {
			if pool.Name == "test" {
				equals(t, "test", pool.Name)
				return
			}
		}
		t.Fatal("Failed to find test pool")
	})
}

func TestRollback(t *testing.T) {
	zpoolTest(t, func() {
		f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
		ok(t, err)

		filesystems, err := zfs.Filesystems("")
		ok(t, err)

		for _, filesystem := range filesystems {
			equals(t, zfs.DatasetFilesystem, filesystem.Type)
		}

		s1, err := f.Snapshot("test", false)
		ok(t, err)

		_, err = f.Snapshot("test2", false)
		ok(t, err)

		s3, err := f.Snapshot("test3", false)
		ok(t, err)

		err = s3.Rollback(false)
		ok(t, err)

		err = s1.Rollback(false)
		assert(t, err != nil, "should error when rolling back beyond most recent without destroyMoreRecent = true")

		err = s1.Rollback(true)
		ok(t, err)

		ok(t, s1.Destroy(zfs.DestroyDefault))

		ok(t, f.Destroy(zfs.DestroyDefault))
	})
}

func TestDiff(t *testing.T) {
	zpoolTest(t, func() {
		fs, err := zfs.CreateFilesystem("test/origin", nil)
		ok(t, err)

		linkedFile, err := os.Create(filepath.Join(fs.Mountpoint, "linked"))
		ok(t, err)

		movedFile, err := os.Create(filepath.Join(fs.Mountpoint, "file"))
		ok(t, err)

		snapshot, err := fs.Snapshot("snapshot", false)
		ok(t, err)

		unicodeFile, err := os.Create(filepath.Join(fs.Mountpoint, "i ❤ unicode"))
		ok(t, err)

		err = os.Rename(movedFile.Name(), movedFile.Name()+"-new")
		ok(t, err)

		err = os.Link(linkedFile.Name(), linkedFile.Name()+"_hard")
		ok(t, err)

		inodeChanges, err := fs.Diff(snapshot.Name)
		ok(t, err)
		equals(t, 4, len(inodeChanges))

		unicodePath := "/test/origin/i\x040\x1c2\x135\x144\x040unicode"
		wants := map[string]*zfs.InodeChange{
			"/test/origin/linked": {
				Type:                 zfs.File,
				Change:               zfs.Modified,
				ReferenceCountChange: 1,
			},
			"/test/origin/file": {
				Type:    zfs.File,
				Change:  zfs.Renamed,
				NewPath: "/test/origin/file-new",
			},
			"/test/origin/i ❤ unicode": {
				Path:   "❤❤ unicode ❤❤",
				Type:   zfs.File,
				Change: zfs.Created,
			},
			unicodePath: {
				Path:   "❤❤ unicode ❤❤",
				Type:   zfs.File,
				Change: zfs.Created,
			},
			"/test/origin/": {
				Type:   zfs.Directory,
				Change: zfs.Modified,
			},
		}
		for _, change := range inodeChanges {
			want := wants[change.Path]
			want.Path = change.Path
			delete(wants, change.Path)

			equals(t, want, change)
		}

		equals(t, 1, len(wants))
		for _, want := range wants {
			equals(t, "❤❤ unicode ❤❤", want.Path)
		}

		ok(t, movedFile.Close())
		ok(t, unicodeFile.Close())
		ok(t, linkedFile.Close())
		ok(t, snapshot.Destroy(zfs.DestroyForceUmount))
		ok(t, fs.Destroy(zfs.DestroyForceUmount))
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/ci-pipeline/cloudformation-resource/utils"
)

func main() {
	utils.Logln("Change to build directory")
	utils.GoToBuildDirectory()
	cwd, _ := os.Getwd()
	utils.Logln(cwd)
	input := utils.GetInput()
	svc := utils.GetCloudformationService(input)
	metadata, success := out(input, svc, &utils.AwsRequestHandler{})
	fmt.Printf("%s", metadata)

	if !success {
		os.Exit(1)
	}
}

type Parameter struct {
	ParameterValue   string
	ParameterKey     string
	UsePreviousValue bool
}

type Tag struct {
	TagKey   string
	TagValue string
}

func stackExists(reqHandler utils.RequestHandler, svc utils.AwsCloudformationSvc, stackName string) bool {
	params := &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackName),
	}
	req, resp := svc.DescribeStacksRequest(params)
	err := reqHandler.HandleRequest(req)
	if err != nil {
		return false
	}
	utils.Logln(resp)

	return resp.Stacks[0] != nil
}

func waitForStack(reqHandler utils.RequestHandler, svc utils.AwsCloudformationSvc, input utils.Input) (success bool, arn, timestamp string) {
	success = true
	params := &cloudformation.DescribeStackEventsInput{
		StackName: aws.String(input.Source.Name),
	}

	pos := 0
	var stackEvents []*cloudformation.StackEvent
	for {
		req, resp := svc.DescribeStackEventsRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			success = input.Params.Delete
			return
		}
		stackEvents = resp.StackEvents
		for j := len(stackEvents) - 1 - pos; j > -1; j-- {
			utils.Logln(stackEvents[j])
		}
		pos = len(stackEvents)

		arn = *stackEvents[0].StackId
		timestamp = stackEvents[0].Timestamp.String()

		if *stackEvents[0].ResourceType == "AWS::CloudFormation::Stack" {
			if *stackEvents[0].ResourceStatus == "CREATE_COMPLETE" {
				return
			} else if *stackEvents[0].ResourceStatus == "UPDATE_COMPLETE" {
				return
			} else if *stackEvents[0].ResourceStatus == "ROLLBACK_COMPLETE" {
				success = false
				return
			} else if *stackEvents[0].ResourceStatus == "UPDATE_ROLLBACK_COMPLETE" {
				success = false
				return
			} else if strings.HasSuffix(*stackEvents[0].ResourceStatus, "_FAILED") {
				success = false
				return
			}
		}

		time.Sleep(10 * time.Millisecond)
	}
}

func out(input utils.Input, svc utils.AwsCloudformationSvc, reqHandler utils.RequestHandler) (metadata string, success bool) {

	var capabilities []*string
	capabilities = nil
	if input.Params.Capabilities != nil {
		for _, c := range input.Params.Capabilities {
			capabilities = append(capabilities, aws.String(c))
		}
	}

	var templateBody string
	if input.Params.Template != "" {
		bytes, err := ioutil.ReadFile(input.Params.Template)
		if err != nil {
			panic(err)
		}
		templateBody = string(bytes)
	}

	parameters := []Parameter{}
	var cloudformationParams []*cloudformation.Parameter
	if input.Params.Parameters != "" {
		bytes, err := ioutil.ReadFile(input.Params.Parameters)
		err = json.Unmarshal(bytes, &parameters)
		if err != nil {
			panic(err)
		}
	}
	for _, p := range parameters {
		param := cloudformation.Parameter{
			ParameterKey:     aws.String(p.ParameterKey),
			ParameterValue:   aws.String(p.ParameterValue),
			UsePreviousValue: aws.Bool(p.UsePreviousValue),
		}
		cloudformationParams = append(cloudformationParams, &param)
	}

	tags := []Tag{}
	var cloudformationTags []*cloudformation.Tag
	if input.Params.Tags != "" {
		bytes, err := ioutil.ReadFile(input.Params.Tags)
		err = json.Unmarshal(bytes, &tags)
		if err != nil {
			panic(err)
		}
	}
	for _, t := range tags {
		tag := cloudformation.Tag{
			Key:   aws.String(t.TagKey),
			Value: aws.String(t.TagValue),
		}
		cloudformationTags = append(cloudformationTags, &tag)
	}

	if input.Params.Delete == true {
		utils.Logln("Deleting stack")
		params := &cloudformation.DeleteStackInput{
			StackName: aws.String(input.Source.Name),
		}
		req, resp := svc.DeleteStackRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			utils.Logln(err.Error())
		}
		utils.Logln(resp)
	} else if !stackExists(reqHandler, svc, input.Source.Name) {
		utils.Logln("Creating stack")
		params := &cloudformation.CreateStackInput{
			StackName:    aws.String(input.Source.Name),
			Capabilities: capabilities,
			Parameters:   cloudformationParams,
			Tags:         cloudformationTags,
			TemplateBody: aws.String(templateBody),
		}
		req, resp := svc.CreateStackRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			utils.Logln(err.Error())
		}
		utils.Logln(resp)
	} else {
		utils.Logln("Updating stack")
		params := &cloudformation.UpdateStackInput{
			StackName:    aws.String(input.Source.Name),
			Capabilities: capabilities,
			Parameters:   cloudformationParams,
			Tags:         cloudformationTags,
			TemplateBody: aws.String(templateBody),
		}
		req, resp := svc.UpdateStackRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			utils.Logln(err.Error())
		}
		utils.Logln(resp)
	}

	success, arn, timestamp := waitForStack(reqHandler, svc, input)
	result := make(map[string]string)
	result["arn"] = arn
	result["timestamp"] = timestamp
	bytes, _ := json.Marshal(result)
	return string(bytes), success
}
<commit_msg>Log Stack deleted and error if it occurs whilst getting stack events<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/ci-pipeline/cloudformation-resource/utils"
)

func main() {
	utils.Logln("Change to build directory")
	utils.GoToBuildDirectory()
	cwd, _ := os.Getwd()
	utils.Logln(cwd)
	input := utils.GetInput()
	svc := utils.GetCloudformationService(input)
	metadata, success := out(input, svc, &utils.AwsRequestHandler{})
	fmt.Printf("%s", metadata)

	if !success {
		os.Exit(1)
	}
}

type Parameter struct {
	ParameterValue   string
	ParameterKey     string
	UsePreviousValue bool
}

type Tag struct {
	TagKey   string
	TagValue string
}

func stackExists(reqHandler utils.RequestHandler, svc utils.AwsCloudformationSvc, stackName string) bool {
	params := &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackName),
	}
	req, resp := svc.DescribeStacksRequest(params)
	err := reqHandler.HandleRequest(req)
	if err != nil {
		return false
	}
	utils.Logln(resp)

	return resp.Stacks[0] != nil
}

func waitForStack(reqHandler utils.RequestHandler, svc utils.AwsCloudformationSvc, input utils.Input) (success bool, arn, timestamp string) {
	success = true
	params := &cloudformation.DescribeStackEventsInput{
		StackName: aws.String(input.Source.Name),
	}

	pos := 0
	var stackEvents []*cloudformation.StackEvent
	for {
		req, resp := svc.DescribeStackEventsRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			if input.Params.Delete {
				success = true
				utils.Logln("Stack deleted")
				return
			}
			utils.Logf("An error occured: %v\n", err)
			return
		}
		stackEvents = resp.StackEvents
		for j := len(stackEvents) - 1 - pos; j > -1; j-- {
			utils.Logln(stackEvents[j])
		}
		pos = len(stackEvents)

		arn = *stackEvents[0].StackId
		timestamp = stackEvents[0].Timestamp.String()

		if *stackEvents[0].ResourceType == "AWS::CloudFormation::Stack" {
			if *stackEvents[0].ResourceStatus == "CREATE_COMPLETE" {
				return
			} else if *stackEvents[0].ResourceStatus == "UPDATE_COMPLETE" {
				return
			} else if *stackEvents[0].ResourceStatus == "ROLLBACK_COMPLETE" {
				success = false
				return
			} else if *stackEvents[0].ResourceStatus == "UPDATE_ROLLBACK_COMPLETE" {
				success = false
				return
			} else if strings.HasSuffix(*stackEvents[0].ResourceStatus, "_FAILED") {
				success = false
				return
			}
		}

		time.Sleep(10 * time.Millisecond)
	}
}

func out(input utils.Input, svc utils.AwsCloudformationSvc, reqHandler utils.RequestHandler) (metadata string, success bool) {

	var capabilities []*string
	capabilities = nil
	if input.Params.Capabilities != nil {
		for _, c := range input.Params.Capabilities {
			capabilities = append(capabilities, aws.String(c))
		}
	}

	var templateBody string
	if input.Params.Template != "" {
		bytes, err := ioutil.ReadFile(input.Params.Template)
		if err != nil {
			panic(err)
		}
		templateBody = string(bytes)
	}

	parameters := []Parameter{}
	var cloudformationParams []*cloudformation.Parameter
	if input.Params.Parameters != "" {
		bytes, err := ioutil.ReadFile(input.Params.Parameters)
		err = json.Unmarshal(bytes, &parameters)
		if err != nil {
			panic(err)
		}
	}
	for _, p := range parameters {
		param := cloudformation.Parameter{
			ParameterKey:     aws.String(p.ParameterKey),
			ParameterValue:   aws.String(p.ParameterValue),
			UsePreviousValue: aws.Bool(p.UsePreviousValue),
		}
		cloudformationParams = append(cloudformationParams, &param)
	}

	tags := []Tag{}
	var cloudformationTags []*cloudformation.Tag
	if input.Params.Tags != "" {
		bytes, err := ioutil.ReadFile(input.Params.Tags)
		err = json.Unmarshal(bytes, &tags)
		if err != nil {
			panic(err)
		}
	}
	for _, t := range tags {
		tag := cloudformation.Tag{
			Key:   aws.String(t.TagKey),
			Value: aws.String(t.TagValue),
		}
		cloudformationTags = append(cloudformationTags, &tag)
	}

	if input.Params.Delete == true {
		utils.Logln("Deleting stack")
		params := &cloudformation.DeleteStackInput{
			StackName: aws.String(input.Source.Name),
		}
		req, resp := svc.DeleteStackRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			utils.Logln(err.Error())
		}
		utils.Logln(resp)
	} else if !stackExists(reqHandler, svc, input.Source.Name) {
		utils.Logln("Creating stack")
		params := &cloudformation.CreateStackInput{
			StackName:    aws.String(input.Source.Name),
			Capabilities: capabilities,
			Parameters:   cloudformationParams,
			Tags:         cloudformationTags,
			TemplateBody: aws.String(templateBody),
		}
		req, resp := svc.CreateStackRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			utils.Logln(err.Error())
		}
		utils.Logln(resp)
	} else {
		utils.Logln("Updating stack")
		params := &cloudformation.UpdateStackInput{
			StackName:    aws.String(input.Source.Name),
			Capabilities: capabilities,
			Parameters:   cloudformationParams,
			Tags:         cloudformationTags,
			TemplateBody: aws.String(templateBody),
		}
		req, resp := svc.UpdateStackRequest(params)
		err := reqHandler.HandleRequest(req)
		if err != nil {
			utils.Logln(err.Error())
		}
		utils.Logln(resp)
	}

	success, arn, timestamp := waitForStack(reqHandler, svc, input)
	result := make(map[string]string)
	result["arn"] = arn
	result["timestamp"] = timestamp
	bytes, _ := json.Marshal(result)
	return string(bytes), success
}
<|endoftext|>


<commit_before>package mcp23008

import "golang.org/x/exp/io/i2c"

const (
	MCP23008_REG_IODIR = 0x00
	MCP23008_REG_IPOL = 0x01
	MCP23008_REG_GPINTEN = 0x02
	MCP23008_REG_DEFVAL = 0x03
	MCP23008_REG_INTCON = 0x04
	MCP23008_REG_IOCON = 0x05
	MCP23008_REG_GPPU = 0x06
	MCP23008_REG_INTF = 0x07
	MCP23008_REG_INTCAP = 0x08
	MCP23008_REG_GPIO = 0x09
	MCP23008_REG_OLAT = 0x0A
)

func relayInit(d *i2c.Device) {
	// SetAllDirection
	err := d.WriteReg(MCP23008_REG_IODIR, []byte{0})
	if err != nil {
		panic(err)
	}

	// SetAllPullUp
	err = d.WriteReg(MCP23008_REG_GPPU, []byte{0})
	if err != nil {
		panic(err)
	}
}

func relaySwitchOn(d *i2c.Device, swi byte) {
	buff := []byte{0}

	stateOn := byte(swi + 1)

	d.ReadReg(MCP23008_REG_GPIO, buff)
	d.WriteReg(MCP23008_REG_GPIO,[]byte{stateOn | buff[0]})
}

func relaySwitchOff(d *i2c.Device, swi byte) {
	buff := []byte{0}

	stateOff := byte(swi + 1) ^ 0xf

	d.ReadReg(MCP23008_REG_GPIO, buff)
	d.WriteReg(MCP23008_REG_GPIO,[]byte{stateOff & buff[0]})
}

func relaySwitchRead(d *i2c.Device, swi byte) byte {
	buff := []byte{0}

	bitPos := swi + 1

	d.ReadReg(MCP23008_REG_GPIO, buff)
	state := (buff[0] & bitPos) >> swi

	return state
}

<commit_msg>Initial commit<commit_after>package mcp23008

import "golang.org/x/exp/io/i2c"

const (
	MCP23008_REG_IODIR = 0x00
	MCP23008_REG_IPOL = 0x01
	MCP23008_REG_GPINTEN = 0x02
	MCP23008_REG_DEFVAL = 0x03
	MCP23008_REG_INTCON = 0x04
	MCP23008_REG_IOCON = 0x05
	MCP23008_REG_GPPU = 0x06
	MCP23008_REG_INTF = 0x07
	MCP23008_REG_INTCAP = 0x08
	MCP23008_REG_GPIO = 0x09
	MCP23008_REG_OLAT = 0x0A
)

func RelayInit(d *i2c.Device) {
	// SetAllDirection
	err := d.WriteReg(MCP23008_REG_IODIR, []byte{0})
	if err != nil {
		panic(err)
	}

	// SetAllPullUp
	err = d.WriteReg(MCP23008_REG_GPPU, []byte{0})
	if err != nil {
		panic(err)
	}
}

func RelaySwitchOn(d *i2c.Device, swi byte) {
	buff := []byte{0}

	stateOn := byte(swi + 1)

	d.ReadReg(MCP23008_REG_GPIO, buff)
	d.WriteReg(MCP23008_REG_GPIO,[]byte{stateOn | buff[0]})
}

func RelaySwitchOff(d *i2c.Device, swi byte) {
	buff := []byte{0}

	stateOff := byte(swi + 1) ^ 0xf

	d.ReadReg(MCP23008_REG_GPIO, buff)
	d.WriteReg(MCP23008_REG_GPIO,[]byte{stateOff & buff[0]})
}

func RelaySwitchRead(d *i2c.Device, swi byte) byte {
	buff := []byte{0}

	bitPos := swi + 1

	d.ReadReg(MCP23008_REG_GPIO, buff)
	state := (buff[0] & bitPos) >> swi

	return state
}

<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package discoverspaces

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/juju/utils/set"
	"launchpad.net/tomb"

	"github.com/juju/juju/api/discoverspaces"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/network"
	"github.com/juju/juju/worker"
)

var logger = loggo.GetLogger("juju.discoverspaces")

type discoverspacesWorker struct {
	api               *discoverspaces.API
	tomb              tomb.Tomb
	discoveringSpaces chan struct{}
}

var dashPrefix = regexp.MustCompile("^-*")
var dashSuffix = regexp.MustCompile("-*$")
var multipleDashes = regexp.MustCompile("--+")

func convertSpaceName(name string, existing set.Strings) string {
	// First lower case and replace spaces with dashes.
	name = strings.Replace(name, " ", "-", -1)
	name = strings.ToLower(name)
	// Replace any character that isn't in the set "-", "a-z", "0-9".
	name = network.SpaceInvalidChars.ReplaceAllString(name, "")
	// Get rid of any dashes at the start as that isn't valid.
	name = dashPrefix.ReplaceAllString(name, "")
	// And any at the end.
	name = dashSuffix.ReplaceAllString(name, "")
	// Repleace multiple dashes with a single dash.
	name = multipleDashes.ReplaceAllString(name, "-")
	// Special case of when the space name was only dashes or invalid
	// characters!
	if name == "" {
		name = "empty"
	}
	// If this name is in use add a numerical suffix.
	if existing.Contains(name) {
		counter := 2
		for existing.Contains(name + fmt.Sprintf("-%d", counter)) {
			counter += 1
		}
		name = name + fmt.Sprintf("-%d", counter)
	}
	return name
}

// NewWorker returns a worker
func NewWorker(api *discoverspaces.API) (worker.Worker, chan struct{}) {
	dw := &discoverspacesWorker{
		api:               api,
		discoveringSpaces: make(chan struct{}),
	}
	go func() {
		defer dw.tomb.Done()
		dw.tomb.Kill(dw.loop())
	}()
	return dw, dw.discoveringSpaces
}

func (dw *discoverspacesWorker) Kill() {
	dw.tomb.Kill(nil)
}

func (dw *discoverspacesWorker) Wait() error {
	return dw.tomb.Wait()
}

func (dw *discoverspacesWorker) loop() (err error) {
	modelCfg, err := dw.api.ModelConfig()
	if err != nil {
		close(dw.discoveringSpaces)
		return err
	}
	model, err := environs.New(modelCfg)
	if err != nil {
		close(dw.discoveringSpaces)
		return err
	}
	networkingModel, ok := environs.SupportsNetworking(model)

	if ok {
		err = dw.handleSubnets(networkingModel)
		if err != nil {
			close(dw.discoveringSpaces)
			return errors.Trace(err)
		}
	}
	close(dw.discoveringSpaces)

	// TODO(mfoord): we'll have a watcher here checking if we need to
	// update the spaces/subnets definition.
	dying := dw.tomb.Dying()
	for {
		select {
		case <-dying:
			return nil
		}
	}
	return nil
}

func (dw *discoverspacesWorker) handleSubnets(env environs.NetworkingEnviron) error {
	ok, err := env.SupportsSpaceDiscovery()
	if err != nil {
		return errors.Trace(err)
	}
	if !ok {
		// Nothing to do.
		return nil
	}
	providerSpaces, err := env.Spaces()
	if err != nil {
		return errors.Trace(err)
	}
	listSpacesResult, err := dw.api.ListSpaces()
	if err != nil {
		return errors.Trace(err)
	}

	stateSubnets, err := dw.api.ListSubnets(params.SubnetsFilters{})
	if err != nil {
		return errors.Trace(err)
	}
	stateSubnetIds := make(set.Strings)
	for _, subnet := range stateSubnets.Results {
		stateSubnetIds.Add(subnet.ProviderId)
	}
	stateSpaceMap := make(map[string]params.ProviderSpace)
	spaceNames := make(set.Strings)
	for _, space := range listSpacesResult.Results {
		stateSpaceMap[space.ProviderId] = space
		spaceNames.Add(space.Name)
	}

	// TODO(mfoord): we need to delete spaces and subnets that no longer
	// exist, so long as they're not in use.
	for _, space := range providerSpaces {
		// Check if the space is already in state, in which case we know
		// its name.
		stateSpace, ok := stateSpaceMap[string(space.ProviderId)]
		var spaceTag names.SpaceTag
		if ok {
			spaceName := stateSpace.Name
			if !names.IsValidSpace(spaceName) {
				// Can only happen if an invalid name is stored
				// in state.
				logger.Errorf("space %q has an invalid name, ignoring", spaceName)
				continue

			}
			spaceTag = names.NewSpaceTag(spaceName)

		} else {
			// The space is new, we need to create a valid name for it
			// in state.
			spaceName := string(space.ProviderId)
			// Convert the name into a valid name that isn't already in
			// use.
			spaceName = convertSpaceName(spaceName, spaceNames)
			spaceNames.Add(spaceName)
			spaceTag = names.NewSpaceTag(spaceName)
			// We need to create the space.
			args := params.CreateSpacesParams{
				Spaces: []params.CreateSpaceParams{{
					Public:     false,
					SpaceTag:   spaceTag.String(),
					ProviderId: string(space.ProviderId),
				}}}
			result, err := dw.api.CreateSpaces(args)
			if err != nil {
				logger.Errorf("error creating space %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from CreateSpaces, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				return errors.Errorf("error from CreateSpaces: %v", result.Results[0].Error)
			}
		}
		// TODO(mfoord): currently no way of removing subnets, or
		// changing the space they're in, so we can only add ones we
		// don't already know about.
		logger.Debugf("Created space %v with %v subnets", spaceTag.String(), len(space.Subnets))
		for _, subnet := range space.Subnets {
			if stateSubnetIds.Contains(string(subnet.ProviderId)) {
				continue
			}
			zones := subnet.AvailabilityZones
			if len(zones) == 0 {
				zones = []string{"default"}
			}
			args := params.AddSubnetsParams{
				Subnets: []params.AddSubnetParams{{
					SubnetProviderId: string(subnet.ProviderId),
					SpaceTag:         spaceTag.String(),
					Zones:            zones,
				}}}
			logger.Tracef("Adding subnet %v", subnet.CIDR)
			result, err := dw.api.AddSubnets(args)
			if err != nil {
				logger.Errorf("invalid creating subnet %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from AddSubnets, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				logger.Errorf("error creating subnet %v", result.Results[0].Error)
				return errors.Errorf("error creating subnet %v", result.Results[0].Error)
			}
		}
	}
	return nil
}
<commit_msg>Close the channel in a defer to catch all code paths<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package discoverspaces

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/juju/utils/set"
	"launchpad.net/tomb"

	"github.com/juju/juju/api/discoverspaces"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/network"
	"github.com/juju/juju/worker"
)

var logger = loggo.GetLogger("juju.discoverspaces")

type discoverspacesWorker struct {
	api               *discoverspaces.API
	tomb              tomb.Tomb
	discoveringSpaces chan struct{}
}

var dashPrefix = regexp.MustCompile("^-*")
var dashSuffix = regexp.MustCompile("-*$")
var multipleDashes = regexp.MustCompile("--+")

func convertSpaceName(name string, existing set.Strings) string {
	// First lower case and replace spaces with dashes.
	name = strings.Replace(name, " ", "-", -1)
	name = strings.ToLower(name)
	// Replace any character that isn't in the set "-", "a-z", "0-9".
	name = network.SpaceInvalidChars.ReplaceAllString(name, "")
	// Get rid of any dashes at the start as that isn't valid.
	name = dashPrefix.ReplaceAllString(name, "")
	// And any at the end.
	name = dashSuffix.ReplaceAllString(name, "")
	// Repleace multiple dashes with a single dash.
	name = multipleDashes.ReplaceAllString(name, "-")
	// Special case of when the space name was only dashes or invalid
	// characters!
	if name == "" {
		name = "empty"
	}
	// If this name is in use add a numerical suffix.
	if existing.Contains(name) {
		counter := 2
		for existing.Contains(name + fmt.Sprintf("-%d", counter)) {
			counter += 1
		}
		name = name + fmt.Sprintf("-%d", counter)
	}
	return name
}

// NewWorker returns a worker
func NewWorker(api *discoverspaces.API) (worker.Worker, chan struct{}) {
	dw := &discoverspacesWorker{
		api:               api,
		discoveringSpaces: make(chan struct{}),
	}
	go func() {
		defer dw.tomb.Done()
		dw.tomb.Kill(dw.loop())
	}()
	return dw, dw.discoveringSpaces
}

func (dw *discoverspacesWorker) Kill() {
	dw.tomb.Kill(nil)
}

func (dw *discoverspacesWorker) Wait() error {
	return dw.tomb.Wait()
}

func (dw *discoverspacesWorker) loop() (err error) {
	maybeClose := func() {
		select {
		case <-dw.discoveringSpaces:
			// Already closed.
			return
		default:
			close(dw.discoveringSpaces)
		}
	}
	defer maybeClose()
	modelCfg, err := dw.api.ModelConfig()
	if err != nil {
		return err
	}
	model, err := environs.New(modelCfg)
	if err != nil {
		return err
	}
	networkingModel, ok := environs.SupportsNetworking(model)

	if ok {
		err = dw.handleSubnets(networkingModel)
		if err != nil {
			return errors.Trace(err)
		}
	}
	close(dw.discoveringSpaces)

	// TODO(mfoord): we'll have a watcher here checking if we need to
	// update the spaces/subnets definition.
	dying := dw.tomb.Dying()
	for {
		select {
		case <-dying:
			return nil
		}
	}
	return nil
}

func (dw *discoverspacesWorker) handleSubnets(env environs.NetworkingEnviron) error {
	ok, err := env.SupportsSpaceDiscovery()
	if err != nil {
		return errors.Trace(err)
	}
	if !ok {
		// Nothing to do.
		return nil
	}
	providerSpaces, err := env.Spaces()
	if err != nil {
		return errors.Trace(err)
	}
	listSpacesResult, err := dw.api.ListSpaces()
	if err != nil {
		return errors.Trace(err)
	}

	stateSubnets, err := dw.api.ListSubnets(params.SubnetsFilters{})
	if err != nil {
		return errors.Trace(err)
	}
	stateSubnetIds := make(set.Strings)
	for _, subnet := range stateSubnets.Results {
		stateSubnetIds.Add(subnet.ProviderId)
	}
	stateSpaceMap := make(map[string]params.ProviderSpace)
	spaceNames := make(set.Strings)
	for _, space := range listSpacesResult.Results {
		stateSpaceMap[space.ProviderId] = space
		spaceNames.Add(space.Name)
	}

	// TODO(mfoord): we need to delete spaces and subnets that no longer
	// exist, so long as they're not in use.
	for _, space := range providerSpaces {
		// Check if the space is already in state, in which case we know
		// its name.
		stateSpace, ok := stateSpaceMap[string(space.ProviderId)]
		var spaceTag names.SpaceTag
		if ok {
			spaceName := stateSpace.Name
			if !names.IsValidSpace(spaceName) {
				// Can only happen if an invalid name is stored
				// in state.
				logger.Errorf("space %q has an invalid name, ignoring", spaceName)
				continue

			}
			spaceTag = names.NewSpaceTag(spaceName)

		} else {
			// The space is new, we need to create a valid name for it
			// in state.
			spaceName := string(space.ProviderId)
			// Convert the name into a valid name that isn't already in
			// use.
			spaceName = convertSpaceName(spaceName, spaceNames)
			spaceNames.Add(spaceName)
			spaceTag = names.NewSpaceTag(spaceName)
			// We need to create the space.
			args := params.CreateSpacesParams{
				Spaces: []params.CreateSpaceParams{{
					Public:     false,
					SpaceTag:   spaceTag.String(),
					ProviderId: string(space.ProviderId),
				}}}
			result, err := dw.api.CreateSpaces(args)
			if err != nil {
				logger.Errorf("error creating space %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from CreateSpaces, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				return errors.Errorf("error from CreateSpaces: %v", result.Results[0].Error)
			}
		}
		// TODO(mfoord): currently no way of removing subnets, or
		// changing the space they're in, so we can only add ones we
		// don't already know about.
		logger.Debugf("Created space %v with %v subnets", spaceTag.String(), len(space.Subnets))
		for _, subnet := range space.Subnets {
			if stateSubnetIds.Contains(string(subnet.ProviderId)) {
				continue
			}
			zones := subnet.AvailabilityZones
			if len(zones) == 0 {
				zones = []string{"default"}
			}
			args := params.AddSubnetsParams{
				Subnets: []params.AddSubnetParams{{
					SubnetProviderId: string(subnet.ProviderId),
					SpaceTag:         spaceTag.String(),
					Zones:            zones,
				}}}
			logger.Tracef("Adding subnet %v", subnet.CIDR)
			result, err := dw.api.AddSubnets(args)
			if err != nil {
				logger.Errorf("invalid creating subnet %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from AddSubnets, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				logger.Errorf("error creating subnet %v", result.Results[0].Error)
				return errors.Errorf("error creating subnet %v", result.Results[0].Error)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package zfs

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"os"
	"syscall"

	"github.com/cerana/cerana/zfs/nv"
)

type pipeHeader struct {
	Size     uint32
	ExtSpace uint8
	Error    uint8
	Endian   uint8
	Reserved uint8
}

func getSize(r io.Reader) (int64, error) {
	buf := make([]byte, 8)

	_, err := io.ReadFull(r, buf)
	if err != nil {
		return 0, err
	}

	h := pipeHeader{}
	err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, &h)
	if err != nil {
		return 0, err
	}

	if h.Endian != 1 {
		err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &h)
		if err != nil {
			return 0, err
		}
	}

	if h.Reserved != 0 {
		return 0, errors.New("non-zero Reserved field")
	}
	if h.Endian > 1 {
		return 0, errors.New("unknown Endian value")
	}
	if h.Error != 0 {
		return 0, syscall.Errno(h.Error)
	}

	return int64(h.Size), nil
}

func properties(name string, types map[string]bool, recurse bool, depth uint64) (map[string]*datasetProperties, error) {
	listing, err := list(name, types, recurse, depth)
	if err != nil {
		return nil, err
	}
	ret := make(map[string]*datasetProperties, len(listing))
	for _, l := range listing {
		name := l.Name
		props := l.Properties
		ret[name] = props
	}
	return ret, nil
}

func list(name string, types map[string]bool, recurse bool, depth uint64) ([]*dataset, error) {
	var reader io.Reader
	reader, writer, err := os.Pipe()
	if err != nil {
		return nil, err
	}
	defer reader.(*os.File).Close()
	defer writer.Close()

	opts := map[string]interface{}{
		"fd": int32(writer.Fd()),
	}
	if types != nil {
		opts["type"] = types
	}
	if recurse != false {
		if depth != 0 {
			opts["recurse"] = depth
		} else {
			opts["recurse"] = true
		}
	}
	args := map[string]interface{}{
		"cmd":     "zfs_list",
		"innvl":   map[string]interface{}{},
		"opts":    opts,
		"version": uint64(0),
	}

	encoded := &bytes.Buffer{}
	err = nv.NewNativeEncoder(encoded).Encode(args)
	if err != nil {
		return nil, err
	}

	err = ioctl(zfs, name, encoded.Bytes(), nil)
	if err != nil {
		return nil, err
	}

	var buf []byte
	reader = bufio.NewReader(reader)

	ret := []*dataset{}
	for {
		var size int64
		size, err = getSize(reader)
		if err != nil {
			break
		}
		if size == 0 {
			break
		}

		if len(buf) < int(size) {
			l := (size + 1023) & ^1023
			buf = make([]byte, l)
		}
		buf = buf[:size]

		_, err = io.ReadFull(reader, buf)
		if err != nil {
			break
		}

		m := &dataset{}
		err = nv.NewXDRDecoder(bytes.NewReader(buf)).Decode(&m)
		if err != nil {
			break
		}

		if m.Properties.Clones.Value == nil {
			m.Properties.Clones.Value = make(map[string]nv.Boolean)
		}

		ret = append(ret, m)
	}
	return ret, err
}
<commit_msg>Refactor list to handle pipe reader/writer errors<commit_after>package zfs

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"os"
	"syscall"

	"github.com/cerana/cerana/zfs/nv"
)

type pipeHeader struct {
	Size     uint32
	ExtSpace uint8
	Error    uint8
	Endian   uint8
	Reserved uint8
}

func getSize(r io.Reader) (int64, error) {
	buf := make([]byte, 8)

	_, err := io.ReadFull(r, buf)
	if err != nil {
		return 0, err
	}

	h := pipeHeader{}
	err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, &h)
	if err != nil {
		return 0, err
	}

	if h.Endian != 1 {
		err := binary.Read(bytes.NewReader(buf), binary.BigEndian, &h)
		if err != nil {
			return 0, err
		}
	}

	if h.Reserved != 0 {
		return 0, errors.New("non-zero Reserved field")
	}
	if h.Endian > 1 {
		return 0, errors.New("unknown Endian value")
	}
	if h.Error != 0 {
		return 0, syscall.Errno(h.Error)
	}

	return int64(h.Size), nil
}

func properties(name string, types map[string]bool, recurse bool, depth uint64) (map[string]*datasetProperties, error) {
	listing, err := list(name, types, recurse, depth)
	if err != nil {
		return nil, err
	}
	ret := make(map[string]*datasetProperties, len(listing))
	for _, l := range listing {
		name := l.Name
		props := l.Properties
		ret[name] = props
	}
	return ret, nil
}

func list(name string, types map[string]bool, recurse bool, depth uint64) (ret []*dataset, err error) {
	pipeReader, writer, err := os.Pipe()
	if err != nil {
		return
	}
	defer func() {
		for _, theErr := range []error{writer.Close(), pipeReader.Close()} {
			if err == nil {
				err = theErr
			}
		}
	}()

	opts := map[string]interface{}{
		"fd": int32(writer.Fd()),
	}
	if types != nil {
		opts["type"] = types
	}
	if recurse != false {
		if depth != 0 {
			opts["recurse"] = depth
		} else {
			opts["recurse"] = true
		}
	}
	args := map[string]interface{}{
		"cmd":     "zfs_list",
		"innvl":   map[string]interface{}{},
		"opts":    opts,
		"version": uint64(0),
	}

	encoded := &bytes.Buffer{}
	err = nv.NewNativeEncoder(encoded).Encode(args)
	if err != nil {
		return
	}

	err = ioctl(zfs, name, encoded.Bytes(), nil)
	if err != nil {
		return
	}

	var buf []byte
	reader := bufio.NewReader(pipeReader)

	for {
		var size int64
		size, err = getSize(reader)
		if err != nil {
			break
		}
		if size == 0 {
			break
		}

		if len(buf) < int(size) {
			l := (size + 1023) & ^1023
			buf = make([]byte, l)
		}
		buf = buf[:size]

		_, err = io.ReadFull(reader, buf)
		if err != nil {
			break
		}

		m := &dataset{}
		err = nv.NewXDRDecoder(bytes.NewReader(buf)).Decode(&m)
		if err != nil {
			break
		}

		if m.Properties.Clones.Value == nil {
			m.Properties.Clones.Value = make(map[string]nv.Boolean)
		}

		ret = append(ret, m)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Opsmate, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License, v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This software is distributed WITHOUT A WARRANTY OF ANY KIND.
// See the Mozilla Public License for details.

package main

import (
	"bytes"
	"encoding/csv"
	"encoding/xml"
	"fmt"
	"os"
	"strings"
	"regexp"
)

var canames = map[string][]string{
	"Asseco Data Systems S.A. (previously Unizeto Certum)": {"Asseco", "Unizeto", "Certum"},
	"Autoridad de Certificacion Firmaprofesional": { "Firmaprofesional" },
	"Certinomis / Docapost": { "Certinomis", "Docapost" },
	"China Financial Certification Authority (CFCA)": { "CFCA", "China Financial" },
	"Sectigo": { "Sectigo", "Comodo CA" },
	"Consorci Administració Oberta de Catalunya (Consorci AOC, CATCert)": { "CATCert", "Consorci AOC" },
	"Cybertrust Japan / JCSI": { "Cybertrust Japan" },
	"Deutscher Sparkassen Verlag GmbH (S-TRUST, DSV-Gruppe)": { "S-TRUST" },
	"Dhimyotis / Certigna": { "Certigna" },
	"DigiCert": {"DigiCert","Symantec","GeoTrust","Thawte","RapidSSL"},
	"DocuSign (OpenTrust/Keynectis)": { "DocuSign", "Keynectis", "OpenTrust", "Certplus" },
	"Global Digital Cybersecurity Authority Co., Ltd. (Formerly Guang Dong Certificate Authority (GDCA))": { "GDCA" },
	"GoDaddy": { "GoDaddy", "Starfield Technologies" },
	"Government of Hong Kong (SAR), Hongkong Post, Certizen": { "Certizen", "Hongkong Post" },
	"Government of Spain, Autoritat de Certificació de la Comunitat Valenciana (ACCV)": { "ACCV", "Government of Spain" },
	"Government of Spain, Fábrica Nacional de Moneda y Timbre (FNMT)": { "FNMT", "Government of Spain" },
	"Government of Taiwan, Government Root Certification Authority (GRCA)": { "GRCA", "Government of Taiwan" },
	"Government of The Netherlands, PKIoverheid (Logius)": { "PKIoverheid" },
	"Government of Turkey, Kamu Sertifikasyon Merkezi (Kamu SM)": { "Kamu SM" },
	"Internet Security Research Group (ISRG)": { "Let's Encrypt" },
	"SECOM Trust Systems CO., LTD.": { "SECOM" },
	"T-Systems International GmbH (Deutsche Telekom)": nil, // defined in extra_cas.xml so we can separate out DFN-PKI
}

var parenregex = regexp.MustCompile(`\s+\([^)]*\)`)
var fluffregex = regexp.MustCompile(`(\s+CA)?(,?\s+(S\.A\.|SA|Inc\.?|Ltd\.?|Limited|AG|Company|a\.s\.|Corporation|LLC))?$`)

func escapexml(str string) string {
	buf := bytes.NewBuffer(nil)
	err := xml.EscapeText(buf, []byte(str))
	if err != nil {
		panic(err)
	}
	return buf.String()
}

func main() {
	records, err := csv.NewReader(os.Stdin).ReadAll()
	if err != nil {
		fmt.Fprintf(os.Stderr, "mkcasxml: %s\n", err)
		os.Exit(1)
	}

	usedca := map[string]bool{}
	fmt.Printf("<cas xmlns=\"http://xmlns.sslmate.com/caa\">\n")
	for _, row := range records {
		name := row[0]
		akas := []string{}
		if names, ok := canames[name]; ok {
			usedca[name] = true
			if len(names) == 0 {
				continue
			}
			name = names[0]
			akas = names[1:]
		} else {
			name = parenregex.ReplaceAllLiteralString(name, "")
			name = fluffregex.ReplaceAllLiteralString(name, "")
		}

		if row[5] == "N/A" || row[5] == "" {
			continue
		}
		fmt.Printf("\t<ca>\n")
		fmt.Printf("\t\t<name>%s</name>\n", escapexml(name))
		for _, aka := range akas {
			fmt.Printf("\t\t<aka>%s</aka>\n", escapexml(aka))
		}

		caas := strings.Split(row[5], ",")
		for _, caa := range caas {
			fmt.Printf("\t\t<caa>%s</caa>\n", escapexml(strings.ToLower(strings.TrimSpace(caa))))
		}
		fmt.Printf("\t</ca>\n")
	}
	fmt.Printf("</cas>\n")

	for caname, _ := range canames {
		if !usedca[caname] {
			fmt.Fprintf(os.Stderr, "Warning: did not see CA `%s'\n", caname)
		}
	}
}
<commit_msg>Drop S-TRUST override<commit_after>// Copyright (C) 2017 Opsmate, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License, v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This software is distributed WITHOUT A WARRANTY OF ANY KIND.
// See the Mozilla Public License for details.

package main

import (
	"bytes"
	"encoding/csv"
	"encoding/xml"
	"fmt"
	"os"
	"strings"
	"regexp"
)

var canames = map[string][]string{
	"Asseco Data Systems S.A. (previously Unizeto Certum)": {"Asseco", "Unizeto", "Certum"},
	"Autoridad de Certificacion Firmaprofesional": { "Firmaprofesional" },
	"Certinomis / Docapost": { "Certinomis", "Docapost" },
	"China Financial Certification Authority (CFCA)": { "CFCA", "China Financial" },
	"Sectigo": { "Sectigo", "Comodo CA" },
	"Consorci Administració Oberta de Catalunya (Consorci AOC, CATCert)": { "CATCert", "Consorci AOC" },
	"Cybertrust Japan / JCSI": { "Cybertrust Japan" },
	"Dhimyotis / Certigna": { "Certigna" },
	"DigiCert": {"DigiCert","Symantec","GeoTrust","Thawte","RapidSSL"},
	"DocuSign (OpenTrust/Keynectis)": { "DocuSign", "Keynectis", "OpenTrust", "Certplus" },
	"Global Digital Cybersecurity Authority Co., Ltd. (Formerly Guang Dong Certificate Authority (GDCA))": { "GDCA" },
	"GoDaddy": { "GoDaddy", "Starfield Technologies" },
	"Government of Hong Kong (SAR), Hongkong Post, Certizen": { "Certizen", "Hongkong Post" },
	"Government of Spain, Autoritat de Certificació de la Comunitat Valenciana (ACCV)": { "ACCV", "Government of Spain" },
	"Government of Spain, Fábrica Nacional de Moneda y Timbre (FNMT)": { "FNMT", "Government of Spain" },
	"Government of Taiwan, Government Root Certification Authority (GRCA)": { "GRCA", "Government of Taiwan" },
	"Government of The Netherlands, PKIoverheid (Logius)": { "PKIoverheid" },
	"Government of Turkey, Kamu Sertifikasyon Merkezi (Kamu SM)": { "Kamu SM" },
	"Internet Security Research Group (ISRG)": { "Let's Encrypt" },
	"SECOM Trust Systems CO., LTD.": { "SECOM" },
	"T-Systems International GmbH (Deutsche Telekom)": nil, // defined in extra_cas.xml so we can separate out DFN-PKI
}

var parenregex = regexp.MustCompile(`\s+\([^)]*\)`)
var fluffregex = regexp.MustCompile(`(\s+CA)?(,?\s+(S\.A\.|SA|Inc\.?|Ltd\.?|Limited|AG|Company|a\.s\.|Corporation|LLC))?$`)

func escapexml(str string) string {
	buf := bytes.NewBuffer(nil)
	err := xml.EscapeText(buf, []byte(str))
	if err != nil {
		panic(err)
	}
	return buf.String()
}

func main() {
	records, err := csv.NewReader(os.Stdin).ReadAll()
	if err != nil {
		fmt.Fprintf(os.Stderr, "mkcasxml: %s\n", err)
		os.Exit(1)
	}

	usedca := map[string]bool{}
	fmt.Printf("<cas xmlns=\"http://xmlns.sslmate.com/caa\">\n")
	for _, row := range records {
		name := row[0]
		akas := []string{}
		if names, ok := canames[name]; ok {
			usedca[name] = true
			if len(names) == 0 {
				continue
			}
			name = names[0]
			akas = names[1:]
		} else {
			name = parenregex.ReplaceAllLiteralString(name, "")
			name = fluffregex.ReplaceAllLiteralString(name, "")
		}

		if row[5] == "N/A" || row[5] == "" {
			continue
		}
		fmt.Printf("\t<ca>\n")
		fmt.Printf("\t\t<name>%s</name>\n", escapexml(name))
		for _, aka := range akas {
			fmt.Printf("\t\t<aka>%s</aka>\n", escapexml(aka))
		}

		caas := strings.Split(row[5], ",")
		for _, caa := range caas {
			fmt.Printf("\t\t<caa>%s</caa>\n", escapexml(strings.ToLower(strings.TrimSpace(caa))))
		}
		fmt.Printf("\t</ca>\n")
	}
	fmt.Printf("</cas>\n")

	for caname, _ := range canames {
		if !usedca[caname] {
			fmt.Fprintf(os.Stderr, "Warning: did not see CA `%s'\n", caname)
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"time"
)

const (
	// ACLCLientType is the client type token
	ACLClientType = "client"

	// ACLManagementType is the management type token
	ACLManagementType = "management"
)

// ACLEntry is used to represent an ACL entry
type ACLEntry struct {
	CreateIndex uint64
	ModifyIndex uint64
	ID          string
	Name        string
	Type        string
	Rules       string
}

// ACLReplicationEntry is used to represent an ACLReplication entry
type ACLReplicationEntry struct {
	Enabled          bool
	Running          bool
	SourceDatacenter string
	ReplicatedIndex  uint64
	LastSuccess      time.Time
	LastError        time.Time
}

// ACL can be used to query the ACL endpoints
type ACL struct {
	c *Client
}

// ACL returns a handle to the ACL endpoints
func (c *Client) ACL() *ACL {
	return &ACL{c}
}

// Create is used to generate a new token with the given parameters
func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/create")
	r.setWriteOptions(q)
	r.obj = acl
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return "", nil, err
	}
	defer resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	var out struct{ ID string }
	if err := decodeBody(resp, &out); err != nil {
		return "", nil, err
	}
	return out.ID, wm, nil
}

// Update is used to update the rules of an existing token
func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/update")
	r.setWriteOptions(q)
	r.obj = acl
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	return wm, nil
}

// Destroy is used to destroy a given ACL token ID
func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
	r.setWriteOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	return wm, nil
}

// Clone is used to return a new token cloned from an existing one
func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
	r.setWriteOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return "", nil, err
	}
	defer resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	var out struct{ ID string }
	if err := decodeBody(resp, &out); err != nil {
		return "", nil, err
	}
	return out.ID, wm, nil
}

// Info is used to query for information about an ACL token
func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
	r := a.c.newRequest("GET", "/v1/acl/info/"+id)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var entries []*ACLEntry
	if err := decodeBody(resp, &entries); err != nil {
		return nil, nil, err
	}
	if len(entries) > 0 {
		return entries[0], qm, nil
	}
	return nil, qm, nil
}

// List is used to get all the ACL tokens
func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
	r := a.c.newRequest("GET", "/v1/acl/list")
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var entries []*ACLEntry
	if err := decodeBody(resp, &entries); err != nil {
		return nil, nil, err
	}
	return entries, qm, nil
}

// Replication returns the status of the ACL replication process in the datacenter
func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationEntry, *QueryMeta, error) {
	r := a.c.newRequest("GET", "/v1/acl/replication")
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var entries *ACLReplicationEntry
	if err := decodeBody(resp, &entries); err != nil {
		return nil, nil, err
	}
	return entries, qm, nil
}
<commit_msg>Tweaks ACL replication status struct name.<commit_after>package api

import (
	"time"
)

const (
	// ACLCLientType is the client type token
	ACLClientType = "client"

	// ACLManagementType is the management type token
	ACLManagementType = "management"
)

// ACLEntry is used to represent an ACL entry
type ACLEntry struct {
	CreateIndex uint64
	ModifyIndex uint64
	ID          string
	Name        string
	Type        string
	Rules       string
}

// ACLReplicationStatus is used to represent the status of ACL replication.
type ACLReplicationEntry struct {
	Enabled          bool
	Running          bool
	SourceDatacenter string
	ReplicatedIndex  uint64
	LastSuccess      time.Time
	LastError        time.Time
}

// ACL can be used to query the ACL endpoints
type ACL struct {
	c *Client
}

// ACL returns a handle to the ACL endpoints
func (c *Client) ACL() *ACL {
	return &ACL{c}
}

// Create is used to generate a new token with the given parameters
func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/create")
	r.setWriteOptions(q)
	r.obj = acl
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return "", nil, err
	}
	defer resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	var out struct{ ID string }
	if err := decodeBody(resp, &out); err != nil {
		return "", nil, err
	}
	return out.ID, wm, nil
}

// Update is used to update the rules of an existing token
func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/update")
	r.setWriteOptions(q)
	r.obj = acl
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	return wm, nil
}

// Destroy is used to destroy a given ACL token ID
func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
	r.setWriteOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	return wm, nil
}

// Clone is used to return a new token cloned from an existing one
func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
	r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
	r.setWriteOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return "", nil, err
	}
	defer resp.Body.Close()

	wm := &WriteMeta{RequestTime: rtt}
	var out struct{ ID string }
	if err := decodeBody(resp, &out); err != nil {
		return "", nil, err
	}
	return out.ID, wm, nil
}

// Info is used to query for information about an ACL token
func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
	r := a.c.newRequest("GET", "/v1/acl/info/"+id)
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var entries []*ACLEntry
	if err := decodeBody(resp, &entries); err != nil {
		return nil, nil, err
	}
	if len(entries) > 0 {
		return entries[0], qm, nil
	}
	return nil, qm, nil
}

// List is used to get all the ACL tokens
func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
	r := a.c.newRequest("GET", "/v1/acl/list")
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var entries []*ACLEntry
	if err := decodeBody(resp, &entries); err != nil {
		return nil, nil, err
	}
	return entries, qm, nil
}

// Replication returns the status of the ACL replication process in the datacenter
func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) {
	r := a.c.newRequest("GET", "/v1/acl/replication")
	r.setQueryOptions(q)
	rtt, resp, err := requireOK(a.c.doRequest(r))
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	qm := &QueryMeta{}
	parseQueryMeta(resp, qm)
	qm.RequestTime = rtt

	var entries *ACLReplicationStatus
	if err := decodeBody(resp, &entries); err != nil {
		return nil, nil, err
	}
	return entries, qm, nil
}
<|endoftext|>