<commit_before>package goja

import (
	"errors"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"testing"
)

const (
	tc39BASE = "testdata/test262"
)

var (
	invalidFormatError = errors.New("Invalid file format")
)

var (
	skipList = map[string]bool{
		"test/language/literals/regexp/S7.8.5_A1.1_T2.js":            true, // UTF-16
		"test/language/literals/regexp/S7.8.5_A1.4_T2.js":            true, // UTF-16
		"test/language/literals/regexp/S7.8.5_A2.1_T2.js":            true, // UTF-16
		"test/language/literals/regexp/S7.8.5_A2.4_T2.js":            true, // UTF-16
		"test/built-ins/Object/getOwnPropertyNames/15.2.3.4-4-44.js": true, // property order
	}
)

type tc39TestCtx struct {
	prgCache map[string]*Program
}

type TC39MetaNegative struct {
	Phase, Type string
}

type tc39Meta struct {
	Negative TC39MetaNegative
	Includes []string
	Flags    []string
	Es5id    string
	Es6id    string
	Esid     string
}

func (m *tc39Meta) hasFlag(flag string) bool {
	for _, f := range m.Flags {
		if f == flag {
			return true
		}
	}
	return false
}

func parseTC39File(name string) (*tc39Meta, string, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, "", err
	}
	defer f.Close()

	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, "", err
	}

	str := string(b)
	metaStart := strings.Index(str, "/*---")
	if metaStart == -1 {
		return nil, "", invalidFormatError
	} else {
		metaStart += 5
	}
	metaEnd := strings.Index(str, "---*/")
	if metaEnd == -1 || metaEnd <= metaStart {
		return nil, "", invalidFormatError
	}

	var meta tc39Meta
	err = yaml.Unmarshal([]byte(str[metaStart:metaEnd]), &meta)
	if err != nil {
		return nil, "", err
	}

	if meta.Negative.Type != "" && meta.Negative.Phase == "" {
		return nil, "", errors.New("negative type is set, but phase isn't")
	}

	return &meta, str, nil
}

func runTC39Test(base, name, src string, meta *tc39Meta, t testing.TB, ctx *tc39TestCtx) {
	vm := New()
	err, early := runTC39Script(base, name, src, meta.Includes, ctx, vm)

	if err != nil {
		if meta.Negative.Type == "" {
			t.Fatalf("%s: %v", name, err)
		} else {
			if meta.Negative.Phase == "early" && !early || meta.Negative.Phase == "runtime" && early {
				t.Fatalf("%s: error %v happened at the wrong phase (expected %s)", name, err, meta.Negative.Phase)
			}
			var errType string

			switch err := err.(type) {
			case *Exception:
				if o, ok := err.Value().(*Object); ok {
					if c := o.Get("constructor"); c != nil {
						if c, ok := c.(*Object); ok {
							errType = c.Get("name").String()
						} else {
							t.Fatalf("%s: error constructor is not an object (%v)", name, o)
						}
					} else {
						t.Fatalf("%s: error does not have a constructor (%v)", name, o)
					}
				} else {
					t.Fatalf("%s: error is not an object (%v)", name, err.Value())
				}
			case *CompilerSyntaxError:
				errType = "SyntaxError"
			case *CompilerReferenceError:
				errType = "ReferenceError"
			default:
				t.Fatalf("%s: error is not a JS error: %v", name, err)
			}

			if errType != meta.Negative.Type {
				vm.vm.prg.dumpCode(t.Logf)
				t.Fatalf("%s: unexpected error type (%s), expected (%s)", name, errType, meta.Negative.Type)
			}
		}
	} else {
		if meta.Negative.Type != "" {
			vm.vm.prg.dumpCode(t.Logf)
			t.Fatalf("%s: Expected error: %v", name, err)
		}
	}
}

func runTC39File(base, name string, t testing.TB, ctx *tc39TestCtx) {
	if skipList[name] {
		t.Logf("Skipped %s", name)
		return
	}
	p := path.Join(base, name)
	meta, src, err := parseTC39File(p)
	if err != nil {
		//t.Fatalf("Could not parse %s: %v", name, err)
		t.Errorf("Could not parse %s: %v", name, err)
		return
	}
	if meta.Es5id == "" {
		//t.Logf("%s: Not ES5, skipped", name)
		return
	}

	hasRaw := meta.hasFlag("raw")

	if hasRaw || !meta.hasFlag("onlyStrict") {
		//log.Printf("Running normal test: %s", name)
		//t.Logf("Running normal test: %s", name)
		runTC39Test(base, name, src, meta, t, ctx)
	}

	if !hasRaw && !meta.hasFlag("noStrict") {
		//log.Printf("Running strict test: %s", name)
		//t.Logf("Running strict test: %s", name)
		runTC39Test(base, name, "'use strict';\n"+src, meta, t, ctx)
	}

}

func (ctx *tc39TestCtx) runFile(base, name string, vm *Runtime) error {
	prg := ctx.prgCache[name]
	if prg == nil {
		fname := path.Join(base, name)
		f, err := os.Open(fname)
		if err != nil {
			return err
		}
		defer f.Close()

		b, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}

		str := string(b)
		prg, err = Compile(name, str, false)
		if err != nil {
			return err
		}
		ctx.prgCache[name] = prg
	}
	_, err := vm.RunProgram(prg)
	return err
}

func runTC39Script(base, name, src string, includes []string, ctx *tc39TestCtx, vm *Runtime) (err error, early bool) {
	early = true
	err = ctx.runFile(base, path.Join("harness", "assert.js"), vm)
	if err != nil {
		return
	}

	err = ctx.runFile(base, path.Join("harness", "sta.js"), vm)
	if err != nil {
		return
	}

	for _, include := range includes {
		err = ctx.runFile(base, path.Join("harness", include), vm)
		if err != nil {
			return
		}
	}

	var p *Program
	p, err = Compile(name, src, false)

	if err != nil {
		return
	}

	early = false
	_, err = vm.RunProgram(p)

	return
}

func runTC39Tests(base, name string, t *testing.T, ctx *tc39TestCtx) {
	files, err := ioutil.ReadDir(path.Join(base, name))
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range files {
		if file.Name()[0] == '.' {
			continue
		}
		if file.IsDir() {
			runTC39Tests(base, path.Join(name, file.Name()), t, ctx)
		} else {
			if strings.HasSuffix(file.Name(), ".js") {
				runTC39File(base, path.Join(name, file.Name()), t, ctx)
			}
		}
	}

}

func TestTC39(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	if _, err := os.Stat(tc39BASE); err != nil {
		t.Skipf("If you want to run tc39 tests, download them from https://github.com/tc39/test262 and put into %s. The last working commit is 1ba3a7c4a93fc93b3d0d7e4146f59934a896837d. (%v)", tc39BASE, err)
	}

	ctx := &tc39TestCtx{
		prgCache: make(map[string]*Program),
	}

	//_ = "breakpoint"
	//runTC39File(tc39BASE, "test/language/types/number/8.5.1.js", t, ctx)
	//runTC39Tests(tc39BASE, "test/language", t, ctx)
	runTC39Tests(tc39BASE, "test/language/expressions", t, ctx)
	runTC39Tests(tc39BASE, "test/language/arguments-object", t, ctx)
	runTC39Tests(tc39BASE, "test/language/asi", t, ctx)
	runTC39Tests(tc39BASE, "test/language/directive-prologue", t, ctx)
	runTC39Tests(tc39BASE, "test/language/function-code", t, ctx)
	runTC39Tests(tc39BASE, "test/language/eval-code", t, ctx)
	runTC39Tests(tc39BASE, "test/language/global-code", t, ctx)
	runTC39Tests(tc39BASE, "test/language/identifier-resolution", t, ctx)
	runTC39Tests(tc39BASE, "test/language/identifiers", t, ctx)
	//runTC39Tests(tc39BASE, "test/language/literals", t, ctx) // octal sequences in strict mode
	runTC39Tests(tc39BASE, "test/language/punctuators", t, ctx)
	runTC39Tests(tc39BASE, "test/language/reserved-words", t, ctx)
	runTC39Tests(tc39BASE, "test/language/source-text", t, ctx)
	runTC39Tests(tc39BASE, "test/language/statements", t, ctx)
	runTC39Tests(tc39BASE, "test/language/types", t, ctx)
	runTC39Tests(tc39BASE, "test/language/white-space", t, ctx)
	runTC39Tests(tc39BASE, "test/built-ins", t, ctx)
	runTC39Tests(tc39BASE, "test/annexB/built-ins/String/prototype/substr", t, ctx)
}
<commit_msg>Disabled 3 timezone specific tests (they would fail in modern browsers anyway)<commit_after>package goja

import (
	"errors"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"testing"
)

const (
	tc39BASE = "testdata/test262"
)

var (
	invalidFormatError = errors.New("Invalid file format")
)

var (
	skipList = map[string]bool{
		"test/language/literals/regexp/S7.8.5_A1.1_T2.js":             true, // UTF-16
		"test/language/literals/regexp/S7.8.5_A1.4_T2.js":             true, // UTF-16
		"test/language/literals/regexp/S7.8.5_A2.1_T2.js":             true, // UTF-16
		"test/language/literals/regexp/S7.8.5_A2.4_T2.js":             true, // UTF-16
		"test/built-ins/Object/getOwnPropertyNames/15.2.3.4-4-44.js":  true, // property order
		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-8.js":  true, // timezone
		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-9.js":  true, // timezone
		"test/built-ins/Date/prototype/toISOString/15.9.5.43-0-10.js": true, // timezone
	}
)

type tc39TestCtx struct {
	prgCache map[string]*Program
}

type TC39MetaNegative struct {
	Phase, Type string
}

type tc39Meta struct {
	Negative TC39MetaNegative
	Includes []string
	Flags    []string
	Es5id    string
	Es6id    string
	Esid     string
}

func (m *tc39Meta) hasFlag(flag string) bool {
	for _, f := range m.Flags {
		if f == flag {
			return true
		}
	}
	return false
}

func parseTC39File(name string) (*tc39Meta, string, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, "", err
	}
	defer f.Close()

	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, "", err
	}

	str := string(b)
	metaStart := strings.Index(str, "/*---")
	if metaStart == -1 {
		return nil, "", invalidFormatError
	} else {
		metaStart += 5
	}
	metaEnd := strings.Index(str, "---*/")
	if metaEnd == -1 || metaEnd <= metaStart {
		return nil, "", invalidFormatError
	}

	var meta tc39Meta
	err = yaml.Unmarshal([]byte(str[metaStart:metaEnd]), &meta)
	if err != nil {
		return nil, "", err
	}

	if meta.Negative.Type != "" && meta.Negative.Phase == "" {
		return nil, "", errors.New("negative type is set, but phase isn't")
	}

	return &meta, str, nil
}

func runTC39Test(base, name, src string, meta *tc39Meta, t testing.TB, ctx *tc39TestCtx) {
	vm := New()
	err, early := runTC39Script(base, name, src, meta.Includes, ctx, vm)

	if err != nil {
		if meta.Negative.Type == "" {
			t.Fatalf("%s: %v", name, err)
		} else {
			if meta.Negative.Phase == "early" && !early || meta.Negative.Phase == "runtime" && early {
				t.Fatalf("%s: error %v happened at the wrong phase (expected %s)", name, err, meta.Negative.Phase)
			}
			var errType string

			switch err := err.(type) {
			case *Exception:
				if o, ok := err.Value().(*Object); ok {
					if c := o.Get("constructor"); c != nil {
						if c, ok := c.(*Object); ok {
							errType = c.Get("name").String()
						} else {
							t.Fatalf("%s: error constructor is not an object (%v)", name, o)
						}
					} else {
						t.Fatalf("%s: error does not have a constructor (%v)", name, o)
					}
				} else {
					t.Fatalf("%s: error is not an object (%v)", name, err.Value())
				}
			case *CompilerSyntaxError:
				errType = "SyntaxError"
			case *CompilerReferenceError:
				errType = "ReferenceError"
			default:
				t.Fatalf("%s: error is not a JS error: %v", name, err)
			}

			if errType != meta.Negative.Type {
				vm.vm.prg.dumpCode(t.Logf)
				t.Fatalf("%s: unexpected error type (%s), expected (%s)", name, errType, meta.Negative.Type)
			}
		}
	} else {
		if meta.Negative.Type != "" {
			vm.vm.prg.dumpCode(t.Logf)
			t.Fatalf("%s: Expected error: %v", name, err)
		}
	}
}

func runTC39File(base, name string, t testing.TB, ctx *tc39TestCtx) {
	if skipList[name] {
		t.Logf("Skipped %s", name)
		return
	}
	p := path.Join(base, name)
	meta, src, err := parseTC39File(p)
	if err != nil {
		//t.Fatalf("Could not parse %s: %v", name, err)
		t.Errorf("Could not parse %s: %v", name, err)
		return
	}
	if meta.Es5id == "" {
		//t.Logf("%s: Not ES5, skipped", name)
		return
	}

	hasRaw := meta.hasFlag("raw")

	if hasRaw || !meta.hasFlag("onlyStrict") {
		//log.Printf("Running normal test: %s", name)
		//t.Logf("Running normal test: %s", name)
		runTC39Test(base, name, src, meta, t, ctx)
	}

	if !hasRaw && !meta.hasFlag("noStrict") {
		//log.Printf("Running strict test: %s", name)
		//t.Logf("Running strict test: %s", name)
		runTC39Test(base, name, "'use strict';\n"+src, meta, t, ctx)
	}

}

func (ctx *tc39TestCtx) runFile(base, name string, vm *Runtime) error {
	prg := ctx.prgCache[name]
	if prg == nil {
		fname := path.Join(base, name)
		f, err := os.Open(fname)
		if err != nil {
			return err
		}
		defer f.Close()

		b, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}

		str := string(b)
		prg, err = Compile(name, str, false)
		if err != nil {
			return err
		}
		ctx.prgCache[name] = prg
	}
	_, err := vm.RunProgram(prg)
	return err
}

func runTC39Script(base, name, src string, includes []string, ctx *tc39TestCtx, vm *Runtime) (err error, early bool) {
	early = true
	err = ctx.runFile(base, path.Join("harness", "assert.js"), vm)
	if err != nil {
		return
	}

	err = ctx.runFile(base, path.Join("harness", "sta.js"), vm)
	if err != nil {
		return
	}

	for _, include := range includes {
		err = ctx.runFile(base, path.Join("harness", include), vm)
		if err != nil {
			return
		}
	}

	var p *Program
	p, err = Compile(name, src, false)

	if err != nil {
		return
	}

	early = false
	_, err = vm.RunProgram(p)

	return
}

func runTC39Tests(base, name string, t *testing.T, ctx *tc39TestCtx) {
	files, err := ioutil.ReadDir(path.Join(base, name))
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range files {
		if file.Name()[0] == '.' {
			continue
		}
		if file.IsDir() {
			runTC39Tests(base, path.Join(name, file.Name()), t, ctx)
		} else {
			if strings.HasSuffix(file.Name(), ".js") {
				runTC39File(base, path.Join(name, file.Name()), t, ctx)
			}
		}
	}

}

func TestTC39(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	if _, err := os.Stat(tc39BASE); err != nil {
		t.Skipf("If you want to run tc39 tests, download them from https://github.com/tc39/test262 and put into %s. The last working commit is 1ba3a7c4a93fc93b3d0d7e4146f59934a896837d. (%v)", tc39BASE, err)
	}

	ctx := &tc39TestCtx{
		prgCache: make(map[string]*Program),
	}

	//_ = "breakpoint"
	//runTC39File(tc39BASE, "test/language/types/number/8.5.1.js", t, ctx)
	//runTC39Tests(tc39BASE, "test/language", t, ctx)
	runTC39Tests(tc39BASE, "test/language/expressions", t, ctx)
	runTC39Tests(tc39BASE, "test/language/arguments-object", t, ctx)
	runTC39Tests(tc39BASE, "test/language/asi", t, ctx)
	runTC39Tests(tc39BASE, "test/language/directive-prologue", t, ctx)
	runTC39Tests(tc39BASE, "test/language/function-code", t, ctx)
	runTC39Tests(tc39BASE, "test/language/eval-code", t, ctx)
	runTC39Tests(tc39BASE, "test/language/global-code", t, ctx)
	runTC39Tests(tc39BASE, "test/language/identifier-resolution", t, ctx)
	runTC39Tests(tc39BASE, "test/language/identifiers", t, ctx)
	//runTC39Tests(tc39BASE, "test/language/literals", t, ctx) // octal sequences in strict mode
	runTC39Tests(tc39BASE, "test/language/punctuators", t, ctx)
	runTC39Tests(tc39BASE, "test/language/reserved-words", t, ctx)
	runTC39Tests(tc39BASE, "test/language/source-text", t, ctx)
	runTC39Tests(tc39BASE, "test/language/statements", t, ctx)
	runTC39Tests(tc39BASE, "test/language/types", t, ctx)
	runTC39Tests(tc39BASE, "test/language/white-space", t, ctx)
	runTC39Tests(tc39BASE, "test/built-ins", t, ctx)
	runTC39Tests(tc39BASE, "test/annexB/built-ins/String/prototype/substr", t, ctx)
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2014, Anupam Kapoor. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:

// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: anupam.kapoor@gmail.com (Anupam Kapoor)
//
// this package provides an implementation of fifo-queue. each element
// in the queue is a chunk of MAX_ITEMS_PER_CHUNK elements. currently
// only fifo-esque operations i.e. add-to-tail and remove-from-head
// are supported...

package queue

const MAX_ITEMS_PER_CHUNK = 4096

type Data interface{}

type chunk struct {
	items [MAX_ITEMS_PER_CHUNK]Data
	first uint16
	last  uint16
	next  *chunk
}

// add an item to the chunk
func (C *chunk) push(item Data) {
	C.items[C.last] = item
	C.last++
}

// remove an item from the chunk
func (C *chunk) pop() (item Data) {
	item = C.items[C.first]
	C.first++

	return
}

//
// a queue made out of list of chunk elements
//
type ChunkedQueue struct {
	front *chunk
	back  *chunk
	size  int32
}

// create a new queue
func New() *ChunkedQueue {
	ch := new(chunk)

	return &ChunkedQueue{
		front: ch,
		back:  ch,
		size:  0,
	}
}

//
// this function is called to add an item to the end of the
// queue. grow the queue by a chunk if required.
//
func (CQ *ChunkedQueue) Push(item Data) {
	tail := CQ.back

	// if the tail chunk is full, create a new one
	if tail.last == MAX_ITEMS_PER_CHUNK {
		new_chunk := new(chunk)
		tail.next = new_chunk
		CQ.back = new_chunk

		tail = new_chunk
	}

	tail.push(item)
	CQ.size++
}

//
// this function is called to remove an item from the head of the
// queue. when the number of items contained in a chunk drops to 0, it
// is removed from the queue
//
func (CQ *ChunkedQueue) Pop() (item Data) {

	head := CQ.front
	item = head.pop()
	CQ.size--

	if head.first >= head.last {
		if CQ.size == 0 {
			head.first = 0
			head.last = 0
			head.next = nil
		} else {
			CQ.front = head.next
		}
	}

	return
}

func (CQ *ChunkedQueue) Len() int32  { return CQ.size }
func (CQ *ChunkedQueue) Empty() bool { return !(CQ.size > 0) }
<commit_msg>*seems* better<commit_after>//
// Copyright (c) 2014, Anupam Kapoor. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:

// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: anupam.kapoor@gmail.com (Anupam Kapoor)
//
// this package provides an implementation of fifo-queue. each element
// in the queue is a chunk of MAX_ITEMS_PER_CHUNK elements. currently
// only fifo-esque operations i.e. add-to-tail and remove-from-head
// are supported...

package queue

const MAX_ITEMS_PER_CHUNK = 4096

type Data interface{}

type chunk struct {
	items [MAX_ITEMS_PER_CHUNK]Data
	first uint16
	last  uint16
	next  *chunk
}

// add an item to the chunk
func (C *chunk) push(item Data) {
	C.items[C.last] = item
	C.last++
}

// remove an item from the chunk
func (C *chunk) pop() (item Data) {
	item = C.items[C.first]
	C.first++

	return
}

//
// a queue made out of list of chunk elements
//
type ChunkedQueue struct {
	front *chunk
	back  *chunk
	size  int32
}

// create a new queue
func New() *ChunkedQueue {
	ch := new(chunk)

	return &ChunkedQueue{
		front: ch,
		back:  ch,
		size:  0,
	}
}

//
// this function is called to add an item to the end of the
// queue. grow the queue by a chunk if required.
//
func (CQ *ChunkedQueue) Push(item Data) {
	tail := CQ.back

	// if the tail chunk is full, create a new one
	if tail.last == MAX_ITEMS_PER_CHUNK {
		new_chunk := new(chunk)
		tail.next = new_chunk
		CQ.back = new_chunk

		tail = new_chunk
	}

	tail.push(item)
	CQ.size++
}

//
// this function is called to remove an item from the head of the
// queue. when the number of items contained in a chunk drops to 0, it
// is removed from the queue
//
func (CQ *ChunkedQueue) Pop() (item Data) {

	head := CQ.front
	item = head.pop()
	CQ.size--

	if head.first >= head.last {
		if CQ.size == 0 {
			head.first = 0
			head.last = 0
			head.next = nil
		} else {
			CQ.front = head.next
		}
	}

	return
}

func (CQ *ChunkedQueue) Len() int32  { return CQ.size }
func (CQ *ChunkedQueue) Empty() bool { return CQ.size == 0 }
<|endoftext|>


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

package jaegerreceiver

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/apache/thrift/lib/go/thrift"
	"github.com/jaegertracing/jaeger/cmd/agent/app/servers/thriftudp"
	"github.com/jaegertracing/jaeger/model"
	jaegerconvert "github.com/jaegertracing/jaeger/model/converter/thrift/jaeger"
	"github.com/jaegertracing/jaeger/proto-gen/api_v2"
	"github.com/jaegertracing/jaeger/thrift-gen/agent"
	jaegerthrift "github.com/jaegertracing/jaeger/thrift-gen/jaeger"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/component/componenttest"
	"go.opentelemetry.io/collector/config"
	"go.opentelemetry.io/collector/config/configgrpc"
	"go.opentelemetry.io/collector/config/configtls"
	"go.opentelemetry.io/collector/consumer/consumertest"
	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/pdata/ptrace"
	conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
	"google.golang.org/grpc"

	"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil"
	"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger"
)

var jaegerAgent = config.NewComponentIDWithName(typeStr, "agent_test")

func TestJaegerAgentUDP_ThriftCompact(t *testing.T) {
	port := testutil.GetAvailablePort(t)
	addrForClient := fmt.Sprintf(":%d", port)
	testJaegerAgent(t, addrForClient, &configuration{
		AgentCompactThriftPort:   int(port),
		AgentCompactThriftConfig: DefaultServerConfigUDP(),
	})
}

func TestJaegerAgentUDP_ThriftCompact_InvalidPort(t *testing.T) {
	port := 999999

	config := &configuration{
		AgentCompactThriftPort: port,
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)

	assert.Error(t, jr.Start(context.Background(), componenttest.NewNopHost()), "should not have been able to startTraceReception")

	require.NoError(t, jr.Shutdown(context.Background()))
}

func TestJaegerAgentUDP_ThriftBinary(t *testing.T) {
	port := testutil.GetAvailablePort(t)
	addrForClient := fmt.Sprintf(":%d", port)
	testJaegerAgent(t, addrForClient, &configuration{
		AgentBinaryThriftPort:   int(port),
		AgentBinaryThriftConfig: DefaultServerConfigUDP(),
	})
}

func TestJaegerAgentUDP_ThriftBinary_PortInUse(t *testing.T) {
	// This test confirms that the thrift binary port is opened correctly.  This is all we can test at the moment.  See above.
	port := testutil.GetAvailablePort(t)

	config := &configuration{
		AgentBinaryThriftPort:   int(port),
		AgentBinaryThriftConfig: DefaultServerConfigUDP(),
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)

	assert.NoError(t, jr.startAgent(componenttest.NewNopHost()), "Start failed")
	t.Cleanup(func() { require.NoError(t, jr.Shutdown(context.Background())) })

	l, err := net.Listen("udp", fmt.Sprintf("localhost:%d", port))
	assert.Error(t, err, "should not have been able to listen to the port")

	if l != nil {
		l.Close()
	}
}

func TestJaegerAgentUDP_ThriftBinary_InvalidPort(t *testing.T) {
	port := 999999

	config := &configuration{
		AgentBinaryThriftPort: port,
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)

	assert.Error(t, jr.Start(context.Background(), componenttest.NewNopHost()), "should not have been able to startTraceReception")

	require.NoError(t, jr.Shutdown(context.Background()))
}

func initializeGRPCTestServer(t *testing.T, beforeServe func(server *grpc.Server), opts ...grpc.ServerOption) (*grpc.Server, net.Addr) {
	server := grpc.NewServer(opts...)
	lis, err := net.Listen("tcp", "localhost:0")
	require.NoError(t, err)
	beforeServe(server)
	go func() {
		err := server.Serve(lis)
		require.NoError(t, err)
	}()
	return server, lis.Addr()
}

type mockSamplingHandler struct {
}

func (*mockSamplingHandler) GetSamplingStrategy(context.Context, *api_v2.SamplingStrategyParameters) (*api_v2.SamplingStrategyResponse, error) {
	return &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, nil
}

func TestJaegerHTTP(t *testing.T) {
	s, addr := initializeGRPCTestServer(t, func(s *grpc.Server) {
		api_v2.RegisterSamplingManagerServer(s, &mockSamplingHandler{})
	})
	defer s.GracefulStop()

	port := testutil.GetAvailablePort(t)
	config := &configuration{
		AgentHTTPPort: int(port),
		RemoteSamplingClientSettings: configgrpc.GRPCClientSettings{
			Endpoint: addr.String(),
			TLSSetting: configtls.TLSClientSetting{
				Insecure: true,
			},
		},
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)
	t.Cleanup(func() { require.NoError(t, jr.Shutdown(context.Background())) })

	assert.NoError(t, jr.Start(context.Background(), componenttest.NewNopHost()), "Start failed")

	// allow http server to start
	assert.Eventually(t, func() bool {
		conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
		if err == nil && conn != nil {
			conn.Close()
			return true
		}
		return false
	}, 10*time.Second, 5*time.Millisecond, "failed to wait for the port to be open")

	resp, err := http.Get(fmt.Sprintf("http://localhost:%d/sampling?service=test", port))
	assert.NoError(t, err, "should not have failed to make request")
	if resp != nil {
		assert.Equal(t, 200, resp.StatusCode, "should have returned 200")
	}

	resp, err = http.Get(fmt.Sprintf("http://localhost:%d/sampling?service=test", port))
	assert.NoError(t, err, "should not have failed to make request")
	if resp != nil {
		assert.Equal(t, 200, resp.StatusCode, "should have returned 200")
	}

	resp, err = http.Get(fmt.Sprintf("http://localhost:%d/baggageRestrictions?service=test", port))
	assert.NoError(t, err, "should not have failed to make request")
	if resp != nil {
		assert.Equal(t, 200, resp.StatusCode, "should have returned 200")
	}
}

func testJaegerAgent(t *testing.T, agentEndpoint string, receiverConfig *configuration) {
	// 1. Create the Jaeger receiver aka "server"
	sink := new(consumertest.TracesSink)
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, receiverConfig, sink, set)
	t.Cleanup(func() { require.NoError(t, jr.Shutdown(context.Background())) })

	var err error
	for i := 0; i < 3; i++ {
		err = jr.Start(context.Background(), componenttest.NewNopHost())
		if err == nil {
			break
		} else {
			time.Sleep(50 * time.Millisecond)
		}
	}
	require.NoError(t, err, "Start failed")

	// 2. Then send spans to the Jaeger receiver.
	jexp, err := newClientUDP(agentEndpoint, jr.agentBinaryThriftEnabled())
	require.NoError(t, err, "Failed to create the Jaeger OpenTelemetry exporter for the live application")

	// 3. Now finally send some spans
	td := generateTraceData()
	batches, err := jaeger.ProtoFromTraces(td)
	require.NoError(t, err)
	for _, batch := range batches {
		require.NoError(t, jexp.EmitBatch(context.Background(), modelToThrift(batch)))
	}

	require.Eventually(t, func() bool {
		return sink.SpanCount() > 0
	}, 10*time.Second, 5*time.Millisecond)

	gotTraces := sink.AllTraces()
	require.Equal(t, 1, len(gotTraces))
	assert.EqualValues(t, td, gotTraces[0])
}

func newClientUDP(hostPort string, binary bool) (*agent.AgentClient, error) {
	clientTransport, err := thriftudp.NewTUDPClientTransport(hostPort, "")
	if err != nil {
		return nil, err
	}
	var protocolFactory thrift.TProtocolFactory
	if binary {
		protocolFactory = thrift.NewTBinaryProtocolFactoryConf(nil)
	} else {
		protocolFactory = thrift.NewTCompactProtocolFactoryConf(nil)
	}
	return agent.NewAgentClientFactory(clientTransport, protocolFactory), nil
}

// Cannot use the testdata because timestamps are nanoseconds.
func generateTraceData() ptrace.Traces {
	td := ptrace.NewTraces()
	rs := td.ResourceSpans().AppendEmpty()
	rs.Resource().Attributes().UpsertString(conventions.AttributeServiceName, "test")
	span := rs.ScopeSpans().AppendEmpty().Spans().AppendEmpty()
	span.SetSpanID(pcommon.NewSpanID([8]byte{0, 1, 2, 3, 4, 5, 6, 7}))
	span.SetTraceID(pcommon.NewTraceID([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}))
	span.SetStartTimestamp(1581452772000000000)
	span.SetEndTimestamp(1581452773000000000)
	return td
}

func modelToThrift(batch *model.Batch) *jaegerthrift.Batch {
	return &jaegerthrift.Batch{
		Process: processModelToThrift(batch.Process),
		Spans:   jaegerconvert.FromDomain(batch.Spans),
	}
}

func processModelToThrift(process *model.Process) *jaegerthrift.Process {
	if process == nil {
		return nil
	}
	return &jaegerthrift.Process{
		ServiceName: process.ServiceName,
	}
}
<commit_msg>[receiver/jaeger] Skip flaky tests (#10371)<commit_after>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jaegerreceiver

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/apache/thrift/lib/go/thrift"
	"github.com/jaegertracing/jaeger/cmd/agent/app/servers/thriftudp"
	"github.com/jaegertracing/jaeger/model"
	jaegerconvert "github.com/jaegertracing/jaeger/model/converter/thrift/jaeger"
	"github.com/jaegertracing/jaeger/proto-gen/api_v2"
	"github.com/jaegertracing/jaeger/thrift-gen/agent"
	jaegerthrift "github.com/jaegertracing/jaeger/thrift-gen/jaeger"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/component/componenttest"
	"go.opentelemetry.io/collector/config"
	"go.opentelemetry.io/collector/config/configgrpc"
	"go.opentelemetry.io/collector/config/configtls"
	"go.opentelemetry.io/collector/consumer/consumertest"
	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/pdata/ptrace"
	conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
	"google.golang.org/grpc"

	"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil"
	"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger"
)

var jaegerAgent = config.NewComponentIDWithName(typeStr, "agent_test")

var skip = func(t *testing.T, why string) {
	t.Skip(why)
}

func TestJaegerAgentUDP_ThriftCompact(t *testing.T) {
	skip(t, "Flaky Test - See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/10368")
	port := testutil.GetAvailablePort(t)
	addrForClient := fmt.Sprintf(":%d", port)
	testJaegerAgent(t, addrForClient, &configuration{
		AgentCompactThriftPort:   int(port),
		AgentCompactThriftConfig: DefaultServerConfigUDP(),
	})
}

func TestJaegerAgentUDP_ThriftCompact_InvalidPort(t *testing.T) {
	port := 999999

	config := &configuration{
		AgentCompactThriftPort: port,
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)

	assert.Error(t, jr.Start(context.Background(), componenttest.NewNopHost()), "should not have been able to startTraceReception")

	require.NoError(t, jr.Shutdown(context.Background()))
}

func TestJaegerAgentUDP_ThriftBinary(t *testing.T) {
	skip(t, "Flaky Test - See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/10369")
	port := testutil.GetAvailablePort(t)
	addrForClient := fmt.Sprintf(":%d", port)
	testJaegerAgent(t, addrForClient, &configuration{
		AgentBinaryThriftPort:   int(port),
		AgentBinaryThriftConfig: DefaultServerConfigUDP(),
	})
}

func TestJaegerAgentUDP_ThriftBinary_PortInUse(t *testing.T) {
	skip(t, "Flaky Test - See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/10370")
	// This test confirms that the thrift binary port is opened correctly.  This is all we can test at the moment.  See above.
	port := testutil.GetAvailablePort(t)

	config := &configuration{
		AgentBinaryThriftPort:   int(port),
		AgentBinaryThriftConfig: DefaultServerConfigUDP(),
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)

	assert.NoError(t, jr.startAgent(componenttest.NewNopHost()), "Start failed")
	t.Cleanup(func() { require.NoError(t, jr.Shutdown(context.Background())) })

	l, err := net.Listen("udp", fmt.Sprintf("localhost:%d", port))
	assert.Error(t, err, "should not have been able to listen to the port")

	if l != nil {
		l.Close()
	}
}

func TestJaegerAgentUDP_ThriftBinary_InvalidPort(t *testing.T) {
	port := 999999

	config := &configuration{
		AgentBinaryThriftPort: port,
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)

	assert.Error(t, jr.Start(context.Background(), componenttest.NewNopHost()), "should not have been able to startTraceReception")

	require.NoError(t, jr.Shutdown(context.Background()))
}

func initializeGRPCTestServer(t *testing.T, beforeServe func(server *grpc.Server), opts ...grpc.ServerOption) (*grpc.Server, net.Addr) {
	server := grpc.NewServer(opts...)
	lis, err := net.Listen("tcp", "localhost:0")
	require.NoError(t, err)
	beforeServe(server)
	go func() {
		err := server.Serve(lis)
		require.NoError(t, err)
	}()
	return server, lis.Addr()
}

type mockSamplingHandler struct {
}

func (*mockSamplingHandler) GetSamplingStrategy(context.Context, *api_v2.SamplingStrategyParameters) (*api_v2.SamplingStrategyResponse, error) {
	return &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, nil
}

func TestJaegerHTTP(t *testing.T) {
	s, addr := initializeGRPCTestServer(t, func(s *grpc.Server) {
		api_v2.RegisterSamplingManagerServer(s, &mockSamplingHandler{})
	})
	defer s.GracefulStop()

	port := testutil.GetAvailablePort(t)
	config := &configuration{
		AgentHTTPPort: int(port),
		RemoteSamplingClientSettings: configgrpc.GRPCClientSettings{
			Endpoint: addr.String(),
			TLSSetting: configtls.TLSClientSetting{
				Insecure: true,
			},
		},
	}
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, config, nil, set)
	t.Cleanup(func() { require.NoError(t, jr.Shutdown(context.Background())) })

	assert.NoError(t, jr.Start(context.Background(), componenttest.NewNopHost()), "Start failed")

	// allow http server to start
	assert.Eventually(t, func() bool {
		conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", port))
		if err == nil && conn != nil {
			conn.Close()
			return true
		}
		return false
	}, 10*time.Second, 5*time.Millisecond, "failed to wait for the port to be open")

	resp, err := http.Get(fmt.Sprintf("http://localhost:%d/sampling?service=test", port))
	assert.NoError(t, err, "should not have failed to make request")
	if resp != nil {
		assert.Equal(t, 200, resp.StatusCode, "should have returned 200")
	}

	resp, err = http.Get(fmt.Sprintf("http://localhost:%d/sampling?service=test", port))
	assert.NoError(t, err, "should not have failed to make request")
	if resp != nil {
		assert.Equal(t, 200, resp.StatusCode, "should have returned 200")
	}

	resp, err = http.Get(fmt.Sprintf("http://localhost:%d/baggageRestrictions?service=test", port))
	assert.NoError(t, err, "should not have failed to make request")
	if resp != nil {
		assert.Equal(t, 200, resp.StatusCode, "should have returned 200")
	}
}

func testJaegerAgent(t *testing.T, agentEndpoint string, receiverConfig *configuration) {
	// 1. Create the Jaeger receiver aka "server"
	sink := new(consumertest.TracesSink)
	set := componenttest.NewNopReceiverCreateSettings()
	jr := newJaegerReceiver(jaegerAgent, receiverConfig, sink, set)
	t.Cleanup(func() { require.NoError(t, jr.Shutdown(context.Background())) })

	var err error
	for i := 0; i < 3; i++ {
		err = jr.Start(context.Background(), componenttest.NewNopHost())
		if err == nil {
			break
		} else {
			time.Sleep(50 * time.Millisecond)
		}
	}
	require.NoError(t, err, "Start failed")

	// 2. Then send spans to the Jaeger receiver.
	jexp, err := newClientUDP(agentEndpoint, jr.agentBinaryThriftEnabled())
	require.NoError(t, err, "Failed to create the Jaeger OpenTelemetry exporter for the live application")

	// 3. Now finally send some spans
	td := generateTraceData()
	batches, err := jaeger.ProtoFromTraces(td)
	require.NoError(t, err)
	for _, batch := range batches {
		require.NoError(t, jexp.EmitBatch(context.Background(), modelToThrift(batch)))
	}

	require.Eventually(t, func() bool {
		return sink.SpanCount() > 0
	}, 10*time.Second, 5*time.Millisecond)

	gotTraces := sink.AllTraces()
	require.Equal(t, 1, len(gotTraces))
	assert.EqualValues(t, td, gotTraces[0])
}

func newClientUDP(hostPort string, binary bool) (*agent.AgentClient, error) {
	clientTransport, err := thriftudp.NewTUDPClientTransport(hostPort, "")
	if err != nil {
		return nil, err
	}
	var protocolFactory thrift.TProtocolFactory
	if binary {
		protocolFactory = thrift.NewTBinaryProtocolFactoryConf(nil)
	} else {
		protocolFactory = thrift.NewTCompactProtocolFactoryConf(nil)
	}
	return agent.NewAgentClientFactory(clientTransport, protocolFactory), nil
}

// Cannot use the testdata because timestamps are nanoseconds.
func generateTraceData() ptrace.Traces {
	td := ptrace.NewTraces()
	rs := td.ResourceSpans().AppendEmpty()
	rs.Resource().Attributes().UpsertString(conventions.AttributeServiceName, "test")
	span := rs.ScopeSpans().AppendEmpty().Spans().AppendEmpty()
	span.SetSpanID(pcommon.NewSpanID([8]byte{0, 1, 2, 3, 4, 5, 6, 7}))
	span.SetTraceID(pcommon.NewTraceID([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}))
	span.SetStartTimestamp(1581452772000000000)
	span.SetEndTimestamp(1581452773000000000)
	return td
}

func modelToThrift(batch *model.Batch) *jaegerthrift.Batch {
	return &jaegerthrift.Batch{
		Process: processModelToThrift(batch.Process),
		Spans:   jaegerconvert.FromDomain(batch.Spans),
	}
}

func processModelToThrift(process *model.Process) *jaegerthrift.Process {
	if process == nil {
		return nil
	}
	return &jaegerthrift.Process{
		ServiceName: process.ServiceName,
	}
}
<|endoftext|>


<commit_before>package digitalocean

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/pearkes/digitalocean"
)

func resourceDigitalOceanDroplet() *schema.Resource {
	return &schema.Resource{
		Create: resourceDigitalOceanDropletCreate,
		Read:   resourceDigitalOceanDropletRead,
		Update: resourceDigitalOceanDropletUpdate,
		Delete: resourceDigitalOceanDropletDelete,

		Schema: map[string]*schema.Schema{
			"image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"region": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"size": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"locked": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"backups": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
			},

			"ipv6": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
			},

			"ipv6_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"ipv6_address_private": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"private_networking": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
			},

			"ipv4_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"ipv4_address_private": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"ssh_keys": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"user_data": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceDigitalOceanDropletCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	// Build up our creation options
	opts := &digitalocean.CreateDroplet{
		Image:  d.Get("image").(string),
		Name:   d.Get("name").(string),
		Region: d.Get("region").(string),
		Size:   d.Get("size").(string),
	}

	if attr, ok := d.GetOk("backups"); ok {
		opts.Backups = attr.(bool)
	}

	if attr, ok := d.GetOk("ipv6"); ok {
		opts.IPV6 = attr.(bool)
	}

	if attr, ok := d.GetOk("private_networking"); ok {
		opts.PrivateNetworking = attr.(bool)
	}

	if attr, ok := d.GetOk("user_data"); ok {
		opts.UserData = attr.(string)
	}

	// Get configured ssh_keys
	ssh_keys := d.Get("ssh_keys.#").(int)
	if ssh_keys > 0 {
		opts.SSHKeys = make([]string, 0, ssh_keys)
		for i := 0; i < ssh_keys; i++ {
			key := fmt.Sprintf("ssh_keys.%d", i)
			opts.SSHKeys = append(opts.SSHKeys, d.Get(key).(string))
		}
	}

	log.Printf("[DEBUG] Droplet create configuration: %#v", opts)

	id, err := client.CreateDroplet(opts)

	if err != nil {
		return fmt.Errorf("Error creating droplet: %s", err)
	}

	// Assign the droplets id
	d.SetId(id)

	log.Printf("[INFO] Droplet ID: %s", d.Id())

	_, err = WaitForDropletAttribute(d, "active", []string{"new"}, "status", meta)
	if err != nil {
		return fmt.Errorf(
			"Error waiting for droplet (%s) to become ready: %s", d.Id(), err)
	}

	return resourceDigitalOceanDropletRead(d, meta)
}

func resourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	// Retrieve the droplet properties for updating the state
	droplet, err := client.RetrieveDroplet(d.Id())
	if err != nil {
		return fmt.Errorf("Error retrieving droplet: %s", err)
	}

	if droplet.ImageSlug() != "" {
		d.Set("image", droplet.ImageSlug())
	} else {
		d.Set("image", droplet.ImageId())
	}

	d.Set("name", droplet.Name)
	d.Set("region", droplet.RegionSlug())
	d.Set("size", droplet.SizeSlug)
	d.Set("status", droplet.Status)
	d.Set("locked", droplet.IsLocked())

	if droplet.IPV6Address("public") != "" {
		d.Set("ipv6", true)
		d.Set("ipv6_address", droplet.IPV6Address("public"))
		d.Set("ipv6_address_private", droplet.IPV6Address("private"))
	}

	d.Set("ipv4_address", droplet.IPV4Address("public"))

	if droplet.NetworkingType() == "private" {
		d.Set("private_networking", true)
		d.Set("ipv4_address_private", droplet.IPV4Address("private"))
	}

	// Initialize the connection info
	d.SetConnInfo(map[string]string{
		"type": "ssh",
		"host": droplet.IPV4Address("public"),
	})

	return nil
}

func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	if d.HasChange("size") {
		oldSize, newSize := d.GetChange("size")

		err := client.PowerOff(d.Id())

		if err != nil && !strings.Contains(err.Error(), "Droplet is already powered off") {
			return fmt.Errorf(
				"Error powering off droplet (%s): %s", d.Id(), err)
		}

		// Wait for power off
		_, err = WaitForDropletAttribute(d, "off", []string{"active"}, "status", client)
		if err != nil {
			return fmt.Errorf(
				"Error waiting for droplet (%s) to become powered off: %s", d.Id(), err)
		}

		// Resize the droplet
		err = client.Resize(d.Id(), newSize.(string))
		if err != nil {
			newErr := powerOnAndWait(d, meta)
			if newErr != nil {
				return fmt.Errorf(
					"Error powering on droplet (%s) after failed resize: %s", d.Id(), err)
			}
			return fmt.Errorf(
				"Error resizing droplet (%s): %s", d.Id(), err)
		}

		// Wait for the size to change
		_, err = WaitForDropletAttribute(
			d, newSize.(string), []string{"", oldSize.(string)}, "size", meta)

		if err != nil {
			newErr := powerOnAndWait(d, meta)
			if newErr != nil {
				return fmt.Errorf(
					"Error powering on droplet (%s) after waiting for resize to finish: %s", d.Id(), err)
			}
			return fmt.Errorf(
				"Error waiting for resize droplet (%s) to finish: %s", d.Id(), err)
		}

		err = client.PowerOn(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error powering on droplet (%s) after resize: %s", d.Id(), err)
		}

		// Wait for power off
		_, err = WaitForDropletAttribute(d, "active", []string{"off"}, "status", meta)
		if err != nil {
			return err
		}
	}

	if d.HasChange("name") {
		oldName, newName := d.GetChange("name")

		// Rename the droplet
		err := client.Rename(d.Id(), newName.(string))

		if err != nil {
			return fmt.Errorf(
				"Error renaming droplet (%s): %s", d.Id(), err)
		}

		// Wait for the name to change
		_, err = WaitForDropletAttribute(
			d, newName.(string), []string{"", oldName.(string)}, "name", meta)

		if err != nil {
			return fmt.Errorf(
				"Error waiting for rename droplet (%s) to finish: %s", d.Id(), err)
		}
	}

	// As there is no way to disable private networking,
	// we only check if it needs to be enabled
	if d.HasChange("private_networking") && d.Get("private_networking").(bool) {
		err := client.EnablePrivateNetworking(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error enabling private networking for droplet (%s): %s", d.Id(), err)
		}

		// Wait for the private_networking to turn on
		_, err = WaitForDropletAttribute(
			d, "true", []string{"", "false"}, "private_networking", meta)

		return fmt.Errorf(
			"Error waiting for private networking to be enabled on for droplet (%s): %s", d.Id(), err)
	}

	// As there is no way to disable IPv6, we only check if it needs to be enabled
	if d.HasChange("ipv6") && d.Get("ipv6").(bool) {
		err := client.EnableIPV6s(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error turning on ipv6 for droplet (%s): %s", d.Id(), err)
		}

		// Wait for ipv6 to turn on
		_, err = WaitForDropletAttribute(
			d, "true", []string{"", "false"}, "ipv6", meta)

		if err != nil {
			return fmt.Errorf(
				"Error waiting for ipv6 to be turned on for droplet (%s): %s", d.Id(), err)
		}
	}

	return resourceDigitalOceanDropletRead(d, meta)
}

func resourceDigitalOceanDropletDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	_, err := WaitForDropletAttribute(
		d, "false", []string{"", "true"}, "locked", meta)

	if err != nil {
		return fmt.Errorf(
			"Error waiting for droplet to be unlocked for destroy (%s): %s", d.Id(), err)
	}

	log.Printf("[INFO] Deleting droplet: %s", d.Id())

	// Destroy the droplet
	err = client.DestroyDroplet(d.Id())

	// Handle remotely destroyed droplets
	if err != nil && strings.Contains(err.Error(), "404 Not Found") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("Error deleting droplet: %s", err)
	}

	return nil
}

func WaitForDropletAttribute(
	d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
	// Wait for the droplet so we can get the networking attributes
	// that show up after a while
	log.Printf(
		"[INFO] Waiting for droplet (%s) to have %s of %s",
		d.Id(), attribute, target)

	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     target,
		Refresh:    newDropletStateRefreshFunc(d, attribute, meta),
		Timeout:    60 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,

		// This is a hack around DO API strangeness.
		// https://github.com/hashicorp/terraform/issues/481
		//
		NotFoundChecks: 60,
	}

	return stateConf.WaitForState()
}

// TODO This function still needs a little more refactoring to make it
// cleaner and more efficient
func newDropletStateRefreshFunc(
	d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
	client := meta.(*digitalocean.Client)
	return func() (interface{}, string, error) {
		err := resourceDigitalOceanDropletRead(d, meta)
		if err != nil {
			return nil, "", err
		}

		// If the droplet is locked, continue waiting. We can
		// only perform actions on unlocked droplets, so it's
		// pointless to look at that status
		if d.Get("locked").(string) == "true" {
			log.Println("[DEBUG] Droplet is locked, skipping status check and retrying")
			return nil, "", nil
		}

		// See if we can access our attribute
		if attr, ok := d.GetOk(attribute); ok {
			// Retrieve the droplet properties
			droplet, err := client.RetrieveDroplet(d.Id())
			if err != nil {
				return nil, "", fmt.Errorf("Error retrieving droplet: %s", err)
			}

			return &droplet, attr.(string), nil
		}

		return nil, "", nil
	}
}

// Powers on the droplet and waits for it to be active
func powerOnAndWait(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)
	err := client.PowerOn(d.Id())
	if err != nil {
		return err
	}

	// Wait for power on
	_, err = WaitForDropletAttribute(d, "active", []string{"off"}, "status", client)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>provider/digitalocean: check if the droplet no longer exists.<commit_after>package digitalocean

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/pearkes/digitalocean"
)

func resourceDigitalOceanDroplet() *schema.Resource {
	return &schema.Resource{
		Create: resourceDigitalOceanDropletCreate,
		Read:   resourceDigitalOceanDropletRead,
		Update: resourceDigitalOceanDropletUpdate,
		Delete: resourceDigitalOceanDropletDelete,

		Schema: map[string]*schema.Schema{
			"image": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"region": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"size": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"status": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"locked": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"backups": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
			},

			"ipv6": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
			},

			"ipv6_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"ipv6_address_private": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"private_networking": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
			},

			"ipv4_address": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"ipv4_address_private": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"ssh_keys": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},

			"user_data": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceDigitalOceanDropletCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	// Build up our creation options
	opts := &digitalocean.CreateDroplet{
		Image:  d.Get("image").(string),
		Name:   d.Get("name").(string),
		Region: d.Get("region").(string),
		Size:   d.Get("size").(string),
	}

	if attr, ok := d.GetOk("backups"); ok {
		opts.Backups = attr.(bool)
	}

	if attr, ok := d.GetOk("ipv6"); ok {
		opts.IPV6 = attr.(bool)
	}

	if attr, ok := d.GetOk("private_networking"); ok {
		opts.PrivateNetworking = attr.(bool)
	}

	if attr, ok := d.GetOk("user_data"); ok {
		opts.UserData = attr.(string)
	}

	// Get configured ssh_keys
	ssh_keys := d.Get("ssh_keys.#").(int)
	if ssh_keys > 0 {
		opts.SSHKeys = make([]string, 0, ssh_keys)
		for i := 0; i < ssh_keys; i++ {
			key := fmt.Sprintf("ssh_keys.%d", i)
			opts.SSHKeys = append(opts.SSHKeys, d.Get(key).(string))
		}
	}

	log.Printf("[DEBUG] Droplet create configuration: %#v", opts)

	id, err := client.CreateDroplet(opts)

	if err != nil {
		return fmt.Errorf("Error creating droplet: %s", err)
	}

	// Assign the droplets id
	d.SetId(id)

	log.Printf("[INFO] Droplet ID: %s", d.Id())

	_, err = WaitForDropletAttribute(d, "active", []string{"new"}, "status", meta)
	if err != nil {
		return fmt.Errorf(
			"Error waiting for droplet (%s) to become ready: %s", d.Id(), err)
	}

	return resourceDigitalOceanDropletRead(d, meta)
}

func resourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	// Retrieve the droplet properties for updating the state
	droplet, err := client.RetrieveDroplet(d.Id())
	if err != nil {
		// check if the droplet no longer exists.
		if err.Error() == "Error retrieving droplet: API Error: 404 Not Found" {
			d.SetId("")
			return nil
		}

		return fmt.Errorf("Error retrieving droplet: %s", err)
	}

	if droplet.ImageSlug() != "" {
		d.Set("image", droplet.ImageSlug())
	} else {
		d.Set("image", droplet.ImageId())
	}

	d.Set("name", droplet.Name)
	d.Set("region", droplet.RegionSlug())
	d.Set("size", droplet.SizeSlug)
	d.Set("status", droplet.Status)
	d.Set("locked", droplet.IsLocked())

	if droplet.IPV6Address("public") != "" {
		d.Set("ipv6", true)
		d.Set("ipv6_address", droplet.IPV6Address("public"))
		d.Set("ipv6_address_private", droplet.IPV6Address("private"))
	}

	d.Set("ipv4_address", droplet.IPV4Address("public"))

	if droplet.NetworkingType() == "private" {
		d.Set("private_networking", true)
		d.Set("ipv4_address_private", droplet.IPV4Address("private"))
	}

	// Initialize the connection info
	d.SetConnInfo(map[string]string{
		"type": "ssh",
		"host": droplet.IPV4Address("public"),
	})

	return nil
}

func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	if d.HasChange("size") {
		oldSize, newSize := d.GetChange("size")

		err := client.PowerOff(d.Id())

		if err != nil && !strings.Contains(err.Error(), "Droplet is already powered off") {
			return fmt.Errorf(
				"Error powering off droplet (%s): %s", d.Id(), err)
		}

		// Wait for power off
		_, err = WaitForDropletAttribute(d, "off", []string{"active"}, "status", client)
		if err != nil {
			return fmt.Errorf(
				"Error waiting for droplet (%s) to become powered off: %s", d.Id(), err)
		}

		// Resize the droplet
		err = client.Resize(d.Id(), newSize.(string))
		if err != nil {
			newErr := powerOnAndWait(d, meta)
			if newErr != nil {
				return fmt.Errorf(
					"Error powering on droplet (%s) after failed resize: %s", d.Id(), err)
			}
			return fmt.Errorf(
				"Error resizing droplet (%s): %s", d.Id(), err)
		}

		// Wait for the size to change
		_, err = WaitForDropletAttribute(
			d, newSize.(string), []string{"", oldSize.(string)}, "size", meta)

		if err != nil {
			newErr := powerOnAndWait(d, meta)
			if newErr != nil {
				return fmt.Errorf(
					"Error powering on droplet (%s) after waiting for resize to finish: %s", d.Id(), err)
			}
			return fmt.Errorf(
				"Error waiting for resize droplet (%s) to finish: %s", d.Id(), err)
		}

		err = client.PowerOn(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error powering on droplet (%s) after resize: %s", d.Id(), err)
		}

		// Wait for power off
		_, err = WaitForDropletAttribute(d, "active", []string{"off"}, "status", meta)
		if err != nil {
			return err
		}
	}

	if d.HasChange("name") {
		oldName, newName := d.GetChange("name")

		// Rename the droplet
		err := client.Rename(d.Id(), newName.(string))

		if err != nil {
			return fmt.Errorf(
				"Error renaming droplet (%s): %s", d.Id(), err)
		}

		// Wait for the name to change
		_, err = WaitForDropletAttribute(
			d, newName.(string), []string{"", oldName.(string)}, "name", meta)

		if err != nil {
			return fmt.Errorf(
				"Error waiting for rename droplet (%s) to finish: %s", d.Id(), err)
		}
	}

	// As there is no way to disable private networking,
	// we only check if it needs to be enabled
	if d.HasChange("private_networking") && d.Get("private_networking").(bool) {
		err := client.EnablePrivateNetworking(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error enabling private networking for droplet (%s): %s", d.Id(), err)
		}

		// Wait for the private_networking to turn on
		_, err = WaitForDropletAttribute(
			d, "true", []string{"", "false"}, "private_networking", meta)

		return fmt.Errorf(
			"Error waiting for private networking to be enabled on for droplet (%s): %s", d.Id(), err)
	}

	// As there is no way to disable IPv6, we only check if it needs to be enabled
	if d.HasChange("ipv6") && d.Get("ipv6").(bool) {
		err := client.EnableIPV6s(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error turning on ipv6 for droplet (%s): %s", d.Id(), err)
		}

		// Wait for ipv6 to turn on
		_, err = WaitForDropletAttribute(
			d, "true", []string{"", "false"}, "ipv6", meta)

		if err != nil {
			return fmt.Errorf(
				"Error waiting for ipv6 to be turned on for droplet (%s): %s", d.Id(), err)
		}
	}

	return resourceDigitalOceanDropletRead(d, meta)
}

func resourceDigitalOceanDropletDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)

	_, err := WaitForDropletAttribute(
		d, "false", []string{"", "true"}, "locked", meta)

	if err != nil {
		return fmt.Errorf(
			"Error waiting for droplet to be unlocked for destroy (%s): %s", d.Id(), err)
	}

	log.Printf("[INFO] Deleting droplet: %s", d.Id())

	// Destroy the droplet
	err = client.DestroyDroplet(d.Id())

	// Handle remotely destroyed droplets
	if err != nil && strings.Contains(err.Error(), "404 Not Found") {
		return nil
	}

	if err != nil {
		return fmt.Errorf("Error deleting droplet: %s", err)
	}

	return nil
}

func WaitForDropletAttribute(
	d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
	// Wait for the droplet so we can get the networking attributes
	// that show up after a while
	log.Printf(
		"[INFO] Waiting for droplet (%s) to have %s of %s",
		d.Id(), attribute, target)

	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     target,
		Refresh:    newDropletStateRefreshFunc(d, attribute, meta),
		Timeout:    60 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,

		// This is a hack around DO API strangeness.
		// https://github.com/hashicorp/terraform/issues/481
		//
		NotFoundChecks: 60,
	}

	return stateConf.WaitForState()
}

// TODO This function still needs a little more refactoring to make it
// cleaner and more efficient
func newDropletStateRefreshFunc(
	d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
	client := meta.(*digitalocean.Client)
	return func() (interface{}, string, error) {
		err := resourceDigitalOceanDropletRead(d, meta)
		if err != nil {
			return nil, "", err
		}

		// If the droplet is locked, continue waiting. We can
		// only perform actions on unlocked droplets, so it's
		// pointless to look at that status
		if d.Get("locked").(string) == "true" {
			log.Println("[DEBUG] Droplet is locked, skipping status check and retrying")
			return nil, "", nil
		}

		// See if we can access our attribute
		if attr, ok := d.GetOk(attribute); ok {
			// Retrieve the droplet properties
			droplet, err := client.RetrieveDroplet(d.Id())
			if err != nil {
				return nil, "", fmt.Errorf("Error retrieving droplet: %s", err)
			}

			return &droplet, attr.(string), nil
		}

		return nil, "", nil
	}
}

// Powers on the droplet and waits for it to be active
func powerOnAndWait(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*digitalocean.Client)
	err := client.PowerOn(d.Id())
	if err != nil {
		return err
	}

	// Wait for power on
	_, err = WaitForDropletAttribute(d, "active", []string{"off"}, "status", client)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package saml2aws

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"

	"fmt"
        "strings"
        b64 "encoding/base64"

	"github.com/PuerkitoBio/goquery"
	"github.com/pkg/errors"
)

var awsURL = "https://signin.aws.amazon.com/saml"

// AWSAccount holds the AWS account name and roles
type AWSAccount struct {
	Name  string
	Roles []*AWSRole
}

// ParseAWSAccounts extract the aws accounts from the saml assertion
func ParseAWSAccounts(samlAssertion string) ([]*AWSAccount, error) {
        decSamlAssertion, _ := b64.StdEncoding.DecodeString(samlAssertion)
        if strings.Contains(string(decSamlAssertion), "signin.amazonaws.cn") {
            fmt.Println("trying to login AWS China ")
            awsURL = "https://signin.amazonaws.cn/saml"
        }

	res, err := http.PostForm(awsURL, url.Values{"SAMLResponse": {samlAssertion}})
	if err != nil {
		return nil, errors.Wrap(err, "error retrieving AWS login form")
	}

	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, errors.Wrap(err, "error retrieving AWS login body")
	}

	return ExtractAWSAccounts(data)
}

// ExtractAWSAccounts extract the accounts from the AWS html page
func ExtractAWSAccounts(data []byte) ([]*AWSAccount, error) {
	accounts := []*AWSAccount{}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(data))
	if err != nil {
		return nil, errors.Wrap(err, "failed to build document from response")
	}

	doc.Find("fieldset > div.saml-account").Each(func(i int, s *goquery.Selection) {
		account := new(AWSAccount)
		account.Name = s.Find("div.saml-account-name").Text()
		s.Find("label").Each(func(i int, s *goquery.Selection) {
			role := new(AWSRole)
			role.Name = s.Text()
			role.RoleARN, _ = s.Attr("for")
			account.Roles = append(account.Roles, role)
		})
		accounts = append(accounts, account)
	})

	return accounts, nil
}

// AssignPrincipals assign principal from roles
func AssignPrincipals(awsRoles []*AWSRole, awsAccounts []*AWSAccount) {

	awsPrincipalARNs := make(map[string]string)
	for _, awsRole := range awsRoles {
		awsPrincipalARNs[awsRole.RoleARN] = awsRole.PrincipalARN
	}

	for _, awsAccount := range awsAccounts {
		for _, awsRole := range awsAccount.Roles {
			awsRole.PrincipalARN = awsPrincipalARNs[awsRole.RoleARN]
		}
	}

}

// LocateRole locate role by name
func LocateRole(awsRoles []*AWSRole, roleName string) (*AWSRole, error) {
	for _, awsRole := range awsRoles {
		if awsRole.RoleARN == roleName {
			return awsRole, nil
		}
	}

	return nil, fmt.Errorf("Supplied RoleArn not found in saml assertion: %s", roleName)
}
<commit_msg>support_china_region<commit_after>package saml2aws

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"

	"fmt"
        "strings"
        b64 "encoding/base64"

	"github.com/PuerkitoBio/goquery"
	"github.com/pkg/errors"
)

var awsURL = "https://signin.aws.amazon.com/saml"

// AWSAccount holds the AWS account name and roles
type AWSAccount struct {
	Name  string
	Roles []*AWSRole
}

// ParseAWSAccounts extract the aws accounts from the saml assertion
func ParseAWSAccounts(samlAssertion string) ([]*AWSAccount, error) {
        decSamlAssertion, _ := b64.StdEncoding.DecodeString(samlAssertion)
        if strings.Contains(string(decSamlAssertion), "signin.amazonaws.cn") {
            fmt.Println("trying to login AWS China")
            awsURL = "https://signin.amazonaws.cn/saml"
        }

	res, err := http.PostForm(awsURL, url.Values{"SAMLResponse": {samlAssertion}})
	if err != nil {
		return nil, errors.Wrap(err, "error retrieving AWS login form")
	}

	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, errors.Wrap(err, "error retrieving AWS login body")
	}

	return ExtractAWSAccounts(data)
}

// ExtractAWSAccounts extract the accounts from the AWS html page
func ExtractAWSAccounts(data []byte) ([]*AWSAccount, error) {
	accounts := []*AWSAccount{}

	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(data))
	if err != nil {
		return nil, errors.Wrap(err, "failed to build document from response")
	}

	doc.Find("fieldset > div.saml-account").Each(func(i int, s *goquery.Selection) {
		account := new(AWSAccount)
		account.Name = s.Find("div.saml-account-name").Text()
		s.Find("label").Each(func(i int, s *goquery.Selection) {
			role := new(AWSRole)
			role.Name = s.Text()
			role.RoleARN, _ = s.Attr("for")
			account.Roles = append(account.Roles, role)
		})
		accounts = append(accounts, account)
	})

	return accounts, nil
}

// AssignPrincipals assign principal from roles
func AssignPrincipals(awsRoles []*AWSRole, awsAccounts []*AWSAccount) {

	awsPrincipalARNs := make(map[string]string)
	for _, awsRole := range awsRoles {
		awsPrincipalARNs[awsRole.RoleARN] = awsRole.PrincipalARN
	}

	for _, awsAccount := range awsAccounts {
		for _, awsRole := range awsAccount.Roles {
			awsRole.PrincipalARN = awsPrincipalARNs[awsRole.RoleARN]
		}
	}

}

// LocateRole locate role by name
func LocateRole(awsRoles []*AWSRole, roleName string) (*AWSRole, error) {
	for _, awsRole := range awsRoles {
		if awsRole.RoleARN == roleName {
			return awsRole, nil
		}
	}

	return nil, fmt.Errorf("Supplied RoleArn not found in saml assertion: %s", roleName)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"path"
	"path/filepath"
	"syscall"

	"github.com/docker/go-plugins-helpers/volume"
)

type beegfsDriver struct {
	root string
}

func newBeeGFSDriver(root string) beegfsDriver {
	d := beegfsDriver{
		root: root,
	}

	return d
}

func (b beegfsDriver) Create(r volume.Request) volume.Response {
	log.Infof("Create: %s, %v", r.Name, r.Options)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot create volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return volume.Response{Err: emsg}
	}

	if err := createDest(dest); err != nil {
		return volume.Response{Err: err.Error()}
	}

	return volume.Response{}
}

func (b beegfsDriver) Remove(r volume.Request) volume.Response {
	log.Infof("Remove: %s", r.Name)
	return volume.Response{}
}

func (b beegfsDriver) Path(r volume.Request) volume.Response {
	log.Debugf("Path: %s", r.Name)
	return volume.Response{Mountpoint: volumeDir(b, r)}
}

func (b beegfsDriver) Mount(r volume.Request) volume.Response {
	log.Infof("Mount: %s", r.Name)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot mount volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return volume.Response{Err: emsg}
	}

	return volume.Response{Mountpoint: dest}
}

func (b beegfsDriver) Unmount(r volume.Request) volume.Response {
	log.Infof("Unmount: %s", r.Name)
	return volume.Response{}
}

func volumeDir(b beegfsDriver, r volume.Request) string {
	// We should use a per volume type to keep track of their individual roots.
	// Then we can use r.Options["beegfsbase"]
	return filepath.Join(b.root, r.Name)
}

// Check if the parent directory (where the volume will be created)
// is of type 'beegfs' using the BEEGFS_MAGIC value.
func isbeegfs(volumepath string) bool {
	log.Debugf("isbeegfs() for %s", volumepath)
	stat := syscall.Statfs_t{}
	err := syscall.Statfs(path.Dir(volumepath), &stat)
	if err != nil {
		log.Errorf("Could not determine filesystem type for %s: %s", volumepath, err)
		return false
	}

	log.Debugf("Type for %s: %d", volumepath, stat.Type)

	// BEEGFS_MAGIC 0x19830326
	return stat.Type == int64(428016422)
}

func createDest(dest string) error {
	fstat, err := os.Lstat(dest)

	if os.IsNotExist(err) {
		if err := os.MkdirAll(dest, 0755); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	if fstat != nil && !fstat.IsDir() {
		return fmt.Errorf("%v already exist and it's not a directory", dest)
	}

	return nil
}
<commit_msg>Implement new Get() and stub List()<commit_after>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os"
	"path"
	"path/filepath"
	"syscall"

	"github.com/docker/go-plugins-helpers/volume"
)

type beegfsDriver struct {
	root string
}

func newBeeGFSDriver(root string) beegfsDriver {
	d := beegfsDriver{
		root: root,
	}

	return d
}

func (b beegfsDriver) Create(r volume.Request) volume.Response {
	log.Infof("Create: %s, %v", r.Name, r.Options)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot create volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return volume.Response{Err: emsg}
	}

	if err := createDest(dest); err != nil {
		return volume.Response{Err: err.Error()}
	}

	return volume.Response{}
}

func (b beegfsDriver) Remove(r volume.Request) volume.Response {
	log.Infof("Remove: %s", r.Name)
	return volume.Response{}
}

func (b beegfsDriver) Path(r volume.Request) volume.Response {
	log.Debugf("Path: %s", r.Name)
	return volume.Response{Mountpoint: volumeDir(b, r)}
}

func (b beegfsDriver) Mount(r volume.Request) volume.Response {
	log.Infof("Mount: %s", r.Name)
	dest := volumeDir(b, r)

	if !isbeegfs(dest) {
		emsg := fmt.Sprintf("Cannot mount volume %s as it's not on a BeeGFS filesystem", dest)
		log.Error(emsg)
		return volume.Response{Err: emsg}
	}

	return volume.Response{Mountpoint: dest}
}

func (b beegfsDriver) Unmount(r volume.Request) volume.Response {
	log.Infof("Unmount: %s", r.Name)
	return volume.Response{}
}

func (b beegfsDriver) Get(r volume.Request) volume.Response {
	log.Info("Get: %s", r.Name)
	return volume.Response{
		Volume: &volume.Volume{
			Name:       r.Name,
			Mountpoint: volumeDir(b, r),
		},
	}
}

func (b beegfsDriver) List(r volume.Request) volume.Response {
	log.Info("List %v", r)
	return volume.Response{}
}

func volumeDir(b beegfsDriver, r volume.Request) string {
	// We should use a per volume type to keep track of their individual roots.
	// Then we can use r.Options["beegfsbase"]
	return filepath.Join(b.root, r.Name)
}

// Check if the parent directory (where the volume will be created)
// is of type 'beegfs' using the BEEGFS_MAGIC value.
func isbeegfs(volumepath string) bool {
	log.Debugf("isbeegfs() for %s", volumepath)
	stat := syscall.Statfs_t{}
	err := syscall.Statfs(path.Dir(volumepath), &stat)
	if err != nil {
		log.Errorf("Could not determine filesystem type for %s: %s", volumepath, err)
		return false
	}

	log.Debugf("Type for %s: %d", volumepath, stat.Type)

	// BEEGFS_MAGIC 0x19830326
	return stat.Type == int64(428016422)
}

func createDest(dest string) error {
	fstat, err := os.Lstat(dest)

	if os.IsNotExist(err) {
		if err := os.MkdirAll(dest, 0755); err != nil {
			return err
		}
	} else if err != nil {
		return err
	}

	if fstat != nil && !fstat.IsDir() {
		return fmt.Errorf("%v already exist and it's not a directory", dest)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "sync"
    "os"

    "github.com/docker/go-plugins-helpers/volume"
    "github.com/docker/engine-api/client"
    "github.com/docker/engine-api/types"
)

type localPersistDriver struct {
    volumes    map[string]string
    mutex      *sync.Mutex
    debug      bool
    name       string
}

func newLocalPersistDriver() localPersistDriver {
    driver := localPersistDriver{
        volumes : map[string]string{},
		mutex   : &sync.Mutex{},
        debug   : true,
        name    : "local-persist",
    }

    // set up the ability to make API calls to the daemon
    defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
    cli, err := client.NewClient("unix:///var/run/docker.sock", "v1.21", nil, defaultHeaders)
    if err != nil {
        panic(err)
    }

    // grab ALL containers...
    options := types.ContainerListOptions{All: true}
    containers, err := cli.ContainerList(options)

    // ...and check to see if any of them belong to this driver and recreate their references
    for _, container := range containers {
        info, err := cli.ContainerInspect(container.ID)
        if err != nil {
            panic(err)
        }

        for _, mount := range info.Mounts {
            if mount.Driver == driver.name {
                // @TODO there could be multiple volumes (mounts) with this { name: source } combo, and while that's okay
                // what if they is the same name with a different source? could that happen? if it could,
                // it'd be bad, so maybe we want to panic here?
                driver.volumes[mount.Name] = mount.Source
            }
        }
    }

    fmt.Printf("Found %d volumes on startup\n", len(driver.volumes))

    return driver
}

func (driver localPersistDriver) Get(req volume.Request) volume.Response {
    fmt.Print("Get Called... ")

    if driver.exists(req.Name) {
        fmt.Printf("[%s] was found\n", req.Name)
        return volume.Response{
            Volume: driver.volume(req.Name),
        }
    }

    fmt.Printf("[%s] not found\n", req.Name)
    return volume.Response{
        Err: fmt.Sprintf("No volume found with the name [%s]", req.Name),
    }

}

func (driver localPersistDriver) List(req volume.Request) volume.Response {
    fmt.Print("List Called... ")

    var volumes []*volume.Volume
    for name, _ := range driver.volumes {
        volumes = append(volumes, driver.volume(name))
    }

    fmt.Printf("Found %d volumes\n", len(volumes))

    return volume.Response{
        Volumes: volumes,
    }
}

func (driver localPersistDriver) Create(req volume.Request) volume.Response {
    fmt.Println("Create Called...")
    mountpoint := req.Options["mountpoint"]
    if mountpoint == "" {
        return volume.Response{ Err: "The `mountpoint` option is required" }
    }

    driver.mutex.Lock()
    defer driver.mutex.Unlock()

    err := os.MkdirAll(mountpoint, 0755)

    if (err != nil) {
        return volume.Response{ Err: err.Error() }
    }

    driver.volumes[req.Name] = mountpoint

    fmt.Printf("Creating volume [%s] mounted at [%s]\n", req.Name, mountpoint)

    return volume.Response{}
}

func (driver localPersistDriver) Remove(req volume.Request) volume.Response {
    fmt.Println("Remove Called...")
    driver.mutex.Lock()
    defer driver.mutex.Unlock()

    delete(driver.volumes, req.Name)

    return volume.Response{}
}

func (driver localPersistDriver) Mount(req volume.Request) volume.Response {
    fmt.Println("Mount Called...")

    return driver.Path(req)
}

func (driver localPersistDriver) Path(req volume.Request) volume.Response {
    fmt.Println("Path Called...")

    return volume.Response{ Mountpoint:  driver.volumes[req.Name] }
}

func (driver localPersistDriver) Unmount(req volume.Request) volume.Response {
    fmt.Println("Unmount Called...")

    return driver.Path(req)
}

func (driver localPersistDriver) exists(name string) bool {
    return driver.volumes[name] != ""
}

func (driver localPersistDriver) volume(name string) *volume.Volume {
    return &volume.Volume{
        Name: name,
        Mountpoint: driver.volumes[name],
    }
}
<commit_msg>Can't create 2 volumes of the same name<commit_after>package main

import (
    "fmt"
    "sync"
    "os"

    "github.com/docker/go-plugins-helpers/volume"
    "github.com/docker/engine-api/client"
    "github.com/docker/engine-api/types"
)

type localPersistDriver struct {
    volumes    map[string]string
    mutex      *sync.Mutex
    debug      bool
    name       string
}

func newLocalPersistDriver() localPersistDriver {
    driver := localPersistDriver{
        volumes : map[string]string{},
		mutex   : &sync.Mutex{},
        debug   : true,
        name    : "local-persist",
    }

    // set up the ability to make API calls to the daemon
    defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
    cli, err := client.NewClient("unix:///var/run/docker.sock", "v1.21", nil, defaultHeaders)
    if err != nil {
        // we want full-on exits here if we have failed already!
        panic(err)
    }

    // grab ALL containers...
    options := types.ContainerListOptions{All: true}
    containers, err := cli.ContainerList(options)

    // ...and check to see if any of them belong to this driver and recreate their references
    for _, container := range containers {
        info, err := cli.ContainerInspect(container.ID)
        if err != nil {
            // we want full-on exits here if we have failed already!
            panic(err)
        }

        for _, mount := range info.Mounts {
            if mount.Driver == driver.name {
                // @TODO there could be multiple volumes (mounts) with this { name: source } combo, and while that's okay
                // what if they is the same name with a different source? could that happen? if it could,
                // it'd be bad, so maybe we want to panic here?
                driver.volumes[mount.Name] = mount.Source
            }
        }
    }

    fmt.Printf("Found %d volumes on startup\n", len(driver.volumes))

    return driver
}

func (driver localPersistDriver) Get(req volume.Request) volume.Response {
    fmt.Print("Get Called... ")

    if driver.exists(req.Name) {
        fmt.Printf("[%s] was found\n", req.Name)
        return volume.Response{
            Volume: driver.volume(req.Name),
        }
    }

    fmt.Printf("[%s] not found\n", req.Name)
    return volume.Response{
        Err: fmt.Sprintf("No volume found with the name [%s]", req.Name),
    }

}

func (driver localPersistDriver) List(req volume.Request) volume.Response {
    fmt.Print("List Called... ")

    var volumes []*volume.Volume
    for name, _ := range driver.volumes {
        volumes = append(volumes, driver.volume(name))
    }

    fmt.Printf("Found %d volumes\n", len(volumes))

    return volume.Response{
        Volumes: volumes,
    }
}

func (driver localPersistDriver) Create(req volume.Request) volume.Response {
    fmt.Println("Create Called...")
    mountpoint := req.Options["mountpoint"]
    if mountpoint == "" {
        return volume.Response{ Err: "The `mountpoint` option is required" }
    }

    driver.mutex.Lock()
    defer driver.mutex.Unlock()

    if driver.exists(req.Name) {
        return volume.Response{ Err: fmt.Sprintf("The volume [%s] already exists", req.Name) }
    }

    err := os.MkdirAll(mountpoint, 0755)

    if err != nil {
        return volume.Response{ Err: err.Error() }
    }

    driver.volumes[req.Name] = mountpoint

    fmt.Printf("Creating volume [%s] mounted at [%s]\n", req.Name, mountpoint)

    return volume.Response{}
}

func (driver localPersistDriver) Remove(req volume.Request) volume.Response {
    fmt.Println("Remove Called...")
    driver.mutex.Lock()
    defer driver.mutex.Unlock()

    delete(driver.volumes, req.Name)

    return volume.Response{}
}

func (driver localPersistDriver) Mount(req volume.Request) volume.Response {
    fmt.Println("Mount Called...")

    return driver.Path(req)
}

func (driver localPersistDriver) Path(req volume.Request) volume.Response {
    fmt.Println("Path Called...")

    return volume.Response{ Mountpoint:  driver.volumes[req.Name] }
}

func (driver localPersistDriver) Unmount(req volume.Request) volume.Response {
    fmt.Println("Unmount Called...")

    return driver.Path(req)
}

func (driver localPersistDriver) exists(name string) bool {
    return driver.volumes[name] != ""
}

func (driver localPersistDriver) volume(name string) *volume.Volume {
    return &volume.Volume{
        Name: name,
        Mountpoint: driver.volumes[name],
    }
}
<|endoftext|>


<commit_before>package main

// index template
const index = `[[define "index"]]<!doctype html>

[[template "head" . ]]

    <ng-include src="'pages/global.html'"></ng-include>

[[template "header" . ]]

[[end]]`

// head items
const head = `[[define "head"]]
    <base href="/">
    <title data-ng-bind="page.title">[[ .title ]]</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="[[ .desc ]]" />[[if .nsfw]]
    <meta name="rating" content="adult" />
    <meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />[[end]]

    <link rel="stylesheet" href="/assets/prim/[[ .primcss ]]" />
    <link rel="stylesheet" href="/assets/styles/[[ .style ]]" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

    [[template "headinclude" . ]][[end]]`

// site header
const header = `[[define "header"]]

                        [[template "navmenuinclude" . ]]
                        [[template "navmenu" . ]]

                <ng-include src="'pages/menus/nav.html'"></ng-include>

                login

                            <ng-include src="'pages/menus/user.html'"></ng-include>

[[end]]`

const navmenu = `[[define "navmenu"]][[ range $ib := .imageboards ]]
[[ $ib.Title ]]

[[end]][[end]]`
<commit_msg>loop through imageboards for auto nav menu<commit_after>package main

// index template
const index = `[[define "index"]]<!doctype html>

[[template "head" . ]]

    <ng-include src="'pages/global.html'"></ng-include>

[[template "header" . ]]

[[end]]`

// head items
const head = `[[define "head"]]
    <base href="/">
    <title data-ng-bind="page.title">[[ .title ]]</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="description" content="[[ .desc ]]" />[[if .nsfw]]
    <meta name="rating" content="adult" />
    <meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />[[end]]

    <link rel="stylesheet" href="/assets/prim/[[ .primcss ]]" />
    <link rel="stylesheet" href="/assets/styles/[[ .style ]]" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

    [[template "headinclude" . ]][[end]]`

// site header
const header = `[[define "header"]]

[[template "navmenuinclude" . ]]
[[template "navmenu" . ]]

                <ng-include src="'pages/menus/nav.html'"></ng-include>

                login

                            <ng-include src="'pages/menus/user.html'"></ng-include>

[[end]]`

const navmenu = `[[define "navmenu"]][[ range $ib := .imageboards ]]
[[ $ib.Title ]]

[[end]][[end]]`
<|endoftext|>


<commit_before>package pipeline

import (
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

const (
	PIPELINEFILE = ".octorunner"
)

/*
 A job is one of the core structs in a pipeline configuration, it defines
 a unit of work. Every job belongs to a stage, and all jobs of a stage are run in parallel.
*/
type Job struct {
	Script       []string `yaml:"script"`
	Image        string   `yaml:"image"`
	Stage        string   `yaml:"stage"`
	AllowFailure bool     `yaml:"allow_failure"`
}

type PipelineConfig struct {
	Jobs   []Job    `yaml:"jobs"`
	Image  string   `yaml:"image"`
	Stages []string `yaml:"stages"`
}

func ParseConfig(file []byte) (PipelineConfig, error) {
	var pipelineConfig PipelineConfig
	err := yaml.Unmarshal(file, &pipelineConfig)
	if err != nil {
		return pipelineConfig, err
	}

	return pipelineConfig, nil
}

func ReadPipelineConfig(directory string) (PipelineConfig, error) {
	var pipelineConfig PipelineConfig
	pipelineConfigPath := path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yaml"}, ""))
	if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
		pipelineConfigPath = path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yml"}, ""))
		if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
			return pipelineConfig, errors.New("Couldn't find .octorunner.yaml or .octorunner.yml in repository")
		}
	}
	pipelineConfigBuf, err := ioutil.ReadFile(pipelineConfigPath)
	if err != nil {
		return pipelineConfig, errors.New(fmt.Sprintf("Error while reading from %s: %v", pipelineConfigPath, err))
	}

	pipelineConfig, err = ParseConfig(pipelineConfigBuf)
	return pipelineConfig, err
}
<commit_msg>Removed stage from jobs struct, added omitempty to image<commit_after>package pipeline

import (
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

const (
	PIPELINEFILE = ".octorunner"
)

/*
 A job is one of the core structs in a pipeline configuration, it defines
 a unit of work. Every job belongs to a stage, and all jobs of a stage are run in parallel.
*/
type Job struct {
	Script       []string `yaml:"script"`
	Image        string   `yaml:"image,omitempty"`
	AllowFailure bool     `yaml:"allow_failure"`
}

type PipelineConfig struct {
	Jobs   []Job    `yaml:"jobs"`
	Image  string   `yaml:"image"`
	Stages []string `yaml:"stages"`
}

func ParseConfig(file []byte) (PipelineConfig, error) {
	var pipelineConfig PipelineConfig
	err := yaml.Unmarshal(file, &pipelineConfig)
	if err != nil {
		return pipelineConfig, err
	}

	return pipelineConfig, nil
}

func ReadPipelineConfig(directory string) (PipelineConfig, error) {
	var pipelineConfig PipelineConfig
	pipelineConfigPath := path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yaml"}, ""))
	if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
		pipelineConfigPath = path.Join(directory, strings.Join([]string{PIPELINEFILE, ".yml"}, ""))
		if _, err := os.Stat(pipelineConfigPath); os.IsNotExist(err) == true {
			return pipelineConfig, errors.New("Couldn't find .octorunner.yaml or .octorunner.yml in repository")
		}
	}
	pipelineConfigBuf, err := ioutil.ReadFile(pipelineConfigPath)
	if err != nil {
		return pipelineConfig, errors.New(fmt.Sprintf("Error while reading from %s: %v", pipelineConfigPath, err))
	}

	pipelineConfig, err = ParseConfig(pipelineConfigBuf)
	return pipelineConfig, err
}
<|endoftext|>


<commit_before>// stack2struct parses raw golang stack traces ([]byte) to a slice of well formated structs.

// As this package will need to evolve with the development of go's stack trace
// format, this is the stack format the package currently works with:
//
//  1: goroutine x [running]:                 <-- ignore this line
//  2: path/to/package.functionName()
//  3: path/to/responsible/file:lineNumber +0xdeadbeef
//  ... repeat 2 + 3 for each stack trace element
//
// To work with this, you need a type satisfying the interface
//
//  type stackTrace interface {
// 	  AddEntry(lineNumber int, packageName string, fileName string, methodName string)
//  }
//
// and from there you can do whatever you like with the accumulated data.
package stack2struct

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
)

// stackTrace is the interface a target stack has to satisfy.
type stackTrace interface {
	AddEntry(lineNumber int, packageName string, fileName string, methodName string)
}

// Current loads the current stacktrace into a given stack
func Current(stack stackTrace) {
	rawStack := make([]byte, 1<<16)
	rawStack = rawStack[:runtime.Stack(rawStack, false)]
	Parse(rawStack, stack)
}

// Parse loads the stack trace (given as trace) into the given stack.
// See Current() on how to obtain a stack trace.
func Parse(trace []byte, stack stackTrace) {
	lines := strings.Split(string(trace), "\n")

	var lineNumber int
	var fileName, packageName, methodName string

	for index, line := range lines[1:] {
		if len(line) == 0 {
			continue
		}
		if index%2 == 0 {
			packageName, methodName = extractPackageName(line)
		} else {
			lineNumber, fileName = extractLineNumberAndFile(line)
			stack.AddEntry(lineNumber, packageName, fileName, methodName)
		}
	}
}

// extractPageName receives a trace line and extracts packageName and
// methodName.
func extractPackageName(line string) (packageName, methodName string) {
	packagePath, packageNameAndFunction := splitAtLastSlash(line)
	parts := strings.Split(packageNameAndFunction, ".")
	packageName = fmt.Sprintf("%s/%s", packagePath, parts[0])
	methodName = parts[1]
	return
}

// extractLineNumberAndFile receives a trace line and extracts lineNumber and
// fileName.
func extractLineNumberAndFile(line string) (lineNumber int, fileName string) {
	_, fileAndLine := splitAtLastSlash(line)
	fileAndLine = removeSpaceAndSuffix(fileAndLine)
	parts := strings.Split(fileAndLine, ":")

	numberAsString := parts[1]
	number, _ := strconv.ParseUint(numberAsString, 10, 32)
	lineNumber = int(number)

	fileName = parts[0]
	return lineNumber, fileName
}

// splitAtLastSlash splits a string at the last found slash and returns the
// respective strings left and right of the slash.
func splitAtLastSlash(line string) (left, right string) {
	parts := strings.Split(line, "/")
	right = parts[len(parts)-1]
	left = strings.Join(parts[:len(parts)-1], "/")
	return
}

// removeSpaceAndSuffix splits the given string at ' ' and cuts off the part
// found after the last space.
func removeSpaceAndSuffix(line string) string {
	parts := strings.Split(line, " ")
	return strings.Join(parts[:len(parts)-1], " ")
}
<commit_msg>replace tabs with spaces<commit_after>// stack2struct parses raw golang stack traces ([]byte) to a slice of well formated structs.

// As this package will need to evolve with the development of go's stack trace
// format, this is the stack format the package currently works with:
//
//  1: goroutine x [running]:                 <-- ignore this line
//  2: path/to/package.functionName()
//  3: path/to/responsible/file:lineNumber +0xdeadbeef
//  ... repeat 2 + 3 for each stack trace element
//
// To work with this, you need a type satisfying the interface
//
//  type stackTrace interface {
//    AddEntry(lineNumber int, packageName string, fileName string, methodName string)
//  }
//
// and from there you can do whatever you like with the accumulated data.
package stack2struct

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
)

// stackTrace is the interface a target stack has to satisfy.
type stackTrace interface {
	AddEntry(lineNumber int, packageName string, fileName string, methodName string)
}

// Current loads the current stacktrace into a given stack
func Current(stack stackTrace) {
	rawStack := make([]byte, 1<<16)
	rawStack = rawStack[:runtime.Stack(rawStack, false)]
	Parse(rawStack, stack)
}

// Parse loads the stack trace (given as trace) into the given stack.
// See Current() on how to obtain a stack trace.
func Parse(trace []byte, stack stackTrace) {
	lines := strings.Split(string(trace), "\n")

	var lineNumber int
	var fileName, packageName, methodName string

	for index, line := range lines[1:] {
		if len(line) == 0 {
			continue
		}
		if index%2 == 0 {
			packageName, methodName = extractPackageName(line)
		} else {
			lineNumber, fileName = extractLineNumberAndFile(line)
			stack.AddEntry(lineNumber, packageName, fileName, methodName)
		}
	}
}

// extractPageName receives a trace line and extracts packageName and
// methodName.
func extractPackageName(line string) (packageName, methodName string) {
	packagePath, packageNameAndFunction := splitAtLastSlash(line)
	parts := strings.Split(packageNameAndFunction, ".")
	packageName = fmt.Sprintf("%s/%s", packagePath, parts[0])
	methodName = parts[1]
	return
}

// extractLineNumberAndFile receives a trace line and extracts lineNumber and
// fileName.
func extractLineNumberAndFile(line string) (lineNumber int, fileName string) {
	_, fileAndLine := splitAtLastSlash(line)
	fileAndLine = removeSpaceAndSuffix(fileAndLine)
	parts := strings.Split(fileAndLine, ":")

	numberAsString := parts[1]
	number, _ := strconv.ParseUint(numberAsString, 10, 32)
	lineNumber = int(number)

	fileName = parts[0]
	return lineNumber, fileName
}

// splitAtLastSlash splits a string at the last found slash and returns the
// respective strings left and right of the slash.
func splitAtLastSlash(line string) (left, right string) {
	parts := strings.Split(line, "/")
	right = parts[len(parts)-1]
	left = strings.Join(parts[:len(parts)-1], "/")
	return
}

// removeSpaceAndSuffix splits the given string at ' ' and cuts off the part
// found after the last space.
func removeSpaceAndSuffix(line string) string {
	parts := strings.Split(line, " ")
	return strings.Join(parts[:len(parts)-1], " ")
}
<|endoftext|>