<commit_before>// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"

	"github.com/google/syzkaller/pkg/ast"
	"github.com/google/syzkaller/pkg/compiler"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/sys/targets"
)

var (
	flagOS        = flag.String("os", runtime.GOOS, "target OS")
	flagBuild     = flag.Bool("build", false, "regenerate arch-specific kernel headers")
	flagSourceDir = flag.String("sourcedir", "", "path to kernel source checkout dir")
	flagIncludes  = flag.String("includedirs", "", "path to other kernel source include dirs separated by commas")
	flagBuildDir  = flag.String("builddir", "", "path to kernel build dir")
	flagArch      = flag.String("arch", "", "comma-separated list of arches to generate (all by default)")
)

type Arch struct {
	target      *targets.Target
	sourceDir   string
	includeDirs string
	buildDir    string
	build       bool
	files       []*File
	err         error
	done        chan bool
}

type File struct {
	arch       *Arch
	name       string
	consts     map[string]uint64
	undeclared map[string]bool
	info       *compiler.ConstInfo
	err        error
	done       chan bool
}

type Extractor interface {
	prepare(sourcedir string, build bool, arches []string) error
	prepareArch(arch *Arch) error
	processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error)
}

var extractors = map[string]Extractor{
	"akaros":  new(akaros),
	"linux":   new(linux),
	"freebsd": new(freebsd),
	"netbsd":  new(netbsd),
	"openbsd": new(openbsd),
	"android": new(linux),
	"fuchsia": new(fuchsia),
	"windows": new(windows),
	"trusty":  new(trusty),
}

func main() {
	failf := func(msg string, args ...interface{}) {
		fmt.Fprintf(os.Stderr, msg+"\n", args...)
		os.Exit(1)
	}
	flag.Parse()
	if *flagBuild && *flagBuildDir != "" {
		failf("-build and -builddir is an invalid combination")
	}

	OS, archArray, files, err := archFileList(*flagOS, *flagArch, flag.Args())
	if err != nil {
		failf("%v", err)
	}

	extractor := extractors[OS]
	if extractor == nil {
		failf("unknown os: %v", OS)
	}
	if err := extractor.prepare(*flagSourceDir, *flagBuild, archArray); err != nil {
		failf("%v", err)
	}

	arches, err := createArches(OS, archArray, files)
	if err != nil {
		failf("%v", err)
	}
	jobC := make(chan interface{}, len(archArray)*len(files))
	for _, arch := range arches {
		jobC <- arch
	}

	for p := 0; p < runtime.GOMAXPROCS(0); p++ {
		go func() {
			for job := range jobC {
				switch j := job.(type) {
				case *Arch:
					infos, err := processArch(extractor, j)
					j.err = err
					close(j.done)
					if j.err == nil {
						for _, f := range j.files {
							f.info = infos[f.name]
							jobC <- f
						}
					}
				case *File:
					j.consts, j.undeclared, j.err = processFile(extractor, j.arch, j)
					close(j.done)
				}
			}
		}()
	}

	failed := false
	for _, arch := range arches {
		fmt.Printf("generating %v/%v...\n", arch.target.OS, arch.target.Arch)
		<-arch.done
		if arch.err != nil {
			failed = true
			fmt.Printf("	%v\n", arch.err)
			continue
		}
		for _, f := range arch.files {
			fmt.Printf("extracting from %v\n", f.name)
			<-f.done
			if f.err != nil {
				failed = true
				fmt.Printf("	%v\n", f.err)
				continue
			}
		}
		fmt.Printf("\n")
	}

	if !failed {
		failed = checkUnsupportedCalls(arches)
	}
	for _, arch := range arches {
		if arch.build {
			os.RemoveAll(arch.buildDir)
		}
	}
	if failed {
		os.Exit(1)
	}
}

func createArches(OS string, archArray, files []string) ([]*Arch, error) {
	var arches []*Arch
	for _, archStr := range archArray {
		buildDir := ""
		if *flagBuild {
			dir, err := ioutil.TempDir("", "syzkaller-kernel-build")
			if err != nil {
				return nil, fmt.Errorf("failed to create temp dir: %v", err)
			}
			buildDir = dir
		} else if *flagBuildDir != "" {
			buildDir = *flagBuildDir
		} else {
			buildDir = *flagSourceDir
		}

		target := targets.Get(OS, archStr)
		if target == nil {
			return nil, fmt.Errorf("unknown arch: %v", archStr)
		}

		arch := &Arch{
			target:      target,
			sourceDir:   *flagSourceDir,
			includeDirs: *flagIncludes,
			buildDir:    buildDir,
			build:       *flagBuild,
			done:        make(chan bool),
		}
		for _, f := range files {
			arch.files = append(arch.files, &File{
				arch: arch,
				name: f,
				done: make(chan bool),
			})
		}
		arches = append(arches, arch)
	}
	return arches, nil
}

func checkUnsupportedCalls(arches []*Arch) bool {
	supported := make(map[string]bool)
	unsupported := make(map[string]string)
	for _, arch := range arches {
		for _, f := range arch.files {
			for name := range f.consts {
				supported[name] = true
			}
			for name := range f.undeclared {
				unsupported[name] = f.name
			}
		}
	}
	failed := false
	for name, file := range unsupported {
		if supported[name] {
			continue
		}
		failed = true
		fmt.Printf("%v: %v is unsupported on all arches (typo?)\n",
			file, name)
	}
	return failed
}

func archFileList(os, arch string, files []string) (string, []string, []string, error) {
	android := false
	if os == "android" {
		android = true
		os = "linux"
	}
	var arches []string
	if arch != "" {
		arches = strings.Split(arch, ",")
	} else {
		for arch := range targets.List[os] {
			arches = append(arches, arch)
		}
		if android {
			arches = []string{"386", "amd64", "arm", "arm64"}
		}
		sort.Strings(arches)
	}
	if len(files) == 0 {
		matches, err := filepath.Glob(filepath.Join("sys", os, "*.txt"))
		if err != nil || len(matches) == 0 {
			return "", nil, nil, fmt.Errorf("failed to find sys files: %v", err)
		}
		manualFiles := map[string]bool{
			// Not upstream, generated on https://github.com/multipath-tcp/mptcp_net-next
			"mptcp.txt": true,
			// This was added to linux-next, then we generated consts and then dropped from linux-next.
			// So for now we can't regenereate this, but it should be resubmitted later
			// so we don't remove the descriptions entirely.
			"fsverity.txt": true,
		}
		androidFiles := map[string]bool{
			"dev_tlk_device.txt": true,
			// This was generated on:
			// https://source.codeaurora.org/quic/la/kernel/msm-4.9 msm-4.9
			"dev_video4linux.txt": true,
		}
		for _, f := range matches {
			f = filepath.Base(f)
			if manualFiles[f] || os == "linux" && android != androidFiles[f] {
				continue
			}
			files = append(files, f)
		}
		sort.Strings(files)
	}
	return os, arches, files, nil
}

func processArch(extractor Extractor, arch *Arch) (map[string]*compiler.ConstInfo, error) {
	errBuf := new(bytes.Buffer)
	eh := func(pos ast.Pos, msg string) {
		fmt.Fprintf(errBuf, "%v: %v\n", pos, msg)
	}
	top := ast.ParseGlob(filepath.Join("sys", arch.target.OS, "*.txt"), eh)
	if top == nil {
		return nil, fmt.Errorf("%v", errBuf.String())
	}
	infos := compiler.ExtractConsts(top, arch.target, eh)
	if infos == nil {
		return nil, fmt.Errorf("%v", errBuf.String())
	}
	if err := extractor.prepareArch(arch); err != nil {
		return nil, err
	}
	return infos, nil
}

func processFile(extractor Extractor, arch *Arch, file *File) (map[string]uint64, map[string]bool, error) {
	inname := filepath.Join("sys", arch.target.OS, file.name)
	outname := strings.TrimSuffix(inname, ".txt") + "_" + arch.target.Arch + ".const"
	if file.info == nil {
		return nil, nil, fmt.Errorf("input file %v is missing", inname)
	}
	if len(file.info.Consts) == 0 {
		os.Remove(outname)
		return nil, nil, nil
	}
	consts, undeclared, err := extractor.processFile(arch, file.info)
	if err != nil {
		return nil, nil, err
	}
	data := compiler.SerializeConsts(consts, undeclared)
	if err := osutil.WriteFile(outname, data); err != nil {
		return nil, nil, fmt.Errorf("failed to write output file: %v", err)
	}
	return consts, undeclared, nil
}
<commit_msg>sys/syz-extract: re-enable extraction for fsverity.txt<commit_after>// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"

	"github.com/google/syzkaller/pkg/ast"
	"github.com/google/syzkaller/pkg/compiler"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/sys/targets"
)

var (
	flagOS        = flag.String("os", runtime.GOOS, "target OS")
	flagBuild     = flag.Bool("build", false, "regenerate arch-specific kernel headers")
	flagSourceDir = flag.String("sourcedir", "", "path to kernel source checkout dir")
	flagIncludes  = flag.String("includedirs", "", "path to other kernel source include dirs separated by commas")
	flagBuildDir  = flag.String("builddir", "", "path to kernel build dir")
	flagArch      = flag.String("arch", "", "comma-separated list of arches to generate (all by default)")
)

type Arch struct {
	target      *targets.Target
	sourceDir   string
	includeDirs string
	buildDir    string
	build       bool
	files       []*File
	err         error
	done        chan bool
}

type File struct {
	arch       *Arch
	name       string
	consts     map[string]uint64
	undeclared map[string]bool
	info       *compiler.ConstInfo
	err        error
	done       chan bool
}

type Extractor interface {
	prepare(sourcedir string, build bool, arches []string) error
	prepareArch(arch *Arch) error
	processFile(arch *Arch, info *compiler.ConstInfo) (map[string]uint64, map[string]bool, error)
}

var extractors = map[string]Extractor{
	"akaros":  new(akaros),
	"linux":   new(linux),
	"freebsd": new(freebsd),
	"netbsd":  new(netbsd),
	"openbsd": new(openbsd),
	"android": new(linux),
	"fuchsia": new(fuchsia),
	"windows": new(windows),
	"trusty":  new(trusty),
}

func main() {
	failf := func(msg string, args ...interface{}) {
		fmt.Fprintf(os.Stderr, msg+"\n", args...)
		os.Exit(1)
	}
	flag.Parse()
	if *flagBuild && *flagBuildDir != "" {
		failf("-build and -builddir is an invalid combination")
	}

	OS, archArray, files, err := archFileList(*flagOS, *flagArch, flag.Args())
	if err != nil {
		failf("%v", err)
	}

	extractor := extractors[OS]
	if extractor == nil {
		failf("unknown os: %v", OS)
	}
	if err := extractor.prepare(*flagSourceDir, *flagBuild, archArray); err != nil {
		failf("%v", err)
	}

	arches, err := createArches(OS, archArray, files)
	if err != nil {
		failf("%v", err)
	}
	jobC := make(chan interface{}, len(archArray)*len(files))
	for _, arch := range arches {
		jobC <- arch
	}

	for p := 0; p < runtime.GOMAXPROCS(0); p++ {
		go func() {
			for job := range jobC {
				switch j := job.(type) {
				case *Arch:
					infos, err := processArch(extractor, j)
					j.err = err
					close(j.done)
					if j.err == nil {
						for _, f := range j.files {
							f.info = infos[f.name]
							jobC <- f
						}
					}
				case *File:
					j.consts, j.undeclared, j.err = processFile(extractor, j.arch, j)
					close(j.done)
				}
			}
		}()
	}

	failed := false
	for _, arch := range arches {
		fmt.Printf("generating %v/%v...\n", arch.target.OS, arch.target.Arch)
		<-arch.done
		if arch.err != nil {
			failed = true
			fmt.Printf("	%v\n", arch.err)
			continue
		}
		for _, f := range arch.files {
			fmt.Printf("extracting from %v\n", f.name)
			<-f.done
			if f.err != nil {
				failed = true
				fmt.Printf("	%v\n", f.err)
				continue
			}
		}
		fmt.Printf("\n")
	}

	if !failed {
		failed = checkUnsupportedCalls(arches)
	}
	for _, arch := range arches {
		if arch.build {
			os.RemoveAll(arch.buildDir)
		}
	}
	if failed {
		os.Exit(1)
	}
}

func createArches(OS string, archArray, files []string) ([]*Arch, error) {
	var arches []*Arch
	for _, archStr := range archArray {
		buildDir := ""
		if *flagBuild {
			dir, err := ioutil.TempDir("", "syzkaller-kernel-build")
			if err != nil {
				return nil, fmt.Errorf("failed to create temp dir: %v", err)
			}
			buildDir = dir
		} else if *flagBuildDir != "" {
			buildDir = *flagBuildDir
		} else {
			buildDir = *flagSourceDir
		}

		target := targets.Get(OS, archStr)
		if target == nil {
			return nil, fmt.Errorf("unknown arch: %v", archStr)
		}

		arch := &Arch{
			target:      target,
			sourceDir:   *flagSourceDir,
			includeDirs: *flagIncludes,
			buildDir:    buildDir,
			build:       *flagBuild,
			done:        make(chan bool),
		}
		for _, f := range files {
			arch.files = append(arch.files, &File{
				arch: arch,
				name: f,
				done: make(chan bool),
			})
		}
		arches = append(arches, arch)
	}
	return arches, nil
}

func checkUnsupportedCalls(arches []*Arch) bool {
	supported := make(map[string]bool)
	unsupported := make(map[string]string)
	for _, arch := range arches {
		for _, f := range arch.files {
			for name := range f.consts {
				supported[name] = true
			}
			for name := range f.undeclared {
				unsupported[name] = f.name
			}
		}
	}
	failed := false
	for name, file := range unsupported {
		if supported[name] {
			continue
		}
		failed = true
		fmt.Printf("%v: %v is unsupported on all arches (typo?)\n",
			file, name)
	}
	return failed
}

func archFileList(os, arch string, files []string) (string, []string, []string, error) {
	android := false
	if os == "android" {
		android = true
		os = "linux"
	}
	var arches []string
	if arch != "" {
		arches = strings.Split(arch, ",")
	} else {
		for arch := range targets.List[os] {
			arches = append(arches, arch)
		}
		if android {
			arches = []string{"386", "amd64", "arm", "arm64"}
		}
		sort.Strings(arches)
	}
	if len(files) == 0 {
		matches, err := filepath.Glob(filepath.Join("sys", os, "*.txt"))
		if err != nil || len(matches) == 0 {
			return "", nil, nil, fmt.Errorf("failed to find sys files: %v", err)
		}
		manualFiles := map[string]bool{
			// Not upstream, generated on https://github.com/multipath-tcp/mptcp_net-next
			"mptcp.txt": true,
		}
		androidFiles := map[string]bool{
			"dev_tlk_device.txt": true,
			// This was generated on:
			// https://source.codeaurora.org/quic/la/kernel/msm-4.9 msm-4.9
			"dev_video4linux.txt": true,
		}
		for _, f := range matches {
			f = filepath.Base(f)
			if manualFiles[f] || os == "linux" && android != androidFiles[f] {
				continue
			}
			files = append(files, f)
		}
		sort.Strings(files)
	}
	return os, arches, files, nil
}

func processArch(extractor Extractor, arch *Arch) (map[string]*compiler.ConstInfo, error) {
	errBuf := new(bytes.Buffer)
	eh := func(pos ast.Pos, msg string) {
		fmt.Fprintf(errBuf, "%v: %v\n", pos, msg)
	}
	top := ast.ParseGlob(filepath.Join("sys", arch.target.OS, "*.txt"), eh)
	if top == nil {
		return nil, fmt.Errorf("%v", errBuf.String())
	}
	infos := compiler.ExtractConsts(top, arch.target, eh)
	if infos == nil {
		return nil, fmt.Errorf("%v", errBuf.String())
	}
	if err := extractor.prepareArch(arch); err != nil {
		return nil, err
	}
	return infos, nil
}

func processFile(extractor Extractor, arch *Arch, file *File) (map[string]uint64, map[string]bool, error) {
	inname := filepath.Join("sys", arch.target.OS, file.name)
	outname := strings.TrimSuffix(inname, ".txt") + "_" + arch.target.Arch + ".const"
	if file.info == nil {
		return nil, nil, fmt.Errorf("input file %v is missing", inname)
	}
	if len(file.info.Consts) == 0 {
		os.Remove(outname)
		return nil, nil, nil
	}
	consts, undeclared, err := extractor.processFile(arch, file.info)
	if err != nil {
		return nil, nil, err
	}
	data := compiler.SerializeConsts(consts, undeclared)
	if err := osutil.WriteFile(outname, data); err != nil {
		return nil, nil, fmt.Errorf("failed to write output file: %v", err)
	}
	return consts, undeclared, nil
}
<|endoftext|>


<commit_before>/*
                       WARNING WARNING WARNING

                Attention all potential contributors

   This testfile is not in the best state. We've been slowly transitioning
   from the built in "testing" package to using Ginkgo. As you can see, we've
   changed the format, but a lot of the setup, test body, descriptions, etc
   are either hardcoded, completely lacking, or misleading.

   For example:

   Describe("Testing with ginkgo"...)      // This is not a great description
   It("TestDoesSoemthing"...)              // This is a horrible description

   Describe("create-user command"...       // Describe the actual object under test
   It("creates a user when provided ..."   // this is more descriptive

   For good examples of writing Ginkgo tests for the cli, refer to

   src/github.com/cloudfoundry/cli/cf/commands/application/delete_app_test.go
   src/github.com/cloudfoundry/cli/cf/terminal/ui_test.go
   src/github.com/cloudfoundry/loggregator_consumer/consumer_test.go
*/

package user_test

import (
	. "github.com/cloudfoundry/cli/cf/commands/user"
	"github.com/cloudfoundry/cli/cf/models"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "github.com/cloudfoundry/cli/testhelpers/matchers"
)

var _ = Describe("Testing with ginkgo", func() {
	It("TestUnsetOrgRoleFailsWithUsage", func() {
		userRepo := &testapi.FakeUserRepository{}
		requirementsFactory := &testreq.FakeReqFactory{}

		ui := callUnsetOrgRole([]string{}, userRepo, requirementsFactory)
		Expect(ui.FailedWithUsage).To(BeTrue())

		ui = callUnsetOrgRole([]string{"username"}, userRepo, requirementsFactory)
		Expect(ui.FailedWithUsage).To(BeTrue())

		ui = callUnsetOrgRole([]string{"username", "org"}, userRepo, requirementsFactory)
		Expect(ui.FailedWithUsage).To(BeTrue())

		ui = callUnsetOrgRole([]string{"username", "org", "role"}, userRepo, requirementsFactory)
		Expect(ui.FailedWithUsage).To(BeFalse())
	})
	It("TestUnsetOrgRoleRequirements", func() {

		userRepo := &testapi.FakeUserRepository{}
		requirementsFactory := &testreq.FakeReqFactory{}
		args := []string{"username", "org", "role"}

		requirementsFactory.LoginSuccess = false
		callUnsetOrgRole(args, userRepo, requirementsFactory)
		Expect(testcmd.CommandDidPassRequirements).To(BeFalse())

		requirementsFactory.LoginSuccess = true
		callUnsetOrgRole(args, userRepo, requirementsFactory)
		Expect(testcmd.CommandDidPassRequirements).To(BeTrue())

		Expect(requirementsFactory.UserUsername).To(Equal("username"))
		Expect(requirementsFactory.OrganizationName).To(Equal("org"))
	})
	It("TestUnsetOrgRole", func() {

		userRepo := &testapi.FakeUserRepository{}
		user := models.UserFields{}
		user.Username = "some-user"
		user.Guid = "some-user-guid"
		org := models.Organization{}
		org.Name = "some-org"
		org.Guid = "some-org-guid"
		requirementsFactory := &testreq.FakeReqFactory{
			LoginSuccess: true,
			UserFields:   user,
			Organization: org,
		}
		args := []string{"my-username", "my-org", "OrgManager"}

		ui := callUnsetOrgRole(args, userRepo, requirementsFactory)

		Expect(ui.Outputs).To(ContainSubstrings(
			[]string{"Removing role", "OrgManager", "my-username", "my-org", "my-user"},
			[]string{"OK"},
		))

		Expect(userRepo.UnsetOrgRoleRole).To(Equal(models.ORG_MANAGER))
		Expect(userRepo.UnsetOrgRoleUserGuid).To(Equal("some-user-guid"))
		Expect(userRepo.UnsetOrgRoleOrganizationGuid).To(Equal("some-org-guid"))
	})
})

func callUnsetOrgRole(args []string, userRepo *testapi.FakeUserRepository, requirementsFactory *testreq.FakeReqFactory) (ui *testterm.FakeUI) {
	ui = &testterm.FakeUI{}
	configRepo := testconfig.NewRepositoryWithDefaults()
	cmd := NewUnsetOrgRole(ui, configRepo, userRepo)
	testcmd.RunCommand(cmd, args, requirementsFactory)
	return
}
<commit_msg>Cleanup unset_org_role test<commit_after>package user_test

import (
	. "github.com/cloudfoundry/cli/cf/commands/user"
	"github.com/cloudfoundry/cli/cf/configuration"
	"github.com/cloudfoundry/cli/cf/models"
	testapi "github.com/cloudfoundry/cli/testhelpers/api"
	testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "github.com/cloudfoundry/cli/testhelpers/matchers"
)

var _ = Describe("unset-org-role command", func() {
	var (
		ui                  *testterm.FakeUI
		userRepo            *testapi.FakeUserRepository
		configRepo          configuration.ReadWriter
		requirementsFactory *testreq.FakeReqFactory
	)

	runCommand := func(args ...string) {
		cmd := NewUnsetOrgRole(ui, configRepo, userRepo)
		testcmd.RunCommand(cmd, args, requirementsFactory)
	}

	BeforeEach(func() {
		ui = &testterm.FakeUI{}
		userRepo = &testapi.FakeUserRepository{}
		requirementsFactory = &testreq.FakeReqFactory{}
		configRepo = testconfig.NewRepositoryWithDefaults()
	})

	It("fails with usage when invoked without exactly three args", func() {
		runCommand("username", "org")
		Expect(ui.FailedWithUsage).To(BeTrue())

		runCommand("woah", "too", "many", "args")
		Expect(ui.FailedWithUsage).To(BeTrue())
	})

	Describe("requirements", func() {
		It("fails when not logged in", func() {
			requirementsFactory.LoginSuccess = false
			runCommand("username", "org", "role")
			Expect(testcmd.CommandDidPassRequirements).To(BeFalse())
		})

		It("succeeds when logged in", func() {
			requirementsFactory.LoginSuccess = true
			runCommand("username", "org", "role")
			Expect(testcmd.CommandDidPassRequirements).To(BeTrue())

			Expect(requirementsFactory.UserUsername).To(Equal("username"))
			Expect(requirementsFactory.OrganizationName).To(Equal("org"))
		})
	})

	Context("when logged in", func() {
		BeforeEach(func() {
			requirementsFactory.LoginSuccess = true

			user := models.UserFields{}
			user.Username = "some-user"
			user.Guid = "some-user-guid"
			org := models.Organization{}
			org.Name = "some-org"
			org.Guid = "some-org-guid"

			requirementsFactory.UserFields = user
			requirementsFactory.Organization = org
		})

		It("unsets a user's org role", func() {
			runCommand("my-username", "my-org", "OrgManager")

			Expect(ui.Outputs).To(ContainSubstrings(
				[]string{"Removing role", "OrgManager", "my-username", "my-org", "my-user"},
				[]string{"OK"},
			))

			Expect(userRepo.UnsetOrgRoleRole).To(Equal(models.ORG_MANAGER))
			Expect(userRepo.UnsetOrgRoleUserGuid).To(Equal("some-user-guid"))
			Expect(userRepo.UnsetOrgRoleOrganizationGuid).To(Equal("some-org-guid"))
		})
	})
})
<|endoftext|>


<commit_before>/*
 * This file is part of easyKV.
 * © 2016 The easyKV Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package etcd

import (
	"errors"

	"github.com/HeavyHorst/easyKV"
	"github.com/HeavyHorst/easyKV/etcd/etcdv2"
	"github.com/HeavyHorst/easyKV/etcd/etcdv3"
)

// ErrUnknownAPILevel is returned if no valid api level is given
var ErrUnknownAPILevel = errors.New("unknown etcd api level - must be 2 or 3")

// NewEtcdClient returns an *etcd{2,3}.Client with a connection to named machines.
func NewEtcdClient(opts ...Option) (easyKV.ReadWatcher, error) {
	var options Options
	for _, o := range opts {
		o(&options)
	}

	if options.Version == 3 {
		return etcdv3.NewEtcdClient(options.Nodes, options.TLS.ClientCert, options.TLS.ClientKey, options.TLS.ClientCaKeys, options.Auth.BasicAuth, options.Auth.Username, options.Auth.Password)
	}

	if options.Version == 2 {
		return etcdv2.NewEtcdClient(options.Nodes, options.TLS.ClientCert, options.TLS.ClientKey, options.TLS.ClientCaKeys, options.Auth.BasicAuth, options.Auth.Username, options.Auth.Password)
	}

	return nil, ErrUnknownApiLevel
}
<commit_msg>fixed typo<commit_after>/*
 * This file is part of easyKV.
 * © 2016 The easyKV Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package etcd

import (
	"errors"

	"github.com/HeavyHorst/easyKV"
	"github.com/HeavyHorst/easyKV/etcd/etcdv2"
	"github.com/HeavyHorst/easyKV/etcd/etcdv3"
)

// ErrUnknownAPILevel is returned if no valid api level is given
var ErrUnknownAPILevel = errors.New("unknown etcd api level - must be 2 or 3")

// NewEtcdClient returns an *etcd{2,3}.Client with a connection to named machines.
func NewEtcdClient(opts ...Option) (easyKV.ReadWatcher, error) {
	var options Options
	for _, o := range opts {
		o(&options)
	}

	if options.Version == 3 {
		return etcdv3.NewEtcdClient(options.Nodes, options.TLS.ClientCert, options.TLS.ClientKey, options.TLS.ClientCaKeys, options.Auth.BasicAuth, options.Auth.Username, options.Auth.Password)
	}

	if options.Version == 2 {
		return etcdv2.NewEtcdClient(options.Nodes, options.TLS.ClientCert, options.TLS.ClientKey, options.TLS.ClientCaKeys, options.Auth.BasicAuth, options.Auth.Username, options.Auth.Password)
	}

	return nil, ErrUnknownAPILevel
}
<|endoftext|>


<commit_before>package etcd

import (
	"bytes"
	"encoding/gob"
	"net/http"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	etcdraft "github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/rafthttp"
	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
)

type tag struct {
	UID  uint64
	Data []byte
}

type future struct {
	res chan raft.Result
}

func (f *future) ResultCh() <-chan raft.Result {
	return f.res
}

// Wrapper wraps an etcd/raft.Node and implements relab/raft.Raft and
// etcd/rafthttp.Raft.
type Wrapper struct {
	n         etcdraft.Node
	sm        raft.StateMachine
	storage   *etcdraft.MemoryStorage
	transport *rafthttp.Transport
	logger    logrus.FieldLogger

	heartbeat time.Duration

	uid       uint64
	propLock  sync.RWMutex
	proposals map[uint64]chan<- raft.Result
}

func (w *Wrapper) encodeProposal(data []byte) (uint64, []byte, error) {
	uid := atomic.AddUint64(&w.uid, 1)
	t := &tag{
		UID:  uid,
		Data: data,
	}

	var b bytes.Buffer
	enc := gob.NewEncoder(&b)
	err := enc.Encode(t)
	if err != nil {
		return 0, nil, err
	}

	return uid, b.Bytes(), nil
}

func (w *Wrapper) newFuture(uid uint64) raft.Future {
	res := make(chan raft.Result, 1)
	future := &future{res}

	w.propLock.Lock()
	w.proposals[uid] = res
	w.propLock.Unlock()

	return future
}

func (w *Wrapper) deleteFuture(uid uint64) {
	w.propLock.Lock()
	delete(w.proposals, uid)
	w.propLock.Unlock()
}

// Hack: Returns nil if commit fails to decode. This when etcd append the
// initial configuration to the log. Fix: Make state machine return
// clientID/clientSeq along with result, we can then use that to figure out who
// to respond to instead of using the tag struct.
func (w *Wrapper) decodeCommit(commit []byte) *tag {
	b := bytes.NewBuffer(commit)
	dec := gob.NewDecoder(b)

	var t tag
	err := dec.Decode(&t)

	if err != nil {
		return nil
	}

	return &t
}

func NewRaft(logger logrus.FieldLogger,
	sm raft.StateMachine, storage *etcdraft.MemoryStorage, cfg *etcdraft.Config,
	peers []etcdraft.Peer, heartbeat time.Duration,
) *Wrapper {
	w := &Wrapper{
		sm:        sm,
		storage:   storage,
		heartbeat: heartbeat,
		proposals: make(map[uint64]chan<- raft.Result),
		logger:    logger,
	}
	w.n = etcdraft.StartNode(cfg, append(peers, etcdraft.Peer{ID: cfg.ID}))

	ss := &stats.ServerStats{}
	ss.Initialize()

	transport := &rafthttp.Transport{
		ID:          types.ID(cfg.ID),
		ClusterID:   1,
		Raft:        w,
		ServerStats: ss,
		LeaderStats: stats.NewLeaderStats(strconv.FormatUint(cfg.ID, 10)),
		ErrorC:      make(chan error),
	}
	err := transport.Start()

	if err != nil {
		panic("start transport: " + err.Error())
	}

	for i, peer := range peers {
		w.logger.WithFields(logrus.Fields{
			"i":       i,
			"id":      peer.ID,
			"context": string(peer.Context),
		}).Warnln("Add peer")

		transport.AddPeer(types.ID(peer.ID), []string{string(peer.Context)})
	}

	w.transport = transport

	go w.run()
	return w
}

func (w *Wrapper) Handler() http.Handler {
	return w.transport.Handler()
}

func (w *Wrapper) ProposeCmd(ctx context.Context, req []byte) (raft.Future, error) {
	w.logger.Warnln("ProposeCmd")

	uid, prop, err := w.encodeProposal(req)

	if err != nil {
		return nil, err
	}

	future := w.newFuture(uid)
	err = w.n.Propose(ctx, prop)

	if err != nil {
		delete(w.proposals, uid)
		return nil, err
	}

	return future, nil
}

func (w *Wrapper) ReadCmd(context.Context, []byte) (raft.Future, error) {
	w.logger.Warnln("ProposeRead")
	return nil, nil
}

func (w *Wrapper) ProposeConf(ctx context.Context, req *commonpb.ReconfRequest) (raft.Future, error) {
	cc := raftpb.ConfChange{
		Type:    raftpb.ConfChangeType(req.ReconfType),
		ID:      req.ServerID, // ?
		NodeID:  req.ServerID,
		Context: []byte(""), // ?
	}

	err := w.n.ProposeConfChange(ctx, cc)

	w.logger.WithError(err).WithFields(logrus.Fields{
		"req": req,
		"cc":  cc,
	}).Warnln("ProposeConf")

	if err != nil {
		return nil, err
	}

	panic("ProposeConf not implemented")
}

func (w *Wrapper) run() {
	s := time.NewTicker(w.heartbeat)

	for {
		select {
		case <-s.C:
			w.n.Tick()
		case rd := <-w.n.Ready():
			w.logger.WithField("rd", rd).Warnln("Ready")
			w.storage.Append(rd.Entries)
			if !etcdraft.IsEmptyHardState(rd.HardState) {
				w.logger.WithField("hardstate", rd.HardState).Warnln("HardState")
				w.storage.SetHardState(rd.HardState)
			}
			if !etcdraft.IsEmptySnap(rd.Snapshot) {
				w.logger.WithField("snapshot", rd.Snapshot).Warnln("Snapshot")
				w.storage.ApplySnapshot(rd.Snapshot)
			}
			w.logger.WithField("messages", rd.Messages).Warnln("Sending")
			w.transport.Send(rd.Messages)
			for _, entry := range rd.CommittedEntries {
				t := w.decodeCommit(entry.Data)

				switch entry.Type {
				case raftpb.EntryNormal:
					w.logger.WithField(
						"entry", etcdraft.DescribeEntry(entry, nil),
					).Warnln("Committed normal entry")

					w.handleNormal(&entry, t)
				case raftpb.EntryConfChange:
					w.logger.WithField(
						"entry", etcdraft.DescribeEntry(entry, nil),
					).Warnln("Committed conf change entry")

					w.handleConfChange(&entry, t)
				}

				// TODO Respond to future.
			}
			w.logger.Warnln("Advance")
			w.n.Advance()
		}
	}
}

func (w *Wrapper) handleNormal(entry *raftpb.Entry, t *tag) {
	if t == nil {
		return
	}

	res := w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryNormal,
		Data:      t.Data,
	})

	w.propLock.RLock()
	respCh, ok := w.proposals[t.UID]
	w.propLock.RUnlock()

	if ok {
		respCh <- raft.Result{
			Index: entry.Index,
			Value: res,
		}
		w.deleteFuture(t.UID)
	}
}

func (w *Wrapper) handleConfChange(entry *raftpb.Entry, t *tag) {
	var cc raftpb.ConfChange
	err := cc.Unmarshal(entry.Data)

	if err != nil {
		panic("unmarshal conf change: " + err.Error())
	}

	w.logger.WithField("cc", cc).Warnln("Applying conf change")
	w.n.ApplyConfChange(cc)
	switch cc.Type {
	case raftpb.ConfChangeAddNode:
		if len(cc.Context) > 0 {
			w.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
		}
	case raftpb.ConfChangeRemoveNode:
		tid := types.ID(cc.NodeID)
		if tid == w.transport.ID {
			w.logger.Warnln("Shutting down")
			return
		}
		w.transport.RemovePeer(tid)
	}

	if t == nil {
		return
	}

	// Inform state machine about new configuration.
	w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryReconf,
		Data:      t.Data,
	})

	// TODO Respond / Cleanup future.
}

func (w *Wrapper) Process(ctx context.Context, m raftpb.Message) error {
	w.logger.WithField("m", m).Warnln("Process")
	return w.n.Step(ctx, m)
}
func (w *Wrapper) IsIDRemoved(id uint64) bool                               { return false }
func (w *Wrapper) ReportUnreachable(id uint64)                              {}
func (w *Wrapper) ReportSnapshot(id uint64, status etcdraft.SnapshotStatus) {}
<commit_msg>etcd/raft.go: Remove debug logging<commit_after>package etcd

import (
	"bytes"
	"encoding/gob"
	"net/http"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	etcdraft "github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/rafthttp"
	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
)

type tag struct {
	UID  uint64
	Data []byte
}

type future struct {
	res chan raft.Result
}

func (f *future) ResultCh() <-chan raft.Result {
	return f.res
}

// Wrapper wraps an etcd/raft.Node and implements relab/raft.Raft and
// etcd/rafthttp.Raft.
type Wrapper struct {
	n         etcdraft.Node
	sm        raft.StateMachine
	storage   *etcdraft.MemoryStorage
	transport *rafthttp.Transport
	logger    logrus.FieldLogger

	heartbeat time.Duration

	uid       uint64
	propLock  sync.RWMutex
	proposals map[uint64]chan<- raft.Result
}

func (w *Wrapper) encodeProposal(data []byte) (uint64, []byte, error) {
	uid := atomic.AddUint64(&w.uid, 1)
	t := &tag{
		UID:  uid,
		Data: data,
	}

	var b bytes.Buffer
	enc := gob.NewEncoder(&b)
	err := enc.Encode(t)
	if err != nil {
		return 0, nil, err
	}

	return uid, b.Bytes(), nil
}

func (w *Wrapper) newFuture(uid uint64) raft.Future {
	res := make(chan raft.Result, 1)
	future := &future{res}

	w.propLock.Lock()
	w.proposals[uid] = res
	w.propLock.Unlock()

	return future
}

func (w *Wrapper) deleteFuture(uid uint64) {
	w.propLock.Lock()
	delete(w.proposals, uid)
	w.propLock.Unlock()
}

// Hack: Returns nil if commit fails to decode. This when etcd append the
// initial configuration to the log. Fix: Make state machine return
// clientID/clientSeq along with result, we can then use that to figure out who
// to respond to instead of using the tag struct.
func (w *Wrapper) decodeCommit(commit []byte) *tag {
	b := bytes.NewBuffer(commit)
	dec := gob.NewDecoder(b)

	var t tag
	err := dec.Decode(&t)

	if err != nil {
		return nil
	}

	return &t
}

func NewRaft(logger logrus.FieldLogger,
	sm raft.StateMachine, storage *etcdraft.MemoryStorage, cfg *etcdraft.Config,
	peers []etcdraft.Peer, heartbeat time.Duration,
) *Wrapper {
	w := &Wrapper{
		sm:        sm,
		storage:   storage,
		heartbeat: heartbeat,
		proposals: make(map[uint64]chan<- raft.Result),
		logger:    logger,
	}
	w.n = etcdraft.StartNode(cfg, append(peers, etcdraft.Peer{ID: cfg.ID}))

	ss := &stats.ServerStats{}
	ss.Initialize()

	transport := &rafthttp.Transport{
		ID:          types.ID(cfg.ID),
		ClusterID:   1,
		Raft:        w,
		ServerStats: ss,
		LeaderStats: stats.NewLeaderStats(strconv.FormatUint(cfg.ID, 10)),
		ErrorC:      make(chan error),
	}
	err := transport.Start()

	if err != nil {
		panic("start transport: " + err.Error())
	}

	for i, peer := range peers {
		w.logger.WithFields(logrus.Fields{
			"i":       i,
			"id":      peer.ID,
			"context": string(peer.Context),
		}).Warnln("Add peer")

		transport.AddPeer(types.ID(peer.ID), []string{string(peer.Context)})
	}

	w.transport = transport

	go w.run()
	return w
}

func (w *Wrapper) Handler() http.Handler {
	return w.transport.Handler()
}

func (w *Wrapper) ProposeCmd(ctx context.Context, req []byte) (raft.Future, error) {
	uid, prop, err := w.encodeProposal(req)

	if err != nil {
		return nil, err
	}

	future := w.newFuture(uid)
	err = w.n.Propose(ctx, prop)

	if err != nil {
		delete(w.proposals, uid)
		return nil, err
	}

	return future, nil
}

func (w *Wrapper) ReadCmd(context.Context, []byte) (raft.Future, error) {
	w.logger.Warnln("ProposeRead")
	return nil, nil
}

func (w *Wrapper) ProposeConf(ctx context.Context, req *commonpb.ReconfRequest) (raft.Future, error) {
	cc := raftpb.ConfChange{
		Type:    raftpb.ConfChangeType(req.ReconfType),
		ID:      req.ServerID, // ?
		NodeID:  req.ServerID,
		Context: []byte(""), // ?
	}

	err := w.n.ProposeConfChange(ctx, cc)

	w.logger.WithError(err).WithFields(logrus.Fields{
		"req": req,
		"cc":  cc,
	}).Warnln("ProposeConf")

	if err != nil {
		return nil, err
	}

	panic("ProposeConf not implemented")
}

func (w *Wrapper) run() {
	s := time.NewTicker(w.heartbeat)

	for {
		select {
		case <-s.C:
			w.n.Tick()
		case rd := <-w.n.Ready():
			w.storage.Append(rd.Entries)
			if !etcdraft.IsEmptyHardState(rd.HardState) {
				w.storage.SetHardState(rd.HardState)
			}
			if !etcdraft.IsEmptySnap(rd.Snapshot) {
				w.storage.ApplySnapshot(rd.Snapshot)
			}
			w.transport.Send(rd.Messages)
			for _, entry := range rd.CommittedEntries {
				t := w.decodeCommit(entry.Data)

				switch entry.Type {
				case raftpb.EntryNormal:
					w.handleNormal(&entry, t)
				case raftpb.EntryConfChange:
					w.logger.WithField(
						"entry", etcdraft.DescribeEntry(entry, nil),
					).Warnln("Committed conf change entry")

					w.handleConfChange(&entry, t)
				}

				// TODO Respond to future.
			}
			w.n.Advance()
		}
	}
}

func (w *Wrapper) handleNormal(entry *raftpb.Entry, t *tag) {
	if t == nil {
		return
	}

	res := w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryNormal,
		Data:      t.Data,
	})

	w.propLock.RLock()
	respCh, ok := w.proposals[t.UID]
	w.propLock.RUnlock()

	if ok {
		respCh <- raft.Result{
			Index: entry.Index,
			Value: res,
		}
		w.deleteFuture(t.UID)
	}
}

func (w *Wrapper) handleConfChange(entry *raftpb.Entry, t *tag) {
	var cc raftpb.ConfChange
	err := cc.Unmarshal(entry.Data)

	if err != nil {
		panic("unmarshal conf change: " + err.Error())
	}

	w.logger.WithField("cc", cc).Warnln("Applying conf change")
	w.n.ApplyConfChange(cc)
	switch cc.Type {
	case raftpb.ConfChangeAddNode:
		if len(cc.Context) > 0 {
			w.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
		}
	case raftpb.ConfChangeRemoveNode:
		tid := types.ID(cc.NodeID)
		if tid == w.transport.ID {
			w.logger.Warnln("Shutting down")
			return
		}
		w.transport.RemovePeer(tid)
	}

	if t == nil {
		return
	}

	// Inform state machine about new configuration.
	w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryReconf,
		Data:      t.Data,
	})

	// TODO Respond / Cleanup future.
}

func (w *Wrapper) Process(ctx context.Context, m raftpb.Message) error {
	return w.n.Step(ctx, m)
}
func (w *Wrapper) IsIDRemoved(id uint64) bool                               { return false }
func (w *Wrapper) ReportUnreachable(id uint64)                              {}
func (w *Wrapper) ReportSnapshot(id uint64, status etcdraft.SnapshotStatus) {}
<|endoftext|>


<commit_before>package eth

import (
	"fmt"
	// "github.com/ethereum/go-ethereum/logger"
)

const (
	ErrMsgTooLarge = iota
	ErrDecode
	ErrInvalidMsgCode
	ErrProtocolVersionMismatch
	ErrNetworkIdMismatch
	ErrGenesisBlockMismatch
	ErrNoStatusMsg
	ErrExtraStatusMsg
	ErrInvalidBlock
)

var errorToString = map[int]string{
	ErrMsgTooLarge:             "Message too long",
	ErrDecode:                  "Invalid message",
	ErrInvalidMsgCode:          "Invalid message code",
	ErrProtocolVersionMismatch: "Protocol version mismatch",
	ErrNetworkIdMismatch:       "NetworkId mismatch",
	ErrGenesisBlockMismatch:    "Genesis block mismatch",
	ErrNoStatusMsg:             "No status message",
	ErrExtraStatusMsg:          "Extra status message",
	ErrInvalidBlock:            "Invalid block",
}

type protocolError struct {
	Code    int
	fatal   bool
	message string
	format  string
	params  []interface{}
	// size    int
}

func newProtocolError(code int, format string, params ...interface{}) *protocolError {
	return &protocolError{Code: code, format: format, params: params}
}

func ProtocolError(code int, format string, params ...interface{}) (err *protocolError) {
	err = newProtocolError(code, format, params...)
	// report(err)
	if err.Fatal() {
		logger.Errorln(err)
	} else {
		logger.Debugln(err)
	}
	return
}

func (self protocolError) Error() (message string) {
	message = self.message
	if message == "" {
		message, ok := errorToString[self.Code]
		if !ok {
			panic("invalid error code")
		}
		if self.format != "" {
			message += ": " + fmt.Sprintf(self.format, self.params...)
		}
		self.message = message
	}
	return
}

func (self *protocolError) Fatal() bool {
	return self.fatal
}
<commit_msg>no logging in error (to be refactored into p2p)<commit_after>package eth

import (
	"fmt"
)

const (
	ErrMsgTooLarge = iota
	ErrDecode
	ErrInvalidMsgCode
	ErrProtocolVersionMismatch
	ErrNetworkIdMismatch
	ErrGenesisBlockMismatch
	ErrNoStatusMsg
	ErrExtraStatusMsg
	ErrInvalidBlock
)

var errorToString = map[int]string{
	ErrMsgTooLarge:             "Message too long",
	ErrDecode:                  "Invalid message",
	ErrInvalidMsgCode:          "Invalid message code",
	ErrProtocolVersionMismatch: "Protocol version mismatch",
	ErrNetworkIdMismatch:       "NetworkId mismatch",
	ErrGenesisBlockMismatch:    "Genesis block mismatch",
	ErrNoStatusMsg:             "No status message",
	ErrExtraStatusMsg:          "Extra status message",
	ErrInvalidBlock:            "Invalid block",
}

type protocolError struct {
	Code    int
	fatal   bool
	message string
	format  string
	params  []interface{}
	// size    int
}

func newProtocolError(code int, format string, params ...interface{}) *protocolError {
	return &protocolError{Code: code, format: format, params: params}
}

func ProtocolError(code int, format string, params ...interface{}) (err *protocolError) {
	err = newProtocolError(code, format, params...)
	// report(err)
	return
}

func (self protocolError) Error() (message string) {
	message = self.message
	if message == "" {
		message, ok := errorToString[self.Code]
		if !ok {
			panic("invalid error code")
		}
		if self.format != "" {
			message += ": " + fmt.Sprintf(self.format, self.params...)
		}
		self.message = message
	}
	return
}

func (self *protocolError) Fatal() bool {
	return self.fatal
}
<|endoftext|>


<commit_before>package cosmic

import (
	"fmt"
	"log"
	"sync"
	"time"

	"strconv"
	"strings"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/xanzy/go-cosmic/cosmic"
)

func resourceCosmicPortForward() *schema.Resource {
	return &schema.Resource{
		Create: resourceCosmicPortForwardCreate,
		Read:   resourceCosmicPortForwardRead,
		Update: resourceCosmicPortForwardUpdate,
		Delete: resourceCosmicPortForwardDelete,

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

			"managed": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

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

			"forward": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"protocol": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"private_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
						},

						"public_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
						},

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

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

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

func resourceCosmicPortForwardCreate(d *schema.ResourceData, meta interface{}) error {
	// We need to set this upfront in order to be able to save a partial state
	d.SetId(d.Get("ip_address_id").(string))

	// Create all forwards that are configured
	if nrs := d.Get("forward").(*schema.Set); nrs.Len() > 0 {
		// Create an empty schema.Set to hold all forwards
		forwards := resourceCosmicPortForward().Schema["forward"].ZeroValue().(*schema.Set)

		err := createPortForwards(d, meta, forwards, nrs)

		// We need to update this first to preserve the correct state
		d.Set("forward", forwards)

		if err != nil {
			return err
		}
	}

	return resourceCosmicPortForwardRead(d, meta)
}

func createPortForwards(d *schema.ResourceData, meta interface{}, forwards *schema.Set, nrs *schema.Set) error {
	var errs *multierror.Error

	var wg sync.WaitGroup
	wg.Add(nrs.Len())

	sem := make(chan struct{}, 10)
	for _, forward := range nrs.List() {
		// Put in a tiny sleep here to avoid DoS'ing the API
		time.Sleep(500 * time.Millisecond)

		go func(forward map[string]interface{}) {
			defer wg.Done()
			sem <- struct{}{}

			// Create a single forward
			err := createPortForward(d, meta, forward)

			// If we have a UUID, we need to save the forward
			if forward["uuid"].(string) != "" {
				forwards.Add(forward)
			}

			if err != nil {
				errs = multierror.Append(errs, err)
			}

			<-sem
		}(forward.(map[string]interface{}))
	}

	wg.Wait()

	return errs.ErrorOrNil()
}

func createPortForward(d *schema.ResourceData, meta interface{}, forward map[string]interface{}) error {
	cs := meta.(*cosmic.CosmicClient)

	// Make sure all required parameters are there
	if err := verifyPortForwardParams(d, forward); err != nil {
		return err
	}

	vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(
		forward["virtual_machine_id"].(string),
		cosmic.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		return err
	}

	// Create a new parameter struct
	p := cs.Firewall.NewCreatePortForwardingRuleParams(d.Id(), forward["private_port"].(int),
		forward["protocol"].(string), forward["public_port"].(int), vm.Id)

	if vmGuestIP, ok := forward["vm_guest_ip"]; ok {
		p.SetVmguestip(vmGuestIP.(string))

		// Set the network ID based on the guest IP, needed when the public IP address
		// is not associated with any network yet
	NICS:
		for _, nic := range vm.Nic {
			if vmGuestIP.(string) == nic.Ipaddress {
				p.SetNetworkid(nic.Networkid)
				break NICS
			}
			for _, ip := range nic.Secondaryip {
				if vmGuestIP.(string) == ip.Ipaddress {
					p.SetNetworkid(nic.Networkid)
					break NICS
				}
			}
		}
	} else {
		// If no guest IP is configured, use the primary NIC
		p.SetNetworkid(vm.Nic[0].Networkid)
	}

	// Do not open the firewall automatically in any case
	p.SetOpenfirewall(false)

	r, err := cs.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		return err
	}

	forward["uuid"] = r.Id

	return nil
}

func resourceCosmicPortForwardRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cosmic.CosmicClient)

	// First check if the IP address is still associated
	_, count, err := cs.Address.GetPublicIpAddressByID(
		d.Id(),
		cosmic.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] IP address with ID %s is no longer associated", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	// Get all the forwards from the running environment
	p := cs.Firewall.NewListPortForwardingRulesParams()
	p.SetIpaddressid(d.Id())
	p.SetListall(true)

	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	l, err := cs.Firewall.ListPortForwardingRules(p)
	if err != nil {
		return err
	}

	// Make a map of all the forwards so we can easily find a forward
	forwardMap := make(map[string]*cosmic.PortForwardingRule, l.Count)
	for _, f := range l.PortForwardingRules {
		forwardMap[f.Id] = f
	}

	// Create an empty schema.Set to hold all forwards
	forwards := resourceCosmicPortForward().Schema["forward"].ZeroValue().(*schema.Set)

	// Read all forwards that are configured
	if rs := d.Get("forward").(*schema.Set); rs.Len() > 0 {
		for _, forward := range rs.List() {
			forward := forward.(map[string]interface{})

			id, ok := forward["uuid"]
			if !ok || id.(string) == "" {
				continue
			}

			// Get the forward
			f, ok := forwardMap[id.(string)]
			if !ok {
				forward["uuid"] = ""
				continue
			}

			// Delete the known rule so only unknown rules remain in the ruleMap
			delete(forwardMap, id.(string))

			privPort, err := strconv.Atoi(f.Privateport)
			if err != nil {
				return err
			}

			pubPort, err := strconv.Atoi(f.Publicport)
			if err != nil {
				return err
			}

			// Update the values
			forward["protocol"] = f.Protocol
			forward["private_port"] = privPort
			forward["public_port"] = pubPort
			forward["virtual_machine_id"] = f.Virtualmachineid
			forward["vm_guest_ip"] = f.Vmguestip

			forwards.Add(forward)
		}
	}

	// If this is a managed resource, add all unknown forwards to dummy forwards
	managed := d.Get("managed").(bool)
	if managed && len(forwardMap) > 0 {
		for uuid := range forwardMap {
			// Make a dummy forward to hold the unknown UUID
			forward := map[string]interface{}{
				"protocol":           uuid,
				"private_port":       0,
				"public_port":        0,
				"virtual_machine_id": uuid,
				"uuid":               uuid,
			}

			// Add the dummy forward to the forwards set
			forwards.Add(forward)
		}
	}

	if forwards.Len() > 0 {
		d.Set("forward", forwards)
	} else if !managed {
		d.SetId("")
	}

	return nil
}

func resourceCosmicPortForwardUpdate(d *schema.ResourceData, meta interface{}) error {
	// Check if the forward set as a whole has changed
	if d.HasChange("forward") {
		o, n := d.GetChange("forward")
		ors := o.(*schema.Set).Difference(n.(*schema.Set))
		nrs := n.(*schema.Set).Difference(o.(*schema.Set))

		// We need to start with a rule set containing all the rules we
		// already have and want to keep. Any rules that are not deleted
		// correctly and any newly created rules, will be added to this
		// set to make sure we end up in a consistent state
		forwards := o.(*schema.Set).Intersection(n.(*schema.Set))

		// First loop through all the old forwards and delete them
		if ors.Len() > 0 {
			err := deletePortForwards(d, meta, forwards, ors)

			// We need to update this first to preserve the correct state
			d.Set("forward", forwards)

			if err != nil {
				return err
			}
		}

		// Then loop through all the new forwards and create them
		if nrs.Len() > 0 {
			err := createPortForwards(d, meta, forwards, nrs)

			// We need to update this first to preserve the correct state
			d.Set("forward", forwards)

			if err != nil {
				return err
			}
		}
	}

	return resourceCosmicPortForwardRead(d, meta)
}

func resourceCosmicPortForwardDelete(d *schema.ResourceData, meta interface{}) error {
	// Create an empty rule set to hold all rules that where
	// not deleted correctly
	forwards := resourceCosmicPortForward().Schema["forward"].ZeroValue().(*schema.Set)

	// Delete all forwards
	if ors := d.Get("forward").(*schema.Set); ors.Len() > 0 {
		err := deletePortForwards(d, meta, forwards, ors)

		// We need to update this first to preserve the correct state
		d.Set("forward", forwards)

		if err != nil {
			return err
		}
	}

	return nil
}

func deletePortForwards(d *schema.ResourceData, meta interface{}, forwards *schema.Set, ors *schema.Set) error {
	var errs *multierror.Error

	var wg sync.WaitGroup
	wg.Add(ors.Len())

	sem := make(chan struct{}, 10)
	for _, forward := range ors.List() {
		// Put a sleep here to avoid DoS'ing the API
		time.Sleep(500 * time.Millisecond)

		go func(forward map[string]interface{}) {
			defer wg.Done()
			sem <- struct{}{}

			// Delete a single forward
			err := deletePortForward(d, meta, forward)

			// If we have a UUID, we need to save the forward
			if forward["uuid"].(string) != "" {
				forwards.Add(forward)
			}

			if err != nil {
				errs = multierror.Append(errs, err)
			}

			<-sem
		}(forward.(map[string]interface{}))
	}

	wg.Wait()

	return errs.ErrorOrNil()
}

func deletePortForward(d *schema.ResourceData, meta interface{}, forward map[string]interface{}) error {
	cs := meta.(*cosmic.CosmicClient)

	// Create the parameter struct
	p := cs.Firewall.NewDeletePortForwardingRuleParams(forward["uuid"].(string))

	// Delete the forward
	if _, err := cs.Firewall.DeletePortForwardingRule(p); err != nil {
		// This is a very poor way to be told the ID does no longer exist :(
		if !strings.Contains(err.Error(), fmt.Sprintf(
			"Invalid parameter id value=%s due to incorrect long value format, "+
				"or entity does not exist", forward["uuid"].(string))) {
			return err
		}
	}

	// Empty the UUID of this rule
	forward["uuid"] = ""

	return nil
}

func verifyPortForwardParams(d *schema.ResourceData, forward map[string]interface{}) error {
	protocol := forward["protocol"].(string)
	if protocol != "tcp" && protocol != "udp" {
		return fmt.Errorf(
			"%s is not a valid protocol. Valid options are 'tcp' and 'udp'", protocol)
	}
	return nil
}
<commit_msg>Make the hash consistent again (#2)<commit_after>package cosmic

import (
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/xanzy/go-cosmic/cosmic"
)

func resourceCosmicPortForward() *schema.Resource {
	return &schema.Resource{
		Create: resourceCosmicPortForwardCreate,
		Read:   resourceCosmicPortForwardRead,
		Update: resourceCosmicPortForwardUpdate,
		Delete: resourceCosmicPortForwardDelete,

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

			"managed": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

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

			"forward": &schema.Schema{
				Type:     schema.TypeSet,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"protocol": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},

						"private_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
						},

						"public_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
						},

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

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

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

func resourceCosmicPortForwardCreate(d *schema.ResourceData, meta interface{}) error {
	// We need to set this upfront in order to be able to save a partial state
	d.SetId(d.Get("ip_address_id").(string))

	// Create all forwards that are configured
	if nrs := d.Get("forward").(*schema.Set); nrs.Len() > 0 {
		// Create an empty schema.Set to hold all forwards
		forwards := resourceCosmicPortForward().Schema["forward"].ZeroValue().(*schema.Set)

		err := createPortForwards(d, meta, forwards, nrs)

		// We need to update this first to preserve the correct state
		d.Set("forward", forwards)

		if err != nil {
			return err
		}
	}

	return resourceCosmicPortForwardRead(d, meta)
}

func createPortForwards(d *schema.ResourceData, meta interface{}, forwards *schema.Set, nrs *schema.Set) error {
	var errs *multierror.Error

	var wg sync.WaitGroup
	wg.Add(nrs.Len())

	sem := make(chan struct{}, 10)
	for _, forward := range nrs.List() {
		// Put in a tiny sleep here to avoid DoS'ing the API
		time.Sleep(500 * time.Millisecond)

		go func(forward map[string]interface{}) {
			defer wg.Done()
			sem <- struct{}{}

			// Create a single forward
			err := createPortForward(d, meta, forward)

			// If we have a UUID, we need to save the forward
			if forward["uuid"].(string) != "" {
				forwards.Add(forward)
			}

			if err != nil {
				errs = multierror.Append(errs, err)
			}

			<-sem
		}(forward.(map[string]interface{}))
	}

	wg.Wait()

	return errs.ErrorOrNil()
}

func createPortForward(d *schema.ResourceData, meta interface{}, forward map[string]interface{}) error {
	cs := meta.(*cosmic.CosmicClient)

	// Make sure all required parameters are there
	if err := verifyPortForwardParams(d, forward); err != nil {
		return err
	}

	vm, _, err := cs.VirtualMachine.GetVirtualMachineByID(
		forward["virtual_machine_id"].(string),
		cosmic.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		return err
	}

	// Create a new parameter struct
	p := cs.Firewall.NewCreatePortForwardingRuleParams(d.Id(), forward["private_port"].(int),
		forward["protocol"].(string), forward["public_port"].(int), vm.Id)

	if vmGuestIP, ok := forward["vm_guest_ip"]; ok && vmGuestIP.(string) != "" {
		p.SetVmguestip(vmGuestIP.(string))

		// Set the network ID based on the guest IP, needed when the public IP address
		// is not associated with any network yet
	NICS:
		for _, nic := range vm.Nic {
			if vmGuestIP.(string) == nic.Ipaddress {
				p.SetNetworkid(nic.Networkid)
				break NICS
			}
			for _, ip := range nic.Secondaryip {
				if vmGuestIP.(string) == ip.Ipaddress {
					p.SetNetworkid(nic.Networkid)
					break NICS
				}
			}
		}
	} else {
		// If no guest IP is configured, use the primary NIC
		p.SetNetworkid(vm.Nic[0].Networkid)
	}

	// Do not open the firewall automatically in any case
	p.SetOpenfirewall(false)

	r, err := cs.Firewall.CreatePortForwardingRule(p)
	if err != nil {
		return err
	}

	forward["uuid"] = r.Id

	return nil
}

func resourceCosmicPortForwardRead(d *schema.ResourceData, meta interface{}) error {
	cs := meta.(*cosmic.CosmicClient)

	// First check if the IP address is still associated
	_, count, err := cs.Address.GetPublicIpAddressByID(
		d.Id(),
		cosmic.WithProject(d.Get("project").(string)),
	)
	if err != nil {
		if count == 0 {
			log.Printf(
				"[DEBUG] IP address with ID %s is no longer associated", d.Id())
			d.SetId("")
			return nil
		}

		return err
	}

	// Get all the forwards from the running environment
	p := cs.Firewall.NewListPortForwardingRulesParams()
	p.SetIpaddressid(d.Id())
	p.SetListall(true)

	if err := setProjectid(p, cs, d); err != nil {
		return err
	}

	l, err := cs.Firewall.ListPortForwardingRules(p)
	if err != nil {
		return err
	}

	// Make a map of all the forwards so we can easily find a forward
	forwardMap := make(map[string]*cosmic.PortForwardingRule, l.Count)
	for _, f := range l.PortForwardingRules {
		forwardMap[f.Id] = f
	}

	// Create an empty schema.Set to hold all forwards
	forwards := resourceCosmicPortForward().Schema["forward"].ZeroValue().(*schema.Set)

	// Read all forwards that are configured
	if rs := d.Get("forward").(*schema.Set); rs.Len() > 0 {
		for _, forward := range rs.List() {
			forward := forward.(map[string]interface{})

			id, ok := forward["uuid"]
			if !ok || id.(string) == "" {
				continue
			}

			// Get the forward
			f, ok := forwardMap[id.(string)]
			if !ok {
				forward["uuid"] = ""
				continue
			}

			// Delete the known rule so only unknown rules remain in the ruleMap
			delete(forwardMap, id.(string))

			privPort, err := strconv.Atoi(f.Privateport)
			if err != nil {
				return err
			}

			pubPort, err := strconv.Atoi(f.Publicport)
			if err != nil {
				return err
			}

			// Update the values
			forward["protocol"] = f.Protocol
			forward["private_port"] = privPort
			forward["public_port"] = pubPort
			forward["virtual_machine_id"] = f.Virtualmachineid

			// This one is a bit tricky. We only want to update this optional value
			// if we've set one ourselves. If not this would become a computed value
			// and that would mess up the calculated hash of the set item.
			if forward["vm_guest_ip"].(string) != "" {
				forward["vm_guest_ip"] = f.Vmguestip
			}

			forwards.Add(forward)
		}
	}

	// If this is a managed resource, add all unknown forwards to dummy forwards
	managed := d.Get("managed").(bool)
	if managed && len(forwardMap) > 0 {
		for uuid := range forwardMap {
			// Make a dummy forward to hold the unknown UUID
			forward := map[string]interface{}{
				"protocol":           uuid,
				"private_port":       0,
				"public_port":        0,
				"virtual_machine_id": uuid,
				"uuid":               uuid,
			}

			// Add the dummy forward to the forwards set
			forwards.Add(forward)
		}
	}

	if forwards.Len() > 0 {
		d.Set("forward", forwards)
	} else if !managed {
		d.SetId("")
	}

	return nil
}

func resourceCosmicPortForwardUpdate(d *schema.ResourceData, meta interface{}) error {
	// Check if the forward set as a whole has changed
	if d.HasChange("forward") {
		o, n := d.GetChange("forward")
		ors := o.(*schema.Set).Difference(n.(*schema.Set))
		nrs := n.(*schema.Set).Difference(o.(*schema.Set))

		// We need to start with a rule set containing all the rules we
		// already have and want to keep. Any rules that are not deleted
		// correctly and any newly created rules, will be added to this
		// set to make sure we end up in a consistent state
		forwards := o.(*schema.Set).Intersection(n.(*schema.Set))

		// First loop through all the old forwards and delete them
		if ors.Len() > 0 {
			err := deletePortForwards(d, meta, forwards, ors)

			// We need to update this first to preserve the correct state
			d.Set("forward", forwards)

			if err != nil {
				return err
			}
		}

		// Then loop through all the new forwards and create them
		if nrs.Len() > 0 {
			err := createPortForwards(d, meta, forwards, nrs)

			// We need to update this first to preserve the correct state
			d.Set("forward", forwards)

			if err != nil {
				return err
			}
		}
	}

	return resourceCosmicPortForwardRead(d, meta)
}

func resourceCosmicPortForwardDelete(d *schema.ResourceData, meta interface{}) error {
	// Create an empty rule set to hold all rules that where
	// not deleted correctly
	forwards := resourceCosmicPortForward().Schema["forward"].ZeroValue().(*schema.Set)

	// Delete all forwards
	if ors := d.Get("forward").(*schema.Set); ors.Len() > 0 {
		err := deletePortForwards(d, meta, forwards, ors)

		// We need to update this first to preserve the correct state
		d.Set("forward", forwards)

		if err != nil {
			return err
		}
	}

	return nil
}

func deletePortForwards(d *schema.ResourceData, meta interface{}, forwards *schema.Set, ors *schema.Set) error {
	var errs *multierror.Error

	var wg sync.WaitGroup
	wg.Add(ors.Len())

	sem := make(chan struct{}, 10)
	for _, forward := range ors.List() {
		// Put a sleep here to avoid DoS'ing the API
		time.Sleep(500 * time.Millisecond)

		go func(forward map[string]interface{}) {
			defer wg.Done()
			sem <- struct{}{}

			// Delete a single forward
			err := deletePortForward(d, meta, forward)

			// If we have a UUID, we need to save the forward
			if forward["uuid"].(string) != "" {
				forwards.Add(forward)
			}

			if err != nil {
				errs = multierror.Append(errs, err)
			}

			<-sem
		}(forward.(map[string]interface{}))
	}

	wg.Wait()

	return errs.ErrorOrNil()
}

func deletePortForward(d *schema.ResourceData, meta interface{}, forward map[string]interface{}) error {
	cs := meta.(*cosmic.CosmicClient)

	// Create the parameter struct
	p := cs.Firewall.NewDeletePortForwardingRuleParams(forward["uuid"].(string))

	// Delete the forward
	if _, err := cs.Firewall.DeletePortForwardingRule(p); err != nil {
		// This is a very poor way to be told the ID does no longer exist :(
		if !strings.Contains(err.Error(), fmt.Sprintf(
			"Invalid parameter id value=%s due to incorrect long value format, "+
				"or entity does not exist", forward["uuid"].(string))) {
			return err
		}
	}

	// Empty the UUID of this rule
	forward["uuid"] = ""

	return nil
}

func verifyPortForwardParams(d *schema.ResourceData, forward map[string]interface{}) error {
	protocol := forward["protocol"].(string)
	if protocol != "tcp" && protocol != "udp" {
		return fmt.Errorf(
			"%s is not a valid protocol. Valid options are 'tcp' and 'udp'", protocol)
	}
	return nil
}
<|endoftext|>


<commit_before>package ironmq

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/httputil"
	"path"
)

// A Client contains an Iron.io project ID and a token for authentication.
type Client struct {
	Debug     bool
	cloud     *Cloud
	projectId string
	token     string
}

// NewClient returns a new Client using the given project ID and token.
// The network is not used during this call.
func NewClient(projectId, token string, cloud *Cloud) *Client {
	return &Client{projectId: projectId, token: token, cloud: cloud}
}

type Error struct {
	Status int
	Msg    string
}

var EmptyQueue = errors.New("queue is empty")

func (e *Error) Error() string { return fmt.Sprintf("Status %d: %s", e.Status, e.Msg) }

func (c *Client) req(method, endpoint string, body []byte, data interface{}) error {
	const apiVersion = "1"
	url := path.Join(c.cloud.host, apiVersion, "projects", c.projectId, endpoint)
	url = c.cloud.scheme + "://" + url + "?oauth=" + c.token
	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
		req.ContentLength = int64(len(body))
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	if c.Debug {
		dump, err := httputil.DumpResponse(resp, true)
		if err != nil {
			fmt.Println("error dumping response:", err)
		} else {
			fmt.Printf("IRONMQ_GO_CLIENT:  %s\n", dump)
		}
	}

	decoder := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		data := map[string]interface{}{}
		decoder.Decode(&data)
		msg, _ := data["msg"].(string)
		return &Error{resp.StatusCode, msg}
	}

	if data != nil {
		err = decoder.Decode(data)
		if err != nil {
			return err
		}
	}
	return nil
}

// Queue represents an IronMQ queue.
type Queue struct {
	name   string
	Client *Client
}

// Queue returns a Queue using the given name.
// The network is not used during this call.
func (c *Client) Queue(name string) *Queue {
	return &Queue{name, c}
}

// QueueInfo provides general information about a queue.
type QueueInfo struct {
	Size int // number of items available on the queue
}

// Info retrieves a QueueInfo structure for the queue.
func (q *Queue) Info() (*QueueInfo, error) {
	var qi QueueInfo
	err := q.Client.req("GET", "queues/"+q.name, nil, &qi)
	if err != nil {
		return nil, err
	}
	return &qi, nil
}

// Get takes one Message off of the queue. The Message will be returned to the queue
// if not deleted before the item's timeout.
func (q *Queue) Get() (*Message, error) {
	var resp struct {
		Msgs []*Message `json:"messages"`
	}
	err := q.Client.req("GET", "queues/"+q.name+"/messages", nil, &resp)
	if err != nil {
		return nil, err
	}
	if len(resp.Msgs) == 0 {
		return nil, EmptyQueue
	}
	msg := resp.Msgs[0]
	msg.q = q
	return msg, nil
}

// Push adds a message to the end of the queue using IronMQ's defaults:
//	timeout - 60 seconds
//	delay - none
func (q *Queue) Push(msg string) (id string, err error) {
	return q.PushMsg(&Message{Body: msg})
}

// PushMsg adds a message to the end of the queue using the fields of msg as
// parameters. msg.Id is ignored.
func (q *Queue) PushMsg(msg *Message) (id string, err error) {
	msgs := struct {
		Messages []*Message `json:"messages"`
	}{
		[]*Message{msg},
	}
	data, err := json.Marshal(msgs)
	if err != nil {
		return "", err
	}
	var resp struct {
		IDs []string `json:"ids"`
	}
	err = q.Client.req("POST", "queues/"+q.name+"/messages", data, &resp)
	if err != nil {
		return "", err
	}
	return resp.IDs[0], nil
}

type Message struct {
	Id   string `json:"id,omitempty"`
	Body string `json:"body"`
	// Timeout is the amount of time in seconds allowed for processing the
	// message.
	Timeout int64 `json:"timeout,omitempty"`
	// Delay is the amount of time in seconds to wait before adding the
	// message to the queue.
	Delay int64 `json:"delay,omitempty"`
	q     *Queue
}

func (m *Message) Delete() error {
	return m.q.Client.req("DELETE", "queues/"+m.q.name+"/messages/"+m.Id, nil, nil)
}
<commit_msg>Exported queue.Name<commit_after>package ironmq

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/httputil"
	"path"
)

// A Client contains an Iron.io project ID and a token for authentication.
type Client struct {
	Debug     bool
	cloud     *Cloud
	projectId string
	token     string
}

// NewClient returns a new Client using the given project ID and token.
// The network is not used during this call.
func NewClient(projectId, token string, cloud *Cloud) *Client {
	return &Client{projectId: projectId, token: token, cloud: cloud}
}

type Error struct {
	Status int
	Msg    string
}

var EmptyQueue = errors.New("queue is empty")

func (e *Error) Error() string { return fmt.Sprintf("Status %d: %s", e.Status, e.Msg) }

func (c *Client) req(method, endpoint string, body []byte, data interface{}) error {
	const apiVersion = "1"
	url := path.Join(c.cloud.host, apiVersion, "projects", c.projectId, endpoint)
	url = c.cloud.scheme + "://" + url + "?oauth=" + c.token
	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
		req.ContentLength = int64(len(body))
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	if c.Debug {
		dump, err := httputil.DumpResponse(resp, true)
		if err != nil {
			fmt.Println("error dumping response:", err)
		} else {
			fmt.Printf("IRONMQ_GO_CLIENT:  %s\n", dump)
		}
	}

	decoder := json.NewDecoder(resp.Body)
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		data := map[string]interface{}{}
		decoder.Decode(&data)
		msg, _ := data["msg"].(string)
		return &Error{resp.StatusCode, msg}
	}

	if data != nil {
		err = decoder.Decode(data)
		if err != nil {
			return err
		}
	}
	return nil
}

// Queue represents an IronMQ queue.
type Queue struct {
	Name   string
	Client *Client
}

// Queue returns a Queue using the given name.
// The network is not used during this call.
func (c *Client) Queue(name string) *Queue {
	return &Queue{name, c}
}

// QueueInfo provides general information about a queue.
type QueueInfo struct {
	Size int // number of items available on the queue
}

// Info retrieves a QueueInfo structure for the queue.
func (q *Queue) Info() (*QueueInfo, error) {
	var qi QueueInfo
	err := q.Client.req("GET", "queues/"+q.Name, nil, &qi)
	if err != nil {
		return nil, err
	}
	return &qi, nil
}

// Get takes one Message off of the queue. The Message will be returned to the queue
// if not deleted before the item's timeout.
func (q *Queue) Get() (*Message, error) {
	var resp struct {
		Msgs []*Message `json:"messages"`
	}
	err := q.Client.req("GET", "queues/"+q.Name+"/messages", nil, &resp)
	if err != nil {
		return nil, err
	}
	if len(resp.Msgs) == 0 {
		return nil, EmptyQueue
	}
	msg := resp.Msgs[0]
	msg.q = q
	return msg, nil
}

// Push adds a message to the end of the queue using IronMQ's defaults:
//	timeout - 60 seconds
//	delay - none
func (q *Queue) Push(msg string) (id string, err error) {
	return q.PushMsg(&Message{Body: msg})
}

// PushMsg adds a message to the end of the queue using the fields of msg as
// parameters. msg.Id is ignored.
func (q *Queue) PushMsg(msg *Message) (id string, err error) {
	msgs := struct {
		Messages []*Message `json:"messages"`
	}{
		[]*Message{msg},
	}
	data, err := json.Marshal(msgs)
	if err != nil {
		return "", err
	}
	var resp struct {
		IDs []string `json:"ids"`
	}
	err = q.Client.req("POST", "queues/"+q.Name+"/messages", data, &resp)
	if err != nil {
		return "", err
	}
	return resp.IDs[0], nil
}

type Message struct {
	Id   string `json:"id,omitempty"`
	Body string `json:"body"`
	// Timeout is the amount of time in seconds allowed for processing the
	// message.
	Timeout int64 `json:"timeout,omitempty"`
	// Delay is the amount of time in seconds to wait before adding the
	// message to the queue.
	Delay int64 `json:"delay,omitempty"`
	q     *Queue
}

func (m *Message) Delete() error {
	return m.q.Client.req("DELETE", "queues/"+m.q.Name+"/messages/"+m.Id, nil, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"

	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
)

type cmdImageAlias struct {
	global *cmdGlobal
	image  *cmdImage
}

func (c *cmdImageAlias) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("alias")
	cmd.Short = i18n.G("Manage image aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Manage image aliases`))

	// Create
	imageAliasCreateCmd := cmdImageAliasCreate{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasCreateCmd.Command())

	// Delete
	imageAliasDeleteCmd := cmdImageAliasDelete{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasDeleteCmd.Command())

	// List
	imageAliasListCmd := cmdImageAliasList{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasListCmd.Command())

	// Rename
	imageAliasRenameCmd := cmdImageAliasRename{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasRenameCmd.Command())

	return cmd
}

// Create
type cmdImageAliasCreate struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasCreate) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("create [<remote>:]<alias> <fingerprint>")
	cmd.Short = i18n.G("Create aliases for existing images")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Create aliases for existing images`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasCreate) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 2, 2)
	if exit {
		return err
	}

	// Parse remote
	resources, err := c.global.ParseServers(args[0])
	if err != nil {
		return err
	}

	resource := resources[0]

	if resource.name == "" {
		return fmt.Errorf(i18n.G("Alias name missing"))
	}

	// Create the alias
	alias := api.ImageAliasesPost{}
	alias.Name = resource.name
	alias.Target = args[1]

	return resource.server.CreateImageAlias(alias)
}

// Delete
type cmdImageAliasDelete struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasDelete) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("delete [<remote>:]<alias>")
	cmd.Aliases = []string{"rm"}
	cmd.Short = i18n.G("Delete image aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Delete image aliases`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasDelete) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 1, 1)
	if exit {
		return err
	}

	// Parse remote
	resources, err := c.global.ParseServers(args[0])
	if err != nil {
		return err
	}

	resource := resources[0]

	if resource.name == "" {
		return fmt.Errorf(i18n.G("Alias name missing"))
	}

	// Delete the alias
	return resource.server.DeleteImageAlias(resource.name)
}

// List
type cmdImageAliasList struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasList) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("list [<remote>:] [<filters>...]")
	cmd.Aliases = []string{"ls"}
	cmd.Short = i18n.G("List image aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`List image aliases

Filters may be part of the image hash or part of the image alias name.
`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasList) aliasShouldShow(filters []string, state *api.ImageAliasesEntry) bool {
	if len(filters) == 0 {
		return true
	}

	for _, filter := range filters {
		if strings.Contains(state.Name, filter) || strings.Contains(state.Target, filter) {
			return true
		}
	}

	return false
}

func (c *cmdImageAliasList) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 0, -1)
	if exit {
		return err
	}

	// Parse remote
	remote := ""
	if len(args) > 0 {
		remote = args[0]
	}

	remoteName, name, err := c.global.conf.ParseRemote(remote)
	if err != nil {
		return err
	}

	remoteServer, err := c.global.conf.GetImageServer(remoteName)
	if err != nil {
		return err
	}

	// Process the filters
	filters := []string{}
	if name != "" {
		filters = append(filters, name)
	}

	if len(args) > 1 {
		filters = append(filters, args[1:]...)
	}

	// List the aliases
	aliases, err := remoteServer.GetImageAliases()
	if err != nil {
		return err
	}

	// Render the table
	data := [][]string{}
	for _, alias := range aliases {
		if !c.aliasShouldShow(filters, &alias) {
			continue
		}

		data = append(data, []string{alias.Name, alias.Target[0:12], alias.Description})
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoWrapText(false)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetRowLine(true)
	table.SetHeader([]string{
		i18n.G("ALIAS"),
		i18n.G("FINGERPRINT"),
		i18n.G("DESCRIPTION")})
	sort.Sort(stringList(data))
	table.AppendBulk(data)
	table.Render()

	return nil
}

// Rename
type cmdImageAliasRename struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasRename) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("rename [<remote>:]<alias> <new-name>")
	cmd.Aliases = []string{"mv"}
	cmd.Short = i18n.G("Rename aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Rename aliases`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasRename) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 2, 2)
	if exit {
		return err
	}

	// Parse remote
	resources, err := c.global.ParseServers(args[0])
	if err != nil {
		return err
	}

	resource := resources[0]

	if resource.name == "" {
		return fmt.Errorf(i18n.G("Alias name missing"))
	}

	// Rename the alias
	return resource.server.RenameImageAlias(resource.name, api.ImageAliasesEntryPost{Name: args[1]})
}
<commit_msg>lxc/image: Use renderTable for alias list<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/spf13/cobra"

	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
)

type cmdImageAlias struct {
	global *cmdGlobal
	image  *cmdImage
}

func (c *cmdImageAlias) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("alias")
	cmd.Short = i18n.G("Manage image aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Manage image aliases`))

	// Create
	imageAliasCreateCmd := cmdImageAliasCreate{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasCreateCmd.Command())

	// Delete
	imageAliasDeleteCmd := cmdImageAliasDelete{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasDeleteCmd.Command())

	// List
	imageAliasListCmd := cmdImageAliasList{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasListCmd.Command())

	// Rename
	imageAliasRenameCmd := cmdImageAliasRename{global: c.global, image: c.image, imageAlias: c}
	cmd.AddCommand(imageAliasRenameCmd.Command())

	return cmd
}

// Create
type cmdImageAliasCreate struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasCreate) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("create [<remote>:]<alias> <fingerprint>")
	cmd.Short = i18n.G("Create aliases for existing images")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Create aliases for existing images`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasCreate) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 2, 2)
	if exit {
		return err
	}

	// Parse remote
	resources, err := c.global.ParseServers(args[0])
	if err != nil {
		return err
	}

	resource := resources[0]

	if resource.name == "" {
		return fmt.Errorf(i18n.G("Alias name missing"))
	}

	// Create the alias
	alias := api.ImageAliasesPost{}
	alias.Name = resource.name
	alias.Target = args[1]

	return resource.server.CreateImageAlias(alias)
}

// Delete
type cmdImageAliasDelete struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasDelete) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("delete [<remote>:]<alias>")
	cmd.Aliases = []string{"rm"}
	cmd.Short = i18n.G("Delete image aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Delete image aliases`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasDelete) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 1, 1)
	if exit {
		return err
	}

	// Parse remote
	resources, err := c.global.ParseServers(args[0])
	if err != nil {
		return err
	}

	resource := resources[0]

	if resource.name == "" {
		return fmt.Errorf(i18n.G("Alias name missing"))
	}

	// Delete the alias
	return resource.server.DeleteImageAlias(resource.name)
}

// List
type cmdImageAliasList struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias

	flagFormat string
}

func (c *cmdImageAliasList) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("list [<remote>:] [<filters>...]")
	cmd.Aliases = []string{"ls"}
	cmd.Short = i18n.G("List image aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`List image aliases

Filters may be part of the image hash or part of the image alias name.
`))
	cmd.Flags().StringVar(&c.flagFormat, "format", "table", i18n.G("Format (csv|json|table|yaml)")+"``")

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasList) aliasShouldShow(filters []string, state *api.ImageAliasesEntry) bool {
	if len(filters) == 0 {
		return true
	}

	for _, filter := range filters {
		if strings.Contains(state.Name, filter) || strings.Contains(state.Target, filter) {
			return true
		}
	}

	return false
}

func (c *cmdImageAliasList) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 0, -1)
	if exit {
		return err
	}

	// Parse remote
	remote := ""
	if len(args) > 0 {
		remote = args[0]
	}

	remoteName, name, err := c.global.conf.ParseRemote(remote)
	if err != nil {
		return err
	}

	remoteServer, err := c.global.conf.GetImageServer(remoteName)
	if err != nil {
		return err
	}

	// Process the filters
	filters := []string{}
	if name != "" {
		filters = append(filters, name)
	}

	if len(args) > 1 {
		filters = append(filters, args[1:]...)
	}

	// List the aliases
	aliases, err := remoteServer.GetImageAliases()
	if err != nil {
		return err
	}

	// Render the table
	data := [][]string{}
	for _, alias := range aliases {
		if !c.aliasShouldShow(filters, &alias) {
			continue
		}

		data = append(data, []string{alias.Name, alias.Target[0:12], alias.Description})
	}

	header := []string{
		i18n.G("ALIAS"),
		i18n.G("FINGERPRINT"),
		i18n.G("DESCRIPTION"),
	}

	return renderTable(c.flagFormat, header, data, aliases)
}

// Rename
type cmdImageAliasRename struct {
	global     *cmdGlobal
	image      *cmdImage
	imageAlias *cmdImageAlias
}

func (c *cmdImageAliasRename) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("rename [<remote>:]<alias> <new-name>")
	cmd.Aliases = []string{"mv"}
	cmd.Short = i18n.G("Rename aliases")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Rename aliases`))

	cmd.RunE = c.Run

	return cmd
}

func (c *cmdImageAliasRename) Run(cmd *cobra.Command, args []string) error {
	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 2, 2)
	if exit {
		return err
	}

	// Parse remote
	resources, err := c.global.ParseServers(args[0])
	if err != nil {
		return err
	}

	resource := resources[0]

	if resource.name == "" {
		return fmt.Errorf(i18n.G("Alias name missing"))
	}

	// Rename the alias
	return resource.server.RenameImageAlias(resource.name, api.ImageAliasesEntryPost{Name: args[1]})
}
<|endoftext|>


<commit_before>//  Copyright 2017 Google Inc. All Rights Reserved.
//
//  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 workflow

import (
	"context"
	"fmt"
	"path/filepath"
	"reflect"
	"strings"
)

// IncludeWorkflow defines a Daisy workflow injection step. This step will
// 'include' the workflow found the path given into the parent workflow. Unlike
// a Subworkflow the included workflow will exist in the same namespace
// as the parent and have access to all its resources.
type IncludeWorkflow struct {
	Path string
	Vars map[string]string `json:",omitempty"`
	w    *Workflow
}

func (i *IncludeWorkflow) populate(ctx context.Context, s *Step) error {
	i.w.GCSPath = s.w.GCSPath
	i.w.Name = s.name
	i.w.Project = s.w.Project
	i.w.Zone = s.w.Zone
	i.w.autovars = s.w.autovars

	for k, v := range i.Vars {
		i.w.AddVar(k, v)
	}

	var replacements []string
	for k, v := range i.w.autovars {
		if k == "NAME" {
			v = s.name
		}
		if k == "WFDIR" {
			v = i.w.workflowDir
		}
		replacements = append(replacements, fmt.Sprintf("${%s}", k), v)
	}
	for k, v := range i.w.Vars {
		replacements = append(replacements, fmt.Sprintf("${%s}", k), v.Value)
	}
	substitute(reflect.ValueOf(i.w).Elem(), strings.NewReplacer(replacements...))

	// Copy Sources up to parent resolving relative paths as we go.
	for k, v := range i.w.Sources {
		if _, ok := s.w.Sources[k]; ok {
			return fmt.Errorf("source %q already exists in workflow", k)
		}
		if s.w.Sources == nil {
			s.w.Sources = map[string]string{}
		}

		if _, _, err := splitGCSPath(v); err != nil && !filepath.IsAbs(v) {
			v = filepath.Join(i.w.workflowDir, v)
		}
		s.w.Sources[k] = v
	}

	for name, st := range i.w.Steps {
		st.name = name
		st.w = s.w
		if err := st.w.populateStep(ctx, st); err != nil {
			return err
		}
	}

	return nil
}

func (i *IncludeWorkflow) validate(ctx context.Context, s *Step) error {
	return i.w.validate(ctx)
}

func (i *IncludeWorkflow) run(ctx context.Context, s *Step) error {
	return i.w.run(ctx)
}
<commit_msg>Skip blank sources (#101)<commit_after>//  Copyright 2017 Google Inc. All Rights Reserved.
//
//  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 workflow

import (
	"context"
	"fmt"
	"path/filepath"
	"reflect"
	"strings"
)

// IncludeWorkflow defines a Daisy workflow injection step. This step will
// 'include' the workflow found the path given into the parent workflow. Unlike
// a Subworkflow the included workflow will exist in the same namespace
// as the parent and have access to all its resources.
type IncludeWorkflow struct {
	Path string
	Vars map[string]string `json:",omitempty"`
	w    *Workflow
}

func (i *IncludeWorkflow) populate(ctx context.Context, s *Step) error {
	i.w.GCSPath = s.w.GCSPath
	i.w.Name = s.name
	i.w.Project = s.w.Project
	i.w.Zone = s.w.Zone
	i.w.autovars = s.w.autovars

	for k, v := range i.Vars {
		i.w.AddVar(k, v)
	}

	var replacements []string
	for k, v := range i.w.autovars {
		if k == "NAME" {
			v = s.name
		}
		if k == "WFDIR" {
			v = i.w.workflowDir
		}
		replacements = append(replacements, fmt.Sprintf("${%s}", k), v)
	}
	for k, v := range i.w.Vars {
		replacements = append(replacements, fmt.Sprintf("${%s}", k), v.Value)
	}
	substitute(reflect.ValueOf(i.w).Elem(), strings.NewReplacer(replacements...))

	// Copy Sources up to parent resolving relative paths as we go.
	for k, v := range i.w.Sources {
		if v == "" {
			continue
		}
		if _, ok := s.w.Sources[k]; ok {
			return fmt.Errorf("source %q already exists in workflow", k)
		}
		if s.w.Sources == nil {
			s.w.Sources = map[string]string{}
		}

		if _, _, err := splitGCSPath(v); err != nil && !filepath.IsAbs(v) {
			v = filepath.Join(i.w.workflowDir, v)
		}
		s.w.Sources[k] = v
	}

	for name, st := range i.w.Steps {
		st.name = name
		st.w = s.w
		if err := st.w.populateStep(ctx, st); err != nil {
			return err
		}
	}

	return nil
}

func (i *IncludeWorkflow) validate(ctx context.Context, s *Step) error {
	return i.w.validate(ctx)
}

func (i *IncludeWorkflow) run(ctx context.Context, s *Step) error {
	return i.w.run(ctx)
}
<|endoftext|>