<commit_before>package git

/*
#include <git2.h>
*/
import "C"
import (
	"os"
	"runtime"
	"unsafe"
)

type CheckoutStrategy uint

const (
	CheckoutNone                      CheckoutStrategy = C.GIT_CHECKOUT_NONE                         // Dry run, no actual updates
	CheckoutSafe                      CheckoutStrategy = C.GIT_CHECKOUT_SAFE                         // Allow safe updates that cannot overwrite uncommitted data
	CheckoutSafeCreate                CheckoutStrategy = C.GIT_CHECKOUT_SAFE_CREATE                  // Allow safe updates plus creation of missing files
	CheckoutForce                     CheckoutStrategy = C.GIT_CHECKOUT_FORCE                        // Allow all updates to force working directory to look like index
	CheckoutAllowConflicts            CheckoutStrategy = C.GIT_CHECKOUT_ALLOW_CONFLICTS              // Allow checkout to make safe updates even if conflicts are found
	CheckoutRemoveUntracked           CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_UNTRACKED             // Remove untracked files not in index (that are not ignored)
	CheckoutRemoveIgnored             CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED               // Remove ignored files not in index
	CheckotUpdateOnly                 CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_ONLY                  // Only update existing files, don't create new ones
	CheckoutDontUpdateIndex           CheckoutStrategy = C.GIT_CHECKOUT_DONT_UPDATE_INDEX            // Normally checkout updates index entries as it goes; this stops that
	CheckoutNoRefresh                 CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH                   // Don't refresh index/config/etc before doing checkout
	CheckooutDisablePathspecMatch     CheckoutStrategy = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH       // Treat pathspec as simple list of exact match file paths
	CheckoutSkipUnmerged              CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED                // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
	CheckoutUserOurs                  CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS                     // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
	CheckoutUseTheirs                 CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS                   // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
	CheckoutUpdateSubmodules          CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES            // Recursively checkout submodules with same options (NOT IMPLEMENTED)
	CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
)

type CheckoutOpts struct {
	Strategy        CheckoutStrategy // Default will be a dry run
	DisableFilters  bool             // Don't apply filters like CRLF conversion
	DirMode         os.FileMode      // Default is 0755
	FileMode        os.FileMode      // Default is 0644 or 0755 as dictated by blob
	FileOpenFlags   int              // Default is O_CREAT | O_TRUNC | O_WRONLY
	TargetDirectory string           // Alternative checkout path to workdir
}

func (opts *CheckoutOpts) toC() *C.git_checkout_options {
	if opts == nil {
		return nil
	}
	c := C.git_checkout_options{}
	populateCheckoutOpts(&c, opts)
	return &c
}

// Convert the CheckoutOpts struct to the corresponding
// C-struct. Returns a pointer to ptr, or nil if opts is nil, in order
// to help with what to pass.
func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.git_checkout_options {
	if opts == nil {
		return nil
	}

	C.git_checkout_init_options(ptr, 1)
	ptr.checkout_strategy = C.uint(opts.Strategy)
	ptr.disable_filters = cbool(opts.DisableFilters)
	ptr.dir_mode = C.uint(opts.DirMode.Perm())
	ptr.file_mode = C.uint(opts.FileMode.Perm())
	if opts.TargetDirectory != "" {
		ptr.target_directory = C.CString(opts.TargetDirectory)
	}
	return ptr
}

func freeCheckoutOpts(ptr *C.git_checkout_options) {
	if ptr == nil {
		return
	}
	C.free(unsafe.Pointer(ptr.target_directory))
}

// Updates files in the index and the working tree to match the content of
// the commit pointed at by HEAD. opts may be nil.
func (v *Repository) CheckoutHead(opts *CheckoutOpts) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	cOpts := opts.toC()
	defer freeCheckoutOpts(cOpts)

	ret := C.git_checkout_head(v.ptr, cOpts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

// Updates files in the working tree to match the content of the given
// index. If index is nil, the repository's index will be used. opts
// may be nil.
func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOpts) error {
	var iptr *C.git_index = nil
	if index != nil {
		iptr = index.ptr
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	cOpts := opts.toC()
	defer freeCheckoutOpts(cOpts)

	ret := C.git_checkout_index(v.ptr, iptr, cOpts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	cOpts := opts.toC()
	defer freeCheckoutOpts(cOpts)

	ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}
<commit_msg>Add possibiliy of checkout on specific path<commit_after>package git

/*
#include <git2.h>
*/
import "C"
import (
	"os"
	"runtime"
	"unsafe"
)

type CheckoutStrategy uint

const (
	CheckoutNone                      CheckoutStrategy = C.GIT_CHECKOUT_NONE                         // Dry run, no actual updates
	CheckoutSafe                      CheckoutStrategy = C.GIT_CHECKOUT_SAFE                         // Allow safe updates that cannot overwrite uncommitted data
	CheckoutSafeCreate                CheckoutStrategy = C.GIT_CHECKOUT_SAFE_CREATE                  // Allow safe updates plus creation of missing files
	CheckoutForce                     CheckoutStrategy = C.GIT_CHECKOUT_FORCE                        // Allow all updates to force working directory to look like index
	CheckoutAllowConflicts            CheckoutStrategy = C.GIT_CHECKOUT_ALLOW_CONFLICTS              // Allow checkout to make safe updates even if conflicts are found
	CheckoutRemoveUntracked           CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_UNTRACKED             // Remove untracked files not in index (that are not ignored)
	CheckoutRemoveIgnored             CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED               // Remove ignored files not in index
	CheckotUpdateOnly                 CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_ONLY                  // Only update existing files, don't create new ones
	CheckoutDontUpdateIndex           CheckoutStrategy = C.GIT_CHECKOUT_DONT_UPDATE_INDEX            // Normally checkout updates index entries as it goes; this stops that
	CheckoutNoRefresh                 CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH                   // Don't refresh index/config/etc before doing checkout
	CheckooutDisablePathspecMatch     CheckoutStrategy = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH       // Treat pathspec as simple list of exact match file paths
	CheckoutSkipUnmerged              CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED                // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
	CheckoutUserOurs                  CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS                     // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
	CheckoutUseTheirs                 CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS                   // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
	CheckoutUpdateSubmodules          CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES            // Recursively checkout submodules with same options (NOT IMPLEMENTED)
	CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
)

type CheckoutOpts struct {
	Strategy        CheckoutStrategy // Default will be a dry run
	DisableFilters  bool             // Don't apply filters like CRLF conversion
	DirMode         os.FileMode      // Default is 0755
	FileMode        os.FileMode      // Default is 0644 or 0755 as dictated by blob
	FileOpenFlags   int              // Default is O_CREAT | O_TRUNC | O_WRONLY
	TargetDirectory string           // Alternative checkout path to workdir
	Paths			[]string
}

func (opts *CheckoutOpts) toC() *C.git_checkout_options {
	if opts == nil {
		return nil
	}
	c := C.git_checkout_options{}
	populateCheckoutOpts(&c, opts)
	return &c
}

// Convert the CheckoutOpts struct to the corresponding
// C-struct. Returns a pointer to ptr, or nil if opts is nil, in order
// to help with what to pass.
func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.git_checkout_options {
	if opts == nil {
		return nil
	}

	C.git_checkout_init_options(ptr, 1)
	ptr.checkout_strategy = C.uint(opts.Strategy)
	ptr.disable_filters = cbool(opts.DisableFilters)
	ptr.dir_mode = C.uint(opts.DirMode.Perm())
	ptr.file_mode = C.uint(opts.FileMode.Perm())
	if opts.TargetDirectory != "" {
		ptr.target_directory = C.CString(opts.TargetDirectory)
	}
	if len(opts.Paths) > 0 {
		ptr.paths.strings = makeCStringsFromStrings(opts.Paths)
		ptr.paths.count = C.size_t(len(opts.Paths))
	}

	return ptr
}

func freeCheckoutOpts(ptr *C.git_checkout_options) {
	if ptr == nil {
		return
	}
	C.free(unsafe.Pointer(ptr.target_directory))
	if ptr.paths.count > 0 {
		freeStrarray(&ptr.paths)
	}
}

// Updates files in the index and the working tree to match the content of
// the commit pointed at by HEAD. opts may be nil.
func (v *Repository) CheckoutHead(opts *CheckoutOpts) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	cOpts := opts.toC()
	defer freeCheckoutOpts(cOpts)

	ret := C.git_checkout_head(v.ptr, cOpts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

// Updates files in the working tree to match the content of the given
// index. If index is nil, the repository's index will be used. opts
// may be nil.
func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOpts) error {
	var iptr *C.git_index = nil
	if index != nil {
		iptr = index.ptr
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	cOpts := opts.toC()
	defer freeCheckoutOpts(cOpts)

	ret := C.git_checkout_index(v.ptr, iptr, cOpts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}

func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	cOpts := opts.toC()
	defer freeCheckoutOpts(cOpts)

	ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts)
	if ret < 0 {
		return MakeGitError(ret)
	}

	return nil
}<|endoftext|>


<commit_before>package gotility

import (
	"regexp"
)

const (
	ASCII_VALUE_0 = 48
	ASCII_VALUE_9 = 57
	EMAIL_REGEX = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
)

func IsNumber (str string) bool {
	if len(str) == 0 {
		return false
	}
	for index := range str {
		if uint8(str[index]) < ASCII_VALUE_0 || uint8(str[index]) > ASCII_VALUE_9 {
			return false
		}
	}
	return true
}

func IsEmail (str string) bool {
	if len(str) == 0 {
		return false
	}
	var validEmail = regexp.MustCompile(EMAIL_REGEX)
	return validEmail.MatchString(str)
}

<commit_msg>Url Check added<commit_after>package gotility

import (
	"regexp"
)

const (
	ASCII_VALUE_0 = 48
	ASCII_VALUE_9 = 57
	EMAIL_REGEX = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
	URL_REGEX = "^((((H|h)(T|t)|(F|f))(T|t)(P|p)((S|s)?))\\://)?(www.|[a-zA-Z0-9].)[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,6}(\\:[0-9]{1,5})*(/($|[a-zA-Z0-9\\.\\,\\;\\?\\'\\\\+&%\\$#\\=~_\\-]+))*$"
)

func IsNumber (str string) bool {
	if len(str) == 0 {
		return false
	}
	for index := range str {
		if uint8(str[index]) < ASCII_VALUE_0 || uint8(str[index]) > ASCII_VALUE_9 {
			return false
		}
	}
	return true
}

func IsEmail (str string) bool {
	var validEmail = regexp.MustCompile(EMAIL_REGEX)
	return validEmail.MatchString(str)
}

func IsUrl (str string) bool {
	var validEmail = regexp.MustCompile(URL_REGEX)
	return validEmail.MatchString(str)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"fmt"
	"log"
	"math/big"
	"os"

	"code.google.com/p/getopt"
)

// For command-line tools
func p(f string, v ...interface{}) {
	fmt.Printf(f+"\n", v...)
}

// For errors during setup and other critical conditions
func f(f string, v ...interface{}) {
	log.Fatalf(f, v...)
}

func initConfig() {
	getopt.Parse()
	loadConfig(cfgfile)
}

func genPassword(size int) string {
	valid := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$()-_.:;[]{}")
	pw := make([]byte, size)
	for i := 0; i < size; i++ {
		n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(valid))))
		c := valid[n.Int64()]
		pw[i] = c
	}
	return string(pw)
}

func exists(fp string) bool {
	_, err := os.Stat(fp)
	return err == nil
}
<commit_msg>Updated getopt to GitHub version.<commit_after>package main

import (
	"crypto/rand"
	"fmt"
	"log"
	"math/big"
	"os"

	"github.com/pborman/getopt"
)

// For command-line tools
func p(f string, v ...interface{}) {
	fmt.Printf(f+"\n", v...)
}

// For errors during setup and other critical conditions
func f(f string, v ...interface{}) {
	log.Fatalf(f, v...)
}

func initConfig() {
	getopt.Parse()
	loadConfig(cfgfile)
}

func genPassword(size int) string {
	valid := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$()-_.:;[]{}")
	pw := make([]byte, size)
	for i := 0; i < size; i++ {
		n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(valid))))
		c := valid[n.Int64()]
		pw[i] = c
	}
	return string(pw)
}

func exists(fp string) bool {
	_, err := os.Stat(fp)
	return err == nil
}
<|endoftext|>


<commit_before>package e2etest

import (
	"context"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"github.com/hashicorp/go-version"

	"github.com/hashicorp/terraform-exec/tfexec"
	"github.com/hashicorp/terraform-exec/tfexec/internal/testutil"
)

const testFixtureDir = "testdata"

var (
	showMinVersion = version.Must(version.NewVersion("0.12.0"))

	providerAddressMinVersion = version.Must(version.NewVersion("0.13.0"))
)

func runTest(t *testing.T, fixtureName string, cb func(t *testing.T, tfVersion *version.Version, tf *tfexec.Terraform)) {
	t.Helper()

	versions := []string{
		testutil.Latest011,
		testutil.Latest012,
		testutil.Latest013,
		testutil.Latest014,
		testutil.Latest015,
		testutil.Latest_v1,
		testutil.Latest_v1_1,
	}
	if override := os.Getenv("TFEXEC_E2ETEST_VERSIONS"); override != "" {
		versions = strings.Split(override, ",")
	}

	runTestVersions(t, versions, fixtureName, cb)
}

func runTestVersions(t *testing.T, versions []string, fixtureName string, cb func(t *testing.T, tfVersion *version.Version, tf *tfexec.Terraform)) {
	t.Helper()

	alreadyRunVersions := map[string]bool{}
	for _, tfv := range versions {
		t.Run(fmt.Sprintf("%s-%s", fixtureName, tfv), func(t *testing.T) {
			if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
				v, err := version.NewVersion(tfv)
				if err != nil {
					t.Fatal(err)
				}
				if v.LessThan(version.Must(version.NewVersion("1.0.2"))) {
					t.Skipf("Terraform not available for darwin/arm64 < 1.0.2 (%s)", v)
				}
			}

			if alreadyRunVersions[tfv] {
				t.Skipf("already run version %q", tfv)
			}
			alreadyRunVersions[tfv] = true

			td, err := ioutil.TempDir("", "tf")
			if err != nil {
				t.Fatalf("error creating temporary test directory: %s", err)
			}
			t.Cleanup(func() {
				os.RemoveAll(td)
			})

			// TODO: do this in a cleaner way than string comparison?
			var execPath string
			switch {
			case strings.HasPrefix(tfv, "refs/"):
				execPath = tfcache.GitRef(t, tfv)
			default:
				execPath = tfcache.Version(t, tfv)
			}

			tf, err := tfexec.NewTerraform(td, execPath)
			if err != nil {
				t.Fatal(err)
			}

			tf.SetAppendUserAgent("tfexec-e2etest")

			runningVersion, _, err := tf.Version(context.Background(), false)
			if err != nil {
				t.Fatalf("unable to determine running version (expected %q): %s", tfv, err)
			}

			if fixtureName != "" {
				err = copyFiles(filepath.Join(testFixtureDir, fixtureName), td)
				if err != nil {
					t.Fatalf("error copying config file into test dir: %s", err)
				}
			}

			// Separate strings.Builder because it's not concurrent safe
			var stdout strings.Builder
			tf.SetStdout(&stdout)
			var stderr strings.Builder
			tf.SetStderr(&stderr)

			tf.SetLogger(&testingPrintfer{t})

			// TODO: capture panics here?
			cb(t, runningVersion, tf)

			t.Logf("CLI Output:\n%s", stdout.String())
			if len(stderr.String()) > 0 {
				t.Logf("CLI Error:\n%s", stderr.String())
			}
		})
	}
}

type testingPrintfer struct {
	t *testing.T
}

func (t *testingPrintfer) Printf(format string, v ...interface{}) {
	t.t.Logf(format, v...)
}

func copyFiles(path string, dstPath string) error {
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return err
	}

	for _, info := range infos {
		srcPath := filepath.Join(path, info.Name())
		if info.IsDir() {
			newDir := filepath.Join(dstPath, info.Name())
			err = os.MkdirAll(newDir, info.Mode())
			if err != nil {
				return err
			}
			err = copyFiles(srcPath, newDir)
			if err != nil {
				return err
			}
		} else {
			err = copyFile(srcPath, dstPath)
			if err != nil {
				return err
			}
		}

	}
	return nil
}

func copyFile(path string, dstPath string) error {
	srcF, err := os.Open(path)
	if err != nil {
		return err
	}
	defer srcF.Close()

	di, err := os.Stat(dstPath)
	if err != nil {
		return err
	}
	if di.IsDir() {
		_, file := filepath.Split(path)
		dstPath = filepath.Join(dstPath, file)
	}

	dstF, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer dstF.Close()

	if _, err := io.Copy(dstF, srcF); err != nil {
		return err
	}

	return nil
}

// filesEqual asserts that two files have the same contents.
func textFilesEqual(t *testing.T, expected, actual string) {
	eb, err := ioutil.ReadFile(expected)
	if err != nil {
		t.Fatal(err)
	}

	ab, err := ioutil.ReadFile(actual)
	if err != nil {
		t.Fatal(err)
	}

	es := string(eb)
	es = strings.ReplaceAll(es, "\r\n", "\n")

	as := string(ab)
	as = strings.ReplaceAll(as, "\r\n", "\n")

	if as != es {
		t.Fatalf("expected:\n%s\n\ngot:\n%s\n", es, as)
	}
}

func checkSum(t *testing.T, filename string) uint32 {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}
	return crc32.ChecksumIEEE(b)
}
<commit_msg>allow use of local Terraform executable in tests<commit_after>package e2etest

import (
	"context"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"github.com/hashicorp/go-version"

	"github.com/hashicorp/terraform-exec/tfexec"
	"github.com/hashicorp/terraform-exec/tfexec/internal/testutil"
)

const testFixtureDir = "testdata"

var (
	showMinVersion = version.Must(version.NewVersion("0.12.0"))

	providerAddressMinVersion = version.Must(version.NewVersion("0.13.0"))
)

func runTest(t *testing.T, fixtureName string, cb func(t *testing.T, tfVersion *version.Version, tf *tfexec.Terraform)) {
	t.Helper()

	versions := []string{
		testutil.Latest011,
		testutil.Latest012,
		testutil.Latest013,
		testutil.Latest014,
		testutil.Latest015,
		testutil.Latest_v1,
		testutil.Latest_v1_1,
	}
	if override := os.Getenv("TFEXEC_E2ETEST_VERSIONS"); override != "" {
		versions = strings.Split(override, ",")
	}

	// If the env var TFEXEC_E2ETEST_TERRAFORM_PATH is set to the path of a
	// valid Terraform executable, only tests appropriate to that
	// executable's version will be run.
	if localBinPath := os.Getenv("TFEXEC_E2ETEST_TERRAFORM_PATH"); localBinPath != "" {
		td, err := ioutil.TempDir("", "tf")
		if err != nil {
			t.Fatalf("error creating temporary test directory: %s", err)
		}
		t.Cleanup(func() {
			os.RemoveAll(td)
		})
		ltf, err := tfexec.NewTerraform(td, localBinPath)
		if err != nil {
			t.Fatal(err)
		}

		ltf.SetAppendUserAgent("tfexec-e2etest")

		lVersion, _, err := ltf.Version(context.Background(), false)
		if err != nil {
			t.Fatalf("unable to determine version of Terraform binary at %s: %s", localBinPath, err)
		}

		versions = []string{lVersion.String()}
	}

	runTestVersions(t, versions, fixtureName, cb)
}

func runTestVersions(t *testing.T, versions []string, fixtureName string, cb func(t *testing.T, tfVersion *version.Version, tf *tfexec.Terraform)) {
	t.Helper()

	alreadyRunVersions := map[string]bool{}
	for _, tfv := range versions {
		t.Run(fmt.Sprintf("%s-%s", fixtureName, tfv), func(t *testing.T) {
			if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
				v, err := version.NewVersion(tfv)
				if err != nil {
					t.Fatal(err)
				}
				if v.LessThan(version.Must(version.NewVersion("1.0.2"))) {
					t.Skipf("Terraform not available for darwin/arm64 < 1.0.2 (%s)", v)
				}
			}

			if alreadyRunVersions[tfv] {
				t.Skipf("already run version %q", tfv)
			}
			alreadyRunVersions[tfv] = true

			td, err := ioutil.TempDir("", "tf")
			if err != nil {
				t.Fatalf("error creating temporary test directory: %s", err)
			}
			t.Cleanup(func() {
				os.RemoveAll(td)
			})

			var execPath string
			if localBinPath := os.Getenv("TFEXEC_E2ETEST_TERRAFORM_PATH"); localBinPath != "" {
				execPath = localBinPath
			} else if strings.HasPrefix(tfv, "refs/") {
				execPath = tfcache.GitRef(t, tfv)
			} else {
				execPath = tfcache.Version(t, tfv)
			}

			tf, err := tfexec.NewTerraform(td, execPath)
			if err != nil {
				t.Fatal(err)
			}

			tf.SetAppendUserAgent("tfexec-e2etest")

			runningVersion, _, err := tf.Version(context.Background(), false)
			if err != nil {
				t.Fatalf("unable to determine running version (expected %q): %s", tfv, err)
			}

			// Check that the runningVersion matches the expected
			// test version. This ensures non-matching tests are
			// skipped when using a local Terraform executable.
			if !strings.HasPrefix(tfv, "refs/") {
				testVersion, err := version.NewVersion(tfv)
				if err != nil {
					t.Fatalf("unable to parse version %s: %s", testVersion, err)
				}
				if !testVersion.Equal(runningVersion) {
					t.Skipf("test applies to version %s, but local executable is version %s", tfv, runningVersion)
				}
			}

			if fixtureName != "" {
				err = copyFiles(filepath.Join(testFixtureDir, fixtureName), td)
				if err != nil {
					t.Fatalf("error copying config file into test dir: %s", err)
				}
			}

			// Separate strings.Builder because it's not concurrent safe
			var stdout strings.Builder
			tf.SetStdout(&stdout)
			var stderr strings.Builder
			tf.SetStderr(&stderr)

			tf.SetLogger(&testingPrintfer{t})

			// TODO: capture panics here?
			cb(t, runningVersion, tf)

			t.Logf("CLI Output:\n%s", stdout.String())
			if len(stderr.String()) > 0 {
				t.Logf("CLI Error:\n%s", stderr.String())
			}
		})
	}
}

type testingPrintfer struct {
	t *testing.T
}

func (t *testingPrintfer) Printf(format string, v ...interface{}) {
	t.t.Logf(format, v...)
}

func copyFiles(path string, dstPath string) error {
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return err
	}

	for _, info := range infos {
		srcPath := filepath.Join(path, info.Name())
		if info.IsDir() {
			newDir := filepath.Join(dstPath, info.Name())
			err = os.MkdirAll(newDir, info.Mode())
			if err != nil {
				return err
			}
			err = copyFiles(srcPath, newDir)
			if err != nil {
				return err
			}
		} else {
			err = copyFile(srcPath, dstPath)
			if err != nil {
				return err
			}
		}

	}
	return nil
}

func copyFile(path string, dstPath string) error {
	srcF, err := os.Open(path)
	if err != nil {
		return err
	}
	defer srcF.Close()

	di, err := os.Stat(dstPath)
	if err != nil {
		return err
	}
	if di.IsDir() {
		_, file := filepath.Split(path)
		dstPath = filepath.Join(dstPath, file)
	}

	dstF, err := os.Create(dstPath)
	if err != nil {
		return err
	}
	defer dstF.Close()

	if _, err := io.Copy(dstF, srcF); err != nil {
		return err
	}

	return nil
}

// filesEqual asserts that two files have the same contents.
func textFilesEqual(t *testing.T, expected, actual string) {
	eb, err := ioutil.ReadFile(expected)
	if err != nil {
		t.Fatal(err)
	}

	ab, err := ioutil.ReadFile(actual)
	if err != nil {
		t.Fatal(err)
	}

	es := string(eb)
	es = strings.ReplaceAll(es, "\r\n", "\n")

	as := string(ab)
	as = strings.ReplaceAll(as, "\r\n", "\n")

	if as != es {
		t.Fatalf("expected:\n%s\n\ngot:\n%s\n", es, as)
	}
}

func checkSum(t *testing.T, filename string) uint32 {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}
	return crc32.ChecksumIEEE(b)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"gopkg.in/Iwark/spreadsheet.v2"
)

// Represents the board members of a Toastmasters meeting.
type board struct {
	president string
	vpe       string
	vpm       string
	vppr      string
	secretary string
	treasurer string
	saa       string
}

// Factory method using a spreadsheet to fill in board members.
func (board) new(sheet *spreadsheet.Sheet) board {
	board := board{}
	board.president = sheet.Columns[1][0].Value
	board.vpe = sheet.Columns[1][1].Value
	board.vpm = sheet.Columns[1][2].Value
	board.vppr = sheet.Columns[1][3].Value
	board.secretary = sheet.Columns[1][4].Value
	board.treasurer = sheet.Columns[1][5].Value
	board.saa = sheet.Columns[1][6].Value

	return board
}

// Represents the editable fields on a Toastmasters agenda.
type agendaRoles struct {
	toastmaster       string
	ge                string
	timer             string
	ahCounter         string
	grammarian        string
	tableTopicsMaster string
	jokeMaster        string
	speakers          []speaker
	boardMembers      board
	futureWeeks       [][]string
}

// Factory method to create agenda roles from a google doc based on the date of the meeting.
func (agendaRoles) new(agendaDate string) agendaRoles {
	spreadsheets := getSheet()
	boardMembers := board{}.new(spreadsheets.boardSheet)

	agendaRoles := agendaRoles{}
	agendaRoles.boardMembers = boardMembers

	rolesSheet := spreadsheets.meetingRoles
	for i := range rolesSheet.Columns {
		if rolesSheet.Columns[i][0].Value == agendaDate {
			agendaRoles.toastmaster = rolesSheet.Columns[i][1].Value
			agendaRoles.jokeMaster = rolesSheet.Columns[i][2].Value
			agendaRoles.ge = rolesSheet.Columns[i][3].Value
			agendaRoles.timer = rolesSheet.Columns[i][4].Value
			agendaRoles.ahCounter = rolesSheet.Columns[i][5].Value
			agendaRoles.grammarian = rolesSheet.Columns[i][6].Value

			for j := 7; j <= 13; j += 2 {
				agendaRoles.speakers = append(agendaRoles.speakers, speaker{}.new(rolesSheet.Columns[i][j].Value, rolesSheet.Columns[i][j+1].Value))
			}

			agendaRoles.tableTopicsMaster = rolesSheet.Columns[i][16].Value
			agendaRoles.futureWeeks = getFutureWeeks(agendaDate, rolesSheet, i)
			break
		}
	}
	return agendaRoles
}

//  Represents a speaker in a Toastmasters meeting.
type speaker struct {
	name string
	speech
	evaluator string
}

// Factory method to create a speaker based on the spreadsheet speaker and evaluator.
func (speaker) new(s string, eval string) speaker {
	name, manual, number := parseManualAndNumber(s)
	info := speech{}.new(manual, number)

	speaker := speaker{}
	speaker.name = name
	speaker.evaluator = eval
	speaker.speech = info

	return speaker
}

// Helper method that returns the first name of a speaker.
func (s speaker) firstName() string {
	return strings.Split(s.name, " ")[0]
}

// Represents the spreadsheet tabs.
type googleDocsSheet struct {
	boardSheet   *spreadsheet.Sheet
	meetingRoles *spreadsheet.Sheet
}

//  GetSheet reads a Google Docs spreadsheet and returns a sheet with roles and another sheet with the board members.
func getSheet() googleDocsSheet {
	data, err := ioutil.ReadFile("client_secret.json")
	if err != nil {
		panic("cannot read client_secret.json")
	}

	conf, err := google.JWTConfigFromJSON(data, spreadsheet.Scope)
	if err != nil {
		panic("problem with google.JWTConfigFromJSON(data, spreadsheet.Scope)")
	}

	client := conf.Client(context.TODO())

	service := spreadsheet.NewServiceWithClient(client)
	spreadsheet, err := service.FetchSpreadsheet("1CBlORqCzL6YvyAUZTk8jezvhyuDzjjumghwGKk5VIK8")
	if err != nil {
		panic("cannot fetch spread sheet: ")
	}

	roles, err := spreadsheet.SheetByIndex(0)
	if err != nil {
		panic("Cannot read spreadsheet by index 0")
	}

	board, err := spreadsheet.SheetByIndex(1)
	if err != nil {
		panic("Cannot read spreadsheet by index 1")
	}

	return googleDocsSheet{boardSheet: board, meetingRoles: roles}
}

// Find the speaker name, manual and number from a string that looks like "Ann Addicks\nCC #9".
func parseManualAndNumber(speaker string) (string, string, int) {
	re := regexp.MustCompile(`([a-zA-Z]+ [a-zA-Z]+)\n([a-zA-Z]+) #(\d{1,2})`)
	result := re.FindStringSubmatch(speaker)
	name := speaker
	manual := ""
	speechNum := 0

	if len(result) > 0 {
		name = result[1]
		manual = result[2]
		speechNum, _ = strconv.Atoi(result[3])
	}
	return name, manual, speechNum
}

// The number of weeks in the future to capture.
const futureWeeks = 4

// GetFutureWeeks finds the next several weeks after the current week based on the constant futureWeeks.
func getFutureWeeks(agendaDate string, sheet *spreadsheet.Sheet, thisWeek int) [][]string {
	week := 0
	var nextSchedule = make([][]string, 0, futureWeeks)

	for i := thisWeek + 1; i < len(sheet.Columns) && week <= futureWeeks; i++ {
		nextWeek := make([]string, 17)

		for j := 0; j < 17; j++ {
			nextWeek[j] = sheet.Columns[i][j].Value
		}
		nextSchedule = append(nextSchedule, nextWeek)
		week++

	}
	return nextSchedule
}
<commit_msg>pulled role count to a constant.<commit_after>package main

import (
	"io/ioutil"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"gopkg.in/Iwark/spreadsheet.v2"
)

// Represents the board members of a Toastmasters meeting.
type board struct {
	president string
	vpe       string
	vpm       string
	vppr      string
	secretary string
	treasurer string
	saa       string
}

// Factory method using a spreadsheet to fill in board members.
func (board) new(sheet *spreadsheet.Sheet) board {
	board := board{}
	board.president = sheet.Columns[1][0].Value
	board.vpe = sheet.Columns[1][1].Value
	board.vpm = sheet.Columns[1][2].Value
	board.vppr = sheet.Columns[1][3].Value
	board.secretary = sheet.Columns[1][4].Value
	board.treasurer = sheet.Columns[1][5].Value
	board.saa = sheet.Columns[1][6].Value

	return board
}

// Represents the editable fields on a Toastmasters agenda.
type agendaRoles struct {
	toastmaster       string
	ge                string
	timer             string
	ahCounter         string
	grammarian        string
	tableTopicsMaster string
	jokeMaster        string
	speakers          []speaker
	boardMembers      board
	futureWeeks       [][]string
}

// Factory method to create agenda roles from a google doc based on the date of the meeting.
func (agendaRoles) new(agendaDate string) agendaRoles {
	spreadsheets := getSheet()
	boardMembers := board{}.new(spreadsheets.boardSheet)

	agendaRoles := agendaRoles{}
	agendaRoles.boardMembers = boardMembers

	rolesSheet := spreadsheets.meetingRoles
	for i := range rolesSheet.Columns {
		if rolesSheet.Columns[i][0].Value == agendaDate {
			agendaRoles.toastmaster = rolesSheet.Columns[i][1].Value
			agendaRoles.jokeMaster = rolesSheet.Columns[i][2].Value
			agendaRoles.ge = rolesSheet.Columns[i][3].Value
			agendaRoles.timer = rolesSheet.Columns[i][4].Value
			agendaRoles.ahCounter = rolesSheet.Columns[i][5].Value
			agendaRoles.grammarian = rolesSheet.Columns[i][6].Value

			for j := 7; j <= 13; j += 2 {
				agendaRoles.speakers = append(agendaRoles.speakers, speaker{}.new(rolesSheet.Columns[i][j].Value, rolesSheet.Columns[i][j+1].Value))
			}

			agendaRoles.tableTopicsMaster = rolesSheet.Columns[i][16].Value
			agendaRoles.futureWeeks = getFutureWeeks(agendaDate, rolesSheet, i)
			break
		}
	}
	return agendaRoles
}

//  Represents a speaker in a Toastmasters meeting.
type speaker struct {
	name string
	speech
	evaluator string
}

// Factory method to create a speaker based on the spreadsheet speaker and evaluator.
func (speaker) new(s string, eval string) speaker {
	name, manual, number := parseManualAndNumber(s)
	info := speech{}.new(manual, number)

	speaker := speaker{}
	speaker.name = name
	speaker.evaluator = eval
	speaker.speech = info

	return speaker
}

// Helper method that returns the first name of a speaker.
func (s speaker) firstName() string {
	return strings.Split(s.name, " ")[0]
}

// Represents the spreadsheet tabs.
type googleDocsSheet struct {
	boardSheet   *spreadsheet.Sheet
	meetingRoles *spreadsheet.Sheet
}

//  GetSheet reads a Google Docs spreadsheet and returns a sheet with roles and another sheet with the board members.
func getSheet() googleDocsSheet {
	data, err := ioutil.ReadFile("client_secret.json")
	if err != nil {
		panic("cannot read client_secret.json")
	}

	conf, err := google.JWTConfigFromJSON(data, spreadsheet.Scope)
	if err != nil {
		panic("problem with google.JWTConfigFromJSON(data, spreadsheet.Scope)")
	}

	client := conf.Client(context.TODO())

	service := spreadsheet.NewServiceWithClient(client)
	spreadsheet, err := service.FetchSpreadsheet("1CBlORqCzL6YvyAUZTk8jezvhyuDzjjumghwGKk5VIK8")
	if err != nil {
		panic("cannot fetch spread sheet: ")
	}

	roles, err := spreadsheet.SheetByIndex(0)
	if err != nil {
		panic("Cannot read spreadsheet by index 0")
	}

	board, err := spreadsheet.SheetByIndex(1)
	if err != nil {
		panic("Cannot read spreadsheet by index 1")
	}

	return googleDocsSheet{boardSheet: board, meetingRoles: roles}
}

// Find the speaker name, manual and number from a string that looks like "Ann Addicks\nCC #9".
func parseManualAndNumber(speaker string) (string, string, int) {
	re := regexp.MustCompile(`([a-zA-Z]+ [a-zA-Z]+)\n([a-zA-Z]+) #(\d{1,2})`)
	result := re.FindStringSubmatch(speaker)
	name := speaker
	manual := ""
	speechNum := 0

	if len(result) > 0 {
		name = result[1]
		manual = result[2]
		speechNum, _ = strconv.Atoi(result[3])
	}
	return name, manual, speechNum
}

// The number of weeks in the future to capture.
const futureWeeks = 4
const numberOfRoles = 17

// GetFutureWeeks finds the next several weeks after the current week based on the constant futureWeeks.
func getFutureWeeks(agendaDate string, sheet *spreadsheet.Sheet, thisWeek int) [][]string {
	week := 0
	var nextSchedule = make([][]string, 0, futureWeeks)

	for i := thisWeek + 1; i < len(sheet.Columns) && week <= futureWeeks; i++ {
		nextWeek := make([]string, numberOfRoles)

		for j := 0; j < numberOfRoles; j++ {
			nextWeek[j] = sheet.Columns[i][j].Value
		}
		nextSchedule = append(nextSchedule, nextWeek)
		week++

	}
	return nextSchedule
}
<|endoftext|>


<commit_before>package apicache

import (
	"log"
	"math/rand"
	"net/http"
	"strconv"

	"time"

	"github.com/prometheus/client_golang/prometheus"
)

// Custom transport to chain into the HTTPClient to gather statistics.
type ApiCacheTransport struct {
	next *http.Transport
}

// RoundTrip wraps http.DefaultTransport.RoundTrip to provide stats and handle error rates.
func (t *ApiCacheTransport) RoundTrip(req *http.Request) (*http.Response, error) {

	// Loop until success
	tries := 0
	for {

		esiRateLimiter := true

		// Tickup retry counter
		tries++

		// Time our response
		start := time.Now()

		// Run the request.
		res, err := t.next.RoundTrip(req)

		metricAPICalls.With(
			prometheus.Labels{"host": req.Host},
		).Observe(float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond))

		// We got a response
		if res != nil {

			// Get the ESI error information
			resetS := res.Header.Get("x-esi-error-limit-reset")
			tokensS := res.Header.Get("x-esi-error-limit-remain")

			// Tick up and log any errors
			if res.StatusCode >= 400 {
				metricAPIErrors.Inc()
				log.Printf("St: %d Res: %s Tok: %s - %s\n", res.StatusCode, resetS, tokensS, req.URL)
			}

			// Early out for "our bad" statuses
			if res.StatusCode >= 400 && res.StatusCode < 420 {
				return res, err
			}

			// If we cannot decode this is likely from another source.
			reset, err := strconv.Atoi(resetS)
			if err != nil {
				esiRateLimiter = false
			}
			tokens, err := strconv.Atoi(tokensS)
			if err != nil {
				esiRateLimiter = false
			}

			// Backoff
			if res.StatusCode == 420 { // Something went wrong
				time.Sleep(time.Duration(reset)*time.Second + time.Duration(rand.Intn(int(time.Second*5))))
			} else if res.StatusCode == 429 { // SNAFU
				time.Sleep(time.Second * time.Duration(60+rand.Intn(30)))
			} else if esiRateLimiter { // Sleep based on error rate.
				time.Sleep(time.Second * time.Duration(float64(reset)*1.1*(1-(float64(tokens)/100))))
			} else if !esiRateLimiter { // Not an ESI error
				time.Sleep(time.Second * time.Duration(tries))
			}

			// break out after 10 tries
			if res.StatusCode == 420 || res.StatusCode == 429 || res.StatusCode >= 500 || res.StatusCode == 0 {
				if tries > 10 {
					return res, err
				}
			}
		}
		return res, err
	}
}

var (
	metricAPICalls = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: "evedata",
		Subsystem: "api",
		Name:      "calls",
		Help:      "API call statistics.",
		Buckets:   prometheus.ExponentialBuckets(10, 1.45, 20),
	},
		[]string{"host"},
	)

	metricAPIErrors = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "evedata",
		Subsystem: "api",
		Name:      "errors",
		Help:      "Count of API errors.",
	})
)

func init() {
	prometheus.MustRegister(
		metricAPICalls,
		metricAPIErrors,
	)
}
<commit_msg>ccp request<commit_after>package apicache

import (
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"

	"github.com/antihax/goesi/esi"

	"time"

	"github.com/prometheus/client_golang/prometheus"
)

// Custom transport to chain into the HTTPClient to gather statistics.
type ApiCacheTransport struct {
	next *http.Transport
}

// RoundTrip wraps http.DefaultTransport.RoundTrip to provide stats and handle error rates.
func (t *ApiCacheTransport) RoundTrip(req *http.Request) (*http.Response, error) {

	// Loop until success
	tries := 0
	for {

		esiRateLimiter := true

		// Tickup retry counter
		tries++

		// Time our response
		start := time.Now()

		// Run the request.
		res, err := t.next.RoundTrip(req)

		metricAPICalls.With(
			prometheus.Labels{"host": req.Host},
		).Observe(float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond))

		// We got a response
		if res != nil {

			// Get the ESI error information
			resetS := res.Header.Get("x-esi-error-limit-reset")
			tokensS := res.Header.Get("x-esi-error-limit-remain")

			// Tick up and log any errors
			if res.StatusCode >= 400 {
				metricAPIErrors.Inc()
				log.Printf("St: %d Res: %s Tok: %s - %s\n", res.StatusCode, resetS, tokensS, req.URL)
			}

			// Early out for "our bad" statuses
			if res.StatusCode >= 400 && res.StatusCode < 420 {
				return res, err
			}

			if res.StatusCode == 500 {
				v, ok := err.(esi.GenericSwaggerError)
				if ok && strings.Contains(string(v.Body()), "Unhandled internal error") {
					log.Printf("TERRIBLE CCP INTERNAL ERROR: %s", req.URL)
					return res, err
				}
			}

			// If we cannot decode this is likely from another source.
			reset, err := strconv.Atoi(resetS)
			if err != nil {
				esiRateLimiter = false
			}
			tokens, err := strconv.Atoi(tokensS)
			if err != nil {
				esiRateLimiter = false
			}

			// Backoff
			if res.StatusCode == 420 { // Something went wrong
				time.Sleep(time.Duration(reset)*time.Second + time.Duration(rand.Intn(int(time.Second*5))))
			} else if res.StatusCode == 429 { // SNAFU
				time.Sleep(time.Second * time.Duration(60+rand.Intn(30)))
			} else if esiRateLimiter { // Sleep based on error rate.
				time.Sleep(time.Second * time.Duration(float64(reset)*1.1*(1-(float64(tokens)/100))))
			} else if !esiRateLimiter { // Not an ESI error
				time.Sleep(time.Second * time.Duration(tries))
			}

			// break out after 10 tries
			if res.StatusCode == 420 || res.StatusCode == 429 || res.StatusCode >= 500 || res.StatusCode == 0 {
				if tries > 10 {
					return res, err
				}
			}
		}
		return res, err
	}
}

var (
	metricAPICalls = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: "evedata",
		Subsystem: "api",
		Name:      "calls",
		Help:      "API call statistics.",
		Buckets:   prometheus.ExponentialBuckets(10, 1.45, 20),
	},
		[]string{"host"},
	)

	metricAPIErrors = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "evedata",
		Subsystem: "api",
		Name:      "errors",
		Help:      "Count of API errors.",
	})
)

func init() {
	prometheus.MustRegister(
		metricAPICalls,
		metricAPIErrors,
	)
}
<|endoftext|>


<commit_before>/*
Copyright 2013 CoreOS Inc.

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 dbus

import (
	"errors"
	"time"

	"github.com/godbus/dbus"
)

const (
	cleanIgnoreInterval = int64(10 * time.Second)
	ignoreInterval      = int64(30 * time.Millisecond)
)

// Subscribe sets up this connection to subscribe to all systemd dbus events.
// This is required before calling SubscribeUnits. When the connection closes
// systemd will automatically stop sending signals so there is no need to
// explicitly call Unsubscribe().
func (c *Conn) Subscribe() error {
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")

	err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

// Unsubscribe this connection from systemd dbus events.
func (c *Conn) Unsubscribe() error {
	err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

func (c *Conn) initSubscription() {
	c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
}

func (c *Conn) initDispatch() {
	ch := make(chan *dbus.Signal, signalBuffer)

	c.sysconn.Signal(ch)

	go func() {
		for {
			signal, ok := <-ch
			if !ok {
				return
			}

			switch signal.Name {
			case "org.freedesktop.systemd1.Manager.JobRemoved":
				c.jobComplete(signal)

				unitName := signal.Body[2].(string)
				var unitPath dbus.ObjectPath
				c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
				if unitPath != dbus.ObjectPath("") {
					c.sendSubStateUpdate(unitPath)
				}
			case "org.freedesktop.systemd1.Manager.UnitNew":
				c.sendSubStateUpdate(signal.Body[1].(dbus.ObjectPath))
			case "org.freedesktop.DBus.Properties.PropertiesChanged":
				if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
					// we only care about SubState updates, which are a Unit property
					c.sendSubStateUpdate(signal.Path)
				}
			}
		}
	}()
}

// Returns two unbuffered channels which will receive all changed units every
// interval.  Deleted units are sent as nil.
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
	return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
}

// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
// size of the channels, the comparison function for detecting changes and a filter
// function for cutting down on the noise that your channel receives.
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func (string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
	old := make(map[string]*UnitStatus)
	statusChan := make(chan map[string]*UnitStatus, buffer)
	errChan := make(chan error, buffer)

	go func() {
		for {
			timerChan := time.After(interval)

			units, err := c.ListUnits()
			if err == nil {
				cur := make(map[string]*UnitStatus)
				for i := range units {
					if filterUnit != nil && filterUnit(units[i].Name) {
						continue
					}
					cur[units[i].Name] = &units[i]
				}

				// add all new or changed units
				changed := make(map[string]*UnitStatus)
				for n, u := range cur {
					if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
						changed[n] = u
					}
					delete(old, n)
				}

				// add all deleted units
				for oldN := range old {
					changed[oldN] = nil
				}

				old = cur

				if len(changed) != 0 {
					statusChan <- changed
				}
			} else {
				errChan <- err
			}

			<-timerChan
		}
	}()

	return statusChan, errChan
}

type SubStateUpdate struct {
	UnitName string
	SubState string
}

// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
// Although this writes to updateCh on every state change, the reported state
// may be more recent than the change that generated it (due to an unavoidable
// race in the systemd dbus interface).  That is, this method provides a good
// way to keep a current view of all units' states, but is not guaranteed to
// show every state transition they go through.  Furthermore, state changes
// will only be written to the channel with non-blocking writes.  If updateCh
// is full, it attempts to write an error to errCh; if errCh is full, the error
// passes silently.
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
	c.subscriber.Lock()
	defer c.subscriber.Unlock()
	c.subscriber.updateCh = updateCh
	c.subscriber.errCh = errCh
}

func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
	c.subscriber.Lock()
	defer c.subscriber.Unlock()
	if c.subscriber.updateCh == nil {
		return
	}

	if c.shouldIgnore(path) {
		return
	}

	info, err := c.GetUnitProperties(string(path))
	if err != nil {
		select {
		case c.subscriber.errCh <- err:
		default:
		}
	}

	name := info["Id"].(string)
	substate := info["SubState"].(string)

	update := &SubStateUpdate{name, substate}
	select {
	case c.subscriber.updateCh <- update:
	default:
		select {
		case c.subscriber.errCh <- errors.New("update channel full!"):
		default:
		}
	}

	c.updateIgnore(path, info)
}

// The ignore functions work around a wart in the systemd dbus interface.
// Requesting the properties of an unloaded unit will cause systemd to send a
// pair of UnitNew/UnitRemoved signals.  Because we need to get a unit's
// properties on UnitNew (as that's the only indication of a new unit coming up
// for the first time), we would enter an infinite loop if we did not attempt
// to detect and ignore these spurious signals.  The signal themselves are
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
// unloaded unit's signals for a short time after requesting its properties.
// This means that we will miss e.g. a transient unit being restarted
// *immediately* upon failure and also a transient unit being started
// immediately after requesting its status (with systemctl status, for example,
// because this causes a UnitNew signal to be sent which then causes us to fetch
// the properties).

func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
	t, ok := c.subscriber.ignore[path]
	return ok && t >= time.Now().UnixNano()
}

func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
	c.cleanIgnore()

	// unit is unloaded - it will trigger bad systemd dbus behavior
	if info["LoadState"].(string) == "not-found" {
		c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
	}
}

// without this, ignore would grow unboundedly over time
func (c *Conn) cleanIgnore() {
	now := time.Now().UnixNano()
	if c.subscriber.cleanIgnore < now {
		c.subscriber.cleanIgnore = now + cleanIgnoreInterval

		for p, t := range c.subscriber.ignore {
			if t < now {
				delete(c.subscriber.ignore, p)
			}
		}
	}
}
<commit_msg>dbus: check updateCh early<commit_after>/*
Copyright 2013 CoreOS Inc.

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 dbus

import (
	"errors"
	"time"

	"github.com/godbus/dbus"
)

const (
	cleanIgnoreInterval = int64(10 * time.Second)
	ignoreInterval      = int64(30 * time.Millisecond)
)

// Subscribe sets up this connection to subscribe to all systemd dbus events.
// This is required before calling SubscribeUnits. When the connection closes
// systemd will automatically stop sending signals so there is no need to
// explicitly call Unsubscribe().
func (c *Conn) Subscribe() error {
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")

	err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

// Unsubscribe this connection from systemd dbus events.
func (c *Conn) Unsubscribe() error {
	err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

func (c *Conn) initSubscription() {
	c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
}

func (c *Conn) initDispatch() {
	ch := make(chan *dbus.Signal, signalBuffer)

	c.sysconn.Signal(ch)

	go func() {
		for {
			signal, ok := <-ch
			if !ok {
				return
			}

			if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
				c.jobComplete(signal)
			}

			if c.subscriber.updateCh == nil {
				continue
			}

			var unitPath dbus.ObjectPath
			switch signal.Name {
			case "org.freedesktop.systemd1.Manager.JobRemoved":
				unitName := signal.Body[2].(string)
				c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
			case "org.freedesktop.systemd1.Manager.UnitNew":
				unitPath = signal.Body[1].(dbus.ObjectPath)
			case "org.freedesktop.DBus.Properties.PropertiesChanged":
				if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
					unitPath = signal.Path
				}
			}

			if unitPath == dbus.ObjectPath("") {
				continue
			}

			c.sendSubStateUpdate(unitPath)
		}
	}()
}

// Returns two unbuffered channels which will receive all changed units every
// interval.  Deleted units are sent as nil.
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
	return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
}

// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
// size of the channels, the comparison function for detecting changes and a filter
// function for cutting down on the noise that your channel receives.
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func (string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
	old := make(map[string]*UnitStatus)
	statusChan := make(chan map[string]*UnitStatus, buffer)
	errChan := make(chan error, buffer)

	go func() {
		for {
			timerChan := time.After(interval)

			units, err := c.ListUnits()
			if err == nil {
				cur := make(map[string]*UnitStatus)
				for i := range units {
					if filterUnit != nil && filterUnit(units[i].Name) {
						continue
					}
					cur[units[i].Name] = &units[i]
				}

				// add all new or changed units
				changed := make(map[string]*UnitStatus)
				for n, u := range cur {
					if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
						changed[n] = u
					}
					delete(old, n)
				}

				// add all deleted units
				for oldN := range old {
					changed[oldN] = nil
				}

				old = cur

				if len(changed) != 0 {
					statusChan <- changed
				}
			} else {
				errChan <- err
			}

			<-timerChan
		}
	}()

	return statusChan, errChan
}

type SubStateUpdate struct {
	UnitName string
	SubState string
}

// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
// Although this writes to updateCh on every state change, the reported state
// may be more recent than the change that generated it (due to an unavoidable
// race in the systemd dbus interface).  That is, this method provides a good
// way to keep a current view of all units' states, but is not guaranteed to
// show every state transition they go through.  Furthermore, state changes
// will only be written to the channel with non-blocking writes.  If updateCh
// is full, it attempts to write an error to errCh; if errCh is full, the error
// passes silently.
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
	c.subscriber.Lock()
	defer c.subscriber.Unlock()
	c.subscriber.updateCh = updateCh
	c.subscriber.errCh = errCh
}

func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
	c.subscriber.Lock()
	defer c.subscriber.Unlock()

	if c.shouldIgnore(path) {
		return
	}

	info, err := c.GetUnitProperties(string(path))
	if err != nil {
		select {
		case c.subscriber.errCh <- err:
		default:
		}
	}

	name := info["Id"].(string)
	substate := info["SubState"].(string)

	update := &SubStateUpdate{name, substate}
	select {
	case c.subscriber.updateCh <- update:
	default:
		select {
		case c.subscriber.errCh <- errors.New("update channel full!"):
		default:
		}
	}

	c.updateIgnore(path, info)
}

// The ignore functions work around a wart in the systemd dbus interface.
// Requesting the properties of an unloaded unit will cause systemd to send a
// pair of UnitNew/UnitRemoved signals.  Because we need to get a unit's
// properties on UnitNew (as that's the only indication of a new unit coming up
// for the first time), we would enter an infinite loop if we did not attempt
// to detect and ignore these spurious signals.  The signal themselves are
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
// unloaded unit's signals for a short time after requesting its properties.
// This means that we will miss e.g. a transient unit being restarted
// *immediately* upon failure and also a transient unit being started
// immediately after requesting its status (with systemctl status, for example,
// because this causes a UnitNew signal to be sent which then causes us to fetch
// the properties).

func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
	t, ok := c.subscriber.ignore[path]
	return ok && t >= time.Now().UnixNano()
}

func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
	c.cleanIgnore()

	// unit is unloaded - it will trigger bad systemd dbus behavior
	if info["LoadState"].(string) == "not-found" {
		c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
	}
}

// without this, ignore would grow unboundedly over time
func (c *Conn) cleanIgnore() {
	now := time.Now().UnixNano()
	if c.subscriber.cleanIgnore < now {
		c.subscriber.cleanIgnore = now + cleanIgnoreInterval

		for p, t := range c.subscriber.ignore {
			if t < now {
				delete(c.subscriber.ignore, p)
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI 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 main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/cli/cmd"
	"github.com/docker/compose-cli/cli/cmd/compose"
	contextcmd "github.com/docker/compose-cli/cli/cmd/context"
	"github.com/docker/compose-cli/cli/cmd/login"
	"github.com/docker/compose-cli/cli/cmd/logout"
	"github.com/docker/compose-cli/cli/cmd/run"
	"github.com/docker/compose-cli/cli/cmd/volume"
	"github.com/docker/compose-cli/cli/mobycli"
	cliopts "github.com/docker/compose-cli/cli/options"
	"github.com/docker/compose-cli/config"
	apicontext "github.com/docker/compose-cli/context"
	"github.com/docker/compose-cli/context/store"
	"github.com/docker/compose-cli/errdefs"
	"github.com/docker/compose-cli/metrics"

	// Backend registrations
	_ "github.com/docker/compose-cli/aci"
	_ "github.com/docker/compose-cli/ecs"
	_ "github.com/docker/compose-cli/ecs/local"
	_ "github.com/docker/compose-cli/example"
	_ "github.com/docker/compose-cli/local"
)

var (
	contextAgnosticCommands = map[string]struct{}{
		"compose": {},
		"context": {},
		"login":   {},
		"logout":  {},
		"serve":   {},
		"version": {},
	}
	unknownCommandRegexp = regexp.MustCompile(`unknown command "([^"]*)"`)
)

func init() {
	// initial hack to get the path of the project's bin dir
	// into the env of this cli for development
	path, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fatal(errors.Wrap(err, "unable to get absolute bin path"))
	}

	if err := os.Setenv("PATH", appendPaths(os.Getenv("PATH"), path)); err != nil {
		panic(err)
	}
	// Seed random
	rand.Seed(time.Now().UnixNano())
}

func appendPaths(envPath string, path string) string {
	if envPath == "" {
		return path
	}
	return strings.Join([]string{envPath, path}, string(os.PathListSeparator))
}

func isContextAgnosticCommand(cmd *cobra.Command) bool {
	if cmd == nil {
		return false
	}
	if _, ok := contextAgnosticCommands[cmd.Name()]; ok {
		return true
	}
	return isContextAgnosticCommand(cmd.Parent())
}

func main() {
	var opts cliopts.GlobalOpts
	root := &cobra.Command{
		Use:           "docker",
		SilenceErrors: true,
		SilenceUsage:  true,
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if !isContextAgnosticCommand(cmd) {
				mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			return cmd.Help()
		},
	}

	root.AddCommand(
		contextcmd.Command(),
		cmd.PsCommand(),
		cmd.ServeCommand(),
		cmd.ExecCommand(),
		cmd.LogsCommand(),
		cmd.RmCommand(),
		cmd.StartCommand(),
		cmd.InspectCommand(),
		login.Command(),
		logout.Command(),
		cmd.VersionCommand(),
		cmd.StopCommand(),
		cmd.KillCommand(),
		cmd.SecretCommand(),
		cmd.PruneCommand(),

		// Place holders
		cmd.EcsCommand(),
	)

	helpFunc := root.HelpFunc()
	root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		if !isContextAgnosticCommand(cmd) {
			mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
		}
		helpFunc(cmd, args)
	})

	root.PersistentFlags().BoolVarP(&opts.Debug, "debug", "D", false, "Enable debug output in the logs")
	root.PersistentFlags().StringVarP(&opts.Host, "host", "H", "", "Daemon socket(s) to connect to")
	opts.AddContextFlags(root.PersistentFlags())
	opts.AddConfigFlags(root.PersistentFlags())
	root.Flags().BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")
	root.PersistentFlags().SetInterspersed(false)
	root.Flags().SetInterspersed(false)
	walk(root, func(c *cobra.Command) {
		c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	})

	// populate the opts with the global flags
	_ = root.PersistentFlags().Parse(os.Args[1:])
	_ = root.Flags().Parse(os.Args[1:])
	if opts.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	ctx, cancel := newSigContext()
	defer cancel()

	// --host and --version should immediately be forwarded to the original cli
	if opts.Host != "" || opts.Version {
		mobycli.Exec(root)
	}

	if opts.Config == "" {
		fatal(errors.New("config path cannot be empty"))
	}
	configDir := opts.Config
	ctx = config.WithDir(ctx, configDir)

	currentContext := determineCurrentContext(opts.Context, configDir)

	s, err := store.New(configDir)
	if err != nil {
		mobycli.Exec(root)
	}

	ctype := store.DefaultContextType
	cc, _ := s.Get(currentContext)
	if cc != nil {
		ctype = cc.Type()
	}

	root.AddCommand(
		run.Command(ctype),
		compose.Command(ctype),
		volume.Command(ctype),
	)

	ctx = apicontext.WithCurrentContext(ctx, currentContext)
	ctx = store.WithContextStore(ctx, s)

	if err = root.ExecuteContext(ctx); err != nil {
		// if user canceled request, simply exit without any error message
		if errdefs.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
			metrics.Track(ctype, os.Args[1:], metrics.CanceledStatus)
			os.Exit(130)
		}
		if ctype == store.AwsContextType {
			exit(currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
$ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
		}

		// Context should always be handled by new CLI
		requiredCmd, _, _ := root.Find(os.Args[1:])
		if requiredCmd != nil && isContextAgnosticCommand(requiredCmd) {
			exit(currentContext, err, ctype)
		}
		mobycli.ExecIfDefaultCtxType(ctx, root)

		checkIfUnknownCommandExistInDefaultContext(err, currentContext, ctype)

		exit(currentContext, err, ctype)
	}
	metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
}

func exit(ctx string, err error, ctype string) {
	metrics.Track(ctype, os.Args[1:], metrics.FailureStatus)

	if errors.Is(err, errdefs.ErrLoginRequired) {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(errdefs.ExitCodeLoginRequired)
	}
	if errors.Is(err, errdefs.ErrNotImplemented) {
		name := metrics.GetCommand(os.Args[1:])
		fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)

		os.Exit(1)
	}

	fatal(err)
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string, contextType string) {
	submatch := unknownCommandRegexp.FindSubmatch([]byte(err.Error()))
	if len(submatch) == 2 {
		dockerCommand := string(submatch[1])

		if mobycli.IsDefaultContextCommand(dockerCommand) {
			fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
			metrics.Track(contextType, os.Args[1:], metrics.FailureStatus)
			os.Exit(1)
		}
	}
}

func newSigContext() (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-s
		cancel()
	}()
	return ctx, cancel
}

func determineCurrentContext(flag string, configDir string) string {
	res := flag
	if res == "" {
		config, err := config.LoadFile(configDir)
		if err != nil {
			fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
			return "default"
		}
		res = config.CurrentContext
	}
	if res == "" {
		res = "default"
	}
	return res
}

func walk(c *cobra.Command, f func(*cobra.Command)) {
	f(c)
	for _, c := range c.Commands() {
		walk(c, f)
	}
}
<commit_msg>Do not change root flags for all commands<commit_after>/*
   Copyright 2020 Docker Compose CLI 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 main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/cli/cmd"
	"github.com/docker/compose-cli/cli/cmd/compose"
	contextcmd "github.com/docker/compose-cli/cli/cmd/context"
	"github.com/docker/compose-cli/cli/cmd/login"
	"github.com/docker/compose-cli/cli/cmd/logout"
	"github.com/docker/compose-cli/cli/cmd/run"
	"github.com/docker/compose-cli/cli/cmd/volume"
	"github.com/docker/compose-cli/cli/mobycli"
	cliopts "github.com/docker/compose-cli/cli/options"
	"github.com/docker/compose-cli/config"
	apicontext "github.com/docker/compose-cli/context"
	"github.com/docker/compose-cli/context/store"
	"github.com/docker/compose-cli/errdefs"
	"github.com/docker/compose-cli/metrics"

	// Backend registrations
	_ "github.com/docker/compose-cli/aci"
	_ "github.com/docker/compose-cli/ecs"
	_ "github.com/docker/compose-cli/ecs/local"
	_ "github.com/docker/compose-cli/example"
	_ "github.com/docker/compose-cli/local"
)

var (
	contextAgnosticCommands = map[string]struct{}{
		"compose": {},
		"context": {},
		"login":   {},
		"logout":  {},
		"serve":   {},
		"version": {},
	}
	unknownCommandRegexp = regexp.MustCompile(`unknown command "([^"]*)"`)
)

func init() {
	// initial hack to get the path of the project's bin dir
	// into the env of this cli for development
	path, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fatal(errors.Wrap(err, "unable to get absolute bin path"))
	}

	if err := os.Setenv("PATH", appendPaths(os.Getenv("PATH"), path)); err != nil {
		panic(err)
	}
	// Seed random
	rand.Seed(time.Now().UnixNano())
}

func appendPaths(envPath string, path string) string {
	if envPath == "" {
		return path
	}
	return strings.Join([]string{envPath, path}, string(os.PathListSeparator))
}

func isContextAgnosticCommand(cmd *cobra.Command) bool {
	if cmd == nil {
		return false
	}
	if _, ok := contextAgnosticCommands[cmd.Name()]; ok {
		return true
	}
	return isContextAgnosticCommand(cmd.Parent())
}

func main() {
	var opts cliopts.GlobalOpts
	root := &cobra.Command{
		Use:           "docker",
		SilenceErrors: true,
		SilenceUsage:  true,
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if !isContextAgnosticCommand(cmd) {
				mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			return cmd.Help()
		},
	}

	root.AddCommand(
		contextcmd.Command(),
		cmd.PsCommand(),
		cmd.ServeCommand(),
		cmd.ExecCommand(),
		cmd.LogsCommand(),
		cmd.RmCommand(),
		cmd.StartCommand(),
		cmd.InspectCommand(),
		login.Command(),
		logout.Command(),
		cmd.VersionCommand(),
		cmd.StopCommand(),
		cmd.KillCommand(),
		cmd.SecretCommand(),
		cmd.PruneCommand(),

		// Place holders
		cmd.EcsCommand(),
	)

	helpFunc := root.HelpFunc()
	root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		if !isContextAgnosticCommand(cmd) {
			mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
		}
		helpFunc(cmd, args)
	})

	root.PersistentFlags().BoolVarP(&opts.Debug, "debug", "D", false, "Enable debug output in the logs")
	root.PersistentFlags().StringVarP(&opts.Host, "host", "H", "", "Daemon socket(s) to connect to")
	opts.AddContextFlags(root.PersistentFlags())
	opts.AddConfigFlags(root.PersistentFlags())
	root.Flags().BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")

	walk(root, func(c *cobra.Command) {
		c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	})

	// populate the opts with the global flags
	_ = root.PersistentFlags().Parse(os.Args[1:])
	_ = root.Flags().Parse(os.Args[1:])
	if opts.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	ctx, cancel := newSigContext()
	defer cancel()

	// --host and --version should immediately be forwarded to the original cli
	if opts.Host != "" || opts.Version {
		mobycli.Exec(root)
	}

	if opts.Config == "" {
		fatal(errors.New("config path cannot be empty"))
	}
	configDir := opts.Config
	ctx = config.WithDir(ctx, configDir)

	currentContext := determineCurrentContext(opts.Context, configDir)

	s, err := store.New(configDir)
	if err != nil {
		mobycli.Exec(root)
	}

	ctype := store.DefaultContextType
	cc, _ := s.Get(currentContext)
	if cc != nil {
		ctype = cc.Type()
	}

	root.AddCommand(
		run.Command(ctype),
		compose.Command(ctype),
		volume.Command(ctype),
	)

	ctx = apicontext.WithCurrentContext(ctx, currentContext)
	ctx = store.WithContextStore(ctx, s)

	if err = root.ExecuteContext(ctx); err != nil {
		// if user canceled request, simply exit without any error message
		if errdefs.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
			metrics.Track(ctype, os.Args[1:], metrics.CanceledStatus)
			os.Exit(130)
		}
		if ctype == store.AwsContextType {
			exit(currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
$ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
		}

		// Context should always be handled by new CLI
		requiredCmd, _, _ := root.Find(os.Args[1:])
		if requiredCmd != nil && isContextAgnosticCommand(requiredCmd) {
			exit(currentContext, err, ctype)
		}
		mobycli.ExecIfDefaultCtxType(ctx, root)

		checkIfUnknownCommandExistInDefaultContext(err, currentContext, ctype)

		exit(currentContext, err, ctype)
	}
	metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
}

func exit(ctx string, err error, ctype string) {
	metrics.Track(ctype, os.Args[1:], metrics.FailureStatus)

	if errors.Is(err, errdefs.ErrLoginRequired) {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(errdefs.ExitCodeLoginRequired)
	}
	if errors.Is(err, errdefs.ErrNotImplemented) {
		name := metrics.GetCommand(os.Args[1:])
		fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)

		os.Exit(1)
	}

	fatal(err)
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string, contextType string) {
	submatch := unknownCommandRegexp.FindSubmatch([]byte(err.Error()))
	if len(submatch) == 2 {
		dockerCommand := string(submatch[1])

		if mobycli.IsDefaultContextCommand(dockerCommand) {
			fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
			metrics.Track(contextType, os.Args[1:], metrics.FailureStatus)
			os.Exit(1)
		}
	}
}

func newSigContext() (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-s
		cancel()
	}()
	return ctx, cancel
}

func determineCurrentContext(flag string, configDir string) string {
	res := flag
	if res == "" {
		config, err := config.LoadFile(configDir)
		if err != nil {
			fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
			return "default"
		}
		res = config.CurrentContext
	}
	if res == "" {
		res = "default"
	}
	return res
}

func walk(c *cobra.Command, f func(*cobra.Command)) {
	f(c)
	for _, c := range c.Commands() {
		walk(c, f)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"testing"

	"github.com/diginatu/nagome/nicolive"
)

func setLogForTest() {
	if testing.Verbose() {
		log.SetFlags(log.Lshortfile | log.Ltime)
	} else {
		log.SetOutput(ioutil.Discard)
	}
}

func TestMainTCP(t *testing.T) {
	setLogForTest()
	App.SavePath = filepath.Join(os.TempDir(), "nagome_test")
	if err := os.MkdirAll(filepath.Join(App.SavePath, pluginDirName), 0777); err != nil {
		t.Fatal(err)
	}
	cv := NewCommentViewer(new(nicolive.Account), "")

	plug := newPlugin(cv)
	plug.Name = "main"
	plug.Description = "main plugin"
	plug.Version = "0.0"
	plug.Method = "tcp"
	plug.Depends = []string{DomainNagome, DomainComment, DomainUI}
	cv.AddPlugin(plug)

	cv.Pgns = append(cv.Pgns, plug)
	cv.TCPPort = "0"

	cv.Start()

	conn, err := net.Dial("tcp", ":"+cv.TCPPort)
	if err != nil {
		t.Fatal(err)
	}

	// Connect as a main plugin
	fmt.Fprintf(conn, "{ \"domain\": \"nagome_direct\", \"command\": \"No\", \"content\": { \"no\": 1 } }\n")

	dec := json.NewDecoder(conn)
	m := new(Message)
	for {
		err := dec.Decode(m)
		if err != nil {
			t.Fatal(err)
		}
		if m.Domain == DomainDirectngm && m.Command == CommDirectngmPlugEnabled {
			break
		}
	}

	conn.Close()
	cv.Wait()
	// shold quit because main plugin was closed
}
<commit_msg>fix output of testing<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"testing"

	"github.com/diginatu/nagome/nicolive"
)

func setLogForTest() {
	if testing.Verbose() {
		log.SetFlags(log.Lshortfile)
		log.SetPrefix("        ")
	} else {
		log.SetOutput(ioutil.Discard)
	}
}

func TestMainTCP(t *testing.T) {
	setLogForTest()
	App.SavePath = filepath.Join(os.TempDir(), "nagome_test")
	if err := os.MkdirAll(filepath.Join(App.SavePath, pluginDirName), 0777); err != nil {
		t.Fatal(err)
	}
	cv := NewCommentViewer(new(nicolive.Account), "")

	plug := newPlugin(cv)
	plug.Name = "main"
	plug.Description = "main plugin"
	plug.Version = "0.0"
	plug.Method = "tcp"
	plug.Depends = []string{DomainNagome, DomainComment, DomainUI}
	cv.AddPlugin(plug)

	cv.Pgns = append(cv.Pgns, plug)
	cv.TCPPort = "0"

	cv.Start()

	conn, err := net.Dial("tcp", ":"+cv.TCPPort)
	if err != nil {
		t.Fatal(err)
	}

	// Connect as a main plugin
	fmt.Fprintf(conn, "{ \"domain\": \"nagome_direct\", \"command\": \"No\", \"content\": { \"no\": 1 } }\n")

	dec := json.NewDecoder(conn)
	m := new(Message)
	for {
		err := dec.Decode(m)
		if err != nil {
			t.Fatal(err)
		}
		if m.Domain == DomainDirectngm && m.Command == CommDirectngmPlugEnabled {
			break
		}
	}

	conn.Close()
	cv.Wait()
	// shold quit because main plugin was closed
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/spf13/cobra"
)

var ClueCommand = &cobra.Command{
	Use: "clue",
	Run: func(cmd *cobra.Command, args []string) {
		printHelp()
	},
}

func printHelp() {
}
<commit_msg>Changing binary name to cluectl<commit_after>package cmd

import (
	"github.com/spf13/cobra"
)

var ClueCommand = &cobra.Command{
	Use: "cluectl",
	Run: func(cmd *cobra.Command, args []string) {
		printHelp()
	},
}

func printHelp() {
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os"
	"sort"

	"github.com/juju/ansiterm"
	"github.com/manifoldco/go-manifold"
	"github.com/urfave/cli"

	"github.com/manifoldco/manifold-cli/clients"
	"github.com/manifoldco/manifold-cli/config"
	"github.com/manifoldco/manifold-cli/data/catalog"
	"github.com/manifoldco/manifold-cli/middleware"
	"github.com/manifoldco/manifold-cli/session"

	"github.com/manifoldco/manifold-cli/generated/marketplace/models"
	pModels "github.com/manifoldco/manifold-cli/generated/provisioning/models"
)

type resourceList struct {
	totalResources int
	totalProjects  int
	groups         []resourceGroup
}

type resourceGroup struct {
	owner     string
	project   string
	resources []*models.Resource
}

func init() {
	listCmd := cli.Command{
		Name:     "list",
		Usage:    "List the status of your provisioned resources",
		Category: "RESOURCES",
		Action: middleware.Chain(middleware.LoadDirPrefs, middleware.EnsureSession,
			middleware.LoadTeamPrefs, list),
		Flags: append(teamFlags, []cli.Flag{
			projectFlag(),
		}...),
	}

	cmds = append(cmds, listCmd)
}

func list(cliCtx *cli.Context) error {
	ctx := context.Background()

	projectLabel, err := validateLabel(cliCtx, "project")
	if err != nil {
		return err
	}

	teamID, err := validateTeamID(cliCtx)
	if err != nil {
		return err
	}

	catalogClient, err := loadCatalogClient()
	if err != nil {
		return err
	}

	marketplaceClient, err := loadMarketplaceClient()
	if err != nil {
		return err
	}

	provisionClient, err := loadProvisioningClient()
	if err != nil {
		return err
	}

	// Get catalog
	catalog, err := catalog.New(ctx, catalogClient)
	if err != nil {
		return cli.NewExitError("Failed to fetch catalog data: "+err.Error(), -1)
	}

	// Get resources
	res, err := clients.FetchResources(ctx, marketplaceClient, teamID, projectLabel)
	if err != nil {
		return cli.NewExitError("Failed to fetch the list of provisioned "+
			"resources: "+err.Error(), -1)
	}

	// Get operations
	oRes, err := clients.FetchOperations(ctx, provisionClient, teamID)
	if err != nil {
		return cli.NewExitError("Failed to fetch the list of operations: "+err.Error(), -1)
	}

	resources, statuses := buildResourceList(res, oRes)

	list, err := groupResources(ctx, resources, teamID)
	if err != nil {
		return err
	}

	fmt.Printf("%d resources in %d projects\n", list.totalResources, list.totalProjects)
	fmt.Println("Use `manifold view [label]` to display resource details")

	tw := ansiterm.NewTabWriter(os.Stdout, 0, 0, 8, ' ', 0)

	for _, group := range list.groups {
		tw.SetStyle(ansiterm.Bold)
		fmt.Fprintf(tw, "\n%s", group.owner)
		tw.ClearStyle(ansiterm.Bold)

		if group.project != "" {

			fmt.Fprint(tw, "/")
			tw.SetStyle(ansiterm.Bold)
			fmt.Fprint(tw, group.project)
			tw.ClearStyle(ansiterm.Bold)
		}

		fmt.Fprintf(tw, "\n")

		tw.SetForeground(ansiterm.Gray)
		fmt.Fprintln(tw, "Label\tType\tStatus")
		tw.SetForeground(ansiterm.Default)

		for _, resource := range group.resources {
			rType := "Custom"

			if *resource.Body.Source != "custom" {
				// Get catalog data
				product, err := catalog.GetProduct(*resource.Body.ProductID)
				if err != nil {
					cli.NewExitError("Product referenced by resource does not exist: "+
						err.Error(), -1)
				}
				plan, err := catalog.GetPlan(*resource.Body.PlanID)
				if err != nil {
					cli.NewExitError("Plan referenced by resource does not exist: "+
						err.Error(), -1)
				}

				rType = fmt.Sprintf("%s %s", product.Body.Name, plan.Body.Name)
			}

			status, ok := statuses[resource.ID]
			if !ok {
				status = "Ready"
			}

			fmt.Fprintf(tw, "%s\t%s\t%s\n", resource.Body.Label, rType, status)
		}
	}

	tw.Flush()
	return nil
}

func buildResourceList(resources []*models.Resource, operations []*pModels.Operation) (
	[]*models.Resource, map[manifold.ID]string) {
	out := []*models.Resource{}
	statuses := make(map[manifold.ID]string)

	for _, op := range operations {
		switch op.Body.Type() {
		case "provision":
			body := op.Body.(*pModels.Provision)
			if body.State == nil {
				panic("State value was nil")
			}

			// if its a terminal state, then we can just ignore the op
			if *body.State == "done" || *body.State == "error" {
				continue
			}

			statuses[op.Body.ResourceID()] = "Creating"
			out = append(out, &models.Resource{
				ID: op.Body.ResourceID(),
				Body: &models.ResourceBody{
					CreatedAt: op.Body.CreatedAt(),
					UpdatedAt: op.Body.UpdatedAt(),
					Label:     manifold.Label(*body.Label),
					Name:      manifold.Name(*body.Name),
					Source:    body.Source,
					PlanID:    body.PlanID,
					ProductID: body.ProductID,
					RegionID:  body.RegionID,
					UserID:    op.Body.UserID(),
					TeamID:    op.Body.TeamID(),
					ProjectID: body.ProjectID,
				},
			})
		case "resize":
			body := op.Body.(*pModels.Resize)
			if body.State == nil {
				panic("State value was nil")
			}

			if *body.State == "done" || *body.State == "error" {
				continue
			}

			statuses[op.Body.ResourceID()] = "Resizing"
		case "deprovision":
			body := op.Body.(*pModels.Deprovision)
			if body.State == nil {
				panic("State value was nil")
			}

			if *body.State == "done" || *body.State == "error" {
				continue
			}

			statuses[op.Body.ResourceID()] = "Deleting"
		}
	}

	for _, r := range resources {
		out = append(out, r)
	}

	return out, statuses
}

func groupResources(ctx context.Context, resources []*models.Resource, teamID *manifold.ID) (resourceList, error) {
	list := resourceList{
		totalResources: len(resources),
	}

	email, err := userEmail(ctx)
	if err != nil {
		return list, err
	}

	marketplaceClient, err := loadMarketplaceClient()
	if err != nil {
		return list, err
	}

	identityClient, err := loadIdentityClient()
	if err != nil {
		return list, err
	}

	projects, err := clients.FetchProjects(ctx, marketplaceClient, teamID)
	if err != nil {
		return list, cli.NewExitError("Failed to fetch the list of projects: "+err.Error(), -1)
	}

	teams, err := clients.FetchTeams(ctx, identityClient)
	if err != nil {
		return list, err
	}

	type group struct {
		user    manifold.ID
		team    manifold.ID
		project manifold.ID
	}

	m := make(map[group][]*models.Resource)

	// Group resources by team/me + projects
	for _, r := range resources {
		key := group{}

		if r.Body.UserID != nil {
			key.user = *r.Body.UserID
		} else {
			key.team = *r.Body.TeamID
		}

		if r.Body.ProjectID != nil {
			key.project = *r.Body.ProjectID
		}

		list := m[key]
		list = append(list, r)
		m[key] = list
	}

	// Assemble groups into a single list, sorting resources by label
	var groups []resourceGroup
	for k, v := range m {
		sort.Slice(v, func(i, j int) bool {
			return v[i].Body.Label < v[j].Body.Label
		})

		var owner string
		var project string

		// Find the correct owner, either a team label or the user email
		if !k.user.IsEmpty() {
			owner = email
		} else {
			for _, t := range teams {
				if t.ID == k.team {
					owner = string(t.Body.Label)
				}
			}
		}

		// Find the project label if any
		if !k.project.IsEmpty() {
			list.totalProjects++
			for _, p := range projects {
				if p.ID == k.project {
					project = string(p.Body.Label)
				}
			}
		}

		groups = append(groups, resourceGroup{
			owner:     owner,
			project:   project,
			resources: v,
		})
	}

	// Sort groups by owner, project and name
	sort.Slice(groups, func(i, j int) bool {
		a := groups[i]
		b := groups[j]

		if a.owner != b.owner {
			return a.owner < b.owner
		}

		return a.project < b.project
	})

	list.groups = groups

	return list, nil
}

// userEmail returns the user email based on the authenticated session
func userEmail(ctx context.Context) (string, error) {
	cfg, err := config.Load()
	if err != nil {
		return "", cli.NewExitError(fmt.Sprintf("Could not load configuration: %s", err), -1)
	}

	s, err := session.Retrieve(ctx, cfg)
	if err != nil {
		return "", cli.NewExitError("Could not retrieve session: "+err.Error(), -1)
	}

	label := s.LabelInfo()

	if label == nil && len(*label) < 2 {
		return "", cli.NewExitError("Could not retrieve user email", -1)
	}

	return (*label)[1], nil
}
<commit_msg>Reset list color<commit_after>package main

import (
	"context"
	"fmt"
	"os"
	"sort"

	"github.com/juju/ansiterm"
	"github.com/manifoldco/go-manifold"
	"github.com/urfave/cli"

	"github.com/manifoldco/manifold-cli/clients"
	"github.com/manifoldco/manifold-cli/config"
	"github.com/manifoldco/manifold-cli/data/catalog"
	"github.com/manifoldco/manifold-cli/middleware"
	"github.com/manifoldco/manifold-cli/session"

	"github.com/manifoldco/manifold-cli/generated/marketplace/models"
	pModels "github.com/manifoldco/manifold-cli/generated/provisioning/models"
)

type resourceList struct {
	totalResources int
	totalProjects  int
	groups         []resourceGroup
}

type resourceGroup struct {
	owner     string
	project   string
	resources []*models.Resource
}

func init() {
	listCmd := cli.Command{
		Name:     "list",
		Usage:    "List the status of your provisioned resources",
		Category: "RESOURCES",
		Action: middleware.Chain(middleware.LoadDirPrefs, middleware.EnsureSession,
			middleware.LoadTeamPrefs, list),
		Flags: append(teamFlags, []cli.Flag{
			projectFlag(),
		}...),
	}

	cmds = append(cmds, listCmd)
}

func list(cliCtx *cli.Context) error {
	ctx := context.Background()

	projectLabel, err := validateLabel(cliCtx, "project")
	if err != nil {
		return err
	}

	teamID, err := validateTeamID(cliCtx)
	if err != nil {
		return err
	}

	catalogClient, err := loadCatalogClient()
	if err != nil {
		return err
	}

	marketplaceClient, err := loadMarketplaceClient()
	if err != nil {
		return err
	}

	provisionClient, err := loadProvisioningClient()
	if err != nil {
		return err
	}

	// Get catalog
	catalog, err := catalog.New(ctx, catalogClient)
	if err != nil {
		return cli.NewExitError("Failed to fetch catalog data: "+err.Error(), -1)
	}

	// Get resources
	res, err := clients.FetchResources(ctx, marketplaceClient, teamID, projectLabel)
	if err != nil {
		return cli.NewExitError("Failed to fetch the list of provisioned "+
			"resources: "+err.Error(), -1)
	}

	// Get operations
	oRes, err := clients.FetchOperations(ctx, provisionClient, teamID)
	if err != nil {
		return cli.NewExitError("Failed to fetch the list of operations: "+err.Error(), -1)
	}

	resources, statuses := buildResourceList(res, oRes)

	list, err := groupResources(ctx, resources, teamID)
	if err != nil {
		return err
	}

	fmt.Printf("%d resources in %d projects\n", list.totalResources, list.totalProjects)
	fmt.Println("Use `manifold view [label]` to display resource details")

	tw := ansiterm.NewTabWriter(os.Stdout, 0, 0, 8, ' ', 0)

	for _, group := range list.groups {
		tw.SetStyle(ansiterm.Bold)
		fmt.Fprintf(tw, "\n%s", group.owner)
		tw.ClearStyle(ansiterm.Bold)

		if group.project != "" {
			fmt.Fprint(tw, "/")
			tw.SetStyle(ansiterm.Bold)
			fmt.Fprint(tw, group.project)
			tw.ClearStyle(ansiterm.Bold)
		}

		fmt.Fprintf(tw, "\n")

		tw.SetForeground(ansiterm.Gray)
		fmt.Fprintln(tw, "Label\tType\tStatus")
		tw.Reset()

		for _, resource := range group.resources {
			rType := "Custom"

			if *resource.Body.Source != "custom" {
				// Get catalog data
				product, err := catalog.GetProduct(*resource.Body.ProductID)
				if err != nil {
					cli.NewExitError("Product referenced by resource does not exist: "+
						err.Error(), -1)
				}
				plan, err := catalog.GetPlan(*resource.Body.PlanID)
				if err != nil {
					cli.NewExitError("Plan referenced by resource does not exist: "+
						err.Error(), -1)
				}

				rType = fmt.Sprintf("%s %s", product.Body.Name, plan.Body.Name)
			}

			status, ok := statuses[resource.ID]
			if !ok {
				status = "Ready"
			}

			fmt.Fprintf(tw, "%s\t%s\t%s\n", resource.Body.Label, rType, status)
		}
	}

	tw.Flush()
	return nil
}

func buildResourceList(resources []*models.Resource, operations []*pModels.Operation) (
	[]*models.Resource, map[manifold.ID]string) {
	out := []*models.Resource{}
	statuses := make(map[manifold.ID]string)

	for _, op := range operations {
		switch op.Body.Type() {
		case "provision":
			body := op.Body.(*pModels.Provision)
			if body.State == nil {
				panic("State value was nil")
			}

			// if its a terminal state, then we can just ignore the op
			if *body.State == "done" || *body.State == "error" {
				continue
			}

			statuses[op.Body.ResourceID()] = "Creating"
			out = append(out, &models.Resource{
				ID: op.Body.ResourceID(),
				Body: &models.ResourceBody{
					CreatedAt: op.Body.CreatedAt(),
					UpdatedAt: op.Body.UpdatedAt(),
					Label:     manifold.Label(*body.Label),
					Name:      manifold.Name(*body.Name),
					Source:    body.Source,
					PlanID:    body.PlanID,
					ProductID: body.ProductID,
					RegionID:  body.RegionID,
					UserID:    op.Body.UserID(),
					TeamID:    op.Body.TeamID(),
					ProjectID: body.ProjectID,
				},
			})
		case "resize":
			body := op.Body.(*pModels.Resize)
			if body.State == nil {
				panic("State value was nil")
			}

			if *body.State == "done" || *body.State == "error" {
				continue
			}

			statuses[op.Body.ResourceID()] = "Resizing"
		case "deprovision":
			body := op.Body.(*pModels.Deprovision)
			if body.State == nil {
				panic("State value was nil")
			}

			if *body.State == "done" || *body.State == "error" {
				continue
			}

			statuses[op.Body.ResourceID()] = "Deleting"
		}
	}

	for _, r := range resources {
		out = append(out, r)
	}

	return out, statuses
}

func groupResources(ctx context.Context, resources []*models.Resource, teamID *manifold.ID) (resourceList, error) {
	list := resourceList{
		totalResources: len(resources),
	}

	email, err := userEmail(ctx)
	if err != nil {
		return list, err
	}

	marketplaceClient, err := loadMarketplaceClient()
	if err != nil {
		return list, err
	}

	identityClient, err := loadIdentityClient()
	if err != nil {
		return list, err
	}

	projects, err := clients.FetchProjects(ctx, marketplaceClient, teamID)
	if err != nil {
		return list, cli.NewExitError("Failed to fetch the list of projects: "+err.Error(), -1)
	}

	teams, err := clients.FetchTeams(ctx, identityClient)
	if err != nil {
		return list, err
	}

	type group struct {
		user    manifold.ID
		team    manifold.ID
		project manifold.ID
	}

	m := make(map[group][]*models.Resource)

	// Group resources by team/me + projects
	for _, r := range resources {
		key := group{}

		if r.Body.UserID != nil {
			key.user = *r.Body.UserID
		} else {
			key.team = *r.Body.TeamID
		}

		if r.Body.ProjectID != nil {
			key.project = *r.Body.ProjectID
		}

		list := m[key]
		list = append(list, r)
		m[key] = list
	}

	// Assemble groups into a single list, sorting resources by label
	var groups []resourceGroup
	for k, v := range m {
		sort.Slice(v, func(i, j int) bool {
			return v[i].Body.Label < v[j].Body.Label
		})

		var owner string
		var project string

		// Find the correct owner, either a team label or the user email
		if !k.user.IsEmpty() {
			owner = email
		} else {
			for _, t := range teams {
				if t.ID == k.team {
					owner = string(t.Body.Label)
				}
			}
		}

		// Find the project label if any
		if !k.project.IsEmpty() {
			list.totalProjects++
			for _, p := range projects {
				if p.ID == k.project {
					project = string(p.Body.Label)
				}
			}
		}

		groups = append(groups, resourceGroup{
			owner:     owner,
			project:   project,
			resources: v,
		})
	}

	// Sort groups by owner, project and name
	sort.Slice(groups, func(i, j int) bool {
		a := groups[i]
		b := groups[j]

		if a.owner != b.owner {
			return a.owner < b.owner
		}

		return a.project < b.project
	})

	list.groups = groups

	return list, nil
}

// userEmail returns the user email based on the authenticated session
func userEmail(ctx context.Context) (string, error) {
	cfg, err := config.Load()
	if err != nil {
		return "", cli.NewExitError(fmt.Sprintf("Could not load configuration: %s", err), -1)
	}

	s, err := session.Retrieve(ctx, cfg)
	if err != nil {
		return "", cli.NewExitError("Could not retrieve session: "+err.Error(), -1)
	}

	label := s.LabelInfo()

	if label == nil && len(*label) < 2 {
		return "", cli.NewExitError("Could not retrieve user email", -1)
	}

	return (*label)[1], nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/urfave/cli"
)

// List returns the list of tasks/todos that have been recorded
func List(ctx *cli.Context) error {
	//m := task.NewFileManager()

	return nil
}
<commit_msg>Added basic list cmd code<commit_after>package cmd

import (
	"fmt"

	"github.com/tuxagon/yata-cli/task"
	"github.com/urfave/cli"
)

// List returns the list of tasks/todos that have been recorded
func List(ctx *cli.Context) error {
	m := task.NewFileManager()
	tasks := m.GetAllOpenTasks()

	fmt.Println(tasks)
	for _, v := range tasks {
		fmt.Println(v)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/lextoumbourou/goodhosts"
	"os"
	"os/user"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	showComments := flag.Bool("all", false, "Show comments when listing.")

	flag.Parse()

	args := flag.Args()

	if len(args) > 0 {
		command := args[0]
		hosts := goodhosts.NewHosts()

		switch command {
		case "list":
			for _, line := range hosts.Lines {
				if line.IsComment() && !*showComments {
					continue
				}

				fmt.Printf("%s\n", line.Raw)
			}

			fmt.Printf("\nTotal: %d\n", len(hosts.Lines))

			return
		case "check":
			if len(os.Args) < 3 {
				fmt.Println("usage: goodhosts check 127.0.0.1 facebook.com")
				os.Exit(1)
			}

			ip := os.Args[2]
			host := os.Args[3]

			if !hosts.HasEntry(ip, host) {
				fmt.Fprintf(os.Stderr, "%s %s is not in the hosts file\n", ip, host)
				os.Exit(1)
			}

			return
		case "add":
			if len(os.Args) < 3 {
				fmt.Println("usage: goodhosts add 127.0.0.1 facebook.com")
				os.Exit(1)
			}
			user, err := user.Current()
			check(err)

			ip := os.Args[2]
			host := os.Args[3]
			if hosts.HasEntry(ip, host) {
				fmt.Fprintf(os.Stderr, "Line already in host file. Nothing to do.\n")
				os.Exit(2)
			}

			if user == nil || user.Uid != "0" {
				fmt.Fprintf(os.Stderr, "Need to be root user. Try running with sudo.\n")
				os.Exit(1)
			}

			hosts.AddEntry(ip, host)

			err = hosts.Flush()
			check(err)

			return
		case "remove":
			if len(os.Args) < 3 {
				fmt.Println("usage: goodhost remove 127.0.0.1 facebook.com")
				os.Exit(1)
			}
			user, err := user.Current()
			check(err)

			ip := os.Args[2]
			host := os.Args[3]

			if !hosts.HasEntry(ip, host) {
				fmt.Fprintf(os.Stderr, "Line not in host file. Nothing to do.\n")
				os.Exit(3)
			}

			if user == nil || user.Uid != "0" {
				fmt.Fprintf(os.Stderr, "Need to be root user. Try running with sudo.\n")
				os.Exit(1)
			}

			err = hosts.RemoveEntry(ip, host)
			check(err)

			err = hosts.Flush()
			check(err)

			return
		}
	}

	fmt.Println("Add --help for usage.")
	os.Exit(2)
}
<commit_msg>Fixed total.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/lextoumbourou/goodhosts"
	"os"
	"os/user"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	showComments := flag.Bool("all", false, "Show comments when listing.")

	flag.Parse()

	args := flag.Args()

	if len(args) > 0 {
		command := args[0]
		hosts := goodhosts.NewHosts()

		switch command {
		case "list":
			total := 0
			for _, line := range hosts.Lines {
				var lineOutput string

				if line.IsComment() && !*showComments {
					continue
				}

				lineOutput = fmt.Sprintf("%s", line.Raw)
				if line.Err != nil {
					lineOutput = fmt.Sprintf("%s # <<< Malformated!", lineOutput)
				}
				total += 1

				fmt.Println(lineOutput)
			}

			fmt.Printf("\nTotal: %d\n", total)

			return
		case "check":
			if len(os.Args) < 3 {
				fmt.Println("usage: goodhosts check 127.0.0.1 facebook.com")
				os.Exit(1)
			}

			ip := os.Args[2]
			host := os.Args[3]

			if !hosts.HasEntry(ip, host) {
				fmt.Fprintf(os.Stderr, "%s %s is not in the hosts file\n", ip, host)
				os.Exit(1)
			}

			return
		case "add":
			if len(os.Args) < 3 {
				fmt.Println("usage: goodhosts add 127.0.0.1 facebook.com")
				os.Exit(1)
			}
			user, err := user.Current()
			check(err)

			ip := os.Args[2]
			host := os.Args[3]
			if hosts.HasEntry(ip, host) {
				fmt.Fprintf(os.Stderr, "Line already in host file. Nothing to do.\n")
				os.Exit(2)
			}

			if user == nil || user.Uid != "0" {
				fmt.Fprintf(os.Stderr, "Need to be root user. Try running with sudo.\n")
				os.Exit(1)
			}

			hosts.AddEntry(ip, host)

			err = hosts.Flush()
			check(err)

			return
		case "remove":
			if len(os.Args) < 3 {
				fmt.Println("usage: goodhost remove 127.0.0.1 facebook.com")
				os.Exit(1)
			}
			user, err := user.Current()
			check(err)

			ip := os.Args[2]
			host := os.Args[3]

			if !hosts.HasEntry(ip, host) {
				fmt.Fprintf(os.Stderr, "Line not in host file. Nothing to do.\n")
				os.Exit(3)
			}

			if user == nil || user.Uid != "0" {
				fmt.Fprintf(os.Stderr, "Need to be root user. Try running with sudo.\n")
				os.Exit(1)
			}

			err = hosts.RemoveEntry(ip, host)
			check(err)

			err = hosts.Flush()
			check(err)

			return
		}
	}

	fmt.Println("Add --help for usage.")
	os.Exit(2)
}
<|endoftext|>