<commit_before>// +build !windows

package template

import (
	"bytes"
	"encoding/json"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func boolPointer(tf bool) *bool {
	return &tf
}

func TestParse(t *testing.T) {
	cases := []struct {
		File   string
		Result *Template
		Err    bool
	}{
		/*
		 * Builders
		 */
		{
			"parse-basic.json",
			&Template{
				Builders: map[string]*Builder{
					"something": {
						Name: "something",
						Type: "something",
					},
				},
			},
			false,
		},
		{
			"parse-basic-config.json",
			&Template{
				Builders: map[string]*Builder{
					"something": {
						Name: "something",
						Type: "something",
						Config: map[string]interface{}{
							"foo": "bar",
						},
					},
				},
			},
			false,
		},
		{
			"parse-builder-no-type.json",
			nil,
			true,
		},
		{
			"parse-builder-repeat.json",
			nil,
			true,
		},

		/*
		 * Provisioners
		 */
		{
			"parse-provisioner-basic.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
					},
				},
			},
			false,
		},
		{
			"parse-provisioner-config.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						Config: map[string]interface{}{
							"inline": "echo 'foo'",
						},
					},
				},
			},
			false,
		},
		{
			"parse-provisioner-pause-before.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type:        "something",
						PauseBefore: 1 * time.Second,
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-timeout.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type:    "something",
						Timeout: 5 * time.Minute,
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-only.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						OnlyExcept: OnlyExcept{
							Only: []string{"foo"},
						},
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-except.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						OnlyExcept: OnlyExcept{
							Except: []string{"foo"},
						},
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-override.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						Override: map[string]interface{}{
							"foo": map[string]interface{}{},
						},
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-no-type.json",
			nil,
			true,
		},

		{
			"parse-variable-default.json",
			&Template{
				Variables: map[string]*Variable{
					"foo": {
						Default: "foo",
						Key:     "foo",
					},
				},
			},
			false,
		},

		{
			"parse-variable-required.json",
			&Template{
				Variables: map[string]*Variable{
					"foo": {
						Required: true,
						Key:      "foo",
					},
				},
			},
			false,
		},

		{
			"parse-pp-basic.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
							Config: map[string]interface{}{
								"foo": "bar",
							},
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-keep.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name:              "foo",
							Type:              "foo",
							KeepInputArtifact: boolPointer(true),
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-only.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
							OnlyExcept: OnlyExcept{
								Only: []string{"bar"},
							},
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-except.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
							OnlyExcept: OnlyExcept{
								Except: []string{"bar"},
							},
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-string.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-map.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-slice.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
					},
					{
						{
							Name: "bar",
							Type: "bar",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-multi.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
						{
							Name: "bar",
							Type: "bar",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-no-type.json",
			nil,
			true,
		},

		{
			"parse-description.json",
			&Template{
				Description: "foo",
			},
			false,
		},

		{
			"parse-min-version.json",
			&Template{
				MinVersion: "1.2",
			},
			false,
		},

		{
			"parse-comment.json",
			&Template{
				Builders: map[string]*Builder{
					"something": {
						Name: "something",
						Type: "something",
					},
				},
				Comments: map[string]string{
					"_info": "foo",
				},
			},
			false,
		},
		{
			"parse-monolithic.json",
			&Template{
				Comments: map[string]string{
					"_comment": "comment",
				},
				Description: "Description Test",
				MinVersion:  "1.3.0",
				SensitiveVariables: []*Variable{
					{
						Required: false,
						Key:      "one",
						Default:  "1",
					},
				},
				Variables: map[string]*Variable{
					"one": {
						Required: false,
						Key:      "one",
						Default:  "1",
					},
					"two": {
						Required: false,
						Key:      "two",
						Default:  "2",
					},
					"three": {
						Required: true,
						Key:      "three",
						Default:  "",
					},
				},
				Builders: map[string]*Builder{
					"amazon-ebs": {
						Name: "amazon-ebs",
						Type: "amazon-ebs",
						Config: map[string]interface{}{
							"ami_name":      "AMI Name",
							"instance_type": "t2.micro",
							"ssh_username":  "ec2-user",
							"source_ami":    "ami-aaaaaaaaaaaaaa",
						},
					},
					"docker": {
						Name: "docker",
						Type: "docker",
						Config: map[string]interface{}{
							"image":       "ubuntu",
							"export_path": "image.tar",
						},
					},
				},
				Provisioners: []*Provisioner{
					{
						Type: "shell",
						Config: map[string]interface{}{
							"script": "script.sh",
						},
					},
					{
						Type: "shell",
						Config: map[string]interface{}{
							"script": "script.sh",
						},
						Override: map[string]interface{}{
							"docker": map[string]interface{}{
								"execute_command": "echo 'override'",
							},
						},
					},
				},
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "compress",
							Type: "compress",
						},
						{
							Name: "vagrant",
							Type: "vagrant",
							OnlyExcept: OnlyExcept{
								Only: []string{"docker"},
							},
						},
					},
					{
						{
							Name: "shell-local",
							Type: "shell-local",
							Config: map[string]interface{}{
								"inline": []interface{}{"echo foo"},
							},
							OnlyExcept: OnlyExcept{
								Except: []string{"amazon-ebs"},
							},
						},
					},
				},
			},
			false,
		},
	}

	for i, tc := range cases {
		path, _ := filepath.Abs(fixtureDir(tc.File))
		tpl, err := ParseFile(fixtureDir(tc.File))
		if (err != nil) != tc.Err {
			t.Fatalf("%s\n\nerr: %s", tc.File, err)
		}

		if tc.Result != nil {
			tc.Result.Path = path
		}
		if tpl != nil {
			tpl.RawContents = nil
		}
		if diff := cmp.Diff(tpl, tc.Result); diff != "" {
			t.Fatalf("[%d]bad: %s\n%v", i, tc.File, diff)
		}

		// Only test template writing if the template is valid
		if tc.Err == false {
			// Get rawTemplate
			raw, err := tpl.Raw()
			if err != nil {
				t.Fatalf("Failed to convert back to raw template: %s\n\n%v\n\n%s", tc.File, tpl, err)
			}

			out, _ := json.MarshalIndent(raw, "", "  ")
			if err != nil {
				t.Fatalf("Failed to marshal raw template: %s\n\n%v\n\n%s", tc.File, raw, err)
			}

			// Write JSON to a buffer (emulates filesystem write without dirtying the workspace)
			fileBuf := bytes.NewBuffer(out)

			// Parse the JSON template we wrote to our buffer
			tplRewritten, err := Parse(fileBuf)
			if err != nil {
				t.Fatalf("Failed to re-read marshalled template: %s\n\n%v\n\n%s\n\n%s", tc.File, tpl, out, err)
			}

			// Override the metadata we don't care about (file path, raw file contents)
			tplRewritten.Path = path
			tplRewritten.RawContents = nil

			// Test that our output raw template is functionally equal
			if !reflect.DeepEqual(tpl, tplRewritten) {
				t.Fatalf("Data lost when writing template to file: %s\n\n%v\n\n%v\n\n%s", tc.File, tpl, tplRewritten, out)
			}
		}
	}
}

func TestParse_contents(t *testing.T) {
	tpl, err := ParseFile(fixtureDir("parse-contents.json"))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(string(tpl.RawContents))
	expected := `{"builders":[{"type":"test"}]}`
	if actual != expected {
		t.Fatalf("bad: %s\n\n%s", actual, expected)
	}
}

func TestParse_bad(t *testing.T) {
	cases := []struct {
		File     string
		Expected string
	}{
		{"error-beginning.json", "line 1, column 1 (offset 1)"},
		{"error-middle.json", "line 5, column 6 (offset 50)"},
		{"error-end.json", "line 1, column 30 (offset 30)"},
		{"malformed.json", "line 16, column 3 (offset 433)"},
	}
	for _, tc := range cases {
		_, err := ParseFile(fixtureDir(tc.File))
		if err == nil {
			t.Fatalf("expected error")
		}
		if !strings.Contains(err.Error(), tc.Expected) {
			t.Fatalf("file: %s\nExpected: %s\n%s\n", tc.File, tc.Expected, err.Error())
		}
	}
}

func TestParse_checkForDuplicateFields(t *testing.T) {
	cases := []struct {
		File     string
		Expected string
	}{
		{"error-duplicate-variables.json", "template has duplicate field: variables"},
		{"error-duplicate-config.json", "template has duplicate field: foo"},
	}
	for _, tc := range cases {
		_, err := ParseFile(fixtureDir(tc.File))
		if err == nil {
			t.Fatalf("expected error")
		}
		if !strings.Contains(err.Error(), tc.Expected) {
			t.Fatalf("file: %s\nExpected: %s\n%s\n", tc.File, tc.Expected, err.Error())
		}
	}
}<commit_msg>Fix format<commit_after>// +build !windows

package template

import (
	"bytes"
	"encoding/json"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func boolPointer(tf bool) *bool {
	return &tf
}

func TestParse(t *testing.T) {
	cases := []struct {
		File   string
		Result *Template
		Err    bool
	}{
		/*
		 * Builders
		 */
		{
			"parse-basic.json",
			&Template{
				Builders: map[string]*Builder{
					"something": {
						Name: "something",
						Type: "something",
					},
				},
			},
			false,
		},
		{
			"parse-basic-config.json",
			&Template{
				Builders: map[string]*Builder{
					"something": {
						Name: "something",
						Type: "something",
						Config: map[string]interface{}{
							"foo": "bar",
						},
					},
				},
			},
			false,
		},
		{
			"parse-builder-no-type.json",
			nil,
			true,
		},
		{
			"parse-builder-repeat.json",
			nil,
			true,
		},

		/*
		 * Provisioners
		 */
		{
			"parse-provisioner-basic.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
					},
				},
			},
			false,
		},
		{
			"parse-provisioner-config.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						Config: map[string]interface{}{
							"inline": "echo 'foo'",
						},
					},
				},
			},
			false,
		},
		{
			"parse-provisioner-pause-before.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type:        "something",
						PauseBefore: 1 * time.Second,
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-timeout.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type:    "something",
						Timeout: 5 * time.Minute,
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-only.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						OnlyExcept: OnlyExcept{
							Only: []string{"foo"},
						},
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-except.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						OnlyExcept: OnlyExcept{
							Except: []string{"foo"},
						},
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-override.json",
			&Template{
				Provisioners: []*Provisioner{
					{
						Type: "something",
						Override: map[string]interface{}{
							"foo": map[string]interface{}{},
						},
					},
				},
			},
			false,
		},

		{
			"parse-provisioner-no-type.json",
			nil,
			true,
		},

		{
			"parse-variable-default.json",
			&Template{
				Variables: map[string]*Variable{
					"foo": {
						Default: "foo",
						Key:     "foo",
					},
				},
			},
			false,
		},

		{
			"parse-variable-required.json",
			&Template{
				Variables: map[string]*Variable{
					"foo": {
						Required: true,
						Key:      "foo",
					},
				},
			},
			false,
		},

		{
			"parse-pp-basic.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
							Config: map[string]interface{}{
								"foo": "bar",
							},
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-keep.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name:              "foo",
							Type:              "foo",
							KeepInputArtifact: boolPointer(true),
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-only.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
							OnlyExcept: OnlyExcept{
								Only: []string{"bar"},
							},
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-except.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
							OnlyExcept: OnlyExcept{
								Except: []string{"bar"},
							},
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-string.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-map.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-slice.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
					},
					{
						{
							Name: "bar",
							Type: "bar",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-multi.json",
			&Template{
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "foo",
							Type: "foo",
						},
						{
							Name: "bar",
							Type: "bar",
						},
					},
				},
			},
			false,
		},

		{
			"parse-pp-no-type.json",
			nil,
			true,
		},

		{
			"parse-description.json",
			&Template{
				Description: "foo",
			},
			false,
		},

		{
			"parse-min-version.json",
			&Template{
				MinVersion: "1.2",
			},
			false,
		},

		{
			"parse-comment.json",
			&Template{
				Builders: map[string]*Builder{
					"something": {
						Name: "something",
						Type: "something",
					},
				},
				Comments: map[string]string{
					"_info": "foo",
				},
			},
			false,
		},
		{
			"parse-monolithic.json",
			&Template{
				Comments: map[string]string{
					"_comment": "comment",
				},
				Description: "Description Test",
				MinVersion:  "1.3.0",
				SensitiveVariables: []*Variable{
					{
						Required: false,
						Key:      "one",
						Default:  "1",
					},
				},
				Variables: map[string]*Variable{
					"one": {
						Required: false,
						Key:      "one",
						Default:  "1",
					},
					"two": {
						Required: false,
						Key:      "two",
						Default:  "2",
					},
					"three": {
						Required: true,
						Key:      "three",
						Default:  "",
					},
				},
				Builders: map[string]*Builder{
					"amazon-ebs": {
						Name: "amazon-ebs",
						Type: "amazon-ebs",
						Config: map[string]interface{}{
							"ami_name":      "AMI Name",
							"instance_type": "t2.micro",
							"ssh_username":  "ec2-user",
							"source_ami":    "ami-aaaaaaaaaaaaaa",
						},
					},
					"docker": {
						Name: "docker",
						Type: "docker",
						Config: map[string]interface{}{
							"image":       "ubuntu",
							"export_path": "image.tar",
						},
					},
				},
				Provisioners: []*Provisioner{
					{
						Type: "shell",
						Config: map[string]interface{}{
							"script": "script.sh",
						},
					},
					{
						Type: "shell",
						Config: map[string]interface{}{
							"script": "script.sh",
						},
						Override: map[string]interface{}{
							"docker": map[string]interface{}{
								"execute_command": "echo 'override'",
							},
						},
					},
				},
				PostProcessors: [][]*PostProcessor{
					{
						{
							Name: "compress",
							Type: "compress",
						},
						{
							Name: "vagrant",
							Type: "vagrant",
							OnlyExcept: OnlyExcept{
								Only: []string{"docker"},
							},
						},
					},
					{
						{
							Name: "shell-local",
							Type: "shell-local",
							Config: map[string]interface{}{
								"inline": []interface{}{"echo foo"},
							},
							OnlyExcept: OnlyExcept{
								Except: []string{"amazon-ebs"},
							},
						},
					},
				},
			},
			false,
		},
	}

	for i, tc := range cases {
		path, _ := filepath.Abs(fixtureDir(tc.File))
		tpl, err := ParseFile(fixtureDir(tc.File))
		if (err != nil) != tc.Err {
			t.Fatalf("%s\n\nerr: %s", tc.File, err)
		}

		if tc.Result != nil {
			tc.Result.Path = path
		}
		if tpl != nil {
			tpl.RawContents = nil
		}
		if diff := cmp.Diff(tpl, tc.Result); diff != "" {
			t.Fatalf("[%d]bad: %s\n%v", i, tc.File, diff)
		}

		// Only test template writing if the template is valid
		if tc.Err == false {
			// Get rawTemplate
			raw, err := tpl.Raw()
			if err != nil {
				t.Fatalf("Failed to convert back to raw template: %s\n\n%v\n\n%s", tc.File, tpl, err)
			}

			out, _ := json.MarshalIndent(raw, "", "  ")
			if err != nil {
				t.Fatalf("Failed to marshal raw template: %s\n\n%v\n\n%s", tc.File, raw, err)
			}

			// Write JSON to a buffer (emulates filesystem write without dirtying the workspace)
			fileBuf := bytes.NewBuffer(out)

			// Parse the JSON template we wrote to our buffer
			tplRewritten, err := Parse(fileBuf)
			if err != nil {
				t.Fatalf("Failed to re-read marshalled template: %s\n\n%v\n\n%s\n\n%s", tc.File, tpl, out, err)
			}

			// Override the metadata we don't care about (file path, raw file contents)
			tplRewritten.Path = path
			tplRewritten.RawContents = nil

			// Test that our output raw template is functionally equal
			if !reflect.DeepEqual(tpl, tplRewritten) {
				t.Fatalf("Data lost when writing template to file: %s\n\n%v\n\n%v\n\n%s", tc.File, tpl, tplRewritten, out)
			}
		}
	}
}

func TestParse_contents(t *testing.T) {
	tpl, err := ParseFile(fixtureDir("parse-contents.json"))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(string(tpl.RawContents))
	expected := `{"builders":[{"type":"test"}]}`
	if actual != expected {
		t.Fatalf("bad: %s\n\n%s", actual, expected)
	}
}

func TestParse_bad(t *testing.T) {
	cases := []struct {
		File     string
		Expected string
	}{
		{"error-beginning.json", "line 1, column 1 (offset 1)"},
		{"error-middle.json", "line 5, column 6 (offset 50)"},
		{"error-end.json", "line 1, column 30 (offset 30)"},
		{"malformed.json", "line 16, column 3 (offset 433)"},
	}
	for _, tc := range cases {
		_, err := ParseFile(fixtureDir(tc.File))
		if err == nil {
			t.Fatalf("expected error")
		}
		if !strings.Contains(err.Error(), tc.Expected) {
			t.Fatalf("file: %s\nExpected: %s\n%s\n", tc.File, tc.Expected, err.Error())
		}
	}
}

func TestParse_checkForDuplicateFields(t *testing.T) {
	cases := []struct {
		File     string
		Expected string
	}{
		{"error-duplicate-variables.json", "template has duplicate field: variables"},
		{"error-duplicate-config.json", "template has duplicate field: foo"},
	}
	for _, tc := range cases {
		_, err := ParseFile(fixtureDir(tc.File))
		if err == nil {
			t.Fatalf("expected error")
		}
		if !strings.Contains(err.Error(), tc.Expected) {
			t.Fatalf("file: %s\nExpected: %s\n%s\n", tc.File, tc.Expected, err.Error())
		}
	}
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"strings"
	"strconv"
	"time"
	"os"
)

func init() {
	cmdFuse.Run = runFuse // break init cycle
}

func runFuse(cmd *Command, args []string) bool {
	argsLen := len(args)
	options := []string{}

	// at least target mount path should be passed
	if argsLen < 1 {
		return false
	}

	// first option is always target mount path
	mountOptions.dir = &args[0]

	// scan parameters looking for one or more -o options
	// -o options receive parameters on format key=value[,key=value]...
	for i := 0; i < argsLen; i++ {
		if args[i] == "-o" && i+1 <= argsLen {
			options = strings.Split(args[i+1], ",")
			i++
		}
	}

	// for each option passed with -o
	for _, option := range options {
		// split just first = character
		parts := strings.SplitN(option, "=", 2)

		// if doesn't key and value skip
		if len(parts) != 2 {
			continue
		}

		key, value := parts[0], parts[1]

		// switch key keeping "weed mount" parameters
		switch key {
			case "filer":
				mountOptions.filer = &value
			case "filer.path":
				mountOptions.filerMountRootPath = &value
			case "dirAutoCreate":
				if parsed, err := strconv.ParseBool(value); err != nil {
					mountOptions.dirAutoCreate = &parsed
				} else {
					panic(fmt.Errorf("dirAutoCreate: %s", err))
				}
			case "collection":
				mountOptions.collection = &value
			case "replication":
				mountOptions.replication = &value
			case "disk":
				mountOptions.diskType = &value
			case "ttl":
				if parsed, err := strconv.ParseInt(value, 0, 32); err != nil {
					intValue := int(parsed)
					mountOptions.ttlSec = &intValue
				} else {
					panic(fmt.Errorf("ttl: %s", err))
				}
			case "chunkSizeLimitMB":
				if parsed, err := strconv.ParseInt(value, 0, 32); err != nil {
					intValue := int(parsed)
					mountOptions.chunkSizeLimitMB = &intValue
				} else {
					panic(fmt.Errorf("chunkSizeLimitMB: %s", err))
				}
			case "concurrentWriters":
				if parsed, err := strconv.ParseInt(value, 0, 32); err != nil {
					intValue := int(parsed)
					mountOptions.concurrentWriters = &intValue
				} else {
					panic(fmt.Errorf("concurrentWriters: %s", err))
				}
			case "cacheDir":
				mountOptions.cacheDir = &value
			case "cacheCapacityMB":
				if parsed, err := strconv.ParseInt(value, 0, 64); err != nil {
					mountOptions.cacheSizeMB = &parsed
				} else {
					panic(fmt.Errorf("cacheCapacityMB: %s", err))
				}
			case "dataCenter":
				mountOptions.dataCenter = &value
			case "allowOthers":
				if parsed, err := strconv.ParseBool(value); err != nil {
					mountOptions.allowOthers = &parsed
				} else {
					panic(fmt.Errorf("allowOthers: %s", err))
				}
			case "umask":
				mountOptions.umaskString = &value
			case "nonempty":
				if parsed, err := strconv.ParseBool(value); err != nil {
					mountOptions.nonempty = &parsed
				} else {
					panic(fmt.Errorf("nonempty: %s", err))
				}
			case "volumeServerAccess":
				mountOptions.volumeServerAccess = &value
			case "map.uid":
				mountOptions.uidMap = &value
			case "map.gid":
				mountOptions.gidMap = &value
			case "readOnly":
				if parsed, err := strconv.ParseBool(value); err != nil {
					mountOptions.readOnly = &parsed
				} else {
					panic(fmt.Errorf("readOnly: %s", err))
				}
			case "cpuprofile":
				mountCpuProfile = &value
			case "memprofile":
				mountMemProfile = &value
			case "readRetryTime":
				if parsed, err := time.ParseDuration(value); err != nil {
					mountReadRetryTime = &parsed
				} else {
					panic(fmt.Errorf("readRetryTime: %s", err))
				}
		}
	}

	// I don't know why PATH environment variable is lost
	if err := os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"); err != nil {
		panic(fmt.Errorf("setenv: %s", err))
	}

	// just call "weed mount" command
	return runMount(cmdMount, []string{})
}

var cmdFuse = &Command{
	UsageLine: "fuse /mnt/mount/point -o \"filer=localhost:8888,filer.path=/\"",
	Short: "Allow use weed with linux's mount command",
	Long: `Allow use weed with linux's mount command

  You can use -t weed on mount command:
  mv weed /sbin/mount.weed
  mount -t weed fuse /mnt -o "filer=localhost:8888,filer.path=/"

  Or you can use -t fuse on mount command:
  mv weed /sbin/weed
  mount -t fuse.weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  mount -t fuse "weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/"

  To use without mess with your /sbin:
  mount -t fuse./home/user/bin/weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  mount -t fuse "/home/user/bin/weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/"

  To check valid options look "weed mount --help"
  `,
}
<commit_msg>fix parameter multiple values<commit_after>package command

import (
	"fmt"
	"strings"
	"strconv"
	"time"
	"os"
)

func init() {
	cmdFuse.Run = runFuse // break init cycle
}

type parameter struct {
	name string
	value string
}

func runFuse(cmd *Command, args []string) bool {
	rawArgs := strings.Join(args, " ")
	rawArgsLen := len(rawArgs)
	option := strings.Builder{}
	options := []parameter{}

	// first parameter
	i := 0
	for i = 0; i < rawArgsLen && rawArgs[i] != ' '; i++ {
		option.WriteByte(rawArgs[i])
	}
	options = append(options, parameter{"arg0", option.String()})
	option.Reset()

	for i++; i < rawArgsLen; i++ {

		// space separator check for filled option
		if rawArgs[i] == ' ' {
			if option.Len() > 0 {
				options = append(options, parameter{option.String(), "true"})
				option.Reset()
			}

		// dash separator read option until next space
		} else if rawArgs[i] == '-' {
			for i++; i < rawArgsLen && rawArgs[i] != ' '; i++ {
				option.WriteByte(rawArgs[i])
			}
			options = append(options, parameter{option.String(), "true"})
			option.Reset()

		// equal separator start option with pending value
		} else if rawArgs[i] == '=' {
			name := option.String()
			option.Reset()

			for i++; i < rawArgsLen && rawArgs[i] != ','; i++ {
				// double quote separator read option until next double quote
				if rawArgs[i] == '"' {
					for i++; i < rawArgsLen && rawArgs[i] != '"'; i++ {
						option.WriteByte(rawArgs[i])
					}

				// single quote separator read option until next single quote
				} else if rawArgs[i] == '\'' {
					for i++; i < rawArgsLen && rawArgs[i] != '\''; i++ {
						option.WriteByte(rawArgs[i])
					}

				// add chars before comma
				} else if rawArgs[i] != ' ' {
					option.WriteByte(rawArgs[i])
				}
			}

			options = append(options, parameter{name, option.String()})
			option.Reset()

		// comma separator just read current option
		} else if rawArgs[i] == ',' {
			options = append(options, parameter{option.String(), "true"})
			option.Reset()

		// what is not a separator fill option buffer
		} else {
			option.WriteByte(rawArgs[i])
		}
	}

	// get residual option data
	if option.Len() > 0 {
		// add value to pending option
		options = append(options, parameter{option.String(), "true"})
		option.Reset()
	}

	// scan each parameter
	for i := 0; i < len(options); i++ {
		parameter := options[i]

		switch parameter.name  {
		case "arg0":
			mountOptions.dir = &parameter.value
		case "filer":
			mountOptions.filer = &parameter.value
		case "filer.path":
			mountOptions.filerMountRootPath = &parameter.value
		case "dirAutoCreate":
			if parsed, err := strconv.ParseBool(parameter.value); err != nil {
				mountOptions.dirAutoCreate = &parsed
			} else {
				panic(fmt.Errorf("dirAutoCreate: %s", err))
			}
		case "collection":
			mountOptions.collection = &parameter.value
		case "replication":
			mountOptions.replication = &parameter.value
		case "disk":
			mountOptions.diskType = &parameter.value
		case "ttl":
			if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil {
				intValue := int(parsed)
				mountOptions.ttlSec = &intValue
			} else {
				panic(fmt.Errorf("ttl: %s", err))
			}
		case "chunkSizeLimitMB":
			if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil {
				intValue := int(parsed)
				mountOptions.chunkSizeLimitMB = &intValue
			} else {
				panic(fmt.Errorf("chunkSizeLimitMB: %s", err))
			}
		case "concurrentWriters":
			i++
			if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err != nil {
				intValue := int(parsed)
				mountOptions.concurrentWriters = &intValue
			} else {
				panic(fmt.Errorf("concurrentWriters: %s", err))
			}
		case "cacheDir":
			mountOptions.cacheDir = &parameter.value
		case "cacheCapacityMB":
			if parsed, err := strconv.ParseInt(parameter.value, 0, 64); err != nil {
				mountOptions.cacheSizeMB = &parsed
			} else {
				panic(fmt.Errorf("cacheCapacityMB: %s", err))
			}
		case "dataCenter":
			mountOptions.dataCenter = &parameter.value
		case "allowOthers":
			if parsed, err := strconv.ParseBool(parameter.value); err != nil {
				mountOptions.allowOthers = &parsed
			} else {
				panic(fmt.Errorf("allowOthers: %s", err))
			}
		case "umask":
			mountOptions.umaskString = &parameter.value
		case "nonempty":
			if parsed, err := strconv.ParseBool(parameter.value); err != nil {
				mountOptions.nonempty = &parsed
			} else {
				panic(fmt.Errorf("nonempty: %s", err))
			}
		case "volumeServerAccess":
			mountOptions.volumeServerAccess = &parameter.value
		case "map.uid":
			mountOptions.uidMap = &parameter.value
		case "map.gid":
			mountOptions.gidMap = &parameter.value
		case "readOnly":
			if parsed, err := strconv.ParseBool(parameter.value); err != nil {
				mountOptions.readOnly = &parsed
			} else {
				panic(fmt.Errorf("readOnly: %s", err))
			}
		case "cpuprofile":
			mountCpuProfile = &parameter.value
		case "memprofile":
			mountMemProfile = &parameter.value
		case "readRetryTime":
			if parsed, err := time.ParseDuration(parameter.value); err != nil {
				mountReadRetryTime = &parsed
			} else {
				panic(fmt.Errorf("readRetryTime: %s", err))
			}
		}
	}

	// I don't know why PATH environment variable is lost
	if err := os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"); err != nil {
		panic(fmt.Errorf("setenv: %s", err))
	}

	// just call "weed mount" command
	return runMount(cmdMount, []string{})
}

var cmdFuse = &Command{
	UsageLine: "fuse /mnt/mount/point -o \"filer=localhost:8888,filer.path=/\"",
	Short: "Allow use weed with linux's mount command",
	Long: `Allow use weed with linux's mount command

  You can use -t weed on mount command:
  mv weed /sbin/mount.weed
  mount -t weed fuse /mnt -o "filer=localhost:8888,filer.path=/"

  Or you can use -t fuse on mount command:
  mv weed /sbin/weed
  mount -t fuse.weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  mount -t fuse "weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/"

  To use without mess with your /sbin:
  mount -t fuse./home/user/bin/weed fuse /mnt -o "filer=localhost:8888,filer.path=/"
  mount -t fuse "/home/user/bin/weed#fuse" /mnt -o "filer=localhost:8888,filer.path=/"

  To pass more than one parameter use quotes, example:
  mount -t weed fuse /mnt -o "filer='192.168.0.1:8888,192.168.0.2:8888',filer.path=/"

  To check valid options look "weed mount --help"
  `,
}
<|endoftext|>


<commit_before>package testing

import (
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

const ChanSize = 64

type TestHTTPServer struct {
	URL      string
	started  bool
	request  chan *http.Request
	response chan *testResponse
	body     chan []byte
	timeout  time.Duration
}

type testResponse struct {
	Status  int
	Headers map[string]string
	Body    string
}

func NewTestHTTPServer(url string, responseTimout time.Duration) *TestHTTPServer {
	return &TestHTTPServer{URL: url, timeout: responseTimout}
}

func (s *TestHTTPServer) Start() {
	if s.started {
		return
	}
	s.started = true
	s.request = make(chan *http.Request, ChanSize)
	s.response = make(chan *testResponse, ChanSize)
	s.body = make(chan []byte, ChanSize)
	url, _ := url.Parse(s.URL)
	go http.ListenAndServe(url.Host, s)
	s.PrepareResponse(202, nil, "Nothing.")
	for {
		resp, err := http.Get(s.URL)
		if err == nil && resp.StatusCode == 202 {
			break
		}
		time.Sleep(1e8)
	}
	s.WaitRequest(1e18)
}

func (s *TestHTTPServer) FlushRequests() {
	for {
		select {
		case <-s.request:
		default:
			return
		}
	}
}

func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	b, _ := ioutil.ReadAll(req.Body)
	s.body <- b
	s.request <- req
	var resp *testResponse
	select {
	case resp = <-s.response:
	case <-time.After(s.timeout):
		log.Panicf("No response from server after %s.", s.timeout)
	}
	if resp.Status != 0 {
		w.WriteHeader(resp.Status)
	}
	w.Write([]byte(resp.Body))
}

func (s *TestHTTPServer) WaitRequest(timeout time.Duration) (*http.Request, []byte, error) {
	select {
	case req := <-s.request:
		b := <-s.body
		return req, b, nil
	case <-time.After(timeout):
	}
	return nil, nil, errors.New("timed out")
}

func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) {
	s.response <- &testResponse{status, headers, body}
}
<commit_msg>testing: added docs for the package<commit_after>// Package testing provides a fake HTTP server, that can be user for tests.
package testing

import (
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

const ChanSize = 64

// TestHTTPServer holds information about the server.
type TestHTTPServer struct {
	// The URL of the server.
	URL string

	started  bool
	request  chan *http.Request
	response chan *testResponse
	body     chan []byte
	timeout  time.Duration
}

type testResponse struct {
	Status  int
	Headers map[string]string
	Body    string
}

// NewTestHTTPServer returns an instance of TestHTTPServer.
//
// The given url is where the server will listen, and the responseTimeout
// parameter is how much time the server will wait before abort a request.
//
// Example of call:
//
//     NewTestHTTPServer("http://localhost:9898", 10e9)
//
// The call above will prepare a server that when started will listen for
// requests in the port 9898 (you should start it using Start method).
func NewTestHTTPServer(url string, responseTimeout time.Duration) *TestHTTPServer {
	return &TestHTTPServer{URL: url, timeout: responseTimeout}
}

// Start starts the server in the url provided to the NewTestHTTPServer call.
//
// It waits until the server is up.
func (s *TestHTTPServer) Start() {
	if s.started {
		return
	}
	s.started = true
	s.request = make(chan *http.Request, ChanSize)
	s.response = make(chan *testResponse, ChanSize)
	s.body = make(chan []byte, ChanSize)
	url, _ := url.Parse(s.URL)
	go http.ListenAndServe(url.Host, s)
	s.PrepareResponse(202, nil, "Nothing.")
	for {
		resp, err := http.Get(s.URL)
		if err == nil && resp.StatusCode == 202 {
			break
		}
		time.Sleep(1e8)
	}
	s.WaitRequest(1e18)
}

// FlushRequests discards all requests. It is useful to call this method in the
// end of the test (teardown), so requests from a test does not affect the
// result of other test.
func (s *TestHTTPServer) FlushRequests() {
	for {
		select {
		case <-s.request:
		default:
			return
		}
	}
}

// ServeHTTP is declared to make TestHTTPServer implement the http.Handler
// interface. It will handle all requests, storing information about them. You
// can use PrepareResponse method to prepare the next response, and WaitRequest
// to wait for a request (so you can test attributes of the request).
func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	b, _ := ioutil.ReadAll(req.Body)
	s.body <- b
	s.request <- req
	var resp *testResponse
	select {
	case resp = <-s.response:
	case <-time.After(s.timeout):
		log.Panicf("No response from server after %s.", s.timeout)
	}
	if resp.Status != 0 {
		w.WriteHeader(resp.Status)
	}
	w.Write([]byte(resp.Body))
}

// WaitRequest waits at most `timeout` for a request to be sent to the server.
//
// It returns the request pointer, the body of the request and a nil error in
// case of success, or an error if the operation times out.
//
// Example of call:
//
//     req, body, err := server.WaitRequest(10e9)
//
// The call above will wait at most 10 seconds for a request to come to the
// server.
func (s *TestHTTPServer) WaitRequest(timeout time.Duration) (*http.Request, []byte, error) {
	select {
	case req := <-s.request:
		b := <-s.body
		return req, b, nil
	case <-time.After(timeout):
	}
	return nil, nil, errors.New("timed out")
}

// PrepareResponse should be called to prepare a response for a request.
// TestHTTPServer keeps an internal queue of responses, so sequent calls of
// PrepareResponse just add response to this queue.
//
// For example, the following calls will prepare three responses and consume
// them in the same sequence that they were prepared:
//
//     server.PrepareResponse(200, nil, "success")
//     server.PrepareResponse(200, map[string]string{"Content-Type": "application/json"}, `{"message":"success"}`)
//     server.PrepareResponse(204, nil, "")
//     http.Get(server.URL) // Gets the first response (200 - "succes").
//     http.Get(server.URL) // Gets the second response (200 - json {"message":"success"}).
//     http.Get(server.URL) // Gets the third response (204 - no content).
//     http.Get(server.URL) // Times out.
//
// Notice that a request in the server without a prepared response will timeout
// after a specific time (defined in the NewTestHTTPServer function).
func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) {
	s.response <- &testResponse{status, headers, body}
}
<|endoftext|>


<commit_before>// Package testing provides a fake HTTP server, that can be user for tests.
package testing

import (
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

const ChanSize = 64

// TestHTTPServer holds information about the server.
type TestHTTPServer struct {
	// The URL of the server.
	URL string

	started  bool
	request  chan *http.Request
	response chan *testResponse
	body     chan []byte
	timeout  time.Duration
}

type testResponse struct {
	Status  int
	Headers map[string]string
	Body    string
}

// NewTestHTTPServer returns an instance of TestHTTPServer.
//
// The given url is where the server will listen, and the responseTimeout
// parameter is how much time the server will wait before abort a request.
//
// Example of call:
//
//     NewTestHTTPServer("http://localhost:9898", 10e9)
//
// The call above will prepare a server that when started will listen for
// requests in the port 9898 (you should start it using Start method).
func NewTestHTTPServer(url string, responseTimeout time.Duration) *TestHTTPServer {
	return &TestHTTPServer{URL: url, timeout: responseTimeout}
}

// Start starts the server in the url provided to the NewTestHTTPServer call.
//
// It waits until the server is up.
func (s *TestHTTPServer) Start() {
	if s.started {
		return
	}
	s.started = true
	s.request = make(chan *http.Request, ChanSize)
	s.response = make(chan *testResponse, ChanSize)
	s.body = make(chan []byte, ChanSize)
	url, _ := url.Parse(s.URL)
	go http.ListenAndServe(url.Host, s)
	s.PrepareResponse(202, nil, "Nothing.")
	for {
		resp, err := http.Get(s.URL)
		if err == nil && resp.StatusCode == 202 {
			break
		}
		time.Sleep(1e8)
	}
	s.WaitRequest(1e18)
}

// FlushRequests discards all requests. It is useful to call this method in the
// end of the test (teardown), so requests from a test does not affect the
// result of other test.
func (s *TestHTTPServer) FlushRequests() {
	for {
		select {
		case <-s.request:
		default:
			return
		}
	}
}

// ServeHTTP is declared to make TestHTTPServer implement the http.Handler
// interface. It will handle all requests, storing information about them. You
// can use PrepareResponse method to prepare the next response, and WaitRequest
// to wait for a request (so you can test attributes of the request).
func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	b, _ := ioutil.ReadAll(req.Body)
	s.body <- b
	s.request <- req
	var resp *testResponse
	select {
	case resp = <-s.response:
	case <-time.After(s.timeout):
		log.Panicf("No response from server after %s.", s.timeout)
	}
	if resp.Status != 0 {
		w.WriteHeader(resp.Status)
	}
	w.Write([]byte(resp.Body))
}

// WaitRequest waits at most `timeout` for a request to be sent to the server.
//
// It returns the request pointer, the body of the request and a nil error in
// case of success, or an error if the operation times out.
//
// Example of call:
//
//     req, body, err := server.WaitRequest(10e9)
//
// The call above will wait at most 10 seconds for a request to come to the
// server.
func (s *TestHTTPServer) WaitRequest(timeout time.Duration) (*http.Request, []byte, error) {
	select {
	case req := <-s.request:
		b := <-s.body
		return req, b, nil
	case <-time.After(timeout):
	}
	return nil, nil, errors.New("timed out")
}

// PrepareResponse should be called to prepare a response for a request.
// TestHTTPServer keeps an internal queue of responses, so sequent calls of
// PrepareResponse just add response to this queue.
//
// For example, the following calls will prepare three responses and consume
// them in the same sequence that they were prepared:
//
//     server.PrepareResponse(200, nil, "success")
//     server.PrepareResponse(200, map[string]string{"Content-Type": "application/json"}, `{"message":"success"}`)
//     server.PrepareResponse(204, nil, "")
//     http.Get(server.URL) // Gets the first response (200 - "succes").
//     http.Get(server.URL) // Gets the second response (200 - json {"message":"success"}).
//     http.Get(server.URL) // Gets the third response (204 - no content).
//     http.Get(server.URL) // Times out.
//
// Notice that a request in the server without a prepared response will timeout
// after a specific time (defined in the NewTestHTTPServer function).
func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) {
	s.response <- &testResponse{status, headers, body}
}
<commit_msg>testing: exporting request member of TestHTTPServer<commit_after>// Package testing provides a fake HTTP server, that can be user for tests.
package testing

import (
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

const ChanSize = 64

// TestHTTPServer holds information about the server.
type TestHTTPServer struct {
	// The URL of the server.
	URL string

	started  bool
	Request  chan *http.Request
	response chan *testResponse
	body     chan []byte
	timeout  time.Duration
}

type testResponse struct {
	Status  int
	Headers map[string]string
	Body    string
}

// NewTestHTTPServer returns an instance of TestHTTPServer.
//
// The given url is where the server will listen, and the responseTimeout
// parameter is how much time the server will wait before abort a request.
//
// Example of call:
//
//     NewTestHTTPServer("http://localhost:9898", 10e9)
//
// The call above will prepare a server that when started will listen for
// requests in the port 9898 (you should start it using Start method).
func NewTestHTTPServer(url string, responseTimeout time.Duration) *TestHTTPServer {
	return &TestHTTPServer{URL: url, timeout: responseTimeout}
}

// Start starts the server in the url provided to the NewTestHTTPServer call.
//
// It waits until the server is up.
func (s *TestHTTPServer) Start() {
	if s.started {
		return
	}
	s.started = true
	s.Request = make(chan *http.Request, ChanSize)
	s.response = make(chan *testResponse, ChanSize)
	s.body = make(chan []byte, ChanSize)
	url, _ := url.Parse(s.URL)
	go http.ListenAndServe(url.Host, s)
	s.PrepareResponse(202, nil, "Nothing.")
	for {
		resp, err := http.Get(s.URL)
		if err == nil && resp.StatusCode == 202 {
			break
		}
		time.Sleep(1e8)
	}
	s.WaitRequest(1e18)
}

// FlushRequests discards all requests. It is useful to call this method in the
// end of the test (teardown), so requests from a test does not affect the
// result of other test.
func (s *TestHTTPServer) FlushRequests() {
	for {
		select {
		case <-s.Request:
		default:
			return
		}
	}
}

// ServeHTTP is declared to make TestHTTPServer implement the http.Handler
// interface. It will handle all requests, storing information about them. You
// can use PrepareResponse method to prepare the next response, and WaitRequest
// to wait for a request (so you can test attributes of the request).
func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	b, _ := ioutil.ReadAll(req.Body)
	s.body <- b
	s.Request <- req
	var resp *testResponse
	select {
	case resp = <-s.response:
	case <-time.After(s.timeout):
		log.Panicf("No response from server after %s.", s.timeout)
	}
	if resp.Status != 0 {
		w.WriteHeader(resp.Status)
	}
	w.Write([]byte(resp.Body))
}

// WaitRequest waits at most `timeout` for a request to be sent to the server.
//
// It returns the request pointer, the body of the request and a nil error in
// case of success, or an error if the operation times out.
//
// Example of call:
//
//     req, body, err := server.WaitRequest(10e9)
//
// The call above will wait at most 10 seconds for a request to come to the
// server.
func (s *TestHTTPServer) WaitRequest(timeout time.Duration) (*http.Request, []byte, error) {
	select {
	case req := <-s.Request:
		b := <-s.body
		return req, b, nil
	case <-time.After(timeout):
	}
	return nil, nil, errors.New("timed out")
}

// PrepareResponse should be called to prepare a response for a request.
// TestHTTPServer keeps an internal queue of responses, so sequent calls of
// PrepareResponse just add response to this queue.
//
// For example, the following calls will prepare three responses and consume
// them in the same sequence that they were prepared:
//
//     server.PrepareResponse(200, nil, "success")
//     server.PrepareResponse(200, map[string]string{"Content-Type": "application/json"}, `{"message":"success"}`)
//     server.PrepareResponse(204, nil, "")
//     http.Get(server.URL) // Gets the first response (200 - "succes").
//     http.Get(server.URL) // Gets the second response (200 - json {"message":"success"}).
//     http.Get(server.URL) // Gets the third response (204 - no content).
//     http.Get(server.URL) // Times out.
//
// Notice that a request in the server without a prepared response will timeout
// after a specific time (defined in the NewTestHTTPServer function).
func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) {
	s.response <- &testResponse{status, headers, body}
}
<|endoftext|>


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

package memfs

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// Common attributes for files and directories.
type inode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	mu syncutil.InvariantMutex

	// The current attributes of this inode.
	//
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	// INVARIANT: !(isDir() && isSymlink())
	// INVARIANT: attrs.Size == len(contents)
	attrs fuseops.InodeAttributes // GUARDED_BY(mu)

	// For directories, entries describing the children of the directory. Unused
	// entries are of type DT_Unknown.
	//
	// This array can never be shortened, nor can its elements be moved, because
	// we use its indices for Dirent.Offset, which is exposed to the user who
	// might be calling readdir in a loop while concurrently modifying the
	// directory. Unused entries can, however, be reused.
	//
	// INVARIANT: If !isDir(), len(entries) == 0
	// INVARIANT: For each i, entries[i].Offset == i+1
	// INVARIANT: Contains no duplicate names in used entries.
	entries []fuseutil.Dirent // GUARDED_BY(mu)

	// For files, the current contents of the file.
	//
	// INVARIANT: If !isFile(), len(contents) == 0
	contents []byte // GUARDED_BY(mu)

	// For symlinks, the target of the symlink.
	//
	// INVARIANT: If !isSymlink(), len(target) == 0
	//
	// GUARDED_BY(mu)
	target string
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Create a new inode with the supplied attributes, which need not contain
// time-related information (the inode object will take care of that).
func newInode(
	clock timeutil.Clock,
	attrs fuseops.InodeAttributes) (in *inode) {
	// Update time info.
	now := clock.Now()
	attrs.Mtime = now
	attrs.Crtime = now

	// Create the object.
	in = &inode{
		clock: clock,
		attrs: attrs,
	}

	in.mu = syncutil.NewInvariantMutex(in.checkInvariants)
	return
}

func (in *inode) checkInvariants() {
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: !(isDir() && isSymlink())
	if in.isDir() && in.isSymlink() {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: attrs.Size == len(contents)
	if in.attrs.Size != uint64(len(in.contents)) {
		panic(fmt.Sprintf(
			"Size mismatch: %d vs. %d",
			in.attrs.Size,
			len(in.contents)))
	}

	// INVARIANT: If !isDir(), len(entries) == 0
	if !in.isDir() && len(in.entries) != 0 {
		panic(fmt.Sprintf("Unexpected entries length: %d", len(in.entries)))
	}

	// INVARIANT: For each i, entries[i].Offset == i+1
	for i, e := range in.entries {
		if !(e.Offset == fuseops.DirOffset(i+1)) {
			panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
		}
	}

	// INVARIANT: Contains no duplicate names in used entries.
	childNames := make(map[string]struct{})
	for i, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			if _, ok := childNames[e.Name]; ok {
				panic(fmt.Sprintf("Duplicate name: %s", e.Name))
			}

			childNames[e.Name] = struct{}{}
		}
	}

	// INVARIANT: If !isFile(), len(contents) == 0
	if !in.isFile() && len(in.contents) != 0 {
		panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
	}

	// INVARIANT: If !isSymlink(), len(target) == 0
	if !in.isSymlink() && len(in.target) != 0 {
		panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
	}

	return
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isDir() bool {
	return in.attrs.Mode&os.ModeDir != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isSymlink() bool {
	return in.attrs.Mode&os.ModeSymlink != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isFile() bool {
	return !(in.isDir() || in.isSymlink())
}

////////////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////////////

// Return the number of children of the directory.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) Len() (n int) {
	for _, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			n++
		}
	}

	return
}

// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
	index, ok := in.findChild(name)
	if ok {
		id = in.entries[index].Inode
	}

	return
}

// Add an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: dt != fuseutil.DT_Unknown
// LOCKS_REQUIRED(in.mu)
func (in *inode) AddChild(
	id fuseops.InodeID,
	name string,
	dt fuseutil.DirentType) {
	var index int

	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// No matter where we place the entry, make sure it has the correct Offset
	// field.
	defer func() {
		in.entries[index].Offset = fuseops.DirOffset(index + 1)
	}()

	// Set up the entry.
	e := fuseutil.Dirent{
		Inode: id,
		Name:  name,
		Type:  dt,
	}

	// Look for a gap in which we can insert it.
	for index = range in.entries {
		if in.entries[index].Type == fuseutil.DT_Unknown {
			in.entries[index] = e
			return
		}
	}

	// Append it to the end.
	index = len(in.entries)
	in.entries = append(in.entries, e)
}

// Remove an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: An entry for the given name exists.
// LOCKS_REQUIRED(in.mu)
func (in *inode) RemoveChild(name string) {
	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Find the entry.
	i, ok := in.findChild(name)
	if !ok {
		panic(fmt.Sprintf("Unknown child: %s", name))
	}

	// Mark it as unused.
	in.entries[i] = fuseutil.Dirent{
		Type:   fuseutil.DT_Unknown,
		Offset: fuseops.DirOffset(i + 1),
	}
}

// Serve a ReadDir request.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
	if !in.dir {
		panic("ReadDir called on non-directory.")
	}

	for i := offset; i < len(in.entries); i++ {
		e := in.entries[i]

		// Skip unused entries.
		if e.Type == fuseutil.DT_Unknown {
			continue
		}

		data = fuseutil.AppendDirent(data, in.entries[i])

		// Trim and stop early if we've exceeded the requested size.
		if len(data) > size {
			data = data[:size]
			break
		}
	}

	return
}

// Read from the file's contents. See documentation for ioutil.ReaderAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("ReadAt called on directory.")
	}

	// Ensure the offset is in range.
	if off > int64(len(in.contents)) {
		err = io.EOF
		return
	}

	// Read what we can.
	n = copy(p, in.contents[off:])
	if n < len(p) {
		err = io.EOF
	}

	return
}

// Write to the file's contents. See documentation for ioutil.WriterAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("WriteAt called on directory.")
	}

	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Ensure that the contents slice is long enough.
	newLen := int(off) + len(p)
	if len(in.contents) < newLen {
		padding := make([]byte, newLen-len(in.contents))
		in.contents = append(in.contents, padding...)
		in.attrs.Size = uint64(newLen)
	}

	// Copy in the data.
	n = copy(in.contents[off:], p)

	// Sanity check.
	if n != len(p) {
		panic(fmt.Sprintf("Unexpected short copy: %v", n))
	}

	return
}

// Update attributes from non-nil parameters.
//
// LOCKS_REQUIRED(in.mu)
func (in *inode) SetAttributes(
	size *uint64,
	mode *os.FileMode,
	mtime *time.Time) {
	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Truncate?
	if size != nil {
		intSize := int(*size)

		// Update contents.
		if intSize <= len(in.contents) {
			in.contents = in.contents[:intSize]
		} else {
			padding := make([]byte, intSize-len(in.contents))
			in.contents = append(in.contents, padding...)
		}

		// Update attributes.
		in.attrs.Size = *size
	}

	// Change mode?
	if mode != nil {
		in.attrs.Mode = *mode
	}

	// Change mtime?
	if mtime != nil {
		in.attrs.Mtime = *mtime
	}
}
<commit_msg>Restored lost code.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package memfs

import (
	"fmt"
	"io"
	"os"
	"time"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/syncutil"
)

// Common attributes for files and directories.
type inode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	clock timeutil.Clock

	/////////////////////////
	// Mutable state
	/////////////////////////

	mu syncutil.InvariantMutex

	// The current attributes of this inode.
	//
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	// INVARIANT: !(isDir() && isSymlink())
	// INVARIANT: attrs.Size == len(contents)
	attrs fuseops.InodeAttributes // GUARDED_BY(mu)

	// For directories, entries describing the children of the directory. Unused
	// entries are of type DT_Unknown.
	//
	// This array can never be shortened, nor can its elements be moved, because
	// we use its indices for Dirent.Offset, which is exposed to the user who
	// might be calling readdir in a loop while concurrently modifying the
	// directory. Unused entries can, however, be reused.
	//
	// INVARIANT: If !isDir(), len(entries) == 0
	// INVARIANT: For each i, entries[i].Offset == i+1
	// INVARIANT: Contains no duplicate names in used entries.
	entries []fuseutil.Dirent // GUARDED_BY(mu)

	// For files, the current contents of the file.
	//
	// INVARIANT: If !isFile(), len(contents) == 0
	contents []byte // GUARDED_BY(mu)

	// For symlinks, the target of the symlink.
	//
	// INVARIANT: If !isSymlink(), len(target) == 0
	//
	// GUARDED_BY(mu)
	target string
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// Create a new inode with the supplied attributes, which need not contain
// time-related information (the inode object will take care of that).
func newInode(
	clock timeutil.Clock,
	attrs fuseops.InodeAttributes) (in *inode) {
	// Update time info.
	now := clock.Now()
	attrs.Mtime = now
	attrs.Crtime = now

	// Create the object.
	in = &inode{
		clock: clock,
		attrs: attrs,
	}

	in.mu = syncutil.NewInvariantMutex(in.checkInvariants)
	return
}

func (in *inode) checkInvariants() {
	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
	if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: !(isDir() && isSymlink())
	if in.isDir() && in.isSymlink() {
		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
	}

	// INVARIANT: attrs.Size == len(contents)
	if in.attrs.Size != uint64(len(in.contents)) {
		panic(fmt.Sprintf(
			"Size mismatch: %d vs. %d",
			in.attrs.Size,
			len(in.contents)))
	}

	// INVARIANT: If !isDir(), len(entries) == 0
	if !in.isDir() && len(in.entries) != 0 {
		panic(fmt.Sprintf("Unexpected entries length: %d", len(in.entries)))
	}

	// INVARIANT: For each i, entries[i].Offset == i+1
	for i, e := range in.entries {
		if !(e.Offset == fuseops.DirOffset(i+1)) {
			panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
		}
	}

	// INVARIANT: Contains no duplicate names in used entries.
	childNames := make(map[string]struct{})
	for i, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			if _, ok := childNames[e.Name]; ok {
				panic(fmt.Sprintf("Duplicate name: %s", e.Name))
			}

			childNames[e.Name] = struct{}{}
		}
	}

	// INVARIANT: If !isFile(), len(contents) == 0
	if !in.isFile() && len(in.contents) != 0 {
		panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
	}

	// INVARIANT: If !isSymlink(), len(target) == 0
	if !in.isSymlink() && len(in.target) != 0 {
		panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
	}

	return
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isDir() bool {
	return in.attrs.Mode&os.ModeDir != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isSymlink() bool {
	return in.attrs.Mode&os.ModeSymlink != 0
}

// LOCKS_REQUIRED(in.mu)
func (in *inode) isFile() bool {
	return !(in.isDir() || in.isSymlink())
}

// Return the index of the child within in.entries, if it exists.
//
// REQUIRES: in.dir
// LOCKS_REQUIRED(in.mu)
func (in *inode) findChild(name string) (i int, ok bool) {
	if !in.isDir() {
		panic("findChild called on non-directory.")
	}

	var e fuseutil.Dirent
	for i, e = range in.entries {
		if e.Name == name {
			ok = true
			return
		}
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Public methods
////////////////////////////////////////////////////////////////////////

// Return the number of children of the directory.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) Len() (n int) {
	for _, e := range in.entries {
		if e.Type != fuseutil.DT_Unknown {
			n++
		}
	}

	return
}

// Find an entry for the given child name and return its inode ID.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) LookUpChild(name string) (id fuseops.InodeID, ok bool) {
	index, ok := in.findChild(name)
	if ok {
		id = in.entries[index].Inode
	}

	return
}

// Add an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: dt != fuseutil.DT_Unknown
// LOCKS_REQUIRED(in.mu)
func (in *inode) AddChild(
	id fuseops.InodeID,
	name string,
	dt fuseutil.DirentType) {
	var index int

	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// No matter where we place the entry, make sure it has the correct Offset
	// field.
	defer func() {
		in.entries[index].Offset = fuseops.DirOffset(index + 1)
	}()

	// Set up the entry.
	e := fuseutil.Dirent{
		Inode: id,
		Name:  name,
		Type:  dt,
	}

	// Look for a gap in which we can insert it.
	for index = range in.entries {
		if in.entries[index].Type == fuseutil.DT_Unknown {
			in.entries[index] = e
			return
		}
	}

	// Append it to the end.
	index = len(in.entries)
	in.entries = append(in.entries, e)
}

// Remove an entry for a child.
//
// REQUIRES: in.isDir()
// REQUIRES: An entry for the given name exists.
// LOCKS_REQUIRED(in.mu)
func (in *inode) RemoveChild(name string) {
	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Find the entry.
	i, ok := in.findChild(name)
	if !ok {
		panic(fmt.Sprintf("Unknown child: %s", name))
	}

	// Mark it as unused.
	in.entries[i] = fuseutil.Dirent{
		Type:   fuseutil.DT_Unknown,
		Offset: fuseops.DirOffset(i + 1),
	}
}

// Serve a ReadDir request.
//
// REQUIRES: in.isDir()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadDir(offset int, size int) (data []byte, err error) {
	if !in.dir {
		panic("ReadDir called on non-directory.")
	}

	for i := offset; i < len(in.entries); i++ {
		e := in.entries[i]

		// Skip unused entries.
		if e.Type == fuseutil.DT_Unknown {
			continue
		}

		data = fuseutil.AppendDirent(data, in.entries[i])

		// Trim and stop early if we've exceeded the requested size.
		if len(data) > size {
			data = data[:size]
			break
		}
	}

	return
}

// Read from the file's contents. See documentation for ioutil.ReaderAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("ReadAt called on directory.")
	}

	// Ensure the offset is in range.
	if off > int64(len(in.contents)) {
		err = io.EOF
		return
	}

	// Read what we can.
	n = copy(p, in.contents[off:])
	if n < len(p) {
		err = io.EOF
	}

	return
}

// Write to the file's contents. See documentation for ioutil.WriterAt.
//
// REQUIRES: in.isFile()
// LOCKS_REQUIRED(in.mu)
func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
	if in.dir {
		panic("WriteAt called on directory.")
	}

	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Ensure that the contents slice is long enough.
	newLen := int(off) + len(p)
	if len(in.contents) < newLen {
		padding := make([]byte, newLen-len(in.contents))
		in.contents = append(in.contents, padding...)
		in.attrs.Size = uint64(newLen)
	}

	// Copy in the data.
	n = copy(in.contents[off:], p)

	// Sanity check.
	if n != len(p) {
		panic(fmt.Sprintf("Unexpected short copy: %v", n))
	}

	return
}

// Update attributes from non-nil parameters.
//
// LOCKS_REQUIRED(in.mu)
func (in *inode) SetAttributes(
	size *uint64,
	mode *os.FileMode,
	mtime *time.Time) {
	// Update the modification time.
	in.attrs.Mtime = in.clock.Now()

	// Truncate?
	if size != nil {
		intSize := int(*size)

		// Update contents.
		if intSize <= len(in.contents) {
			in.contents = in.contents[:intSize]
		} else {
			padding := make([]byte, intSize-len(in.contents))
			in.contents = append(in.contents, padding...)
		}

		// Update attributes.
		in.attrs.Size = *size
	}

	// Change mode?
	if mode != nil {
		in.attrs.Mode = *mode
	}

	// Change mtime?
	if mtime != nil {
		in.attrs.Mtime = *mtime
	}
}
<|endoftext|>


<commit_before>package workflow

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	aw "github.com/deanishe/awgo"
	"github.com/rkoval/alfred-aws-console-services-workflow/aliases"
	"github.com/rkoval/alfred-aws-console-services-workflow/awsconfig"
	"github.com/rkoval/alfred-aws-console-services-workflow/awsworkflow"
	"github.com/rkoval/alfred-aws-console-services-workflow/parsers"
	"github.com/rkoval/alfred-aws-console-services-workflow/searchers"
	"github.com/rkoval/alfred-aws-console-services-workflow/searchers/searchutil"
	"github.com/rkoval/alfred-aws-console-services-workflow/util"
)

func Run(wf *aw.Workflow, rawQuery string, transport http.RoundTripper, forceFetch, openAll bool, ymlPath string) {
	log.Println("using workflow cacheDir: " + wf.CacheDir())
	log.Println("using workflow dataDir: " + wf.DataDir())

	parser := parsers.NewParser(rawQuery)
	query, awsServices := parser.Parse(ymlPath)
	defer finalize(wf, query)

	log.Printf("using query: %#v", query)

	if query.RegionQuery != nil {
		for _, region := range awsconfig.AllAWSRegions {
			autocomplete := strings.Replace(rawQuery, aliases.OverrideAwsRegion+*query.RegionQuery, aliases.OverrideAwsRegion+region.Name+" ", 1)
			wf.NewItem(region.Name).
				Subtitle(region.Description).
				Icon(aw.IconWeb).
				Autocomplete(autocomplete).
				UID(region.Name)
		}
		log.Printf("filtering with region override %q", *query.RegionQuery)
		res := wf.Filter(*query.RegionQuery)
		log.Printf("%d results match %q", len(res), *query.RegionQuery)
		return
	}

	if query.ProfileQuery != nil {
		for _, profile := range awsconfig.GetAwsProfiles() {
			autocomplete := strings.Replace(rawQuery, aliases.OverrideAwsProfile+*query.ProfileQuery, aliases.OverrideAwsProfile+profile.Name+" ", 1)
			item := wf.NewItem(profile.Name).
				Icon(aw.IconAccount).
				Autocomplete(autocomplete).
				UID(profile.Name)

			if profile.Region != "" {
				item.Subtitle(fmt.Sprintf("🌎 %s", profile.Region))
			} else {
				item.Subtitle("⚠ This profile does not specify a region. Functionality will be limited")
			}
		}
		log.Printf("filtering with profile override %q", *query.ProfileQuery)
		res := wf.Filter(*query.ProfileQuery)
		log.Printf("%d results match %q", len(res), *query.ProfileQuery)
		return
	}

	cfg := awsworkflow.InitAWS(transport, query.ProfileOverride, query.GetRegionOverride())
	searchArgs := searchutil.SearchArgs{
		Cfg:        cfg,
		ForceFetch: forceFetch,
		FullQuery:  rawQuery,
		Profile:    util.GetProfile(cfg),
	}

	if query.IsEmpty() {
		handleEmptyQuery(wf, searchArgs)
		return
	}

	if query.HasOpenAll {
		handleOpenAll(wf, query.Service, awsServices, openAll, rawQuery, cfg)
		return
	}

	if query.Service == nil || (!query.HasTrailingWhitespace && query.SubService == nil && !query.HasDefaultSearchAlias && query.RemainingQuery == "") {
		if query.Service == nil {
			searchArgs.Query = query.RemainingQuery
		} else if query.Service.ShortName != "" {
			searchArgs.Query = strings.ToLower(query.Service.ShortName)
		} else {
			searchArgs.Query = query.Service.Id
		}
		log.Printf("using searcher associated with services with query %q", searchArgs.Query)
		SearchServices(wf, awsServices, searchArgs)
	} else {
		if !query.HasDefaultSearchAlias && (query.Service.SubServices == nil || len(query.Service.SubServices) <= 0) {
			handleUnimplemented(wf, query.Service, nil, fmt.Sprintf("%s doesn't have sub-services configured (yet)", query.Service.Id), searchArgs)
			return
		}

		searchArgs.GetRegionFunc = query.Service.GetRegion

		if query.HasDefaultSearchAlias || query.SubService != nil && (query.HasTrailingWhitespace || query.RemainingQuery != "") {
			serviceId := query.Service.Id
			if query.SubService != nil {
				serviceId += "_" + query.SubService.Id
			}
			log.Println("using searcher associated with " + serviceId)
			searcher := searchers.SearchersByServiceId[serviceId]
			if searcher != nil {
				searchArgs.Query = query.RemainingQuery
				err := searcher.Search(wf, searchArgs)
				if err != nil {
					wf.FatalError(err)
				}
			} else {
				handleUnimplemented(wf, query.Service, query.SubService, fmt.Sprintf("No searcher for `%s %s` (yet)", query.Service.Id, query.SubService.Id), searchArgs)
				return
			}
		} else {
			log.Println("using searcher associated with sub-services")
			if query.SubService != nil {
				searchArgs.Query = query.SubService.Id
			} else {
				searchArgs.Query = query.RemainingQuery
			}
			SearchSubServices(wf, *query.Service, searchArgs)
		}
	}

	if searchArgs.Query != "" {
		log.Printf("filtering with query %q", searchArgs.Query)
		res := wf.Filter(searchArgs.Query)
		log.Printf("%d results match %q", len(res), searchArgs.Query)
	}
}

func finalize(wf *aw.Workflow, query *parsers.Query) {
	if wf.IsEmpty() {
		title := ""
		subtitle := ""
		if query.RegionQuery != nil {
			title = "No matching regions found"
			subtitle = fmt.Sprintf("Try starting over with \"%s\" again to see the full list", aliases.OverrideAwsRegion)
		} else if query.ProfileQuery != nil {
			title = "No matching profiles found"
			subtitle = fmt.Sprintf("Try starting over with \"%s\" again to see the full list", aliases.OverrideAwsProfile)
		} else {
			title = "No matching services found"
			subtitle = "Try another query (example: `aws ec2 instances`)"
		}
		wf.NewItem(title).
			Subtitle(subtitle).
			Icon(aw.IconNote)
		handleUpdateAvailable(wf)
	}
	wf.SendFeedback()
}

func handleEmptyQuery(wf *aw.Workflow, searchArgs searchutil.SearchArgs) {
	log.Println("no search type parsed")
	wf.NewItem("Search for an AWS Service ...").
		Subtitle("e.g., cloudformation, ec2, s3 ...")

	if searchArgs.Profile == "" {
		util.NewURLItem(wf, "No profile configured").
			Subtitle("Select this option to open AWS docs on how to configure").
			Arg("https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#creating-the-config-file").
			Icon(aw.IconWarning)
	} else {
		wf.NewItem("Using profile \"" + searchArgs.Profile + "\"").
			Subtitle("Use \"" + aliases.OverrideAwsProfile + "\" to override for the current query").
			Icon(aw.IconAccount)
	}

	if searchArgs.Cfg.Region == "" {
		util.NewURLItem(wf, "No region configured for this profile").
			Subtitle("Select this option to open AWS docs on how to configure").
			Arg("https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#creating-the-config-file").
			Icon(aw.IconWarning)
	} else {
		wf.NewItem("Using region \"" + searchArgs.Cfg.Region + "\"").
			Subtitle("Use \"" + aliases.OverrideAwsRegion + "\" to override for the current query").
			Icon(aw.IconWeb)
	}

	util.NewURLItem(wf, "Like this workflow? Consider donating! 😻").
		Subtitle("Select this option to open this project's Patreon").
		Arg("https://www.patreon.com/rkoval_alfred_aws_console_services_workflow").
		Icon(aw.IconFavorite)

	if wf.UpdateCheckDue() {
		if err := wf.CheckForUpdate(); err != nil {
			wf.FatalError(err)
		}
	}
	handleUpdateAvailable(wf)
}

func handleUnimplemented(wf *aw.Workflow, awsService, subService *awsworkflow.AwsService, header string, searchArgs searchutil.SearchArgs) {
	searchArgs.IgnoreAutocompleteTerm = true
	if subService == nil {
		AddServiceToWorkflow(wf, *awsService, searchArgs)
	} else {
		AddSubServiceToWorkflow(wf, *awsService, *subService, searchArgs)
	}
	util.NewURLItem(wf, header).
		Subtitle("Select this result to open the contributing guide to easily add them!").
		Arg("https://github.com/rkoval/alfred-aws-console-services-workflow/blob/master/CONTRIBUTING.md").
		Icon(aw.IconNote)
	handleUpdateAvailable(wf)
}

func handleUpdateAvailable(wf *aw.Workflow) {
	if wf.UpdateAvailable() {
		util.NewURLItem(wf, fmt.Sprintf("Update available (current version: %s)", wf.Version())).
			Subtitle("Select this result to navigate to download").
			Arg("https://github.com/rkoval/alfred-aws-console-services-workflow/releases").
			Icon(aw.IconInfo)
	}
}

func handleOpenAll(wf *aw.Workflow, awsService *awsworkflow.AwsService, allAwsServices []awsworkflow.AwsService, openAll bool, rawQuery string, cfg aws.Config) {
	if openAll {
		if awsService == nil {
			for _, awsService := range allAwsServices {
				openServiceInBrowser(wf, &awsService, cfg)
			}
		} else {
			for _, subService := range awsService.SubServices {
				openServiceInBrowser(wf, &subService, cfg)
			}
		}
	} else {
		var length int
		var entityName string
		if awsService == nil {
			length = len(allAwsServices)
			entityName = "services"
		} else {
			length = len(awsService.SubServices)
			entityName = awsService.Id + " sub-services"
		}

		title := fmt.Sprintf("Open the %d %s in browser", length, entityName)
		cmd := fmt.Sprintf(`%s -query="%s" -open_all`, os.Args[0], rawQuery)
		wf.NewItem(title).
			Subtitle("Fair warning: this may briefly overload your system").
			Icon(aw.IconWarning).
			Arg(cmd).
			Var("action", "run-script").
			Valid(true)
	}
}

func openServiceInBrowser(wf *aw.Workflow, awsService *awsworkflow.AwsService, cfg aws.Config) {
	cmd := exec.Command("open", util.ConstructAWSConsoleUrl(awsService.Url, awsService.GetRegion(cfg)))
	if err := wf.RunInBackground("open-service-in-browser-"+awsService.Id, cmd); err != nil {
		panic(err)
	}
	time.Sleep(250 * time.Millisecond) // sleep so that tabs are more-or-less opened in the order by which this function is called
}
<commit_msg>fixed region for open all<commit_after>package workflow

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	aw "github.com/deanishe/awgo"
	"github.com/rkoval/alfred-aws-console-services-workflow/aliases"
	"github.com/rkoval/alfred-aws-console-services-workflow/awsconfig"
	"github.com/rkoval/alfred-aws-console-services-workflow/awsworkflow"
	"github.com/rkoval/alfred-aws-console-services-workflow/parsers"
	"github.com/rkoval/alfred-aws-console-services-workflow/searchers"
	"github.com/rkoval/alfred-aws-console-services-workflow/searchers/searchutil"
	"github.com/rkoval/alfred-aws-console-services-workflow/util"
)

func Run(wf *aw.Workflow, rawQuery string, transport http.RoundTripper, forceFetch, openAll bool, ymlPath string) {
	log.Println("using workflow cacheDir: " + wf.CacheDir())
	log.Println("using workflow dataDir: " + wf.DataDir())

	parser := parsers.NewParser(rawQuery)
	query, awsServices := parser.Parse(ymlPath)
	defer finalize(wf, query)

	log.Printf("using query: %#v", query)

	if query.RegionQuery != nil {
		for _, region := range awsconfig.AllAWSRegions {
			autocomplete := strings.Replace(rawQuery, aliases.OverrideAwsRegion+*query.RegionQuery, aliases.OverrideAwsRegion+region.Name+" ", 1)
			wf.NewItem(region.Name).
				Subtitle(region.Description).
				Icon(aw.IconWeb).
				Autocomplete(autocomplete).
				UID(region.Name)
		}
		log.Printf("filtering with region override %q", *query.RegionQuery)
		res := wf.Filter(*query.RegionQuery)
		log.Printf("%d results match %q", len(res), *query.RegionQuery)
		return
	}

	if query.ProfileQuery != nil {
		for _, profile := range awsconfig.GetAwsProfiles() {
			autocomplete := strings.Replace(rawQuery, aliases.OverrideAwsProfile+*query.ProfileQuery, aliases.OverrideAwsProfile+profile.Name+" ", 1)
			item := wf.NewItem(profile.Name).
				Icon(aw.IconAccount).
				Autocomplete(autocomplete).
				UID(profile.Name)

			if profile.Region != "" {
				item.Subtitle(fmt.Sprintf("🌎 %s", profile.Region))
			} else {
				item.Subtitle("⚠ This profile does not specify a region. Functionality will be limited")
			}
		}
		log.Printf("filtering with profile override %q", *query.ProfileQuery)
		res := wf.Filter(*query.ProfileQuery)
		log.Printf("%d results match %q", len(res), *query.ProfileQuery)
		return
	}

	cfg := awsworkflow.InitAWS(transport, query.ProfileOverride, query.GetRegionOverride())
	searchArgs := searchutil.SearchArgs{
		Cfg:        cfg,
		ForceFetch: forceFetch,
		FullQuery:  rawQuery,
		Profile:    util.GetProfile(cfg),
	}

	if query.IsEmpty() {
		handleEmptyQuery(wf, searchArgs)
		return
	}

	if query.HasOpenAll {
		handleOpenAll(wf, query.Service, awsServices, openAll, rawQuery, cfg)
		return
	}

	if query.Service == nil || (!query.HasTrailingWhitespace && query.SubService == nil && !query.HasDefaultSearchAlias && query.RemainingQuery == "") {
		if query.Service == nil {
			searchArgs.Query = query.RemainingQuery
		} else if query.Service.ShortName != "" {
			searchArgs.Query = strings.ToLower(query.Service.ShortName)
		} else {
			searchArgs.Query = query.Service.Id
		}
		log.Printf("using searcher associated with services with query %q", searchArgs.Query)
		SearchServices(wf, awsServices, searchArgs)
	} else {
		if !query.HasDefaultSearchAlias && (query.Service.SubServices == nil || len(query.Service.SubServices) <= 0) {
			handleUnimplemented(wf, query.Service, nil, fmt.Sprintf("%s doesn't have sub-services configured (yet)", query.Service.Id), searchArgs)
			return
		}

		searchArgs.GetRegionFunc = query.Service.GetRegion

		if query.HasDefaultSearchAlias || query.SubService != nil && (query.HasTrailingWhitespace || query.RemainingQuery != "") {
			serviceId := query.Service.Id
			if query.SubService != nil {
				serviceId += "_" + query.SubService.Id
			}
			log.Println("using searcher associated with " + serviceId)
			searcher := searchers.SearchersByServiceId[serviceId]
			if searcher != nil {
				searchArgs.Query = query.RemainingQuery
				err := searcher.Search(wf, searchArgs)
				if err != nil {
					wf.FatalError(err)
				}
			} else {
				handleUnimplemented(wf, query.Service, query.SubService, fmt.Sprintf("No searcher for `%s %s` (yet)", query.Service.Id, query.SubService.Id), searchArgs)
				return
			}
		} else {
			log.Println("using searcher associated with sub-services")
			if query.SubService != nil {
				searchArgs.Query = query.SubService.Id
			} else {
				searchArgs.Query = query.RemainingQuery
			}
			SearchSubServices(wf, *query.Service, searchArgs)
		}
	}

	if searchArgs.Query != "" {
		log.Printf("filtering with query %q", searchArgs.Query)
		res := wf.Filter(searchArgs.Query)
		log.Printf("%d results match %q", len(res), searchArgs.Query)
	}
}

func finalize(wf *aw.Workflow, query *parsers.Query) {
	if wf.IsEmpty() {
		title := ""
		subtitle := ""
		if query.RegionQuery != nil {
			title = "No matching regions found"
			subtitle = fmt.Sprintf("Try starting over with \"%s\" again to see the full list", aliases.OverrideAwsRegion)
		} else if query.ProfileQuery != nil {
			title = "No matching profiles found"
			subtitle = fmt.Sprintf("Try starting over with \"%s\" again to see the full list", aliases.OverrideAwsProfile)
		} else {
			title = "No matching services found"
			subtitle = "Try another query (example: `aws ec2 instances`)"
		}
		wf.NewItem(title).
			Subtitle(subtitle).
			Icon(aw.IconNote)
		handleUpdateAvailable(wf)
	}
	wf.SendFeedback()
}

func handleEmptyQuery(wf *aw.Workflow, searchArgs searchutil.SearchArgs) {
	log.Println("no search type parsed")
	wf.NewItem("Search for an AWS Service ...").
		Subtitle("e.g., cloudformation, ec2, s3 ...")

	if searchArgs.Profile == "" {
		util.NewURLItem(wf, "No profile configured").
			Subtitle("Select this option to open AWS docs on how to configure").
			Arg("https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#creating-the-config-file").
			Icon(aw.IconWarning)
	} else {
		wf.NewItem("Using profile \"" + searchArgs.Profile + "\"").
			Subtitle("Use \"" + aliases.OverrideAwsProfile + "\" to override for the current query").
			Icon(aw.IconAccount)
	}

	if searchArgs.Cfg.Region == "" {
		util.NewURLItem(wf, "No region configured for this profile").
			Subtitle("Select this option to open AWS docs on how to configure").
			Arg("https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#creating-the-config-file").
			Icon(aw.IconWarning)
	} else {
		wf.NewItem("Using region \"" + searchArgs.Cfg.Region + "\"").
			Subtitle("Use \"" + aliases.OverrideAwsRegion + "\" to override for the current query").
			Icon(aw.IconWeb)
	}

	util.NewURLItem(wf, "Like this workflow? Consider donating! 😻").
		Subtitle("Select this option to open this project's Patreon").
		Arg("https://www.patreon.com/rkoval_alfred_aws_console_services_workflow").
		Icon(aw.IconFavorite)

	if wf.UpdateCheckDue() {
		if err := wf.CheckForUpdate(); err != nil {
			wf.FatalError(err)
		}
	}
	handleUpdateAvailable(wf)
}

func handleUnimplemented(wf *aw.Workflow, awsService, subService *awsworkflow.AwsService, header string, searchArgs searchutil.SearchArgs) {
	searchArgs.IgnoreAutocompleteTerm = true
	if subService == nil {
		AddServiceToWorkflow(wf, *awsService, searchArgs)
	} else {
		AddSubServiceToWorkflow(wf, *awsService, *subService, searchArgs)
	}
	util.NewURLItem(wf, header).
		Subtitle("Select this result to open the contributing guide to easily add them!").
		Arg("https://github.com/rkoval/alfred-aws-console-services-workflow/blob/master/CONTRIBUTING.md").
		Icon(aw.IconNote)
	handleUpdateAvailable(wf)
}

func handleUpdateAvailable(wf *aw.Workflow) {
	if wf.UpdateAvailable() {
		util.NewURLItem(wf, fmt.Sprintf("Update available (current version: %s)", wf.Version())).
			Subtitle("Select this result to navigate to download").
			Arg("https://github.com/rkoval/alfred-aws-console-services-workflow/releases").
			Icon(aw.IconInfo)
	}
}

func handleOpenAll(wf *aw.Workflow, awsService *awsworkflow.AwsService, allAwsServices []awsworkflow.AwsService, openAll bool, rawQuery string, cfg aws.Config) {
	if openAll {
		if awsService == nil {
			for _, awsService := range allAwsServices {
				openServiceInBrowser(wf, awsService, awsService, cfg)
			}
		} else {
			for _, subService := range awsService.SubServices {
				openServiceInBrowser(wf, subService, *awsService, cfg)
			}
		}
	} else {
		var length int
		var entityName string
		if awsService == nil {
			length = len(allAwsServices)
			entityName = "services"
		} else {
			length = len(awsService.SubServices)
			entityName = awsService.Id + " sub-services"
		}

		title := fmt.Sprintf("Open the %d %s in browser", length, entityName)
		cmd := fmt.Sprintf(`%s -query="%s" -open_all`, os.Args[0], rawQuery)
		wf.NewItem(title).
			Subtitle("Fair warning: this may briefly overload your system").
			Icon(aw.IconWarning).
			Arg(cmd).
			Var("action", "run-script").
			Valid(true)
	}
}

func openServiceInBrowser(wf *aw.Workflow, awsService, awsServiceForRegion awsworkflow.AwsService, cfg aws.Config) {
	cmd := exec.Command("open", util.ConstructAWSConsoleUrl(awsService.Url, awsServiceForRegion.GetRegion(cfg)))
	if err := wf.RunInBackground("open-service-in-browser-"+awsService.Id, cmd); err != nil {
		panic(err)
	}
	time.Sleep(250 * time.Millisecond) // sleep so that tabs are more-or-less opened in the order by which this function is called
}
<|endoftext|>