<commit_before>package main

import (
	"fmt"
	"net"
	"bytes"
	"io/ioutil"
	"crypto/rand"
	"io"
	"os"
	"os/exec"
	"strings"
	"flag"
	"unicode/utf8"
)

var (
	banner []byte
	outDir string
	staticDir string
	debugging bool
	rsyncHost string
)

const (
	colBlue = "\033[0;34m"
	colGreen = "\033[0;32m"
	colBGreen = "\033[1;32m"
	colCyan = "\033[0;36m"
	colBRed = "\033[1;31m"
	colBold = "\033[1;37m"
	noCol = "\033[0m"

	nameLen = 12

	hr = "————————————————————————————————————————————————————————————————————————————————"
)

func main() {
	// Get any arguments
	outDirPtr := flag.String("o", "/var/write", "Directory where text files will be stored.")
	staticDirPtr := flag.String("s", ".", "Directory where required static files exist.")
	rsyncHostPtr := flag.String("h", "", "Hostname of the server to rsync saved files to.")
	portPtr := flag.Int("p", 2323, "Port to listen on.")
	debugPtr := flag.Bool("debug", false, "Enables garrulous debug logging.")
	flag.Parse()

	outDir = *outDirPtr
	staticDir = *staticDirPtr
	rsyncHost = *rsyncHostPtr
	debugging = *debugPtr

	fmt.Print("\nCONFIG:\n")
	fmt.Printf("Output directory  : %s\n", outDir)
	fmt.Printf("Static directory  : %s\n", staticDir)
	fmt.Printf("rsync host        : %s\n", rsyncHost)
	fmt.Printf("Debugging enabled : %t\n\n", debugging)

	fmt.Print("Initializing...")
	var err error
	banner, err = ioutil.ReadFile(staticDir + "/banner.txt")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("DONE")

	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *portPtr))
	if err != nil {
		panic(err)
	}
	fmt.Printf("Listening on localhost:%d\n", *portPtr)

	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}

		go handleConnection(conn)
	}
}

func output(c net.Conn, m string) bool {
	_, err := c.Write([]byte(m))
	if err != nil {
		c.Close()
		return false
	}
	return true
}

func outputBytes(c net.Conn, m []byte) bool {
	_, err := c.Write(m)
	if err != nil {
		c.Close()
		return false
	}
	return true
}

func handleConnection(c net.Conn) {
	outputBytes(c, banner)
	output(c, fmt.Sprintf("\n%sWelcome to write.as!%s\n", colBGreen, noCol))
	output(c, fmt.Sprintf("If this is freaking you out, you can get notified of the %sbrowser-based%s launch\ninstead at https://write.as.\n\n", colBold, noCol))

	waitForEnter(c)

	c.Close()

	fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())
}

func waitForEnter(c net.Conn) {
	b := make([]byte, 4)

	output(c, fmt.Sprintf("%sPress Enter to continue...%s\n", colBRed, noCol))
	for {
		n, err := c.Read(b)

		if debugging {
			fmt.Print(b[0:n])
			fmt.Printf("\n%d: %s\n", n, b[0:n])
		}

		if bytes.IndexRune(b[0:n], '\n') > -1 {
			break
		}
		if err != nil || n == 0 {
			c.Close()
			break
		}
	}

	output(c, fmt.Sprintf("Enter anything you like.\nPress %sCtrl-D%s to publish and quit.\n%s\n", colBold, noCol, hr))
	readInput(c)
}

func checkExit(b []byte, n int) bool {
	return n > 0 && bytes.IndexRune(b[0:n], '\n') == -1
}

func readInput(c net.Conn) {
	defer c.Close()

	b := make([]byte, 4096)

	var post bytes.Buffer

	for {
		n, err := c.Read(b)
		post.Write(b[0:n])

		if debugging {
			fmt.Print(b[0:n])
			fmt.Printf("\n%d: %s\n", n, b[0:n])
		}

		if checkExit(b, n) {
			file, err := savePost(post.Bytes())
			if err != nil {
				fmt.Printf("There was an error saving: %s\n", err)
				output(c, "Something went terribly wrong, sorry. Try again later?\n\n")
				break
			}
			output(c, fmt.Sprintf("\n%s\nPosted to %shttp://nerds.write.as/%s%s", hr, colBlue, file, noCol))

			if rsyncHost != "" {
				output(c, "\nPosting to secure site...")
				exec.Command("rsync", "-ptgou", outDir + "/" + file, rsyncHost + ":").Run()
				output(c, fmt.Sprintf("\nPosted! View at %shttps://write.as/%s%s", colBlue, file, noCol))
			}

			output(c, "\nSee you later.\n\n")
			break
		}

		if err != nil || n == 0 {
			break
		}
	}
}

func savePost(post []byte) (string, error) {
	filename := generateFileName()
	f, err := os.Create(outDir + "/" + filename)

	defer f.Close()

	if err != nil {
		fmt.Println(err)
	}

	var decodedPost bytes.Buffer

	// Decode UTF-8
	for len(post) > 0 {
		r, size := utf8.DecodeRune(post)
		decodedPost.WriteRune(r)

		post = post[size:]
	}

	_, err = io.WriteString(f, stripCtlAndExtFromUTF8(string(decodedPost.Bytes())))

	return filename, err
}

func generateFileName() string {
	c := nameLen
	var dictionary string = "0123456789abcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, c)
	rand.Read(bytes)
	for k, v := range bytes {
		 bytes[k] = dictionary[v%byte(len(dictionary))]
	}
	return string(bytes)
}

func stripCtlAndExtFromUTF8(str string) string {
	return strings.Map(func(r rune) rune {
		if r == 10 || r == 13 || (r >= 32 && r < 65533) {
			return r
		}
		return -1
	}, str)
}
<commit_msg>Correctly handle UTF-8 chars<commit_after>package main

import (
	"fmt"
	"net"
	"bytes"
	"io/ioutil"
	"crypto/rand"
	"io"
	"os"
	"os/exec"
	"flag"
)

var (
	banner []byte
	outDir string
	staticDir string
	debugging bool
	rsyncHost string
)

const (
	colBlue = "\033[0;34m"
	colGreen = "\033[0;32m"
	colBGreen = "\033[1;32m"
	colCyan = "\033[0;36m"
	colBRed = "\033[1;31m"
	colBold = "\033[1;37m"
	noCol = "\033[0m"

	nameLen = 12

	hr = "————————————————————————————————————————————————————————————————————————————————"
)

func main() {
	// Get any arguments
	outDirPtr := flag.String("o", "/var/write", "Directory where text files will be stored.")
	staticDirPtr := flag.String("s", ".", "Directory where required static files exist.")
	rsyncHostPtr := flag.String("h", "", "Hostname of the server to rsync saved files to.")
	portPtr := flag.Int("p", 2323, "Port to listen on.")
	debugPtr := flag.Bool("debug", false, "Enables garrulous debug logging.")
	flag.Parse()

	outDir = *outDirPtr
	staticDir = *staticDirPtr
	rsyncHost = *rsyncHostPtr
	debugging = *debugPtr

	fmt.Print("\nCONFIG:\n")
	fmt.Printf("Output directory  : %s\n", outDir)
	fmt.Printf("Static directory  : %s\n", staticDir)
	fmt.Printf("rsync host        : %s\n", rsyncHost)
	fmt.Printf("Debugging enabled : %t\n\n", debugging)

	fmt.Print("Initializing...")
	var err error
	banner, err = ioutil.ReadFile(staticDir + "/banner.txt")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("DONE")

	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", *portPtr))
	if err != nil {
		panic(err)
	}
	fmt.Printf("Listening on localhost:%d\n", *portPtr)

	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}

		go handleConnection(conn)
	}
}

func output(c net.Conn, m string) bool {
	_, err := c.Write([]byte(m))
	if err != nil {
		c.Close()
		return false
	}
	return true
}

func outputBytes(c net.Conn, m []byte) bool {
	_, err := c.Write(m)
	if err != nil {
		c.Close()
		return false
	}
	return true
}

func handleConnection(c net.Conn) {
	outputBytes(c, banner)
	output(c, fmt.Sprintf("\n%sWelcome to write.as!%s\n", colBGreen, noCol))
	output(c, fmt.Sprintf("If this is freaking you out, you can get notified of the %sbrowser-based%s launch\ninstead at https://write.as.\n\n", colBold, noCol))

	waitForEnter(c)

	c.Close()

	fmt.Printf("Connection from %v closed.\n", c.RemoteAddr())
}

func waitForEnter(c net.Conn) {
	b := make([]byte, 4)

	output(c, fmt.Sprintf("%sPress Enter to continue...%s\n", colBRed, noCol))
	for {
		n, err := c.Read(b)

		if debugging {
			fmt.Print(b[0:n])
			fmt.Printf("\n%d: %s\n", n, b[0:n])
		}

		if bytes.IndexRune(b[0:n], '\n') > -1 {
			break
		}
		if err != nil || n == 0 {
			c.Close()
			break
		}
	}

	output(c, fmt.Sprintf("Enter anything you like.\nPress %sCtrl-D%s to publish and quit.\n%s\n", colBold, noCol, hr))
	readInput(c)
}

func checkExit(b []byte, n int) bool {
	return n > 0 && bytes.IndexRune(b[0:n], '\n') == -1
}

func readInput(c net.Conn) {
	defer c.Close()

	b := make([]byte, 4096)

	var post bytes.Buffer

	for {
		n, err := c.Read(b)
		post.Write(b[0:n])

		if debugging {
			fmt.Print(b[0:n])
			fmt.Printf("\n%d: %s\n", n, b[0:n])
		}

		if checkExit(b, n) {
			file, err := savePost(post.Bytes())
			if err != nil {
				fmt.Printf("There was an error saving: %s\n", err)
				output(c, "Something went terribly wrong, sorry. Try again later?\n\n")
				break
			}
			output(c, fmt.Sprintf("\n%s\nPosted to %shttp://nerds.write.as/%s%s", hr, colBlue, file, noCol))

			if rsyncHost != "" {
				output(c, "\nPosting to secure site...")
				exec.Command("rsync", "-ptgou", outDir + "/" + file, rsyncHost + ":").Run()
				output(c, fmt.Sprintf("\nPosted! View at %shttps://write.as/%s%s", colBlue, file, noCol))
			}

			output(c, "\nSee you later.\n\n")
			break
		}

		if err != nil || n == 0 {
			break
		}
	}
}

func savePost(post []byte) (string, error) {
	filename := generateFileName()
	f, err := os.Create(outDir + "/" + filename)
	if err != nil {
		return "", err
	}

	defer f.Close()

	out := post[:0]
	for _, b := range post {
		if b < 32 && b != 10 && b != 13 {
			continue
		}
		out = append(out, b)
	}
	_, err = io.Copy(f, bytes.NewReader(out))

	return filename, err
}

func generateFileName() string {
	c := nameLen
	var dictionary string = "0123456789abcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, c)
	rand.Read(bytes)
	for k, v := range bytes {
		 bytes[k] = dictionary[v%byte(len(dictionary))]
	}
	return string(bytes)
}
<|endoftext|>


<commit_before>package reedsolomon

import (
	"bytes"
	"math/rand"
	"testing"
	"runtime"
	"sync"
)

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

func TestEncode(t *testing.T) {
	size := 50000
	r, err := New(10, 3)
	if err != nil {
		t.Fatal(err)
	}
	dp := NewMatrix(13, size)
	rand.Seed(0)
	for s := 0; s < 13; s++ {
		fillRandom(dp[s])
	}
	err = r.Encode(dp)
	if err != nil {
		t.Fatal(err)
	}
	badDP := NewMatrix(13, 100)
	badDP[0] = make([]byte, 1)
	err = r.Encode(badDP)
	if err != ErrShardSize {
		t.Errorf("expected %v, got %v", ErrShardSize, err)
	}
}

// test low, high table work
func TestVerifyEncode(t *testing.T) {
	r, err := New(5, 5)
	if err != nil {
		t.Fatal(err)
	}
	shards := [][]byte{
		{0, 1},
		{4, 5},
		{2, 3},
		{6, 7},
		{8, 9},
		{0, 0},
		{0, 0},
		{0, 0},
		{0, 0},
		{0, 0},
	}
	r.Encode(shards)
	if shards[5][0] != 97 || shards[5][1] != 64 {
		t.Fatal("shard 5 mismatch")
	}
	if shards[6][0] != 173 || shards[6][1] != 3 {
		t.Fatal("shard 6 mismatch")
	}
	if shards[7][0] != 218 || shards[7][1] != 14 {
		t.Fatal("shard 7 mismatch")
	}
	if shards[8][0] != 107 || shards[8][1] != 35 {
		t.Fatal("shard 8 mismatch")
	}
	if shards[9][0] != 110 || shards[9][1] != 177 {
		t.Fatal("shard 9 mismatch")
	}
}

func TestASM(t *testing.T) {
	d := 10
	p := 4
	size := 65 * 1024
	r, err := New(d, p)
	if err != nil {
		t.Fatal(err)
	}
	// asm
	dp := NewMatrix(d+p, size)
	rand.Seed(0)
	for i := 0; i < d; i++ {
		fillRandom(dp[i])
	}
	err = r.Encode(dp)
	if err != nil {
		t.Fatal(err)
	}
	// mulTable
	mDP := NewMatrix(d+p, size)
	for i := 0; i < d; i++ {
		mDP[i] = dp[i]
	}
	err = r.noasmEncode(mDP)
	if err != nil {
		t.Fatal(err)
	}
	for i, asm := range dp {
		if !bytes.Equal(asm, mDP[i]) {
			t.Fatal("verify asm failed, no match noasm version; shards: ", i)
		}
	}
}

func BenchmarkEncode10x4x1M(b *testing.B) {
	benchmarkEncode(b, 10, 4, 1024*1024)
}

func BenchmarkEncode10x4x4M(b *testing.B) {
	benchmarkEncode(b, 10, 4, 4*1024*1024)
}

func BenchmarkEncode10x4x16M(b *testing.B) {
	benchmarkEncode(b, 10, 4, 16*1024*1024)
}

func BenchmarkEncode17x3x1M(b *testing.B) {
	benchmarkEncode(b, 17, 3, 1024*1024)
}

func BenchmarkEncode17x3x4M(b *testing.B) {
	benchmarkEncode(b, 17, 3, 4*1024*1024)
}

func BenchmarkEncode17x3x16M(b *testing.B) {
	benchmarkEncode(b, 17, 3, 16*1024*1024)
}

func benchmarkEncode(b *testing.B, data, parity, size int) {
	r, err := New(data, parity)
	if err != nil {
		b.Fatal(err)
	}
	dp := NewMatrix(data+parity, size)
	rand.Seed(0)
	for i := 0; i < data; i++ {
		fillRandom(dp[i])
	}
	b.SetBytes(int64(size * data))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		r.Encode(dp)
	}
}

func fillRandom(p []byte) {
	for i := 0; i < len(p); i += 7 {
		val := rand.Int63()
		for j := 0; i+j < len(p) && j < 7; j++ {
			p[i+j] = byte(val)
			val >>= 8
		}
	}
}

func (r *rs) noasmEncode(dp matrix) error {
	// check args
	if len(dp) != r.shards {
		return ErrTooFewShards
	}
	_, err := checkShardSize(dp)
	if err != nil {
		return err
	}
	// encoding
	input := dp[0:r.data]
	output := dp[r.data:]
	noasmRunner(r.gen, input, output, r.data, r.parity)
	return nil
}

func noasmRunner(gen, input, output matrix, numData, numParity int) {
	for i := 0; i < numData; i++ {
		in := input[i]
		for oi := 0; oi < numParity; oi++ {
			if i == 0 {
				noasmGfVectMul(gen[oi][i], in[:], output[oi][:])
			} else {
				noasmGfVectMulXor(gen[oi][i], in[:], output[oi][:])
			}
		}
	}
}

func noasmGfVectMul(c byte, in, out []byte) {
	mt := mulTable[c]
	for i := 0; i < len(in); i++ {
		out[i] = mt[in[i]]
	}
}

func noasmGfVectMulXor(c byte, in, out []byte) {
	mt := mulTable[c]
	for i := 0; i < len(in); i++ {
		out[i] ^= mt[in[i]]
	}
}

func BenchmarkEncode28x4x16M(b *testing.B) {
	benchmarkEncode(b, 28, 4, 16776168)
}

func BenchmarkEncode14x10x16M(b *testing.B) {
	benchmarkEncode(b, 14, 10, 16776168)
}

func BenchmarkEncode28x4x16M_ConCurrency(b *testing.B) {
	benchmarkEncode_ConCurrency(b, 28, 4, 16776168)
}

func BenchmarkEncode14x10x16M_ConCurrency(b *testing.B) {
	benchmarkEncode_ConCurrency(b, 14, 10, 16776168)
}

func benchmarkEncode_ConCurrency(b *testing.B, data, parity, size int) {
	count := runtime.NumCPU()
	Instances := make([]*rs, count)
	dps := make([]matrix, count)
	for i := 0; i < count; i++ {
		r, err := New(data, parity)
		if err != nil {
			b.Fatal(err)
		}
		Instances[i] = r
	}
	for i := 0; i < count; i++ {
		dps[i] = NewMatrix(data+parity, size)
		rand.Seed(0)
		for j := 0; j < data; j++ {
			fillRandom(dps[i][j])
		}
	}

	b.SetBytes(int64(size * data * count))
	b.ResetTimer()
	var g sync.WaitGroup
	for j := 0; j < b.N; j ++ {
		for i := 0; i < count; i++ {
			g.Add(1)
			go func(i int) {
				Instances[i].Encode(dps[i])
				g.Done()
			}(i)
		}
	}
	g.Wait()
}<commit_msg>fix testcase<commit_after>package reedsolomon

import (
	"bytes"
	"math/rand"
	"testing"
	"runtime"
	"sync"
	"time"
	"fmt"
)

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

func TestEncode(t *testing.T) {
	size := 50000
	r, err := New(10, 3)
	if err != nil {
		t.Fatal(err)
	}
	dp := NewMatrix(13, size)
	rand.Seed(0)
	for s := 0; s < 13; s++ {
		fillRandom(dp[s])
	}
	err = r.Encode(dp)
	if err != nil {
		t.Fatal(err)
	}
	badDP := NewMatrix(13, 100)
	badDP[0] = make([]byte, 1)
	err = r.Encode(badDP)
	if err != ErrShardSize {
		t.Errorf("expected %v, got %v", ErrShardSize, err)
	}
}

// test low, high table work
func TestVerifyEncode(t *testing.T) {
	r, err := New(5, 5)
	if err != nil {
		t.Fatal(err)
	}
	shards := [][]byte{
		{0, 1},
		{4, 5},
		{2, 3},
		{6, 7},
		{8, 9},
		{0, 0},
		{0, 0},
		{0, 0},
		{0, 0},
		{0, 0},
	}
	r.Encode(shards)
	if shards[5][0] != 97 || shards[5][1] != 64 {
		t.Fatal("shard 5 mismatch")
	}
	if shards[6][0] != 173 || shards[6][1] != 3 {
		t.Fatal("shard 6 mismatch")
	}
	if shards[7][0] != 218 || shards[7][1] != 14 {
		t.Fatal("shard 7 mismatch")
	}
	if shards[8][0] != 107 || shards[8][1] != 35 {
		t.Fatal("shard 8 mismatch")
	}
	if shards[9][0] != 110 || shards[9][1] != 177 {
		t.Fatal("shard 9 mismatch")
	}
}

func TestASM(t *testing.T) {
	d := 10
	p := 4
	size := 65 * 1024
	r, err := New(d, p)
	if err != nil {
		t.Fatal(err)
	}
	// asm
	dp := NewMatrix(d+p, size)
	rand.Seed(0)
	for i := 0; i < d; i++ {
		fillRandom(dp[i])
	}
	err = r.Encode(dp)
	if err != nil {
		t.Fatal(err)
	}
	// mulTable
	mDP := NewMatrix(d+p, size)
	for i := 0; i < d; i++ {
		mDP[i] = dp[i]
	}
	err = r.noasmEncode(mDP)
	if err != nil {
		t.Fatal(err)
	}
	for i, asm := range dp {
		if !bytes.Equal(asm, mDP[i]) {
			t.Fatal("verify asm failed, no match noasm version; shards: ", i)
		}
	}
}

func BenchmarkEncode10x4x1M(b *testing.B) {
	benchmarkEncode(b, 10, 4, 1024*1024)
}

func BenchmarkEncode10x4x4M(b *testing.B) {
	benchmarkEncode(b, 10, 4, 4*1024*1024)
}

func BenchmarkEncode10x4x16M(b *testing.B) {
	benchmarkEncode(b, 10, 4, 16*1024*1024)
}

func BenchmarkEncode17x3x1M(b *testing.B) {
	benchmarkEncode(b, 17, 3, 1024*1024)
}

func BenchmarkEncode17x3x4M(b *testing.B) {
	benchmarkEncode(b, 17, 3, 4*1024*1024)
}

func BenchmarkEncode17x3x16M(b *testing.B) {
	benchmarkEncode(b, 17, 3, 16*1024*1024)
}

func benchmarkEncode(b *testing.B, data, parity, size int) {
	r, err := New(data, parity)
	if err != nil {
		b.Fatal(err)
	}
	dp := NewMatrix(data+parity, size)
	rand.Seed(0)
	for i := 0; i < data; i++ {
		fillRandom(dp[i])
	}
	b.SetBytes(int64(size * data))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		r.Encode(dp)
	}
}

func fillRandom(p []byte) {
	for i := 0; i < len(p); i += 7 {
		val := rand.Int63()
		for j := 0; i+j < len(p) && j < 7; j++ {
			p[i+j] = byte(val)
			val >>= 8
		}
	}
}

func (r *rs) noasmEncode(dp matrix) error {
	// check args
	if len(dp) != r.shards {
		return ErrTooFewShards
	}
	_, err := checkShardSize(dp)
	if err != nil {
		return err
	}
	// encoding
	input := dp[0:r.data]
	output := dp[r.data:]
	noasmRunner(r.gen, input, output, r.data, r.parity)
	return nil
}

func noasmRunner(gen, input, output matrix, numData, numParity int) {
	for i := 0; i < numData; i++ {
		in := input[i]
		for oi := 0; oi < numParity; oi++ {
			if i == 0 {
				noasmGfVectMul(gen[oi][i], in[:], output[oi][:])
			} else {
				noasmGfVectMulXor(gen[oi][i], in[:], output[oi][:])
			}
		}
	}
}

func noasmGfVectMul(c byte, in, out []byte) {
	mt := mulTable[c]
	for i := 0; i < len(in); i++ {
		out[i] = mt[in[i]]
	}
}

func noasmGfVectMulXor(c byte, in, out []byte) {
	mt := mulTable[c]
	for i := 0; i < len(in); i++ {
		out[i] ^= mt[in[i]]
	}
}

func BenchmarkEncode28x4x16M(b *testing.B) {
	benchmarkEncode(b, 28, 4, 16776168)
}

func BenchmarkEncode14x10x16M(b *testing.B) {
	benchmarkEncode(b, 14, 10, 16776168)
}

func BenchmarkEncode28x4x16M_ConCurrency(b *testing.B) {
	benchmarkEncode_ConCurrency(b, 28, 4, 16776168)
}

func BenchmarkEncode14x10x16M_ConCurrency(b *testing.B) {
	benchmarkEncode_ConCurrency(b, 14, 10, 16776168)
}

func benchmarkEncode_ConCurrency(b *testing.B, data, parity, size int) {
	count := runtime.NumCPU()
	Instances := make([]*rs, count)
	dps := make([]matrix, count)
	for i := 0; i < count; i++ {
		r, err := New(data, parity)
		if err != nil {
			b.Fatal(err)
		}
		Instances[i] = r
	}
	for i := 0; i < count; i++ {
		dps[i] = NewMatrix(data+parity, size)
		rand.Seed(0)
		for j := 0; j < data; j++ {
			fillRandom(dps[i][j])
		}
	}

	b.SetBytes(int64(size * data * count))
	b.ResetTimer()
	var g sync.WaitGroup
	for j := 0; j < b.N; j ++ {
		for i := 0; i < count; i++ {
			g.Add(1)
			go func(i int) {
				Instances[i].Encode(dps[i])
				g.Done()
			}(i)
		}
	}
	g.Wait()
}

func encode_ConCurrency(t testing.T,data, parity, size int) {
	count := runtime.NumCPU()
	Instances := make([]*rs, count)
	dps := make([]matrix, count)
	for i := 0; i < count; i++ {
		r, err := New(data, parity)
		if err != nil {
			t.Fatal(err)
		}
		Instances[i] = r
	}
	for i := 0; i < count; i++ {
		dps[i] = NewMatrix(data+parity, size)
		rand.Seed(0)
		for j := 0; j < data; j++ {
			fillRandom(dps[i][j])
		}
	}

	beginTime := time.Now()
	var g sync.WaitGroup
	for i := 0; i < count; i++ {
		g.Add(1)
		go func(i int) {
			Instances[i].Encode(dps[i])
			g.Done()
		}(i)
	}
	g.Wait()
	consume := time.Since(beginTime).Nanoseconds()
	fmt.Printf("parity number:[%v + %v] with size [%v]\n", data, parity, size)
	fmt.Println("----------------------------------------")
	fmt.Println("corrency:", count)
	fmt.Println("speed:", float32(count * data * size) * float32(time.Nanosecond) / float32(consume))
}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"log"
	"math"
	"os"

	"github.com/nfnt/resize"
)

const ANSIClear = "\033[0m"

func main() {
	pad := flag.Bool("pad", false, "pad output on the left with whitespace")
	paletteName := flag.String("color", "256", "color palette (8, 256, gray, ...)")
	fontAspect := flag.Float64("fontaspect", 0.5, "aspect ratio (width/height)")
	flag.Parse()

	palette := ansiPalettes[*paletteName]
	if palette == nil {
		log.Fatalf("color palette not one of %q", ANSIPalettes())
	}

	if flag.NArg() < 1 {
		log.Fatal("missing filename")
	}
	if flag.NArg() > 1 {
		log.Fatal("unexpected arguments")
	}
	filename := flag.Arg(0)

	img, _, err := readImage(filename)
	if err != nil {
		log.Fatalf("image: %v", err)
	}
	sizenorm := normalSize(img.Bounds().Size(), *fontAspect)
	imgnorm := resize.Resize(uint(sizenorm.X), uint(sizenorm.Y), img, 0)
	err = writePixelsANSI(os.Stdout, imgnorm, palette, *pad)
	if err != nil {
		log.Fatalf("write: %v", err)
	}
}

func writePixelsANSI(w io.Writer, img image.Image, p ANSIPalette, pad bool) error {
	wbuf := bufio.NewWriter(w)
	rect := img.Bounds()
	size := rect.Size()
	for y := 0; y < size.Y; y++ {
		if pad {
			fmt.Fprint(wbuf, "  ")
		}
		for x := 0; x < size.X; x++ {
			color := img.At(rect.Min.X+x, rect.Min.Y+y)
			fmt.Fprint(wbuf, p.ANSI(color)+" ")
		}
		fmt.Fprintln(wbuf, ANSIClear)
	}
	return wbuf.Flush()
}

// readImage reads an image.Image from a specified file.
func readImage(filename string) (image.Image, string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, "", err
	}
	defer f.Close()

	img, format, err := image.Decode(f)
	if err != nil {
		return nil, "", err
	}
	return img, format, nil
}

// normalSize scales size according to aspect ratio fontAspect and returns the
// new size.
func normalSize(size image.Point, fontAspect float64) image.Point {
	aspect := float64(size.X) / float64(size.Y)
	norm := size
	norm.Y = size.Y
	w := float64(norm.Y) * aspect / fontAspect
	norm.X = int(round(w))
	return norm
}

// round x to the nearest integer biased toward +Inf.
func round(x float64) float64 {
	return math.Ceil(x - 0.5)
}

type ANSIPalette interface {
	ANSI(color.Color) string
}

var ansiPalettes = map[string]ANSIPalette{
	"256":       new(Palette256),
	"256-color": new(Palette256),
	"8":         DefaultPalette8,
	"8-color":   DefaultPalette8,
	"gray":      new(PaletteGray),
	"grayscale": new(PaletteGray),
	"grey":      new(PaletteGray),
	"greyscale": new(PaletteGray),
}

func ANSIPalettes() []string {
	var names []string
	for name := range ansiPalettes {
		names = append(names, name)
	}
	return names
}

// PaletteGray is an ANSIPalette that maps color.Color values to one of twenty
// four grayscale values.
type PaletteGray struct {
}

func (p *PaletteGray) ANSI(c color.Color) string {
	const begin = 0xe8
	const ratio = 24.0 / 255.0
	_, _, _, a := c.RGBA()
	if a == 0 {
		return ANSIClear
	}
	gray := color.GrayModel.Convert(c).(color.Gray).Y
	scaled := int(round(ratio * float64(gray)))
	return fmt.Sprintf("\033[48;5;%dm", scaled+begin)
}

// Palette8 is an ANSIPalette that maps color.Color values to one of 8 color
// indexes by minimizing euclidean RGB distance.
type Palette8 [8][3]uint8

var DefaultPalette8 = &Palette8{
	{0, 0, 0},       // black
	{191, 25, 25},   // red
	{25, 184, 25},   // green
	{188, 110, 25},  // orange/brown/yellow
	{25, 25, 184},   // blue
	{186, 25, 186},  // magenta
	{25, 187, 187},  // cyan
	{178, 178, 178}, // gray
}

func (p *Palette8) ANSI(c color.Color) string {
	_, _, _, a := c.RGBA()
	if a == 0 {
		return ANSIClear
	}
	min := math.Inf(1) // minimum distance from c
	var imin int       // minimizing index
	for i, rgb := range *p {
		other := color.RGBA{rgb[0], rgb[1], rgb[2], 0}
		dist := Distance(other, c)
		if dist < min {
			min = dist
			imin = i
		}
	}
	return fmt.Sprintf("\033[4%dm", imin)
}

// Palette256 is an ANSIPalette that maps color.Color to one of 256 RGB colors.
type Palette256 struct {
}

func (p *Palette256) ANSI(c color.Color) string {
	const begin = 16
	const ratio = 5.0 / (1<<16 - 1)
	rf, gf, bf, af := c.RGBA()
	if af == 0 {
		return ANSIClear
	}
	r := int(round(ratio * float64(rf)))
	g := int(round(ratio * float64(gf)))
	b := int(round(ratio * float64(bf)))
	return fmt.Sprintf("\033[48;5;%dm", r*6*6+g*6+b+begin)
}

// Distance computes euclidean distance between the RGB values of c1 and c2.
func Distance(c1, c2 color.Color) float64 {
	r1, g1, b1, _ := c1.RGBA()
	r2, g2, b2, _ := c2.RGBA()
	rdiff := float64(int(r1) - int(r2))
	gdiff := float64(int(g1) - int(g2))
	bdiff := float64(int(b1) - int(b2))
	return math.Sqrt(rdiff*rdiff + gdiff*gdiff + bdiff*bdiff)
}
<commit_msg>remove log timestamp<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"log"
	"math"
	"os"

	"github.com/nfnt/resize"
)

const ANSIClear = "\033[0m"

func init() {
	log.SetFlags(0)
}

func main() {
	pad := flag.Bool("pad", false, "pad output on the left with whitespace")
	paletteName := flag.String("color", "256", "color palette (8, 256, gray, ...)")
	fontAspect := flag.Float64("fontaspect", 0.5, "aspect ratio (width/height)")
	flag.Parse()

	palette := ansiPalettes[*paletteName]
	if palette == nil {
		log.Fatalf("color palette not one of %q", ANSIPalettes())
	}

	if flag.NArg() < 1 {
		log.Fatal("missing filename")
	}
	if flag.NArg() > 1 {
		log.Fatal("unexpected arguments")
	}
	filename := flag.Arg(0)

	img, _, err := readImage(filename)
	if err != nil {
		log.Fatalf("image: %v", err)
	}
	sizenorm := normalSize(img.Bounds().Size(), *fontAspect)
	imgnorm := resize.Resize(uint(sizenorm.X), uint(sizenorm.Y), img, 0)
	err = writePixelsANSI(os.Stdout, imgnorm, palette, *pad)
	if err != nil {
		log.Fatalf("write: %v", err)
	}
}

func writePixelsANSI(w io.Writer, img image.Image, p ANSIPalette, pad bool) error {
	wbuf := bufio.NewWriter(w)
	rect := img.Bounds()
	size := rect.Size()
	for y := 0; y < size.Y; y++ {
		if pad {
			fmt.Fprint(wbuf, "  ")
		}
		for x := 0; x < size.X; x++ {
			color := img.At(rect.Min.X+x, rect.Min.Y+y)
			fmt.Fprint(wbuf, p.ANSI(color)+" ")
		}
		fmt.Fprintln(wbuf, ANSIClear)
	}
	return wbuf.Flush()
}

// readImage reads an image.Image from a specified file.
func readImage(filename string) (image.Image, string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, "", err
	}
	defer f.Close()

	img, format, err := image.Decode(f)
	if err != nil {
		return nil, "", err
	}
	return img, format, nil
}

// normalSize scales size according to aspect ratio fontAspect and returns the
// new size.
func normalSize(size image.Point, fontAspect float64) image.Point {
	aspect := float64(size.X) / float64(size.Y)
	norm := size
	norm.Y = size.Y
	w := float64(norm.Y) * aspect / fontAspect
	norm.X = int(round(w))
	return norm
}

// round x to the nearest integer biased toward +Inf.
func round(x float64) float64 {
	return math.Ceil(x - 0.5)
}

type ANSIPalette interface {
	ANSI(color.Color) string
}

var ansiPalettes = map[string]ANSIPalette{
	"256":       new(Palette256),
	"256-color": new(Palette256),
	"8":         DefaultPalette8,
	"8-color":   DefaultPalette8,
	"gray":      new(PaletteGray),
	"grayscale": new(PaletteGray),
	"grey":      new(PaletteGray),
	"greyscale": new(PaletteGray),
}

func ANSIPalettes() []string {
	var names []string
	for name := range ansiPalettes {
		names = append(names, name)
	}
	return names
}

// PaletteGray is an ANSIPalette that maps color.Color values to one of twenty
// four grayscale values.
type PaletteGray struct {
}

func (p *PaletteGray) ANSI(c color.Color) string {
	const begin = 0xe8
	const ratio = 24.0 / 255.0
	_, _, _, a := c.RGBA()
	if a == 0 {
		return ANSIClear
	}
	gray := color.GrayModel.Convert(c).(color.Gray).Y
	scaled := int(round(ratio * float64(gray)))
	return fmt.Sprintf("\033[48;5;%dm", scaled+begin)
}

// Palette8 is an ANSIPalette that maps color.Color values to one of 8 color
// indexes by minimizing euclidean RGB distance.
type Palette8 [8][3]uint8

var DefaultPalette8 = &Palette8{
	{0, 0, 0},       // black
	{191, 25, 25},   // red
	{25, 184, 25},   // green
	{188, 110, 25},  // orange/brown/yellow
	{25, 25, 184},   // blue
	{186, 25, 186},  // magenta
	{25, 187, 187},  // cyan
	{178, 178, 178}, // gray
}

func (p *Palette8) ANSI(c color.Color) string {
	_, _, _, a := c.RGBA()
	if a == 0 {
		return ANSIClear
	}
	min := math.Inf(1) // minimum distance from c
	var imin int       // minimizing index
	for i, rgb := range *p {
		other := color.RGBA{rgb[0], rgb[1], rgb[2], 0}
		dist := Distance(other, c)
		if dist < min {
			min = dist
			imin = i
		}
	}
	return fmt.Sprintf("\033[4%dm", imin)
}

// Palette256 is an ANSIPalette that maps color.Color to one of 256 RGB colors.
type Palette256 struct {
}

func (p *Palette256) ANSI(c color.Color) string {
	const begin = 16
	const ratio = 5.0 / (1<<16 - 1)
	rf, gf, bf, af := c.RGBA()
	if af == 0 {
		return ANSIClear
	}
	r := int(round(ratio * float64(rf)))
	g := int(round(ratio * float64(gf)))
	b := int(round(ratio * float64(bf)))
	return fmt.Sprintf("\033[48;5;%dm", r*6*6+g*6+b+begin)
}

// Distance computes euclidean distance between the RGB values of c1 and c2.
func Distance(c1, c2 color.Color) float64 {
	r1, g1, b1, _ := c1.RGBA()
	r2, g2, b2, _ := c2.RGBA()
	rdiff := float64(int(r1) - int(r2))
	gdiff := float64(int(g1) - int(g2))
	bdiff := float64(int(b1) - int(b2))
	return math.Sqrt(rdiff*rdiff + gdiff*gdiff + bdiff*bdiff)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"text/template"

	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/random"
	tc "github.com/flynn/flynn/test/cluster"
	"github.com/flynn/flynn/updater/types"
	c "github.com/flynn/go-check"
)

type ReleaseSuite struct {
	Helper
}

var _ = c.ConcurrentSuite(&ReleaseSuite{})

func (s *ReleaseSuite) addReleaseHosts(t *c.C) *tc.BootResult {
	res, err := testCluster.AddReleaseHosts()
	t.Assert(err, c.IsNil)
	t.Assert(res.Instances, c.HasLen, 4)
	return res
}

var releaseScript = template.Must(template.New("release-script").Parse(`
export DISCOVERD="{{ .Discoverd }}"
export TUF_TARGETS_PASSPHRASE="flynn-test"
export TUF_SNAPSHOT_PASSPHRASE="flynn-test"
export TUF_TIMESTAMP_PASSPHRASE="flynn-test"
export GOPATH=~/go

ROOT="${GOPATH}/src/github.com/flynn/flynn"
cd "${ROOT}"

# send all output to stderr so only images.json is output to stdout
(
  # update the TUF root keys and create new image manifests for each released
  # image by updating entrypoints
  curl -fsSLo jq "https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64"
  chmod +x jq
  ./jq \
    --argjson root_keys       "$(tuf --dir test/release root-keys)" \
    --argjson released_images "$(jq --compact-output 'keys | reduce .[] as $name ({}; .[$name] = true)' build/manifests/images.json)" \
    '.tuf.root_keys = $root_keys | .images = (.images | map(if (.id | in($released_images)) then .entrypoint.env = {"FOO":"BAR"} else . end))' \
    builder/manifest.json \
    > /tmp/manifest.json
  mv /tmp/manifest.json builder/manifest.json

  # build new images and binaries
  FLYNN_VERSION="v20161108.0-test"
  script/build-flynn --host "{{ .HostID }}" --version "${FLYNN_VERSION}"

  # release components
  script/export-components --host "{{ .HostID }}" "${ROOT}/test/release"
  script/release-channel --tuf-dir "${ROOT}/test/release" --no-sync --no-changelog "stable" "${FLYNN_VERSION}"

  # create a slug for testing slug based app updates
  build/bin/flynn-host run \
    --volume /tmp \
    build/image/slugbuilder.json \
    /usr/bin/env \
    CONTROLLER_KEY="{{ .ControllerKey }}" \
    SLUG_IMAGE_ID="{{ .SlugImageID }}" \
    /builder/build.sh \
    < <(tar c -C test/apps/http .)

  # serve the TUF repository over HTTP
  dir="$(mktemp --directory)"
  ln -s "${ROOT}/test/release/repository" "${dir}/tuf"
  ln -s "${ROOT}/script/install-flynn" "${dir}/install-flynn"
  sudo start-stop-daemon \
    --start \
    --background \
    --chdir "${dir}" \
    --exec "${ROOT}/build/bin/flynn-test-file-server"

) </dev/null >&2

cat "${ROOT}/build/manifests/images.json"
`))

var installScript = template.Must(template.New("install-script").Parse(`
# download to a tmp file so the script fails on download error rather than
# executing nothing and succeeding
curl -sL --fail http://{{ .Blobstore }}/install-flynn > /tmp/install-flynn
bash -e /tmp/install-flynn -r "http://{{ .Blobstore }}"
`))

var updateScript = template.Must(template.New("update-script").Parse(`
timeout --signal=QUIT --kill-after=10 10m bash -ex <<-SCRIPT
cd ~/go/src/github.com/flynn/flynn
tuf --dir test/release root-keys | tuf-client init --store /tmp/tuf.db http://{{ .Blobstore }}/tuf
echo stable | sudo tee /etc/flynn/channel.txt
export DISCOVERD="{{ .Discoverd }}"
build/bin/flynn-host update --repository http://{{ .Blobstore }}/tuf --tuf-db /tmp/tuf.db
SCRIPT
`))

func (s *ReleaseSuite) TestReleaseImages(t *c.C) {
	if testCluster == nil {
		t.Skip("cannot boot release cluster")
	}

	// stream script output to t.Log
	logWriter := debugLogWriter(t)

	// boot the release cluster, release components to a blobstore and output the new images.json
	releaseCluster := s.addReleaseHosts(t)
	buildHost := releaseCluster.Instances[0]
	var imagesJSON bytes.Buffer
	var script bytes.Buffer
	slugImageID := random.UUID()
	releaseScript.Execute(&script, struct {
		Discoverd, HostID, HostIP, ControllerKey, SlugImageID string
	}{fmt.Sprintf("http://%s:1111", buildHost.IP), buildHost.ID, buildHost.IP, releaseCluster.ControllerKey, slugImageID})
	t.Assert(buildHost.Run("bash -ex", &tc.Streams{Stdin: &script, Stdout: &imagesJSON, Stderr: logWriter}), c.IsNil)
	var images map[string]*ct.Artifact
	t.Assert(json.Unmarshal(imagesJSON.Bytes(), &images), c.IsNil)

	// install Flynn from the blobstore on the vanilla host
	blobstoreAddr := buildHost.IP + ":8080"
	installHost := releaseCluster.Instances[3]
	script.Reset()
	installScript.Execute(&script, map[string]string{"Blobstore": blobstoreAddr})
	var installOutput bytes.Buffer
	out := io.MultiWriter(logWriter, &installOutput)
	t.Assert(installHost.Run("sudo bash -ex", &tc.Streams{Stdin: &script, Stdout: out, Stderr: out}), c.IsNil)

	// check the flynn-host version is correct
	var hostVersion bytes.Buffer
	t.Assert(installHost.Run("flynn-host version", &tc.Streams{Stdout: &hostVersion}), c.IsNil)
	t.Assert(strings.TrimSpace(hostVersion.String()), c.Equals, "v20161108.0-test")

	// check rebuilt images were downloaded
	assertInstallOutput := func(format string, v ...interface{}) {
		expected := fmt.Sprintf(format, v...)
		if !strings.Contains(installOutput.String(), expected) {
			t.Fatalf(`expected install to output %q`, expected)
		}
	}
	for name, image := range images {
		assertInstallOutput("pulling %s image", name)
		for _, layer := range image.Manifest().Rootfs[0].Layers {
			assertInstallOutput("pulling %s layer %s", name, layer.ID)
		}
	}

	// installing on an instance with Flynn running should fail
	script.Reset()
	installScript.Execute(&script, map[string]string{"Blobstore": blobstoreAddr})
	installOutput.Reset()
	err := buildHost.Run("sudo bash -ex", &tc.Streams{Stdin: &script, Stdout: out, Stderr: out})
	if err == nil || !strings.Contains(installOutput.String(), "ERROR: Flynn is already installed.") {
		t.Fatal("expected Flynn install to fail but it didn't")
	}

	// create a controller client for the release cluster
	pin, err := base64.StdEncoding.DecodeString(releaseCluster.ControllerPin)
	t.Assert(err, c.IsNil)
	client, err := controller.NewClientWithConfig(
		"https://"+buildHost.IP,
		releaseCluster.ControllerKey,
		controller.Config{Pin: pin, Domain: releaseCluster.ControllerDomain},
	)
	t.Assert(err, c.IsNil)

	// deploy a slug based app + Redis resource
	slugApp := &ct.App{}
	t.Assert(client.CreateApp(slugApp), c.IsNil)
	gitreceive, err := client.GetAppRelease("gitreceive")
	t.Assert(err, c.IsNil)
	imageArtifact, err := client.GetArtifact(gitreceive.Env["SLUGRUNNER_IMAGE_ID"])
	t.Assert(err, c.IsNil)
	slugArtifact, err := client.GetArtifact(slugImageID)
	t.Assert(err, c.IsNil)
	resource, err := client.ProvisionResource(&ct.ResourceReq{ProviderID: "redis", Apps: []string{slugApp.ID}})
	t.Assert(err, c.IsNil)
	release := &ct.Release{
		ArtifactIDs: []string{imageArtifact.ID, slugArtifact.ID},
		Processes:   map[string]ct.ProcessType{"web": {Args: []string{"/runner/init", "bin/http"}}},
		Meta:        map[string]string{"git": "true"},
		Env:         resource.Env,
	}
	t.Assert(client.CreateRelease(slugApp.ID, release), c.IsNil)
	t.Assert(client.SetAppRelease(slugApp.ID, release.ID), c.IsNil)
	watcher, err := client.WatchJobEvents(slugApp.ID, release.ID)
	t.Assert(err, c.IsNil)
	defer watcher.Close()
	t.Assert(client.PutFormation(&ct.Formation{
		AppID:     slugApp.ID,
		ReleaseID: release.ID,
		Processes: map[string]int{"web": 1},
	}), c.IsNil)
	err = watcher.WaitFor(ct.JobEvents{"web": {ct.JobStateUp: 1}}, scaleTimeout, nil)
	t.Assert(err, c.IsNil)

	// run a cluster update from the blobstore
	updateHost := releaseCluster.Instances[1]
	script.Reset()
	updateScript.Execute(&script, map[string]string{"Blobstore": blobstoreAddr, "Discoverd": updateHost.IP + ":1111"})
	var updateOutput bytes.Buffer
	out = io.MultiWriter(logWriter, &updateOutput)
	t.Assert(updateHost.Run("bash -ex", &tc.Streams{Stdin: &script, Stdout: out, Stderr: out}), c.IsNil)

	// check rebuilt images were downloaded
	for name := range images {
		for _, host := range releaseCluster.Instances[0:2] {
			expected := fmt.Sprintf(`"pulling %s image" host=%s`, name, host.ID)
			if !strings.Contains(updateOutput.String(), expected) {
				t.Fatalf(`expected update to download %s on host %s`, name, host.ID)
			}
		}
	}

	assertImage := func(uri, image string) {
		t.Assert(uri, c.Equals, images[image].URI)
	}

	// check system apps were deployed correctly
	for _, app := range updater.SystemApps {
		if app.ImageOnly {
			continue // we don't deploy ImageOnly updates
		}
		debugf(t, "checking new %s release is using image %s", app.Name, images[app.Name].URI)
		expected := fmt.Sprintf(`"finished deploy of system app" name=%s`, app.Name)
		if !strings.Contains(updateOutput.String(), expected) {
			t.Fatalf(`expected update to deploy %s`, app.Name)
		}
		release, err := client.GetAppRelease(app.Name)
		t.Assert(err, c.IsNil)
		debugf(t, "new %s release ID: %s", app.Name, release.ID)
		artifact, err := client.GetArtifact(release.ArtifactIDs[0])
		t.Assert(err, c.IsNil)
		debugf(t, "new %s artifact: ID: %s, URI: %s", app.Name, artifact.ID, artifact.URI)
		assertImage(artifact.URI, app.Name)
	}

	// check gitreceive has the correct slug env vars
	gitreceive, err = client.GetAppRelease("gitreceive")
	t.Assert(err, c.IsNil)
	for _, name := range []string{"slugbuilder", "slugrunner"} {
		artifact, err := client.GetArtifact(gitreceive.Env[strings.ToUpper(name)+"_IMAGE_ID"])
		t.Assert(err, c.IsNil)
		assertImage(artifact.URI, name)
	}

	// check slug based app was deployed correctly
	release, err = client.GetAppRelease(slugApp.Name)
	t.Assert(err, c.IsNil)
	imageArtifact, err = client.GetArtifact(release.ArtifactIDs[0])
	t.Assert(err, c.IsNil)
	assertImage(imageArtifact.URI, "slugrunner")

	// check Redis app was deployed correctly
	release, err = client.GetAppRelease(resource.Env["FLYNN_REDIS"])
	t.Assert(err, c.IsNil)
	imageArtifact, err = client.GetArtifact(release.ArtifactIDs[0])
	t.Assert(err, c.IsNil)
	assertImage(imageArtifact.URI, "redis")
}
<commit_msg>test: Fix version in TestReleaseImages<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"text/template"

	"github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/pkg/random"
	tc "github.com/flynn/flynn/test/cluster"
	"github.com/flynn/flynn/updater/types"
	c "github.com/flynn/go-check"
)

type ReleaseSuite struct {
	Helper
}

var _ = c.ConcurrentSuite(&ReleaseSuite{})

func (s *ReleaseSuite) addReleaseHosts(t *c.C) *tc.BootResult {
	res, err := testCluster.AddReleaseHosts()
	t.Assert(err, c.IsNil)
	t.Assert(res.Instances, c.HasLen, 4)
	return res
}

var releaseScript = template.Must(template.New("release-script").Parse(`
export DISCOVERD="{{ .Discoverd }}"
export TUF_TARGETS_PASSPHRASE="flynn-test"
export TUF_SNAPSHOT_PASSPHRASE="flynn-test"
export TUF_TIMESTAMP_PASSPHRASE="flynn-test"
export GOPATH=~/go

ROOT="${GOPATH}/src/github.com/flynn/flynn"
cd "${ROOT}"

# send all output to stderr so only images.json is output to stdout
(
  # update the TUF root keys and create new image manifests for each released
  # image by updating entrypoints
  curl -fsSLo jq "https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64"
  chmod +x jq
  ./jq \
    --argjson root_keys       "$(tuf --dir test/release root-keys)" \
    --argjson released_images "$(jq --compact-output 'keys | reduce .[] as $name ({}; .[$name] = true)' build/manifests/images.json)" \
    '.tuf.root_keys = $root_keys | .images = (.images | map(if (.id | in($released_images)) then .entrypoint.env = {"FOO":"BAR"} else . end))' \
    builder/manifest.json \
    > /tmp/manifest.json
  mv /tmp/manifest.json builder/manifest.json

  # build new images and binaries
  FLYNN_VERSION="v20161108.0.test"
  script/build-flynn --host "{{ .HostID }}" --version "${FLYNN_VERSION}"

  # release components
  script/export-components --host "{{ .HostID }}" "${ROOT}/test/release"
  script/release-channel --tuf-dir "${ROOT}/test/release" --no-sync --no-changelog "stable" "${FLYNN_VERSION}"

  # create a slug for testing slug based app updates
  build/bin/flynn-host run \
    --volume /tmp \
    build/image/slugbuilder.json \
    /usr/bin/env \
    CONTROLLER_KEY="{{ .ControllerKey }}" \
    SLUG_IMAGE_ID="{{ .SlugImageID }}" \
    /builder/build.sh \
    < <(tar c -C test/apps/http .)

  # serve the TUF repository over HTTP
  dir="$(mktemp --directory)"
  ln -s "${ROOT}/test/release/repository" "${dir}/tuf"
  ln -s "${ROOT}/script/install-flynn" "${dir}/install-flynn"
  sudo start-stop-daemon \
    --start \
    --background \
    --chdir "${dir}" \
    --exec "${ROOT}/build/bin/flynn-test-file-server"

) </dev/null >&2

cat "${ROOT}/build/manifests/images.json"
`))

var installScript = template.Must(template.New("install-script").Parse(`
# download to a tmp file so the script fails on download error rather than
# executing nothing and succeeding
curl -sL --fail http://{{ .Blobstore }}/install-flynn > /tmp/install-flynn
bash -e /tmp/install-flynn -r "http://{{ .Blobstore }}"
`))

var updateScript = template.Must(template.New("update-script").Parse(`
timeout --signal=QUIT --kill-after=10 10m bash -ex <<-SCRIPT
cd ~/go/src/github.com/flynn/flynn
tuf --dir test/release root-keys | tuf-client init --store /tmp/tuf.db http://{{ .Blobstore }}/tuf
echo stable | sudo tee /etc/flynn/channel.txt
export DISCOVERD="{{ .Discoverd }}"
build/bin/flynn-host update --repository http://{{ .Blobstore }}/tuf --tuf-db /tmp/tuf.db
SCRIPT
`))

func (s *ReleaseSuite) TestReleaseImages(t *c.C) {
	if testCluster == nil {
		t.Skip("cannot boot release cluster")
	}

	// stream script output to t.Log
	logWriter := debugLogWriter(t)

	// boot the release cluster, release components to a blobstore and output the new images.json
	releaseCluster := s.addReleaseHosts(t)
	buildHost := releaseCluster.Instances[0]
	var imagesJSON bytes.Buffer
	var script bytes.Buffer
	slugImageID := random.UUID()
	releaseScript.Execute(&script, struct {
		Discoverd, HostID, HostIP, ControllerKey, SlugImageID string
	}{fmt.Sprintf("http://%s:1111", buildHost.IP), buildHost.ID, buildHost.IP, releaseCluster.ControllerKey, slugImageID})
	t.Assert(buildHost.Run("bash -ex", &tc.Streams{Stdin: &script, Stdout: &imagesJSON, Stderr: logWriter}), c.IsNil)
	var images map[string]*ct.Artifact
	t.Assert(json.Unmarshal(imagesJSON.Bytes(), &images), c.IsNil)

	// install Flynn from the blobstore on the vanilla host
	blobstoreAddr := buildHost.IP + ":8080"
	installHost := releaseCluster.Instances[3]
	script.Reset()
	installScript.Execute(&script, map[string]string{"Blobstore": blobstoreAddr})
	var installOutput bytes.Buffer
	out := io.MultiWriter(logWriter, &installOutput)
	t.Assert(installHost.Run("sudo bash -ex", &tc.Streams{Stdin: &script, Stdout: out, Stderr: out}), c.IsNil)

	// check the flynn-host version is correct
	var hostVersion bytes.Buffer
	t.Assert(installHost.Run("flynn-host version", &tc.Streams{Stdout: &hostVersion}), c.IsNil)
	t.Assert(strings.TrimSpace(hostVersion.String()), c.Equals, "v20161108.0.test")

	// check rebuilt images were downloaded
	assertInstallOutput := func(format string, v ...interface{}) {
		expected := fmt.Sprintf(format, v...)
		if !strings.Contains(installOutput.String(), expected) {
			t.Fatalf(`expected install to output %q`, expected)
		}
	}
	for name, image := range images {
		assertInstallOutput("pulling %s image", name)
		for _, layer := range image.Manifest().Rootfs[0].Layers {
			assertInstallOutput("pulling %s layer %s", name, layer.ID)
		}
	}

	// installing on an instance with Flynn running should fail
	script.Reset()
	installScript.Execute(&script, map[string]string{"Blobstore": blobstoreAddr})
	installOutput.Reset()
	err := buildHost.Run("sudo bash -ex", &tc.Streams{Stdin: &script, Stdout: out, Stderr: out})
	if err == nil || !strings.Contains(installOutput.String(), "ERROR: Flynn is already installed.") {
		t.Fatal("expected Flynn install to fail but it didn't")
	}

	// create a controller client for the release cluster
	pin, err := base64.StdEncoding.DecodeString(releaseCluster.ControllerPin)
	t.Assert(err, c.IsNil)
	client, err := controller.NewClientWithConfig(
		"https://"+buildHost.IP,
		releaseCluster.ControllerKey,
		controller.Config{Pin: pin, Domain: releaseCluster.ControllerDomain},
	)
	t.Assert(err, c.IsNil)

	// deploy a slug based app + Redis resource
	slugApp := &ct.App{}
	t.Assert(client.CreateApp(slugApp), c.IsNil)
	gitreceive, err := client.GetAppRelease("gitreceive")
	t.Assert(err, c.IsNil)
	imageArtifact, err := client.GetArtifact(gitreceive.Env["SLUGRUNNER_IMAGE_ID"])
	t.Assert(err, c.IsNil)
	slugArtifact, err := client.GetArtifact(slugImageID)
	t.Assert(err, c.IsNil)
	resource, err := client.ProvisionResource(&ct.ResourceReq{ProviderID: "redis", Apps: []string{slugApp.ID}})
	t.Assert(err, c.IsNil)
	release := &ct.Release{
		ArtifactIDs: []string{imageArtifact.ID, slugArtifact.ID},
		Processes:   map[string]ct.ProcessType{"web": {Args: []string{"/runner/init", "bin/http"}}},
		Meta:        map[string]string{"git": "true"},
		Env:         resource.Env,
	}
	t.Assert(client.CreateRelease(slugApp.ID, release), c.IsNil)
	t.Assert(client.SetAppRelease(slugApp.ID, release.ID), c.IsNil)
	watcher, err := client.WatchJobEvents(slugApp.ID, release.ID)
	t.Assert(err, c.IsNil)
	defer watcher.Close()
	t.Assert(client.PutFormation(&ct.Formation{
		AppID:     slugApp.ID,
		ReleaseID: release.ID,
		Processes: map[string]int{"web": 1},
	}), c.IsNil)
	err = watcher.WaitFor(ct.JobEvents{"web": {ct.JobStateUp: 1}}, scaleTimeout, nil)
	t.Assert(err, c.IsNil)

	// run a cluster update from the blobstore
	updateHost := releaseCluster.Instances[1]
	script.Reset()
	updateScript.Execute(&script, map[string]string{"Blobstore": blobstoreAddr, "Discoverd": updateHost.IP + ":1111"})
	var updateOutput bytes.Buffer
	out = io.MultiWriter(logWriter, &updateOutput)
	t.Assert(updateHost.Run("bash -ex", &tc.Streams{Stdin: &script, Stdout: out, Stderr: out}), c.IsNil)

	// check rebuilt images were downloaded
	for name := range images {
		for _, host := range releaseCluster.Instances[0:2] {
			expected := fmt.Sprintf(`"pulling %s image" host=%s`, name, host.ID)
			if !strings.Contains(updateOutput.String(), expected) {
				t.Fatalf(`expected update to download %s on host %s`, name, host.ID)
			}
		}
	}

	assertImage := func(uri, image string) {
		t.Assert(uri, c.Equals, images[image].URI)
	}

	// check system apps were deployed correctly
	for _, app := range updater.SystemApps {
		if app.ImageOnly {
			continue // we don't deploy ImageOnly updates
		}
		debugf(t, "checking new %s release is using image %s", app.Name, images[app.Name].URI)
		expected := fmt.Sprintf(`"finished deploy of system app" name=%s`, app.Name)
		if !strings.Contains(updateOutput.String(), expected) {
			t.Fatalf(`expected update to deploy %s`, app.Name)
		}
		release, err := client.GetAppRelease(app.Name)
		t.Assert(err, c.IsNil)
		debugf(t, "new %s release ID: %s", app.Name, release.ID)
		artifact, err := client.GetArtifact(release.ArtifactIDs[0])
		t.Assert(err, c.IsNil)
		debugf(t, "new %s artifact: ID: %s, URI: %s", app.Name, artifact.ID, artifact.URI)
		assertImage(artifact.URI, app.Name)
	}

	// check gitreceive has the correct slug env vars
	gitreceive, err = client.GetAppRelease("gitreceive")
	t.Assert(err, c.IsNil)
	for _, name := range []string{"slugbuilder", "slugrunner"} {
		artifact, err := client.GetArtifact(gitreceive.Env[strings.ToUpper(name)+"_IMAGE_ID"])
		t.Assert(err, c.IsNil)
		assertImage(artifact.URI, name)
	}

	// check slug based app was deployed correctly
	release, err = client.GetAppRelease(slugApp.Name)
	t.Assert(err, c.IsNil)
	imageArtifact, err = client.GetArtifact(release.ArtifactIDs[0])
	t.Assert(err, c.IsNil)
	assertImage(imageArtifact.URI, "slugrunner")

	// check Redis app was deployed correctly
	release, err = client.GetAppRelease(resource.Env["FLYNN_REDIS"])
	t.Assert(err, c.IsNil)
	imageArtifact, err = client.GetArtifact(release.ArtifactIDs[0])
	t.Assert(err, c.IsNil)
	assertImage(imageArtifact.URI, "redis")
}
<|endoftext|>


<commit_before>//This was originally an assembler written by me, I am now bootstrapping Universal Code Translator with this.
package uct

import (
	"os"
	"bufio"
	"fmt"
	"strings"
	"io"
	"path/filepath"
	"errors"
	"strconv"
)

//Map of aliases, we store all replacements in this map.
var aliases map[string]string = make(map[string]string)
//Map of inline functions.
var inlines map[string]*InlineFunction = make(map[string]*InlineFunction)

var imported map[string]bool = make(map[string]bool)

//Alias hook, if we are creating an inline function then prepend the inline name.
func alias(name, value string) {
	if inlining != "" {
		aliases[inlining+"."+name] = value
	} else {
		aliases[name] = value
	}
}

//This is the inmemory structure of an inline function.
type InlineFunction struct {
	//These are the parameters the function takes.
	Aliases []string
	//This is the string of instructions.
	Instructions string
}

type NestedInlineFunction struct {
	*InlineFunction
	*bufio.Reader
	Name string
}

//An interface which assembles into machinecode.
type Assembler interface {

	//This should write any necessary headers to the binary.
	Header() []byte

	//This will assemble a instruction from text to binary.
	//All arguments are decimal numbers. $1 will be passed as 1.
	Assemble(string, []string) ([]byte, error)

	//This should write any necessary footers to the binary.
	Footer() []byte

	SetFileName(string)
}

var number = 0 				//The line number.
var instruction uint = 0 	//The instruction counter.
var assembler Assembler		//What assembler are we using?

//Output file.
var Output io.Writer
var comment string

var inlining string //Are we defining an inline function?

//TODO: make nested inlines.

var running int //Are we "running" an inline? LOL
var runningstack []NestedInlineFunction = make([]NestedInlineFunction, 20)

var inlinereader *bufio.Reader

type registeredAssembler struct {
	Flag *bool
	Ext string
	Comment string
	Assembler
}

var Assemblers []registeredAssembler

func RegisterAssembler(asm Assembler, flag *bool, ext, comment string) {
	Assemblers = append(Assemblers, registeredAssembler{Flag:flag, Ext:ext, Assembler: asm, Comment: comment})
}

func SetAssembler(asm registeredAssembler) {
	assembler = asm.Assembler
	comment = asm.Comment
}

func AssemblerReady() bool {
	return !(assembler == nil)
}

func SetFileName(name string) {
	assembler.SetFileName(name)
}

func Header() []byte {
	return assembler.Header()
}

func Footer() []byte {
	return assembler.Footer()
}

func Assemble(filename string) error {

	//Open main.s in the current directory and begin compilation.
	file, err := os.Open(filename)
	if err != nil {
		return errors.New("Could not find "+filename+" file!"+err.Error())
	}

	//Read the first line of the file and check the architecture.
	reader := bufio.NewReader(file)

	//Set our line number to 0.
	var number = 0

	//Loop through the lines of the file.
	for  {

		var line string

		if running == 0 {
			line, err = reader.ReadString('\n')
			if err == io.EOF {
				break
			} else if err != nil {
				return errors.New("Error reading file.. Is it courrupted? D: "+err.Error())
			}

			number++

		} else {

			line, err = runningstack[running].ReadString('\n')
			if err == io.EOF {

				running--
				continue

			} else if err != nil {
				return errors.New("Error reading file.. Is it courrupted? D: "+err.Error())
			}

		}

		//Figure out what the line is doing.
		//If the line is a comment, skip the line.
		if trim := strings.TrimSpace(line); len(trim) > 0 {
			if trim[0] == '#' {
				if comment != "" {
					Output.Write([]byte(strings.Replace(line, "#", comment+" ", 1)))
				}
				continue
			}
			line = trim
		} else {
			continue
		}

		//Split the line into space-sperated tokens.
		tokens := strings.Split(line, " ")
		if len(tokens) == 0 {
			continue
		}

		if inlining != "" {
			//If we are inlining a function and we hit .return then stop inlining and continue.
			if len(tokens[0]) > 0 && tokens[0] == ".return" {
				inlining = ""
				continue
			}
			inlines[inlining].Instructions += line+"\n"
			continue
		}

		//Detect labels.
		if len(tokens[0]) > 0 && tokens[0][len(tokens[0])-1] == ':' {

			var name string
			if filename != "main.s" {
				var extension = filepath.Ext(filename)
				name = filename[0:len(filename)-len(extension)]+"."
			}

			alias(name+tokens[0][:len(tokens[0])-1], fmt.Sprint(instruction))
			continue
		}

		//Resolve aliases.
		resolve:
		for i, token := range tokens {

			if token == "\\" && len(tokens) > i+1 && tokens[i+1] == "t" {
				token = "\t"
				tokens[i] = token
				 tokens[i+1] = ""
			}

			if token == "=" && len(tokens) > i+1 {
				tokens[i] = token+tokens[i+1]
				 tokens[i+1] = ""
			}

			if _, ok := Languages[tokens[0]]; tokens[0] == "DATA" || ok {
				continue
			}
			if len(token) == 0 {
				continue
			}
			if token != "," && token[len(token)-1] == ',' {
				token = token[:len(token)-1]
				tokens[i] = token
			}

			var extension = filepath.Ext(filename)
			var name = filepath.Base(filename[0:len(filename)-len(extension)])+"."

			var found bool

			if alias, ok := aliases[name+token]; ok {
				token = alias
				tokens[i] = alias
				found = true
				//output.Writeln("aliasing", token, alias)
			}
			if alias, ok := aliases[token]; ok {
				token = alias
				tokens[i] = alias
				found = true
				//output.Writeln("aliasing", token, alias)
			}

			if running > 0 {
				if alias, ok := aliases[runningstack[running].Name+"."+token]; ok {
					token = alias
					tokens[i] = alias
					found = true
					//output.Writeln("aliasing", token, alias)
				}
			}

			if inlining != "" {
				if alias, ok := aliases[inlining+"."+token]; ok {
					token = alias
					tokens[i] = alias
					found = true
					//output.Writeln("aliasing", token, alias)
				}
			}
			if len(token) > 0 && token[0] == '$' {
				token = token[1:]
				tokens[i] = token
			}

			if token == "=" && len(tokens) > i+1 && tokens[i+1] == "=" {
				token = "=="
				tokens[i] = token
			 	tokens[i+1] = ""
			 	continue
			}

			if len(token) > 2 && token[0] == '0' && token[1] == 'x' {
				var hex uint
				fmt.Sscanf(token[2:], "%x", &hex)
				//output.Writeln("converted hex", token ,"value to", hex)
				token = fmt.Sprint(hex)
				tokens[i] = token
			} else if len(token) > 2 && token[0] == '0' {
				var binary uint
				fmt.Sscanf(token[1:], "%b", &binary)
				//output.Writeln("converted hex", token ,"value to", hex)
				token = fmt.Sprint(binary)
				tokens[i] = token
			}

			//Blank aliases are ignored.
			if token == "_" {
				tokens = append(tokens[:i], tokens[i+1:]...)
				goto resolve
			}

			var v bool

			_, ok := Languages[tokens[0]]

			if !ok && len(token) > 1 && !found && tokens[0] != ".alias"  {
				for ii, c := range token {
					switch c {
						case '=':
							token = strings.Replace(token, string(c), "_eq_", -1)
							tokens[i] = token
							v = true
						case '+':
							token = strings.Replace(token, string(c), "_plus_", -1)
							tokens[i] = token
							v = true
						case '-':
							if _, err := strconv.Atoi(token); err != nil {
								token = strings.Replace(token, string(c), "_minus_", -1)
								tokens[i] = token
								v = true
							}
						case '/':
							token = strings.Replace(token, string(c), "_over_", -1)
							tokens[i] = token
							v = true
						case '*':
							token = strings.Replace(token, string(c), "_times_", -1)
							tokens[i] = token
							v = true
						case ')', '(':
							if ii != len(token)-1 {
								token = strings.Replace(token, string(c), "_l_", -1)
								tokens[i] = token
								v = true
							}
						case '<':
							 token = strings.Replace(token, string(c), "_lt_", -1)
							tokens[i] = token
							v = true
						case '>':
							token = strings.Replace(token, string(c), "_gt_", -1)
							tokens[i] = token
							v = true
						case '!':
							token = strings.Replace(token, string(c), "_not_", -1)
							tokens[i] = token
							v = true
						case '.':
							if i > 0 {
								token = strings.Replace(token, ".", "_", 1)
								tokens[i] = token
							}
					}
				}
				if v {
					if token[0] == '#' {
						//token = "#v_"+token[1:]
						tokens[i] = token
					} else {
						//token = "v_"+token
						tokens[i] = token
					}
				}

				//Something to do with keeping things lowercase
				//I don't know why this is important??

				/*if i > 0 && token != "ERROR" && token != strings.ToLower(token) {
					if token[0] == '#' {
						token = "#l_"+strings.ToLower(token)[1:]
						tokens[i] = token
					} else {
						token = "l_"+strings.ToLower(token)
						tokens[i] = token
					}
				}*/
			}
		}

		switch tokens[0] {

			//Import another file and assemble it.
			case ".import":
				if len(tokens) != 2 {
					return  errors.New(fmt.Sprint(number)+": Import needs a filename.")
				}
				if !imported[tokens[1]+".u"] {
					err := Assemble(tokens[1]+".u")
					if err != nil {
						return errors.New(filename+":"+err.Error())
					}
					imported[tokens[1]+".u"] = true
				}

			//This is why quasm is so powerful! EVERYTHING IS AN ALIAS!
			case ".var", ".alias", ".const", ".global":
				if len(tokens) != 3 {
					return  errors.New(fmt.Sprint(number)+": Alias decleration needs a name and a value padded with single spaces.")
				}
				var name string
				if filename != "main.s" && tokens[0] != ".global" {
					var extension = filepath.Ext(filename)
					name = filepath.Base(filename[0:len(filename)-len(extension)])+"."
				}
				alias(name+tokens[1], tokens[2])
			//Create placeholder aliases!
			case ".blank":
				if len(tokens) != 2 {
					return  errors.New(fmt.Sprint(number)+": Blank decleration needs a name")
				}
				var name string
				if filename != "main.s" {
					var extension = filepath.Ext(filename)
					name = filepath.Base(filename[0:len(filename)-len(extension)])+"."
				}
				alias(name+tokens[1], "_")
			//RUN INLINE FUNCTIONS WITH PARAMETERS 8)
			case ".run", ".":
				if len(tokens) < 2 {
					return  errors.New(fmt.Sprint(number)+": Run requires a label.")
				}
				if _, ok := inlines[tokens[1]]; ok {

					runningstack[running+1]=NestedInlineFunction{
						Name: tokens[1],
						InlineFunction: inlines[tokens[1]],
						Reader: bufio.NewReader(strings.NewReader(inlines[tokens[1]].Instructions)),
					}
					running++

					for i, aliasname := range inlines[tokens[1]].Aliases {
						if len(tokens)-1 < i {
							break
						}
						alias(tokens[1]+"."+aliasname, tokens[i])
					}
				} else {
					return  errors.New(fmt.Sprint(number)+": Could not find inline definition for "+tokens[1])
				}
			//Create inline functions!!! <3 <3 <3
			case ".inline":
				if len(tokens) < 2 {
					return  errors.New(fmt.Sprint(number)+": Inline decleration needs a name.")
				}
				inlining = tokens[1]
				inlines[inlining] = &InlineFunction{Aliases:tokens}

			//Use assembler to finally assemble commands to machine code.
			default:
				assembly, err := assembler.Assemble(tokens[0], tokens[1:])
				if err != nil {
					return  errors.New(fmt.Sprint(number)+": "+err.Error())
				}
				Output.Write(assembly)
				instruction++
		}
 	}
 	return nil
}
<commit_msg>Better inline support for languages.<commit_after>//This was originally an assembler written by me, I am now bootstrapping Universal Code Translator with this.
package uct

import (
	"os"
	"bufio"
	"fmt"
	"strings"
	"io"
	"path/filepath"
	"errors"
	"strconv"
)

//Map of aliases, we store all replacements in this map.
var aliases map[string]string = make(map[string]string)
//Map of inline functions.
var inlines map[string]*InlineFunction = make(map[string]*InlineFunction)

var imported map[string]bool = make(map[string]bool)

//Alias hook, if we are creating an inline function then prepend the inline name.
func alias(name, value string) {
	if inlining != "" {
		aliases[inlining+"."+name] = value
	} else {
		aliases[name] = value
	}
}

//This is the inmemory structure of an inline function.
type InlineFunction struct {
	//These are the parameters the function takes.
	Aliases []string
	//This is the string of instructions.
	Instructions string
}

type NestedInlineFunction struct {
	*InlineFunction
	*bufio.Reader
	Name string
}

//An interface which assembles into machinecode.
type Assembler interface {

	//This should write any necessary headers to the binary.
	Header() []byte

	//This will assemble a instruction from text to binary.
	//All arguments are decimal numbers. $1 will be passed as 1.
	Assemble(string, []string) ([]byte, error)

	//This should write any necessary footers to the binary.
	Footer() []byte

	SetFileName(string)
}

var number = 0 				//The line number.
var instruction uint = 0 	//The instruction counter.
var assembler Assembler		//What assembler are we using?

//Output file.
var Output io.Writer
var comment string

var inlining string //Are we defining an inline function?

//TODO: make nested inlines.

var running int //Are we "running" an inline? LOL
var runningstack []NestedInlineFunction = make([]NestedInlineFunction, 20)

var inlinereader *bufio.Reader

type registeredAssembler struct {
	Flag *bool
	Ext string
	Comment string
	Assembler
}

var Assemblers []registeredAssembler

func RegisterAssembler(asm Assembler, flag *bool, ext, comment string) {
	Assemblers = append(Assemblers, registeredAssembler{Flag:flag, Ext:ext, Assembler: asm, Comment: comment})
}

func SetAssembler(asm registeredAssembler) {
	assembler = asm.Assembler
	comment = asm.Comment
}

func AssemblerReady() bool {
	return !(assembler == nil)
}

func SetFileName(name string) {
	assembler.SetFileName(name)
}

func Header() []byte {
	return assembler.Header()
}

func Footer() []byte {
	return assembler.Footer()
}

func Assemble(filename string) error {

	//Open main.s in the current directory and begin compilation.
	file, err := os.Open(filename)
	if err != nil {
		return errors.New("Could not find "+filename+" file!"+err.Error())
	}

	//Read the first line of the file and check the architecture.
	reader := bufio.NewReader(file)

	//Set our line number to 0.
	var number = 0

	//Loop through the lines of the file.
	for  {

		var line string

		if running == 0 {
			line, err = reader.ReadString('\n')
			if err == io.EOF {
				break
			} else if err != nil {
				return errors.New("Error reading file.. Is it courrupted? D: "+err.Error())
			}

			number++

		} else {

			line, err = runningstack[running].ReadString('\n')
			if err == io.EOF {

				running--
				continue

			} else if err != nil {
				return errors.New("Error reading file.. Is it courrupted? D: "+err.Error())
			}

		}

		//Figure out what the line is doing.
		//If the line is a comment, skip the line.
		if trim := strings.TrimSpace(line); len(trim) > 0 {
			if trim[0] == '#' {
				if comment != "" {
					Output.Write([]byte(strings.Replace(line, "#", comment+" ", 1)))
				}
				continue
			}
			line = trim
		} else {
			continue
		}

		//Split the line into space-sperated tokens.
		tokens := strings.Split(line, " ")
		if len(tokens) == 0 {
			continue
		}

		if inlining != "" {
			//If we are inlining a function and we hit .return then stop inlining and continue.
			if len(tokens[0]) > 0 && tokens[0] == ".return" {
				inlining = ""
				continue
			}
			inlines[inlining].Instructions += line+"\n"
			continue
		}

		if _, ok := Languages[tokens[0]]; ok {
			assembly, err := assembler.Assemble(tokens[0], []string{line[len(tokens[0]):]})
			if err != nil {
				return  errors.New(fmt.Sprint(number)+": "+err.Error())
			}
			Output.Write(assembly)
			instruction++
			continue
		}

		//Detect labels.
		if len(tokens[0]) > 0 && tokens[0][len(tokens[0])-1] == ':' {

			var name string
			if filename != "main.s" {
				var extension = filepath.Ext(filename)
				name = filename[0:len(filename)-len(extension)]+"."
			}

			alias(name+tokens[0][:len(tokens[0])-1], fmt.Sprint(instruction))
			continue
		}

		//Resolve aliases.
		resolve:
		for i, token := range tokens {

			if token == "\\" && len(tokens) > i+1 && tokens[i+1] == "t" {
				token = "\t"
				tokens[i] = token
				 tokens[i+1] = ""
			}

			if token == "=" && len(tokens) > i+1 {
				tokens[i] = token+tokens[i+1]
				 tokens[i+1] = ""
			}

			if _, ok := Languages[tokens[0]]; tokens[0] == "DATA" || ok {
				continue
			}
			if len(token) == 0 {
				continue
			}
			if token != "," && token[len(token)-1] == ',' {
				token = token[:len(token)-1]
				tokens[i] = token
			}

			var extension = filepath.Ext(filename)
			var name = filepath.Base(filename[0:len(filename)-len(extension)])+"."

			var found bool

			if alias, ok := aliases[name+token]; ok {
				token = alias
				tokens[i] = alias
				found = true
				//output.Writeln("aliasing", token, alias)
			}
			if alias, ok := aliases[token]; ok {
				token = alias
				tokens[i] = alias
				found = true
				//output.Writeln("aliasing", token, alias)
			}

			if running > 0 {
				if alias, ok := aliases[runningstack[running].Name+"."+token]; ok {
					token = alias
					tokens[i] = alias
					found = true
					//output.Writeln("aliasing", token, alias)
				}
			}

			if inlining != "" {
				if alias, ok := aliases[inlining+"."+token]; ok {
					token = alias
					tokens[i] = alias
					found = true
					//output.Writeln("aliasing", token, alias)
				}
			}
			if len(token) > 0 && token[0] == '$' {
				token = token[1:]
				tokens[i] = token
			}

			if token == "=" && len(tokens) > i+1 && tokens[i+1] == "=" {
				token = "=="
				tokens[i] = token
			 	tokens[i+1] = ""
			 	continue
			}

			if len(token) > 2 && token[0] == '0' && token[1] == 'x' {
				var hex uint
				fmt.Sscanf(token[2:], "%x", &hex)
				//output.Writeln("converted hex", token ,"value to", hex)
				token = fmt.Sprint(hex)
				tokens[i] = token
			} else if len(token) > 2 && token[0] == '0' {
				var binary uint
				fmt.Sscanf(token[1:], "%b", &binary)
				//output.Writeln("converted hex", token ,"value to", hex)
				token = fmt.Sprint(binary)
				tokens[i] = token
			}

			//Blank aliases are ignored.
			if token == "_" {
				tokens = append(tokens[:i], tokens[i+1:]...)
				goto resolve
			}

			var v bool

			_, ok := Languages[tokens[0]]

			if !ok && len(token) > 1 && !found && tokens[0] != ".alias"  {
				for ii, c := range token {
					switch c {
						case '=':
							token = strings.Replace(token, string(c), "_eq_", -1)
							tokens[i] = token
							v = true
						case '+':
							token = strings.Replace(token, string(c), "_plus_", -1)
							tokens[i] = token
							v = true
						case '-':
							if _, err := strconv.Atoi(token); err != nil {
								token = strings.Replace(token, string(c), "_minus_", -1)
								tokens[i] = token
								v = true
							}
						case '/':
							token = strings.Replace(token, string(c), "_over_", -1)
							tokens[i] = token
							v = true
						case '*':
							token = strings.Replace(token, string(c), "_times_", -1)
							tokens[i] = token
							v = true
						case ')', '(':
							if ii != len(token)-1 {
								token = strings.Replace(token, string(c), "_l_", -1)
								tokens[i] = token
								v = true
							}
						case '<':
							 token = strings.Replace(token, string(c), "_lt_", -1)
							tokens[i] = token
							v = true
						case '>':
							token = strings.Replace(token, string(c), "_gt_", -1)
							tokens[i] = token
							v = true
						case '!':
							token = strings.Replace(token, string(c), "_not_", -1)
							tokens[i] = token
							v = true
						case '.':
							if i > 0 {
								token = strings.Replace(token, ".", "_", 1)
								tokens[i] = token
							}
					}
				}
				if v {
					if token[0] == '#' {
						//token = "#v_"+token[1:]
						tokens[i] = token
					} else {
						//token = "v_"+token
						tokens[i] = token
					}
				}

				//Something to do with keeping things lowercase
				//I don't know why this is important??

				/*if i > 0 && token != "ERROR" && token != strings.ToLower(token) {
					if token[0] == '#' {
						token = "#l_"+strings.ToLower(token)[1:]
						tokens[i] = token
					} else {
						token = "l_"+strings.ToLower(token)
						tokens[i] = token
					}
				}*/
			}
		}

		switch tokens[0] {

			//Import another file and assemble it.
			case ".import":
				if len(tokens) != 2 {
					return  errors.New(fmt.Sprint(number)+": Import needs a filename.")
				}
				if !imported[tokens[1]+".u"] {
					err := Assemble(tokens[1]+".u")
					if err != nil {
						return errors.New(filename+":"+err.Error())
					}
					imported[tokens[1]+".u"] = true
				}

			//This is why quasm is so powerful! EVERYTHING IS AN ALIAS!
			case ".var", ".alias", ".const", ".global":
				if len(tokens) != 3 {
					return  errors.New(fmt.Sprint(number)+": Alias decleration needs a name and a value padded with single spaces.")
				}
				var name string
				if filename != "main.s" && tokens[0] != ".global" {
					var extension = filepath.Ext(filename)
					name = filepath.Base(filename[0:len(filename)-len(extension)])+"."
				}
				alias(name+tokens[1], tokens[2])
			//Create placeholder aliases!
			case ".blank":
				if len(tokens) != 2 {
					return  errors.New(fmt.Sprint(number)+": Blank decleration needs a name")
				}
				var name string
				if filename != "main.s" {
					var extension = filepath.Ext(filename)
					name = filepath.Base(filename[0:len(filename)-len(extension)])+"."
				}
				alias(name+tokens[1], "_")
			//RUN INLINE FUNCTIONS WITH PARAMETERS 8)
			case ".run", ".":
				if len(tokens) < 2 {
					return  errors.New(fmt.Sprint(number)+": Run requires a label.")
				}
				if _, ok := inlines[tokens[1]]; ok {

					runningstack[running+1]=NestedInlineFunction{
						Name: tokens[1],
						InlineFunction: inlines[tokens[1]],
						Reader: bufio.NewReader(strings.NewReader(inlines[tokens[1]].Instructions)),
					}
					running++

					for i, aliasname := range inlines[tokens[1]].Aliases {
						if len(tokens)-1 < i {
							break
						}
						alias(tokens[1]+"."+aliasname, tokens[i])
					}
				} else {
					return  errors.New(fmt.Sprint(number)+": Could not find inline definition for "+tokens[1])
				}
			//Create inline functions!!! <3 <3 <3
			case ".inline":
				if len(tokens) < 2 {
					return  errors.New(fmt.Sprint(number)+": Inline decleration needs a name.")
				}
				inlining = tokens[1]
				inlines[inlining] = &InlineFunction{Aliases:tokens}

			//Use assembler to finally assemble commands to machine code.
			default:
				assembly, err := assembler.Assemble(tokens[0], tokens[1:])
				if err != nil {
					return  errors.New(fmt.Sprint(number)+": "+err.Error())
				}
				Output.Write(assembly)
				instruction++
		}
 	}
 	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/configservice"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func init() {
	resource.AddTestSweepers("aws_config_configuration_aggregator", &resource.Sweeper{
		Name: "aws_config_configuration_aggregator",
		F:    testSweepConfigConfigurationAggregators,
	})
}

func testSweepConfigConfigurationAggregators(region string) error {
	client, err := sharedClientForRegion(region)
	if err != nil {
		return fmt.Errorf("Error getting client: %s", err)
	}
	conn := client.(*AWSClient).configconn

	resp, err := conn.DescribeConfigurationAggregators(&configservice.DescribeConfigurationAggregatorsInput{})
	if err != nil {
		if testSweepSkipSweepError(err) {
			log.Printf("[WARN] Skipping Config Configuration Aggregators sweep for %s: %s", region, err)
			return nil
		}
		return fmt.Errorf("Error retrieving config configuration aggregators: %s", err)
	}

	if len(resp.ConfigurationAggregators) == 0 {
		log.Print("[DEBUG] No config configuration aggregators to sweep")
		return nil
	}

	log.Printf("[INFO] Found %d config configuration aggregators", len(resp.ConfigurationAggregators))

	for _, agg := range resp.ConfigurationAggregators {
		log.Printf("[INFO] Deleting config configuration aggregator %s", *agg.ConfigurationAggregatorName)
		_, err := conn.DeleteConfigurationAggregator(&configservice.DeleteConfigurationAggregatorInput{
			ConfigurationAggregatorName: agg.ConfigurationAggregatorName,
		})

		if err != nil {
			return fmt.Errorf("Error deleting config configuration aggregator %s: %s", *agg.ConfigurationAggregatorName, err)
		}
	}

	return nil
}

func TestAccAWSConfigConfigurationAggregator_account(t *testing.T) {
	var ca configservice.ConfigurationAggregator
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_account(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.#", "1"),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.0.account_ids.#", "1"),
					testAccCheckResourceAttrAccountID(resourceName, "account_aggregation_source.0.account_ids.0"),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.0.regions.#", "1"),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.0.regions.0", "us-west-2"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSConfigConfigurationAggregator_organization(t *testing.T) {
	var ca configservice.ConfigurationAggregator
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_organization(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.#", "1"),
					resource.TestCheckResourceAttrPair(resourceName, "organization_aggregation_source.0.role_arn", "aws_iam_role.r", "arn"),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.0.all_regions", "true"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSConfigConfigurationAggregator_switch(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_account(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.#", "1"),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.#", "0"),
				),
			},
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_organization(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.#", "0"),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.#", "1"),
				),
			},
		},
	})
}

func TestAccAWSConfigConfigurationAggregator_tags(t *testing.T) {
	var ca configservice.ConfigurationAggregator
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_tags(rName, "foo", "bar", "fizz", "buzz"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "3"),
					resource.TestCheckResourceAttr(resourceName, "tags.Name", rName),
					resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar"),
					resource.TestCheckResourceAttr(resourceName, "tags.fizz", "buzz"),
				),
			},
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_tags(rName, "foo", "bar2", "fizz2", "buzz2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "3"),
					resource.TestCheckResourceAttr(resourceName, "tags.Name", rName),
					resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar2"),
					resource.TestCheckResourceAttr(resourceName, "tags.fizz2", "buzz2"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_account(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
		},
	})
}

func testAccCheckAWSConfigConfigurationAggregatorName(n, desired string, obj *configservice.ConfigurationAggregator) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}
		if rs.Primary.Attributes["name"] != *obj.ConfigurationAggregatorName {
			return fmt.Errorf("Expected name: %q, given: %q", desired, *obj.ConfigurationAggregatorName)
		}
		return nil
	}
}

func testAccCheckAWSConfigConfigurationAggregatorExists(n string, obj *configservice.ConfigurationAggregator) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not Found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No config configuration aggregator ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).configconn
		out, err := conn.DescribeConfigurationAggregators(&configservice.DescribeConfigurationAggregatorsInput{
			ConfigurationAggregatorNames: []*string{aws.String(rs.Primary.Attributes["name"])},
		})
		if err != nil {
			return fmt.Errorf("Failed to describe config configuration aggregator: %s", err)
		}
		if len(out.ConfigurationAggregators) < 1 {
			return fmt.Errorf("No config configuration aggregator found when describing %q", rs.Primary.Attributes["name"])
		}

		ca := out.ConfigurationAggregators[0]
		*obj = *ca

		return nil
	}
}

func testAccCheckAWSConfigConfigurationAggregatorDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).configconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_config_configuration_aggregator" {
			continue
		}

		resp, err := conn.DescribeConfigurationAggregators(&configservice.DescribeConfigurationAggregatorsInput{
			ConfigurationAggregatorNames: []*string{aws.String(rs.Primary.Attributes["name"])},
		})

		if err == nil {
			if len(resp.ConfigurationAggregators) != 0 &&
				*resp.ConfigurationAggregators[0].ConfigurationAggregatorName == rs.Primary.Attributes["name"] {
				return fmt.Errorf("config configuration aggregator still exists: %s", rs.Primary.Attributes["name"])
			}
		}
	}

	return nil
}

func testAccAWSConfigConfigurationAggregatorConfig_account(rName string) string {
	return fmt.Sprintf(`
resource "aws_config_configuration_aggregator" "example" {
  name = %[1]q

  account_aggregation_source {
    account_ids = [data.aws_caller_identity.current.account_id]
    regions     = ["us-west-2"]
  }
}

data "aws_caller_identity" "current" {
}
`, rName)
}

func testAccAWSConfigConfigurationAggregatorConfig_organization(rName string) string {
	return fmt.Sprintf(`
resource "aws_organizations_organization" "test" {
}

resource "aws_config_configuration_aggregator" "example" {
  depends_on = [aws_iam_role_policy_attachment.example]

  name = %[1]q

  organization_aggregation_source {
    all_regions = true
    role_arn    = aws_iam_role.example.arn
  }
}

resource "aws_iam_role" "example" {
  name = %[1]q

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "config.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "example" {
  role       = aws_iam_role.example.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations"
}
`, rName)
}

func testAccAWSConfigConfigurationAggregatorConfig_tags(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
	return fmt.Sprintf(`
resource "aws_config_configuration_aggregator" "example" {
  name = %[1]q

  account_aggregation_source {
    account_ids = [data.aws_caller_identity.current.account_id]
    regions     = ["us-west-2"]
  }

  tags = {
    Name = %[1]q

    %[2]s = %[3]q
    %[4]s = %[5]q
  }
}

data "aws_caller_identity" "current" {}
`, rName, tagKey1, tagValue1, tagKey2, tagValue2)
}
<commit_msg>tests/provider: Fix hardcoded (Config Config Aggregator)<commit_after>package aws

import (
	"fmt"
	"log"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/configservice"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func init() {
	resource.AddTestSweepers("aws_config_configuration_aggregator", &resource.Sweeper{
		Name: "aws_config_configuration_aggregator",
		F:    testSweepConfigConfigurationAggregators,
	})
}

func testSweepConfigConfigurationAggregators(region string) error {
	client, err := sharedClientForRegion(region)
	if err != nil {
		return fmt.Errorf("Error getting client: %s", err)
	}
	conn := client.(*AWSClient).configconn

	resp, err := conn.DescribeConfigurationAggregators(&configservice.DescribeConfigurationAggregatorsInput{})
	if err != nil {
		if testSweepSkipSweepError(err) {
			log.Printf("[WARN] Skipping Config Configuration Aggregators sweep for %s: %s", region, err)
			return nil
		}
		return fmt.Errorf("Error retrieving config configuration aggregators: %s", err)
	}

	if len(resp.ConfigurationAggregators) == 0 {
		log.Print("[DEBUG] No config configuration aggregators to sweep")
		return nil
	}

	log.Printf("[INFO] Found %d config configuration aggregators", len(resp.ConfigurationAggregators))

	for _, agg := range resp.ConfigurationAggregators {
		log.Printf("[INFO] Deleting config configuration aggregator %s", *agg.ConfigurationAggregatorName)
		_, err := conn.DeleteConfigurationAggregator(&configservice.DeleteConfigurationAggregatorInput{
			ConfigurationAggregatorName: agg.ConfigurationAggregatorName,
		})

		if err != nil {
			return fmt.Errorf("Error deleting config configuration aggregator %s: %s", *agg.ConfigurationAggregatorName, err)
		}
	}

	return nil
}

func TestAccAWSConfigConfigurationAggregator_account(t *testing.T) {
	var ca configservice.ConfigurationAggregator
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_account(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.#", "1"),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.0.account_ids.#", "1"),
					testAccCheckResourceAttrAccountID(resourceName, "account_aggregation_source.0.account_ids.0"),
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.0.regions.#", "1"),
					resource.TestCheckResourceAttrPair(resourceName, "account_aggregation_source.0.regions.0", "data.aws_region.current", "name"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSConfigConfigurationAggregator_organization(t *testing.T) {
	var ca configservice.ConfigurationAggregator
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_organization(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.#", "1"),
					resource.TestCheckResourceAttrPair(resourceName, "organization_aggregation_source.0.role_arn", "aws_iam_role.r", "arn"),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.0.all_regions", "true"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func TestAccAWSConfigConfigurationAggregator_switch(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccOrganizationsAccountPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_account(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.#", "1"),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.#", "0"),
				),
			},
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_organization(rName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(resourceName, "account_aggregation_source.#", "0"),
					resource.TestCheckResourceAttr(resourceName, "organization_aggregation_source.#", "1"),
				),
			},
		},
	})
}

func TestAccAWSConfigConfigurationAggregator_tags(t *testing.T) {
	var ca configservice.ConfigurationAggregator
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_config_configuration_aggregator.example"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSConfigConfigurationAggregatorDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_tags(rName, "foo", "bar", "fizz", "buzz"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "3"),
					resource.TestCheckResourceAttr(resourceName, "tags.Name", rName),
					resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar"),
					resource.TestCheckResourceAttr(resourceName, "tags.fizz", "buzz"),
				),
			},
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_tags(rName, "foo", "bar2", "fizz2", "buzz2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "3"),
					resource.TestCheckResourceAttr(resourceName, "tags.Name", rName),
					resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar2"),
					resource.TestCheckResourceAttr(resourceName, "tags.fizz2", "buzz2"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccAWSConfigConfigurationAggregatorConfig_account(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSConfigConfigurationAggregatorExists(resourceName, &ca),
					testAccCheckAWSConfigConfigurationAggregatorName(resourceName, rName, &ca),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
		},
	})
}

func testAccCheckAWSConfigConfigurationAggregatorName(n, desired string, obj *configservice.ConfigurationAggregator) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}
		if rs.Primary.Attributes["name"] != *obj.ConfigurationAggregatorName {
			return fmt.Errorf("Expected name: %q, given: %q", desired, *obj.ConfigurationAggregatorName)
		}
		return nil
	}
}

func testAccCheckAWSConfigConfigurationAggregatorExists(n string, obj *configservice.ConfigurationAggregator) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not Found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No config configuration aggregator ID is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).configconn
		out, err := conn.DescribeConfigurationAggregators(&configservice.DescribeConfigurationAggregatorsInput{
			ConfigurationAggregatorNames: []*string{aws.String(rs.Primary.Attributes["name"])},
		})
		if err != nil {
			return fmt.Errorf("Failed to describe config configuration aggregator: %s", err)
		}
		if len(out.ConfigurationAggregators) < 1 {
			return fmt.Errorf("No config configuration aggregator found when describing %q", rs.Primary.Attributes["name"])
		}

		ca := out.ConfigurationAggregators[0]
		*obj = *ca

		return nil
	}
}

func testAccCheckAWSConfigConfigurationAggregatorDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).configconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_config_configuration_aggregator" {
			continue
		}

		resp, err := conn.DescribeConfigurationAggregators(&configservice.DescribeConfigurationAggregatorsInput{
			ConfigurationAggregatorNames: []*string{aws.String(rs.Primary.Attributes["name"])},
		})

		if err == nil {
			if len(resp.ConfigurationAggregators) != 0 &&
				*resp.ConfigurationAggregators[0].ConfigurationAggregatorName == rs.Primary.Attributes["name"] {
				return fmt.Errorf("config configuration aggregator still exists: %s", rs.Primary.Attributes["name"])
			}
		}
	}

	return nil
}

func testAccAWSConfigConfigurationAggregatorConfig_account(rName string) string {
	return fmt.Sprintf(`
data "aws_region" "current" {}

resource "aws_config_configuration_aggregator" "example" {
  name = %[1]q

  account_aggregation_source {
    account_ids = [data.aws_caller_identity.current.account_id]
    regions     = [data.aws_region.current.name]
  }
}

data "aws_caller_identity" "current" {
}
`, rName)
}

func testAccAWSConfigConfigurationAggregatorConfig_organization(rName string) string {
	return fmt.Sprintf(`
resource "aws_organizations_organization" "test" {
}

resource "aws_config_configuration_aggregator" "example" {
  depends_on = [aws_iam_role_policy_attachment.example]

  name = %[1]q

  organization_aggregation_source {
    all_regions = true
    role_arn    = aws_iam_role.example.arn
  }
}

resource "aws_iam_role" "example" {
  name = %[1]q

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "config.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "example" {
  role       = aws_iam_role.example.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRoleForOrganizations"
}
`, rName)
}

func testAccAWSConfigConfigurationAggregatorConfig_tags(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
	return fmt.Sprintf(`
data "aws_region" "current" {}

resource "aws_config_configuration_aggregator" "example" {
  name = %[1]q

  account_aggregation_source {
    account_ids = [data.aws_caller_identity.current.account_id]
    regions     = [data.aws_region.current.name]
  }

  tags = {
    Name = %[1]q

    %[2]s = %[3]q
    %[4]s = %[5]q
  }
}

data "aws_caller_identity" "current" {}
`, rName, tagKey1, tagValue1, tagKey2, tagValue2)
}
<|endoftext|>