<commit_before>package command

import (
	"bytes"
	"fmt"
	"github.com/hashicorp/go-version"
	"github.com/hashicorp/terraform/internal/states/statefile"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/mitchellh/cli"

	"github.com/hashicorp/terraform/internal/addrs"
	"github.com/hashicorp/terraform/internal/states"
)

func TestStateReplaceProvider(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "alpha",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "beta",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "azurerm_virtual_machine",
				Name: "gamma",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewLegacyProvider("azurerm"),
				Module:   addrs.RootModule,
			},
		)
	})

	t.Run("Schemas not initialized and JSON output not generated", func(t *testing.T) {
		td := t.TempDir()
		testCopyDir(t, testFixturePath("init-cloud-simple"), td)
		defer testChdir(t, td)()

		// Some of the tests need a non-empty placeholder state file to work
		// with.
		fakeStateFile := &statefile.File{
			Lineage:          "boop",
			Serial:           4,
			TerraformVersion: version.Must(version.NewVersion("1.0.0")),
			State:            state,
		}
		var fakeStateBuf bytes.Buffer
		err := statefile.WriteForTest(fakeStateFile, &fakeStateBuf)
		if err != nil {
			t.Error(err)
		}
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf
		inputBuf.WriteString("yes\n")

		args := []string{
			"-state", statePath,
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		// Check for the warning
		actual := strings.TrimSpace(ui.ErrorWriter.String())
		expected := strings.TrimSpace(fmt.Sprintf(failedToLoadSchemasMessage, ""))
		re, err := regexp.Compile(expected)
		if err != nil {
			t.Fatalf("Error compiling regexp: %s", err)
		}

		if !re.MatchString(actual) {
			t.Fatalf("wrong output\n expected: %s \n actual: %s", expected, actual)
		}
	})

	t.Run("happy path", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf
		inputBuf.WriteString("yes\n")

		args := []string{
			"-state", statePath,
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutput)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 1 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
		testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal)
	})

	t.Run("auto approve", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf

		args := []string{
			"-state", statePath,
			"-auto-approve",
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutput)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 1 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
		testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal)
	})

	t.Run("cancel at approval step", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf
		inputBuf.WriteString("no\n")

		args := []string{
			"-state", statePath,
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 0 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
	})

	t.Run("no matching provider found", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{
			"-state", statePath,
			"hashicorp/google",
			"acmecorp/google",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 0 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
	})

	t.Run("invalid flags", func(t *testing.T) {
		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{
			"-invalid",
			"hashicorp/google",
			"acmecorp/google",
		}
		if code := c.Run(args); code == 0 {
			t.Fatalf("successful exit; want error")
		}

		if got, want := ui.ErrorWriter.String(), "Error parsing command-line flags"; !strings.Contains(got, want) {
			t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
		}
	})

	t.Run("wrong number of arguments", func(t *testing.T) {
		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{"a", "b", "c", "d"}
		if code := c.Run(args); code == 0 {
			t.Fatalf("successful exit; want error")
		}

		if got, want := ui.ErrorWriter.String(), "Exactly two arguments expected"; !strings.Contains(got, want) {
			t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
		}
	})

	t.Run("invalid provider strings", func(t *testing.T) {
		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{
			"hashicorp/google_cloud",
			"-/-/google",
		}
		if code := c.Run(args); code == 0 {
			t.Fatalf("successful exit; want error")
		}

		got := ui.ErrorWriter.String()
		msgs := []string{
			`Invalid "from" provider "hashicorp/google_cloud"`,
			"Invalid provider type",
			`Invalid "to" provider "-/-/google"`,
			"Invalid provider source hostname",
		}
		for _, msg := range msgs {
			if !strings.Contains(got, msg) {
				t.Errorf("missing expected error message\nwant: %s\nfull output:\n%s", msg, got)
			}
		}
	})
}

func TestStateReplaceProvider_docs(t *testing.T) {
	c := &StateReplaceProviderCommand{}

	if got, want := c.Help(), "Usage: terraform [global options] state replace-provider"; !strings.Contains(got, want) {
		t.Fatalf("unexpected help text\nwant: %s\nfull output:\n%s", want, got)
	}

	if got, want := c.Synopsis(), "Replace provider in the state"; got != want {
		t.Fatalf("unexpected synopsis\nwant: %s\nfull output:\n%s", want, got)
	}
}

func TestStateReplaceProvider_checkRequiredVersion(t *testing.T) {
	// Create a temporary working directory that is empty
	td := t.TempDir()
	testCopyDir(t, testFixturePath("command-check-required-version"), td)
	defer testChdir(t, td)()

	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "alpha",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "beta",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "azurerm_virtual_machine",
				Name: "gamma",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewLegacyProvider("azurerm"),
				Module:   addrs.RootModule,
			},
		)
	})

	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	view, _ := testView(t)
	c := &StateReplaceProviderCommand{
		StateMeta{
			Meta: Meta{
				Ui:   ui,
				View: view,
			},
		},
	}

	inputBuf := &bytes.Buffer{}
	ui.InputReader = inputBuf
	inputBuf.WriteString("yes\n")

	args := []string{
		"-state", statePath,
		"hashicorp/aws",
		"acmecorp/aws",
	}
	if code := c.Run(args); code != 1 {
		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
	}

	// State is unchanged
	testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal)

	// Required version diags are correct
	errStr := ui.ErrorWriter.String()
	if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) {
		t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
	}
	if strings.Contains(errStr, `required_version = ">= 0.13.0"`) {
		t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr)
	}
}

const testStateReplaceProviderOutputOriginal = `
aws_instance.alpha:
  ID = alpha
  provider = provider["registry.terraform.io/hashicorp/aws"]
  bar = value
  foo = value
aws_instance.beta:
  ID = beta
  provider = provider["registry.terraform.io/hashicorp/aws"]
  bar = value
  foo = value
azurerm_virtual_machine.gamma:
  ID = gamma
  provider = provider["registry.terraform.io/-/azurerm"]
  baz = value
`

const testStateReplaceProviderOutput = `
aws_instance.alpha:
  ID = alpha
  provider = provider["registry.terraform.io/acmecorp/aws"]
  bar = value
  foo = value
aws_instance.beta:
  ID = beta
  provider = provider["registry.terraform.io/acmecorp/aws"]
  bar = value
  foo = value
azurerm_virtual_machine.gamma:
  ID = gamma
  provider = provider["registry.terraform.io/-/azurerm"]
  baz = value
`
<commit_msg>remove test for error<commit_after>package command

import (
	"bytes"
	"path/filepath"
	"strings"
	"testing"

	"github.com/mitchellh/cli"

	"github.com/hashicorp/terraform/internal/addrs"
	"github.com/hashicorp/terraform/internal/states"
)

func TestStateReplaceProvider(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "alpha",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "beta",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "azurerm_virtual_machine",
				Name: "gamma",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewLegacyProvider("azurerm"),
				Module:   addrs.RootModule,
			},
		)
	})

	t.Run("happy path", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf
		inputBuf.WriteString("yes\n")

		args := []string{
			"-state", statePath,
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutput)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 1 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
		testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal)
	})

	t.Run("auto approve", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf

		args := []string{
			"-state", statePath,
			"-auto-approve",
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutput)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 1 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
		testStateOutput(t, backups[0], testStateReplaceProviderOutputOriginal)
	})

	t.Run("cancel at approval step", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		inputBuf := &bytes.Buffer{}
		ui.InputReader = inputBuf
		inputBuf.WriteString("no\n")

		args := []string{
			"-state", statePath,
			"hashicorp/aws",
			"acmecorp/aws",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 0 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
	})

	t.Run("no matching provider found", func(t *testing.T) {
		statePath := testStateFile(t, state)

		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{
			"-state", statePath,
			"hashicorp/google",
			"acmecorp/google",
		}
		if code := c.Run(args); code != 0 {
			t.Fatalf("return code: %d\n\n%s", code, ui.ErrorWriter.String())
		}

		testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal)

		backups := testStateBackups(t, filepath.Dir(statePath))
		if len(backups) != 0 {
			t.Fatalf("unexpected backups: %#v", backups)
		}
	})

	t.Run("invalid flags", func(t *testing.T) {
		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{
			"-invalid",
			"hashicorp/google",
			"acmecorp/google",
		}
		if code := c.Run(args); code == 0 {
			t.Fatalf("successful exit; want error")
		}

		if got, want := ui.ErrorWriter.String(), "Error parsing command-line flags"; !strings.Contains(got, want) {
			t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
		}
	})

	t.Run("wrong number of arguments", func(t *testing.T) {
		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{"a", "b", "c", "d"}
		if code := c.Run(args); code == 0 {
			t.Fatalf("successful exit; want error")
		}

		if got, want := ui.ErrorWriter.String(), "Exactly two arguments expected"; !strings.Contains(got, want) {
			t.Fatalf("missing expected error message\nwant: %s\nfull output:\n%s", want, got)
		}
	})

	t.Run("invalid provider strings", func(t *testing.T) {
		ui := new(cli.MockUi)
		view, _ := testView(t)
		c := &StateReplaceProviderCommand{
			StateMeta{
				Meta: Meta{
					Ui:   ui,
					View: view,
				},
			},
		}

		args := []string{
			"hashicorp/google_cloud",
			"-/-/google",
		}
		if code := c.Run(args); code == 0 {
			t.Fatalf("successful exit; want error")
		}

		got := ui.ErrorWriter.String()
		msgs := []string{
			`Invalid "from" provider "hashicorp/google_cloud"`,
			"Invalid provider type",
			`Invalid "to" provider "-/-/google"`,
			"Invalid provider source hostname",
		}
		for _, msg := range msgs {
			if !strings.Contains(got, msg) {
				t.Errorf("missing expected error message\nwant: %s\nfull output:\n%s", msg, got)
			}
		}
	})
}

func TestStateReplaceProvider_docs(t *testing.T) {
	c := &StateReplaceProviderCommand{}

	if got, want := c.Help(), "Usage: terraform [global options] state replace-provider"; !strings.Contains(got, want) {
		t.Fatalf("unexpected help text\nwant: %s\nfull output:\n%s", want, got)
	}

	if got, want := c.Synopsis(), "Replace provider in the state"; got != want {
		t.Fatalf("unexpected synopsis\nwant: %s\nfull output:\n%s", want, got)
	}
}

func TestStateReplaceProvider_checkRequiredVersion(t *testing.T) {
	// Create a temporary working directory that is empty
	td := t.TempDir()
	testCopyDir(t, testFixturePath("command-check-required-version"), td)
	defer testChdir(t, td)()

	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "alpha",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"alpha","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "aws_instance",
				Name: "beta",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"beta","foo":"value","bar":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewDefaultProvider("aws"),
				Module:   addrs.RootModule,
			},
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "azurerm_virtual_machine",
				Name: "gamma",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"gamma","baz":"value"}`),
				Status:    states.ObjectReady,
			},
			addrs.AbsProviderConfig{
				Provider: addrs.NewLegacyProvider("azurerm"),
				Module:   addrs.RootModule,
			},
		)
	})

	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	view, _ := testView(t)
	c := &StateReplaceProviderCommand{
		StateMeta{
			Meta: Meta{
				Ui:   ui,
				View: view,
			},
		},
	}

	inputBuf := &bytes.Buffer{}
	ui.InputReader = inputBuf
	inputBuf.WriteString("yes\n")

	args := []string{
		"-state", statePath,
		"hashicorp/aws",
		"acmecorp/aws",
	}
	if code := c.Run(args); code != 1 {
		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
	}

	// State is unchanged
	testStateOutput(t, statePath, testStateReplaceProviderOutputOriginal)

	// Required version diags are correct
	errStr := ui.ErrorWriter.String()
	if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) {
		t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
	}
	if strings.Contains(errStr, `required_version = ">= 0.13.0"`) {
		t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr)
	}
}

const testStateReplaceProviderOutputOriginal = `
aws_instance.alpha:
  ID = alpha
  provider = provider["registry.terraform.io/hashicorp/aws"]
  bar = value
  foo = value
aws_instance.beta:
  ID = beta
  provider = provider["registry.terraform.io/hashicorp/aws"]
  bar = value
  foo = value
azurerm_virtual_machine.gamma:
  ID = gamma
  provider = provider["registry.terraform.io/-/azurerm"]
  baz = value
`

const testStateReplaceProviderOutput = `
aws_instance.alpha:
  ID = alpha
  provider = provider["registry.terraform.io/acmecorp/aws"]
  bar = value
  foo = value
aws_instance.beta:
  ID = beta
  provider = provider["registry.terraform.io/acmecorp/aws"]
  bar = value
  foo = value
azurerm_virtual_machine.gamma:
  ID = gamma
  provider = provider["registry.terraform.io/-/azurerm"]
  baz = value
`
<|endoftext|>


<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package mm

import (
	"github.com/percona/cloud-protocol/proto"
	"github.com/percona/cloud-tools/data"
	"github.com/percona/cloud-tools/pct"
	"math"
	"time"
)

type Aggregator struct {
	logger         *pct.Logger
	interval       int64
	collectionChan chan *Collection
	spool          data.Spooler
	// --
	sync    *pct.SyncChan
	running bool
}

func NewAggregator(logger *pct.Logger, interval int64, collectionChan chan *Collection, spool data.Spooler) *Aggregator {
	a := &Aggregator{
		logger:         logger,
		interval:       interval,
		collectionChan: collectionChan,
		spool:          spool,
		// --
		sync: pct.NewSyncChan(),
	}
	return a
}

/////////////////////////////////////////////////////////////////////////////
// Interface
/////////////////////////////////////////////////////////////////////////////

// @goroutine[0]
func (a *Aggregator) Start() {
	go a.run()
	a.running = true // XXX: not guarded
}

// @goroutine[0]
func (a *Aggregator) Stop() {
	a.sync.Stop()
	a.sync.Wait()
}

// @goroutine[0]
func (a *Aggregator) IsRunning() bool {
	return a.running // XXX: not guarded
}

/////////////////////////////////////////////////////////////////////////////
// Implementation
/////////////////////////////////////////////////////////////////////////////

// @goroutine[1]
func (a *Aggregator) run() {
	defer func() {
		a.running = false // XXX: not guarded
		a.sync.Done()
	}()

	var curInterval int64
	var startTs time.Time
	cur := []*InstanceStats{}

	for {
		select {
		case collection := <-a.collectionChan:
			interval := (collection.Ts / a.interval) * a.interval
			if curInterval == 0 {
				curInterval = interval
				startTs = GoTime(a.interval, interval)
				a.logger.Debug("Start first interval", startTs)
			}
			if interval > curInterval {
				// Metrics for next interval have arrived.  Process and spool
				// the current interval, then advance to this interval.
				a.report(startTs, cur)

				// Init next stats based on current ones to avoid re-creating them.
				// todo: what if metrics from an instance aren't collected?
				next := make([]*InstanceStats, len(cur))
				for n := range cur {
					i := &InstanceStats{
						ServiceInstance: cur[n].ServiceInstance,
						Stats:           make(map[string]*Stats),
					}
					next[n] = i
				}
				cur = next
				curInterval = interval
				startTs = GoTime(a.interval, interval)
				a.logger.Debug("Start interval", startTs)
			} else if interval < curInterval {
				t := GoTime(a.interval, interval)
				a.logger.Info("Lost collection for interval", t, "; current interval is", startTs)
			}

			// Each collection is from a specific service instance ("it").
			// Find the stats for this instance, create if they don't exist.
			var is *InstanceStats
			for _, i := range cur {
				if collection.Service == i.Service && collection.InstanceId == i.InstanceId {
					is = i
					break
				}
			}

			if is == nil {
				// New service instance, create stats for it.
				is = &InstanceStats{
					ServiceInstance: proto.ServiceInstance{
						Service:    collection.Service,
						InstanceId: collection.InstanceId,
					},
					Stats: make(map[string]*Stats),
				}
				cur = append(cur, is)
			}

			// Add each metric in the collection to its Stats.
			for _, metric := range collection.Metrics {
				stats, haveStats := is.Stats[metric.Name]
				if !haveStats {
					// New metric, create stats for it.
					var err error
					stats, err = NewStats(metric.Type)
					if err != nil {
						a.logger.Error(metric.Name, "invalid:", err.Error())
						continue
					}
					is.Stats[metric.Name] = stats
				}
				stats.Add(&metric, collection.Ts)
			}
		case <-a.sync.StopChan:
			return
		}
	}
}

// @goroutine[1]
func (a *Aggregator) report(startTs time.Time, is []*InstanceStats) {
	a.logger.Info("Summarize metrics for", startTs)
	for _, i := range is {
		for _, s := range i.Stats {
			s.Summarize()
		}
	}
	report := &Report{
		Ts:       startTs,
		Duration: uint(a.interval),
		Stats:    is,
	}
	a.spool.Write("mm", report)
}

func GoTime(interval, unixTs int64) time.Time {
	// Calculate seconds (d) from begin to next interval.
	i := float64(interval)
	t := float64(unixTs)
	d := int64(i - math.Mod(t, i))
	if d != interval {
		/**
		 * unixTs is not an interval, so it's after the interval's start ts.
		 * E.g. if i=60 and unxiTs (t)=130, then t falls between intervals:
		 *   120
		 *   130  =t
		 *   180  d=50
		 * Real begin is 120, so decrease t by 10: i - d.
		 */
		unixTs = unixTs - (interval - d)
	}
	return time.Unix(int64(unixTs), 0).UTC()
}
<commit_msg>Minor debug tweak.<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package mm

import (
	"github.com/percona/cloud-protocol/proto"
	"github.com/percona/cloud-tools/data"
	"github.com/percona/cloud-tools/pct"
	"math"
	"time"
)

type Aggregator struct {
	logger         *pct.Logger
	interval       int64
	collectionChan chan *Collection
	spool          data.Spooler
	// --
	sync    *pct.SyncChan
	running bool
}

func NewAggregator(logger *pct.Logger, interval int64, collectionChan chan *Collection, spool data.Spooler) *Aggregator {
	a := &Aggregator{
		logger:         logger,
		interval:       interval,
		collectionChan: collectionChan,
		spool:          spool,
		// --
		sync: pct.NewSyncChan(),
	}
	return a
}

/////////////////////////////////////////////////////////////////////////////
// Interface
/////////////////////////////////////////////////////////////////////////////

// @goroutine[0]
func (a *Aggregator) Start() {
	go a.run()
	a.running = true // XXX: not guarded
}

// @goroutine[0]
func (a *Aggregator) Stop() {
	a.sync.Stop()
	a.sync.Wait()
}

// @goroutine[0]
func (a *Aggregator) IsRunning() bool {
	return a.running // XXX: not guarded
}

/////////////////////////////////////////////////////////////////////////////
// Implementation
/////////////////////////////////////////////////////////////////////////////

// @goroutine[1]
func (a *Aggregator) run() {
	defer func() {
		a.running = false // XXX: not guarded
		a.sync.Done()
	}()

	var curInterval int64
	var startTs time.Time
	cur := []*InstanceStats{}

	for {
		select {
		case collection := <-a.collectionChan:
			interval := (collection.Ts / a.interval) * a.interval
			if curInterval == 0 {
				curInterval = interval
				startTs = GoTime(a.interval, interval)
				a.logger.Debug("Start first interval", startTs)
			}
			if interval > curInterval {
				// Metrics for next interval have arrived.  Process and spool
				// the current interval, then advance to this interval.
				a.report(startTs, cur)

				// Init next stats based on current ones to avoid re-creating them.
				// todo: what if metrics from an instance aren't collected?
				next := make([]*InstanceStats, len(cur))
				for n := range cur {
					i := &InstanceStats{
						ServiceInstance: cur[n].ServiceInstance,
						Stats:           make(map[string]*Stats),
					}
					next[n] = i
				}
				cur = next
				curInterval = interval
				startTs = GoTime(a.interval, interval)
				a.logger.Debug("Start interval", startTs)
			} else if interval < curInterval {
				t := GoTime(a.interval, interval)
				a.logger.Info("Lost collection for interval", t, "; current interval is", startTs)
			}

			// Each collection is from a specific service instance ("it").
			// Find the stats for this instance, create if they don't exist.
			var is *InstanceStats
			for _, i := range cur {
				if collection.Service == i.Service && collection.InstanceId == i.InstanceId {
					is = i
					break
				}
			}

			if is == nil {
				// New service instance, create stats for it.
				is = &InstanceStats{
					ServiceInstance: proto.ServiceInstance{
						Service:    collection.Service,
						InstanceId: collection.InstanceId,
					},
					Stats: make(map[string]*Stats),
				}
				cur = append(cur, is)
			}

			// Add each metric in the collection to its Stats.
			for _, metric := range collection.Metrics {
				stats, haveStats := is.Stats[metric.Name]
				if !haveStats {
					// New metric, create stats for it.
					var err error
					stats, err = NewStats(metric.Type)
					if err != nil {
						a.logger.Error(metric.Name, "invalid:", err.Error())
						continue
					}
					is.Stats[metric.Name] = stats
				}
				stats.Add(&metric, collection.Ts)
			}
		case <-a.sync.StopChan:
			return
		}
	}
}

// @goroutine[1]
func (a *Aggregator) report(startTs time.Time, is []*InstanceStats) {
	a.logger.Debug("Summarize metrics for", startTs)
	for _, i := range is {
		for _, s := range i.Stats {
			s.Summarize()
		}
	}
	report := &Report{
		Ts:       startTs,
		Duration: uint(a.interval),
		Stats:    is,
	}
	a.spool.Write("mm", report)
}

func GoTime(interval, unixTs int64) time.Time {
	// Calculate seconds (d) from begin to next interval.
	i := float64(interval)
	t := float64(unixTs)
	d := int64(i - math.Mod(t, i))
	if d != interval {
		/**
		 * unixTs is not an interval, so it's after the interval's start ts.
		 * E.g. if i=60 and unxiTs (t)=130, then t falls between intervals:
		 *   120
		 *   130  =t
		 *   180  d=50
		 * Real begin is 120, so decrease t by 10: i - d.
		 */
		unixTs = unixTs - (interval - d)
	}
	return time.Unix(int64(unixTs), 0).UTC()
}
<|endoftext|>


<commit_before>package client

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strconv"
)

// Response handler
type ResponseHandler func(*Response)

// response
type Response struct {
	DataType  uint32
	Data, UID []byte
	Handle    string
}

// Extract the Response's result.
// if data == nil, err != nil, then worker failing to execute job
// if data != nil, err != nil, then worker has a exception
// if data != nil, err == nil, then worker complate job
// after calling this method, the Response.Handle will be filled
func (resp *Response) Result() (data []byte, err error) {
	switch resp.DataType {
	case dtWorkFail:
		resp.Handle = string(resp.Data)
		err = ErrWorkFail
		return
	case dtWorkException:
		err = ErrWorkException
		fallthrough
	case dtWorkComplete:
		data = resp.Data
	default:
		err = ErrDataType
	}
	return
}

// Extract the job's update
func (resp *Response) Update() (data []byte, err error) {
	if resp.DataType != dtWorkData &&
		resp.DataType != dtWorkWarning {
		err = ErrDataType
		return
	}
	data = resp.Data
	if resp.DataType == dtWorkWarning {
		err = ErrWorkWarning
	}
	return
}

// Decode a job from byte slice
func decodeResponse(data []byte) (resp *Response, l int, err error) {
	a := len(data)
	if a < minPacketLength { // valid package should not less 12 bytes
		err = fmt.Errorf("Invalid data: %v", data)
		return
	}
	dl := int(binary.BigEndian.Uint32(data[8:12]))
	if a < minPacketLength+dl {
		err = fmt.Errorf("Invalid data: %v", data)
		return
	}
	dt := data[minPacketLength : dl+minPacketLength]
	if len(dt) != int(dl) { // length not equal
		err = fmt.Errorf("Invalid data: %v", data)
		return
	}
	resp = getResponse()
	resp.DataType = binary.BigEndian.Uint32(data[4:8])
	switch resp.DataType {
	case dtJobCreated:
		resp.Handle = string(dt)
	case dtStatusRes, dtWorkData, dtWorkWarning, dtWorkStatus,
		dtWorkComplete, dtWorkFail, dtWorkException:
		s := bytes.SplitN(dt, []byte{'\x00'}, 2)
		if len(s) >= 2 {
			resp.Handle = string(s[0])
			resp.Data = s[1]
		} else {
			err = fmt.Errorf("Invalid data: %v", data)
			return
		}
	case dtEchoRes:
		fallthrough
	default:
		resp.Data = dt
	}
	l = dl + minPacketLength
	return
}

func (resp *Response) Status() (status *Status, err error) {
	data := bytes.SplitN(resp.Data, []byte{'\x00'}, 2)
	if len(data) != 2 {
		err = fmt.Errorf("Invalid data: %v", resp.Data)
		return
	}
	status = &Status{}
	status.Handle = resp.Handle
	status.Known = true
	status.Running = true
	status.Numerator, err = strconv.ParseUint(string(data[0]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[0])
		return
	}
	status.Denominator, err = strconv.ParseUint(string(data[1]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[1])
		return
	}
	return
}

// status handler
func (resp *Response) _status() (status *Status, err error) {
	data := bytes.SplitN(resp.Data, []byte{'\x00'}, 4)
	if len(data) != 4 {
		err = fmt.Errorf("Invalid data: %v", resp.Data)
		return
	}
	status = &Status{}
	status.Handle = resp.Handle
	status.Known = (data[0][0] == '1')
	status.Running = (data[1][0] == '1')
	status.Numerator, err = strconv.ParseUint(string(data[2]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[2])
		return
	}
	status.Denominator, err = strconv.ParseUint(string(data[3]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[3])
		return
	}
	return
}

func getResponse() (resp *Response) {
	// TODO add a pool
	resp = &Response{}
	return
}
<commit_msg>Fixed WORK_FAIL needing 2 arguments<commit_after>package client

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strconv"
)

// Response handler
type ResponseHandler func(*Response)

// response
type Response struct {
	DataType  uint32
	Data, UID []byte
	Handle    string
}

// Extract the Response's result.
// if data == nil, err != nil, then worker failing to execute job
// if data != nil, err != nil, then worker has a exception
// if data != nil, err == nil, then worker complate job
// after calling this method, the Response.Handle will be filled
func (resp *Response) Result() (data []byte, err error) {
	switch resp.DataType {
	case dtWorkFail:
		resp.Handle = string(resp.Data)
		err = ErrWorkFail
		return
	case dtWorkException:
		err = ErrWorkException
		fallthrough
	case dtWorkComplete:
		data = resp.Data
	default:
		err = ErrDataType
	}
	return
}

// Extract the job's update
func (resp *Response) Update() (data []byte, err error) {
	if resp.DataType != dtWorkData &&
		resp.DataType != dtWorkWarning {
		err = ErrDataType
		return
	}
	data = resp.Data
	if resp.DataType == dtWorkWarning {
		err = ErrWorkWarning
	}
	return
}

// Decode a job from byte slice
func decodeResponse(data []byte) (resp *Response, l int, err error) {
	a := len(data)
	if a < minPacketLength { // valid package should not less 12 bytes
		err = fmt.Errorf("Invalid data: %v", data)
		return
	}
	dl := int(binary.BigEndian.Uint32(data[8:12]))
	if a < minPacketLength+dl {
		err = fmt.Errorf("Invalid data: %v", data)
		return
	}
	dt := data[minPacketLength : dl+minPacketLength]
	if len(dt) != int(dl) { // length not equal
		err = fmt.Errorf("Invalid data: %v", data)
		return
	}
	resp = getResponse()
	resp.DataType = binary.BigEndian.Uint32(data[4:8])
	switch resp.DataType {
	case dtJobCreated:
		resp.Handle = string(dt)
	case dtStatusRes, dtWorkData, dtWorkWarning, dtWorkStatus,
		dtWorkComplete, dtWorkException:
		s := bytes.SplitN(dt, []byte{'\x00'}, 2)
		if len(s) >= 2 {
			resp.Handle = string(s[0])
			resp.Data = s[1]
		} else {
			err = fmt.Errorf("Invalid data: %v", data)
			return
		}
	case dtWorkFail:
		s := bytes.SplitN(dt, []byte{'\x00'}, 2)
		if len(s) >= 1 {
			resp.Handle = string(s[0])
		} else {
			err = fmt.Errorf("Invalid data: %v", data)
			return
		}
	case dtEchoRes:
		fallthrough
	default:
		resp.Data = dt
	}
	l = dl + minPacketLength
	return
}

func (resp *Response) Status() (status *Status, err error) {
	data := bytes.SplitN(resp.Data, []byte{'\x00'}, 2)
	if len(data) != 2 {
		err = fmt.Errorf("Invalid data: %v", resp.Data)
		return
	}
	status = &Status{}
	status.Handle = resp.Handle
	status.Known = true
	status.Running = true
	status.Numerator, err = strconv.ParseUint(string(data[0]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[0])
		return
	}
	status.Denominator, err = strconv.ParseUint(string(data[1]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[1])
		return
	}
	return
}

// status handler
func (resp *Response) _status() (status *Status, err error) {
	data := bytes.SplitN(resp.Data, []byte{'\x00'}, 4)
	if len(data) != 4 {
		err = fmt.Errorf("Invalid data: %v", resp.Data)
		return
	}
	status = &Status{}
	status.Handle = resp.Handle
	status.Known = (data[0][0] == '1')
	status.Running = (data[1][0] == '1')
	status.Numerator, err = strconv.ParseUint(string(data[2]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[2])
		return
	}
	status.Denominator, err = strconv.ParseUint(string(data[3]), 10, 0)
	if err != nil {
		err = fmt.Errorf("Invalid Integer: %s", data[3])
		return
	}
	return
}

func getResponse() (resp *Response) {
	// TODO add a pool
	resp = &Response{}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package model

import (
	"strconv"
	"strings"
)

// This is a list of all the current viersions including any patches.
// It should be maitained in chronological order with most current
// release at the front of the list.
var versions = []string{
	"1.1.0",
	"1.0.0",
	"0.7.1",
	"0.7.0",
	"0.6.0",
	"0.5.0",
}

var CurrentVersion string = versions[0]
var BuildNumber = "_BUILD_NUMBER_"
var BuildDate = "_BUILD_DATE_"
var BuildHash = "_BUILD_HASH_"

func SplitVersion(version string) (int64, int64, int64) {
	parts := strings.Split(version, ".")

	major := int64(0)
	minor := int64(0)
	patch := int64(0)

	if len(parts) > 0 {
		major, _ = strconv.ParseInt(parts[0], 10, 64)
	}

	if len(parts) > 1 {
		minor, _ = strconv.ParseInt(parts[1], 10, 64)
	}

	if len(parts) > 2 {
		patch, _ = strconv.ParseInt(parts[2], 10, 64)
	}

	return major, minor, patch
}

func GetPreviousVersion(currentVersion string) (int64, int64) {
	currentIndex := -1
	currentMajor, currentMinor, _ := SplitVersion(currentVersion)

	for index, version := range versions {
		major, minor, _ := SplitVersion(version)

		if currentMajor == major && currentMinor == minor {
			currentIndex = index
		}

		if currentIndex >= 0 {
			if currentMajor != major || currentMinor != minor {
				return major, minor
			}
		}
	}

	return 0, 0
}

func IsOfficalBuild() bool {
	return BuildNumber != "_BUILD_NUMBER_"
}

func IsCurrentVersion(versionToCheck string) bool {
	currentMajor, currentMinor, _ := SplitVersion(CurrentVersion)
	toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)

	if toCheckMajor == currentMajor && toCheckMinor == currentMinor {
		return true
	} else {
		return false
	}
}

func IsPreviousVersion(versionToCheck string) bool {
	toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)
	prevMajor, prevMinor := GetPreviousVersion(CurrentVersion)

	if toCheckMajor == prevMajor && toCheckMinor == prevMinor {
		return true
	} else {
		return false
	}
}
<commit_msg>Updating version to v1.2.0<commit_after>// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package model

import (
	"strconv"
	"strings"
)

// This is a list of all the current viersions including any patches.
// It should be maitained in chronological order with most current
// release at the front of the list.
var versions = []string{
	"1.2.0",
	"1.1.0",
	"1.0.0",
	"0.7.1",
	"0.7.0",
	"0.6.0",
	"0.5.0",
}

var CurrentVersion string = versions[0]
var BuildNumber = "_BUILD_NUMBER_"
var BuildDate = "_BUILD_DATE_"
var BuildHash = "_BUILD_HASH_"

func SplitVersion(version string) (int64, int64, int64) {
	parts := strings.Split(version, ".")

	major := int64(0)
	minor := int64(0)
	patch := int64(0)

	if len(parts) > 0 {
		major, _ = strconv.ParseInt(parts[0], 10, 64)
	}

	if len(parts) > 1 {
		minor, _ = strconv.ParseInt(parts[1], 10, 64)
	}

	if len(parts) > 2 {
		patch, _ = strconv.ParseInt(parts[2], 10, 64)
	}

	return major, minor, patch
}

func GetPreviousVersion(currentVersion string) (int64, int64) {
	currentIndex := -1
	currentMajor, currentMinor, _ := SplitVersion(currentVersion)

	for index, version := range versions {
		major, minor, _ := SplitVersion(version)

		if currentMajor == major && currentMinor == minor {
			currentIndex = index
		}

		if currentIndex >= 0 {
			if currentMajor != major || currentMinor != minor {
				return major, minor
			}
		}
	}

	return 0, 0
}

func IsOfficalBuild() bool {
	return BuildNumber != "_BUILD_NUMBER_"
}

func IsCurrentVersion(versionToCheck string) bool {
	currentMajor, currentMinor, _ := SplitVersion(CurrentVersion)
	toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)

	if toCheckMajor == currentMajor && toCheckMinor == currentMinor {
		return true
	} else {
		return false
	}
}

func IsPreviousVersion(versionToCheck string) bool {
	toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)
	prevMajor, prevMinor := GetPreviousVersion(CurrentVersion)

	if toCheckMajor == prevMajor && toCheckMinor == prevMinor {
		return true
	} else {
		return false
	}
}
<|endoftext|>


<commit_before>package models

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
)

type LegacyBlog struct {
	Key       int    `json:"key"`
	Title     string `json:"title"`
	Summary   string `json:"summary"`
	Url       string `json:"url"`
	CreatedOn string `json:"createdOn"`
	UpdatedOn string `json:"updatedOn"`
	PostedOn  string `json:"postedOn"`
	MongoId   string `json:"_id"`
}

func (b LegacyBlog) String() string {
	str := ""
	str += fmt.Sprintf("%d\r\n", b.Key)
	str += b.Title + "\r\n"
	str += b.Url + "\r\n"
	return str
}

func ImportOne(fileName string) error {
	log.Printf("Importing %s", fileName)
	raw, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Printf("ERROR: %s", err)
		return err
	}

	var legacy LegacyBlog
	err = json.Unmarshal(raw, &legacy)
	if err != nil {
		log.Printf("ERROR: %s", err)
		return err
	}

	log.Printf("\timporting metadata\r\n%s", legacy)
	blog := Blog{}
	blog.Id = int64(legacy.Key)
	blog.Title = legacy.Title
	blog.Summary = importSummary(legacy.Summary)
	blog.CreatedOn = fromZDate(legacy.CreatedOn)
	blog.UpdatedOn = fromZDate(legacy.UpdatedOn)
	blog.PostedOn = fromZDate(legacy.PostedOn)

	log.Printf("\timporting content")
	contentFile := strings.Replace(fileName, ".json", ".html", 1)
	bytes, err := ioutil.ReadFile(contentFile)
	if err != nil {
		log.Printf("ERROR: %s", err)
		return err
	}
	blog.Content = string(bytes)

	err = blog.Import()
	if err != nil {
		log.Printf("ERROR: %s", err)
	}
	return err
}

func importSummary(text string) string {
	if len(text) <= 255 {
		return text
	}
	log.Printf("INFO: trimmed summary %s", text)
	return text[0:250] + "..."
}

// Converts a date in Z format (yyyy-MM-ddTHH:mm:ss.xxxZ)
// to a date that we can save in MySQL. Removes the "T"
// separator, drops the milliseconds and the "Z" marker.
func fromZDate(zdate string) string {
	date := zdate[0:10] + " " + zdate[11:19]
	return date
}
<commit_msg>Tweaked the logging<commit_after>package models

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
)

type LegacyBlog struct {
	Key       int    `json:"key"`
	Title     string `json:"title"`
	Summary   string `json:"summary"`
	Url       string `json:"url"`
	CreatedOn string `json:"createdOn"`
	UpdatedOn string `json:"updatedOn"`
	PostedOn  string `json:"postedOn"`
	MongoId   string `json:"_id"`
}

func (b LegacyBlog) String() string {
	return fmt.Sprintf("%d, %s", b.Key, b.Title)
}

func ImportOne(fileName string) error {
	log.Printf("Importing %s", fileName)
	raw, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Printf("ERROR: %s", err)
		return err
	}

	var legacy LegacyBlog
	err = json.Unmarshal(raw, &legacy)
	if err != nil {
		log.Printf("ERROR: %s", err)
		return err
	}

	log.Printf("\timporting metadata for: %s", legacy)
	blog := Blog{}
	blog.Id = int64(legacy.Key)
	blog.Title = legacy.Title
	blog.Summary = importSummary(legacy.Summary)
	blog.CreatedOn = fromZDate(legacy.CreatedOn)
	blog.UpdatedOn = fromZDate(legacy.UpdatedOn)
	blog.PostedOn = fromZDate(legacy.PostedOn)

	log.Printf("\timporting content")
	contentFile := strings.Replace(fileName, ".json", ".html", 1)
	bytes, err := ioutil.ReadFile(contentFile)
	if err != nil {
		log.Printf("ERROR: %s", err)
		return err
	}
	blog.Content = string(bytes)

	err = blog.Import()
	if err != nil {
		log.Printf("ERROR: %s", err)
	}
	return err
}

func importSummary(text string) string {
	if len(text) <= 255 {
		return text
	}
	log.Printf("INFO: trimmed summary %s", text)
	return text[0:250] + "..."
}

// Converts a date in Z format (yyyy-MM-ddTHH:mm:ss.xxxZ)
// to a date that we can save in MySQL. Removes the "T"
// separator, drops the milliseconds and the "Z" marker.
func fromZDate(zdate string) string {
	date := zdate[0:10] + " " + zdate[11:19]
	return date
}
<|endoftext|>


<commit_before>package models

import (
	"evedata/null"
	"fmt"
)

type IskPerLP struct {
	ItemName     string      `db:"itemName" json:"itemName"`
	TypeName     string      `db:"typeName" json:"typeName"`
	JitaPrice    float64     `db:"JitaPrice" json:"jitaPrice"`
	ItemCost     float64     `db:"itemCost" json:"itemCost"`
	IskPerLP     int64       `db:"iskPerLP" json:"iskPerLP"`
	JitaVolume   int64       `db:"JitaVolume" json:"jitaVolume"`
	Requirements null.String `db:"requirements" json:"requirements"`
}

func GetISKPerLP(corporationName string) ([]IskPerLP, error) {
	s := []IskPerLP{}
	if err := database.Select(&s, `
		SELECT itemName, Lp.typeName, JitaPrice, itemCost, iskPerLP, JitaVolume, GROUP_CONCAT(quantity, " x ", T.typeName SEPARATOR '
\n') AS requirements
			FROM iskPerLp Lp
			LEFT JOIN lpOfferRequirements R ON Lp.offerID = R.offerID
			LEFT JOIN invTypes T ON R.typeID = T.typeID
			WHERE itemName = ?
			GROUP BY Lp.typeName
			ORDER BY ISKperLP DESC;
	;`, corporationName); err != nil {

		return nil, err
	}
	return s, nil
}

type IskPerLPCorporation struct {
	ItemName string `db:"itemName" json:"itemName" `
}

func GetISKPerLPCorporations() ([]IskPerLPCorporation, error) {
	s := []IskPerLPCorporation{}
	if err := database.Select(&s, `SELECT DISTINCT itemName FROM iskPerLp ORDER BY itemName ASC;`); err != nil {
		return nil, err
	}
	return s, nil
}

type ArbitrageCalculatorStations struct {
	StationName string `db:"stationName" json:"stationName" `
	StationID   string `db:"stationID" json:"stationID" `
}

func GetArbitrageCalculatorStations() ([]ArbitrageCalculatorStations, error) {
	s := []ArbitrageCalculatorStations{}
	if err := database.Select(&s, `
		SELECT stationID, stationName
			FROM    marketStations
			WHERE 	Count > 4000
			ORDER BY stationName
	`); err != nil {
		return nil, err
	}
	return s, nil
}

type ArbitrageCalculator struct {
	TypeID   int64  `db:"typeID" json:"typeID" `
	TypeName string `db:"typeName" json:"typeName" `
	Volume   int64  `db:"volume" json:"volume" `
	Buys     int64  `db:"buys" json:"buys" `
	Margin   string `db:"margin" json:"margin" `
}

func GetArbitrageCalculator(hours int64, stationID int64, minVolume int64, maxPrice int64, brokersFee float64, tax float64) ([]ArbitrageCalculator, error) {
	type buys struct {
		TypeID   int64   `db:"typeID"`
		TypeName string  `db:"typeName"`
		Buys     int64   `db:"buys"`
		Volume   int64   `db:"volume"`
		Price    float64 `db:"price"`
	}
	type sells struct {
		TypeID int64   `db:"typeID"`
		Price  float64 `db:"price"`
	}

	b := []buys{}
	if err := database.Select(&b, `
		SELECT  market.typeID AS typeID, typeName, count(*) as buys, ROUND(market_vol.quantity / 2) as volume, ROUND(max(price) + (max(price) * ?),2) AS price
		FROM    market, invTypes, market_vol
		WHERE   market.done = 0 AND
		        market.typeID = market_vol.itemID AND
		        market.regionID = market_vol.regionID AND
		        market.typeID = invTypes.typeID AND
		        reported >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL ? DAY_HOUR) AND
		        market.stationID = ?
		        AND bid = 1
		        AND done = 0 AND
		        market_vol.quantity /2 > ?
		GROUP BY market.typeID
		HAVING price < ?`, brokersFee, hours, stationID, minVolume, maxPrice); err != nil {
		return nil, err
	}

	sellOrders := make(map[int64]sells)
	s := []sells{}
	if err := database.Select(&s, `
		SELECT  typeID, ROUND(min(price) - (min(price) * ?) - (min(price) * ?),2) AS price
		FROM    market
		WHERE   reported >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL ? DAY_HOUR) AND
		        market.stationID = ?
		        AND bid = 0
		        GROUP BY typeID`, brokersFee, tax, hours, stationID); err != nil {
		return nil, err
	}

	// Add broker fee to all orders.
	for _, order := range s {
		sellOrders[order.TypeID] = order
	}

	margins := []ArbitrageCalculator{}

	for _, buyOrder := range b {
		sellOrder, ok := sellOrders[buyOrder.TypeID]

		if !ok {
			continue
		}

		if (sellOrder.Price - buyOrder.Price) > 500000 {
			newOrder := ArbitrageCalculator{}

			newOrder.Buys = buyOrder.Buys
			newOrder.Volume = buyOrder.Volume
			newOrder.TypeID = buyOrder.TypeID
			newOrder.TypeName = buyOrder.TypeName
			newOrder.Margin = fmt.Sprintf("%.2f", sellOrder.Price-buyOrder.Price)

			margins = append(margins, newOrder)
		}
	}

	return margins, nil
}
<commit_msg>Optimize abritrage calculator.<commit_after>package models

import (
	"evedata/null"
	"fmt"
)

type IskPerLP struct {
	ItemName     string      `db:"itemName" json:"itemName"`
	TypeName     string      `db:"typeName" json:"typeName"`
	JitaPrice    float64     `db:"JitaPrice" json:"jitaPrice"`
	ItemCost     float64     `db:"itemCost" json:"itemCost"`
	IskPerLP     int64       `db:"iskPerLP" json:"iskPerLP"`
	JitaVolume   int64       `db:"JitaVolume" json:"jitaVolume"`
	Requirements null.String `db:"requirements" json:"requirements"`
}

func GetISKPerLP(corporationName string) ([]IskPerLP, error) {
	s := []IskPerLP{}
	if err := database.Select(&s, `
		SELECT itemName, Lp.typeName, JitaPrice, itemCost, iskPerLP, JitaVolume, GROUP_CONCAT(quantity, " x ", T.typeName SEPARATOR '
\n') AS requirements
			FROM iskPerLp Lp
			LEFT JOIN lpOfferRequirements R ON Lp.offerID = R.offerID
			LEFT JOIN invTypes T ON R.typeID = T.typeID
			WHERE itemName = ?
			GROUP BY Lp.typeName
			ORDER BY ISKperLP DESC;
	;`, corporationName); err != nil {

		return nil, err
	}
	return s, nil
}

type IskPerLPCorporation struct {
	ItemName string `db:"itemName" json:"itemName" `
}

func GetISKPerLPCorporations() ([]IskPerLPCorporation, error) {
	s := []IskPerLPCorporation{}
	if err := database.Select(&s, `SELECT DISTINCT itemName FROM iskPerLp ORDER BY itemName ASC;`); err != nil {
		return nil, err
	}
	return s, nil
}

type ArbitrageCalculatorStations struct {
	StationName string `db:"stationName" json:"stationName" `
	StationID   string `db:"stationID" json:"stationID" `
}

func GetArbitrageCalculatorStations() ([]ArbitrageCalculatorStations, error) {
	s := []ArbitrageCalculatorStations{}
	if err := database.Select(&s, `
		SELECT stationID, stationName
			FROM    marketStations
			WHERE 	Count > 4000
			ORDER BY stationName
	`); err != nil {
		return nil, err
	}
	return s, nil
}

type ArbitrageCalculator struct {
	TypeID   int64  `db:"typeID" json:"typeID" `
	TypeName string `db:"typeName" json:"typeName" `
	Volume   int64  `db:"volume" json:"volume" `
	Buys     int64  `db:"buys" json:"buys" `
	Margin   string `db:"margin" json:"margin" `
}

func GetArbitrageCalculator(hours int64, stationID int64, minVolume int64, maxPrice int64, brokersFee float64, tax float64) ([]ArbitrageCalculator, error) {
	type buys struct {
		TypeID   int64   `db:"typeID"`
		TypeName string  `db:"typeName"`
		Buys     int64   `db:"buys"`
		Volume   int64   `db:"volume"`
		Price    float64 `db:"price"`
	}
	type sells struct {
		TypeID int64   `db:"typeID"`
		Price  float64 `db:"price"`
	}

	errc := make(chan error)

	b := []buys{}

	go func() {
		err := database.Select(&b, `
		SELECT  market.typeID AS typeID, typeName, count(*) as buys, ROUND(market_vol.quantity / 2) as volume, ROUND(max(price) + (max(price) * ?),2) AS price
		FROM    market, invTypes, market_vol
		WHERE   market.done = 0 AND
		        market.typeID = market_vol.itemID AND
		        market.regionID = market_vol.regionID AND
		        market.typeID = invTypes.typeID AND
		        reported >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL ? DAY_HOUR) AND
		        market.stationID = ?
		        AND bid = 1
		        AND done = 0 AND
		        market_vol.quantity /2 > ?
		GROUP BY market.typeID
		HAVING price < ?`, brokersFee, hours, stationID, minVolume, maxPrice)

		errc <- err
	}()

	sellOrders := make(map[int64]sells)
	s := []sells{}
	go func() {
		err := database.Select(&s, `
		SELECT  typeID, ROUND(min(price) - (min(price) * ?) - (min(price) * ?),2) AS price
		FROM    market
		WHERE   reported >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL ? DAY_HOUR) AND
		        market.stationID = ?
		        AND bid = 0
		        GROUP BY typeID`, brokersFee, tax, hours, stationID)

		// Add broker fee to all orders.
		for _, order := range s {
			sellOrders[order.TypeID] = order
		}
		errc <- err
	}()

	margins := []ArbitrageCalculator{}

	err1, err2 := <-errc, <-errc

	if err1 != nil {
		return nil, err1
	}
	if err2 != nil {
		return nil, err2
	}

	for _, buyOrder := range b {
		sellOrder, ok := sellOrders[buyOrder.TypeID]

		if !ok {
			continue
		}

		if (sellOrder.Price - buyOrder.Price) > 500000 {
			newOrder := ArbitrageCalculator{}

			newOrder.Buys = buyOrder.Buys
			newOrder.Volume = buyOrder.Volume
			newOrder.TypeID = buyOrder.TypeID
			newOrder.TypeName = buyOrder.TypeName
			newOrder.Margin = fmt.Sprintf("%.2f", sellOrder.Price-buyOrder.Price)

			margins = append(margins, newOrder)
		}
	}

	return margins, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strconv"
	"strings"
	"time"

	derpiSearch "github.com/PonyvilleFM/aura/cmd/aerial/derpi"
	"github.com/PonyvilleFM/aura/internal/pvfm"
	"github.com/PonyvilleFM/aura/internal/pvfm/pvl"
	pvfmschedule "github.com/PonyvilleFM/aura/internal/pvfm/schedule"
	"github.com/PonyvilleFM/aura/internal/pvfm/station"
	"github.com/bwmarrin/discordgo"
	"github.com/tebeka/strftime"
)

func init() {
	rand.Seed(time.Now().Unix())
}

// randomRange gives a random whole integer between the given integers [min, max)
func randomRange(min, max int) int {
	return rand.Intn(max-min) + min
}

func pesterLink(s *discordgo.Session, m *discordgo.MessageCreate) {
	if musicLinkRegex.Match([]byte(m.Content)) {
		i, err := pvfm.GetStats()
		if err != nil {
			log.Println(err)
			return
		}

		if i.IsDJLive() && m.ChannelID == youtubeSpamRoomID {
			s.ChannelMessageSend(m.ChannelID, "Please be mindful sharing links to music when a DJ is performing. Thanks!")
		}
	}
}

func stats(s *discordgo.Session, m *discordgo.Message, parv []string) error {

	// Regular metadata info

	i, err := pvfm.GetStats()
	if err != nil {
		log.Printf("Error getting the station info: %v, falling back to plan b", err)
		return doStatsFromStation(s, m, parv)
	}

	st, err := station.GetStats()
	if err != nil {
		return err
	}

	var l int
	var peak int

	for _, source := range st.Icestats.Source {
		l = l + source.Listeners
		peak = peak + source.ListenerPeak
	}

	// Live DJ info

	// init variables
	cal, err := pvl.Get()
	if err != nil {
		return err
	}
	now := cal.Result[0]

	// times
	localTime := time.Now()
	thentime := time.Unix(now.StartTime, 0)

	// checks if the event is currently happening
	djInfo := "" // since we start with a conditional...
	if thentime.Unix() < localTime.Unix() {
		djInfo += fmt.Sprintf("**Currently live!**\n%s\n\n", now.Title)
		now = cal.Result[1]
	}

	// Prepare time string
	nowTime := time.Unix(now.StartTime, 0).UTC()
	zone, _ := nowTime.Zone()
	fmttime, _ := strftime.Format("%Y-%m-%d %H:%M:%S", nowTime)

	// Piece data together into the result
	djInfo += fmt.Sprintf("Next event:\n%s\n%s \x02%s\x02",
		now.Title,
		fmttime,
		zone,
	)

	outputEmbed := NewEmbed().
		SetTitle("Listener Statistics").
		SetDescription("Use `;streams` if you need a link to the radio!\nTotal listeners across all stations: " + strconv.Itoa(i.Listeners.Listeners) + " with a maximum  of " + strconv.Itoa(peak) + ".")

	outputEmbed.AddField("🎵 Main", strconv.Itoa(i.Main.Listeners)+" listeners.\n"+i.Main.Nowplaying)
	outputEmbed.AddField("🎵 Chill", strconv.Itoa(i.Secondary.Listeners)+" listeners.\n"+i.Secondary.Nowplaying)
	outputEmbed.AddField("🎵 Free! (no DJ sets)", strconv.Itoa(i.MusicOnly.Listeners)+" listeners.\n"+i.MusicOnly.Nowplaying)
	outputEmbed.AddField("🎛 Live DJs", djInfo)

	outputEmbed.InlineAllFields()

	s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)

	return nil
}

func schedule(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	schEntries, err := pvfmschedule.Get()
	if err != nil {
		return err
	}

	// Create embed object
	outputEmbed := NewEmbed().
		SetTitle("Upcoming Shows").
		SetDescription("These are the upcoming shows and events airing soon on PVFM 1.\n[Convert to your timezone](https://www.worldtimebuddy.com/?pl=1&lid=100&h=100)")

	for _, entry := range schEntries {

		// Format countdown timer
		startTimeUnix := time.Unix(int64(entry.StartUnix), 0)
		nowWithoutNanoseconds := time.Unix(time.Now().Unix(), 0)
		dur := startTimeUnix.Sub(nowWithoutNanoseconds)

		// Show "Live Now!" if the timer is less than 0h0m0s
		if dur > 0 {
			outputEmbed.AddField(":musical_note:  "+entry.Host+" - "+entry.Name, entry.StartTime+" "+entry.Timezone+"\nAirs in "+dur.String())
		} else {
			outputEmbed.AddField(":musical_note:  "+entry.Host+" - "+entry.Name, "Live now!")
		}
	}

	s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)
	return nil
}

func doStationRequest(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	stats, err := station.GetStats()
	if err != nil {
		return err
	}

	result := fmt.Sprintf(
		"Now playing: %s - %s on Ponyville FM!",
		stats.Icestats.Source[0].Title,
		stats.Icestats.Source[0].Artist,
	)

	s.ChannelMessageSend(m.ChannelID, result)
	return nil
}

func doStatsFromStation(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	st, err := station.GetStats()
	if err != nil {
		return err
	}

	var l int
	var peak int

	for _, source := range st.Icestats.Source {
		l = l + source.Listeners
		peak = peak + source.ListenerPeak
	}

	result := []string{
		fmt.Sprintf("Current listeners: %d with a maximum of %d!", l, peak),
	}

	s.ChannelMessageSend(m.ChannelID, strings.Join(result, "\n"))
	return nil
}

func curTime(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("The time currently is %s\nUse <https://www.worldtimebuddy.com/?pl=1&lid=100&h=100> to convert UTC to your local timezone.", time.Now().UTC().Format("2006-01-02 15:04:05 UTC")))

	return nil
}

func streams(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	currentMeta, metaErr := station.GetStats()
	if metaErr != nil {
		s.ChannelMessageSend(m.ChannelID, "Error receiving pvfm metadata")
		return metaErr
	}

	// start building custom embed
	outputEmbed := NewEmbed().
		SetTitle("Stream Links").
		SetDescription("These are direct feeds of the live streams; most browsers and media players can play them!")

	// this will dynamically build the list from station metadata
	pvfmList := ""
	for _, element := range currentMeta.Icestats.Source {
		pvfmList += element.ServerDescription + ":\n<" + strings.Replace(element.Listenurl, "aerial", "dj.bronyradio.com", -1) + ">\n"
	}

	// PVFM
	outputEmbed.AddField(":musical_note:  PVFM Servers", pvfmList)
	// Luna Radio
	outputEmbed.AddField(":musical_note:  Luna Radio Servers", "Luna Radio MP3 128Kbps Stream:\n<http://radio.ponyvillelive.com:8002/stream.mp3>\nLuna Radio Mobile MP3 64Kbps Stream:\n<http://radio.ponyvillelive.com:8002/mobile?;stream.mp3>\n")
	// Recordings
	outputEmbed.AddField(":cd:  DJ Recordings", "Archive\n<https://pvfmsets.cf/var/93252527679639552/>\nLegacy Archive\n<http://darkling.darkwizards.com/wang/BronyRadio/?M=D>")

	s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)

	// no errors yay!!!!
	return nil
}

func derpi(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	if m.ChannelID == "292755043684450304" {

		searchResults, err := derpiSearch.SearchDerpi(m.Content[7:len(m.Content)]) // Safe tag will be added in derpi/derpi.go
		if err != nil {
			s.ChannelMessageSend(m.ChannelID, "An error occured.")
			return err
		}
		if len(searchResults.Search) < 1 {
			s.ChannelMessageSend(m.ChannelID, "Error: No results")
			return nil
		}
		derpiImage := searchResults.Search[randomRange(0, len(searchResults.Search))]

		tags := strings.Split(derpiImage.Tags, ", ") // because this isn't an array for some reason

		// Check for artist tag
		artist := ""
		for _, tag := range tags {
			if strings.Contains(tag, "artist:") {
				artist = tag[7:]
			}
		}

		outputEmbed := NewEmbed().
			SetTitle("Derpibooru Image").
			SetURL("https://derpibooru.org/" + derpiImage.ID).
			SetDescription(derpiImage.Description).
			SetImage("http:" + derpiImage.Image).
			SetFooter("Image score: " + strconv.Itoa(derpiImage.Score) + " | Uploaded: " + derpiImage.CreatedAt.String())

		// Credit the artist!
		if artist == "" {
			outputEmbed.SetAuthor("No artist")
		} else {
			outputEmbed.SetAuthor("Artist: " + artist)
		}

		s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)
	} else {
		s.ChannelMessageSend(m.ChannelID, "Please use this command in <#292755043684450304> only.")
	}
	return nil
}

func weather(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	responses := []string{
		"Cloudy with a chance of meatballs.",
		"It's currently pouring down even more than Pinkie.",
		"It's the most overcast I've ever seen. In other words, same as always.",
		"Do you have a better conversation starter than that?",
		"There's at least 5 or 6 weather right now, my dude.",
		"It's soggy enough for Rainbow Dash to get fired, if she didn't have a literal deity keeping her in charge.",
		"Surprisingly, the weather is pretty alright.",
		"You'd be happy to know that it's hot enough to make a phoenix sweat.",
		"The weather right now is like you took London and stuck it in a dishwasher.",
		"The Crystal Empire is warmer than this weather.",
	}

	s.ChannelMessageSend(m.ChannelID, responses[randomRange(0, len(responses))])

	return nil
}
<commit_msg>cmd/aerial: remove pvl api call here<commit_after>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strconv"
	"strings"
	"time"

	derpiSearch "github.com/PonyvilleFM/aura/cmd/aerial/derpi"
	"github.com/PonyvilleFM/aura/internal/pvfm"
	pvfmschedule "github.com/PonyvilleFM/aura/internal/pvfm/schedule"
	"github.com/PonyvilleFM/aura/internal/pvfm/station"
	"github.com/bwmarrin/discordgo"
)

func init() {
	rand.Seed(time.Now().Unix())
}

// randomRange gives a random whole integer between the given integers [min, max)
func randomRange(min, max int) int {
	return rand.Intn(max-min) + min
}

func pesterLink(s *discordgo.Session, m *discordgo.MessageCreate) {
	if musicLinkRegex.Match([]byte(m.Content)) {
		i, err := pvfm.GetStats()
		if err != nil {
			log.Println(err)
			return
		}

		if i.IsDJLive() && m.ChannelID == youtubeSpamRoomID {
			s.ChannelMessageSend(m.ChannelID, "Please be mindful sharing links to music when a DJ is performing. Thanks!")
		}
	}
}

func stats(s *discordgo.Session, m *discordgo.Message, parv []string) error {

	// Regular metadata info

	i, err := pvfm.GetStats()
	if err != nil {
		log.Printf("Error getting the station info: %v, falling back to plan b", err)
		return doStatsFromStation(s, m, parv)
	}

	st, err := station.GetStats()
	if err != nil {
		return err
	}

	var l int
	var peak int

	for _, source := range st.Icestats.Source {
		l = l + source.Listeners
		peak = peak + source.ListenerPeak
	}

	// checks if the event is currently happening
	outputEmbed := NewEmbed().
		SetTitle("Listener Statistics").
		SetDescription("Use `;streams` if you need a link to the radio!\nTotal listeners across all stations: " + strconv.Itoa(i.Listeners.Listeners) + " with a maximum  of " + strconv.Itoa(peak) + ".")

	outputEmbed.AddField("🎵 Main", strconv.Itoa(i.Main.Listeners)+" listeners.\n"+i.Main.Nowplaying)
	outputEmbed.AddField("🎵 Chill", strconv.Itoa(i.Secondary.Listeners)+" listeners.\n"+i.Secondary.Nowplaying)
	outputEmbed.AddField("🎵 Free! (no DJ sets)", strconv.Itoa(i.MusicOnly.Listeners)+" listeners.\n"+i.MusicOnly.Nowplaying)

	outputEmbed.InlineAllFields()

	s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)

	return nil
}

func schedule(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	schEntries, err := pvfmschedule.Get()
	if err != nil {
		return err
	}

	// Create embed object
	outputEmbed := NewEmbed().
		SetTitle("Upcoming Shows").
		SetDescription("These are the upcoming shows and events airing soon on PVFM 1.\n[Convert to your timezone](https://www.worldtimebuddy.com/?pl=1&lid=100&h=100)")

	for _, entry := range schEntries {

		// Format countdown timer
		startTimeUnix := time.Unix(int64(entry.StartUnix), 0)
		nowWithoutNanoseconds := time.Unix(time.Now().Unix(), 0)
		dur := startTimeUnix.Sub(nowWithoutNanoseconds)

		// Show "Live Now!" if the timer is less than 0h0m0s
		if dur > 0 {
			outputEmbed.AddField(":musical_note:  "+entry.Host+" - "+entry.Name, entry.StartTime+" "+entry.Timezone+"\nAirs in "+dur.String())
		} else {
			outputEmbed.AddField(":musical_note:  "+entry.Host+" - "+entry.Name, "Live now!")
		}
	}

	s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)
	return nil
}

func doStationRequest(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	stats, err := station.GetStats()
	if err != nil {
		return err
	}

	result := fmt.Sprintf(
		"Now playing: %s - %s on Ponyville FM!",
		stats.Icestats.Source[0].Title,
		stats.Icestats.Source[0].Artist,
	)

	s.ChannelMessageSend(m.ChannelID, result)
	return nil
}

func doStatsFromStation(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	st, err := station.GetStats()
	if err != nil {
		return err
	}

	var l int
	var peak int

	for _, source := range st.Icestats.Source {
		l = l + source.Listeners
		peak = peak + source.ListenerPeak
	}

	result := []string{
		fmt.Sprintf("Current listeners: %d with a maximum of %d!", l, peak),
	}

	s.ChannelMessageSend(m.ChannelID, strings.Join(result, "\n"))
	return nil
}

func curTime(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("The time currently is %s\nUse <https://www.worldtimebuddy.com/?pl=1&lid=100&h=100> to convert UTC to your local timezone.", time.Now().UTC().Format("2006-01-02 15:04:05 UTC")))

	return nil
}

func streams(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	currentMeta, metaErr := station.GetStats()
	if metaErr != nil {
		s.ChannelMessageSend(m.ChannelID, "Error receiving pvfm metadata")
		return metaErr
	}

	// start building custom embed
	outputEmbed := NewEmbed().
		SetTitle("Stream Links").
		SetDescription("These are direct feeds of the live streams; most browsers and media players can play them!")

	// this will dynamically build the list from station metadata
	pvfmList := ""
	for _, element := range currentMeta.Icestats.Source {
		pvfmList += element.ServerDescription + ":\n<" + strings.Replace(element.Listenurl, "aerial", "dj.bronyradio.com", -1) + ">\n"
	}

	// PVFM
	outputEmbed.AddField(":musical_note:  PVFM Servers", pvfmList)
	// Luna Radio
	outputEmbed.AddField(":musical_note:  Luna Radio Servers", "Luna Radio MP3 128Kbps Stream:\n<http://radio.ponyvillelive.com:8002/stream.mp3>\nLuna Radio Mobile MP3 64Kbps Stream:\n<http://radio.ponyvillelive.com:8002/mobile?;stream.mp3>\n")
	// Recordings
	outputEmbed.AddField(":cd:  DJ Recordings", "Archive\n<https://pvfmsets.cf/var/93252527679639552/>\nLegacy Archive\n<http://darkling.darkwizards.com/wang/BronyRadio/?M=D>")

	s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)

	// no errors yay!!!!
	return nil
}

func derpi(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	if m.ChannelID == "292755043684450304" {

		searchResults, err := derpiSearch.SearchDerpi(m.Content[7:len(m.Content)]) // Safe tag will be added in derpi/derpi.go
		if err != nil {
			s.ChannelMessageSend(m.ChannelID, "An error occured.")
			return err
		}
		if len(searchResults.Search) < 1 {
			s.ChannelMessageSend(m.ChannelID, "Error: No results")
			return nil
		}
		derpiImage := searchResults.Search[randomRange(0, len(searchResults.Search))]

		tags := strings.Split(derpiImage.Tags, ", ") // because this isn't an array for some reason

		// Check for artist tag
		artist := ""
		for _, tag := range tags {
			if strings.Contains(tag, "artist:") {
				artist = tag[7:]
			}
		}

		outputEmbed := NewEmbed().
			SetTitle("Derpibooru Image").
			SetURL("https://derpibooru.org/" + derpiImage.ID).
			SetDescription(derpiImage.Description).
			SetImage("http:" + derpiImage.Image).
			SetFooter("Image score: " + strconv.Itoa(derpiImage.Score) + " | Uploaded: " + derpiImage.CreatedAt.String())

		// Credit the artist!
		if artist == "" {
			outputEmbed.SetAuthor("No artist")
		} else {
			outputEmbed.SetAuthor("Artist: " + artist)
		}

		s.ChannelMessageSendEmbed(m.ChannelID, outputEmbed.MessageEmbed)
	} else {
		s.ChannelMessageSend(m.ChannelID, "Please use this command in <#292755043684450304> only.")
	}
	return nil
}

func weather(s *discordgo.Session, m *discordgo.Message, parv []string) error {
	responses := []string{
		"Cloudy with a chance of meatballs.",
		"It's currently pouring down even more than Pinkie.",
		"It's the most overcast I've ever seen. In other words, same as always.",
		"Do you have a better conversation starter than that?",
		"There's at least 5 or 6 weather right now, my dude.",
		"It's soggy enough for Rainbow Dash to get fired, if she didn't have a literal deity keeping her in charge.",
		"Surprisingly, the weather is pretty alright.",
		"You'd be happy to know that it's hot enough to make a phoenix sweat.",
		"The weather right now is like you took London and stuck it in a dishwasher.",
		"The Crystal Empire is warmer than this weather.",
	}

	s.ChannelMessageSend(m.ChannelID, responses[randomRange(0, len(responses))])

	return nil
}
<|endoftext|>


<commit_before>// The amore command for bundling assets and helpers for quick project creation.
package main

import (
	"flag"
	"fmt"
	"os"
)

const (
	amoreVersion = "0.0.5" // command line version
)

var (
	namePackage    = flag.String("pkg", "main", "name of the go package for the source to be generated in")
	nameSourceFile = flag.String("out", "asset_bundle.go", "name of the outputted file for bundling")
)

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) > 0 {
		if args[0] == "bundle" {
			bundle(args[1:]...)
		} else if args[0] == "init" || args[0] == "new" {
			project_name := ""
			if len(args) > 1 {
				project_name = args[1]
			}
			newProject(project_name)
		} else if args[0] == "version" {
			printVersion()
		}
	} else {
		printVersion()
	}
}

// printVersion outputs the command line version
func printVersion() {
	fmt.Printf("Amore version: %v", amoreVersion)
}

// Prints out the error message and exists with a non-success signal.
func exitWithError(err error) {
	fmt.Println(err)
	os.Exit(1)
}
<commit_msg>added a help message to the cli<commit_after>// The amore command for bundling assets and helpers for quick project creation.
package main

import (
	"flag"
	"fmt"
	"os"
)

const (
	amoreVersion = "0.0.5" // command line version
)

var (
	namePackage    = flag.String("pkg", "main", "name of the go package for the source to be generated in")
	nameSourceFile = flag.String("out", "asset_bundle.go", "name of the outputted file for bundling")
)

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) > 0 {
		if args[0] == "bundle" {
			bundle(args[1:]...)
		} else if args[0] == "init" || args[0] == "new" {
			project_name := ""
			if len(args) > 1 {
				project_name = args[1]
			}
			newProject(project_name)
		} else if args[0] == "version" {
			printVersion()
		} else {
			printHelp()
		}
	} else {
		printHelp()
	}
}

// printVersion outputs the command line version
func printVersion() {
	fmt.Printf("Amore version: %v", amoreVersion)
}

func printHelp() {
	fmt.Printf(`
Amore - %v

usage: amore <command> [args ..]

Commands:
  new <project_name>
    Generates all basic files and folders to get started in the current directory

  bundle [paths...]
    Bundles inputed file paths.
    If no paths were given then it will bundle the default assets folder and the conf.toml file.

  version
    Will print the version of this cli.

  help
    Will print this message.

`, amoreVersion)
}

// Prints out the error message and exists with a non-success signal.
func exitWithError(err error) {
	fmt.Println(err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// flags
	fWant4 bool
	fWant6 bool

	fAllProbes       bool
	fAllMeasurements bool

	// Global options
	fFieldList string
	fFormat    string
	fInclude   string
	fLogfile   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Measurement-specific ones
	fAsn         string
	fCountry     string
	fProtocol    string
	fMeasureType string

	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	fBitCD         bool
	fDisableDNSSEC bool

	fDebug      bool
	fVerbose    bool
	fWantAnchor bool

	fMaxHops    int
	fPacketSize int

	cnf *Config

	cliCommands []cli.Command

	client *atlas.Client

	mylog *log.Logger
)

const (
	atlasVersion = "0.22"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err error
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		if fVerbose {
			log.Printf("No configuration file found.")
		}
	}

	// Logical
	if fDebug {
		fVerbose = true
		log.Printf("config: %#v", cnf)
	}

	// Various messages
	if fVerbose {
		if cnf.APIKey != "" {
			log.Printf("Found API key!")
		} else {
			log.Printf("No API key!")
		}

		if cnf.DefaultProbe != 0 {
			log.Printf("Found default probe: %d\n", cnf.DefaultProbe)
		}
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := setupProxyAuth()
	if err != nil {
		if fVerbose {
			log.Printf("Invalid or no proxy auth credentials")
		}
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		fh, err := os.OpenFile(fLogfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			log.Fatalf("error: can not open logfile %s: %v", fLogfile, err)
		}

		mylog = log.New(fh, "", log.LstdFlags)
		if fVerbose {
			log.Printf("Logfile: %s %#v", fLogfile, mylog)
		}
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		PoolSize:     cnf.PoolSize,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWantMine {
		client.SetOption("mine", "true")
	}

	if fWant4 {
		cnf.WantAF = Want4
	}

	if fWant6 {
		cnf.WantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		cnf.WantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		cnf.WantAF = WantBoth
	}

	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},
		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<commit_msg>Refactor: create openlog() and use it.<commit_after>/*
This package is just a collection of use-case for the various aspects of the RIPE API.
Consider this both as an example on how to use the API and a testing tool for the API wrapper.
*/
package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"sort"
)

var (
	// flags
	fWant4 bool
	fWant6 bool

	fAllProbes       bool
	fAllMeasurements bool

	// Global options
	fFieldList string
	fFormat    string
	fInclude   string
	fLogfile   string
	fOptFields string
	fPageNum   string
	fPageSize  string
	fSortOrder string
	fWantMine  bool

	// Measurement-specific ones
	fAsn         string
	fCountry     string
	fProtocol    string
	fMeasureType string

	fHTTPMethod  string
	fUserAgent   string
	fHTTPVersion string

	fBitCD         bool
	fDisableDNSSEC bool

	fDebug      bool
	fVerbose    bool
	fWantAnchor bool

	fMaxHops    int
	fPacketSize int

	cnf *Config

	cliCommands []cli.Command

	client *atlas.Client
)

const (
	atlasVersion = "0.22"
	// MyName is the application name
	MyName = "ripe-atlas"

	// WantBoth is the way to ask for both IPv4 & IPv6.
	WantBoth = "64"

	// Want4 only 4
	Want4 = "4"
	// Want6 only 6
	Want6 = "6"
)

func openlog(fn string) *log.Logger {
	fh, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error: can not open logfile %s: %v", fn, err)
	}

	mylog := log.New(fh, "", log.LstdFlags)
	if fVerbose {
		log.Printf("Logfile: %s %#v", fn, mylog)
	}

	return mylog
}

// -4 & -6 are special, if neither is specified, then we turn both as true
// Check a few other things while we are here
func finalcheck(c *cli.Context) error {

	var (
		err error
		mylog *log.Logger
	)

	// Load main configuration
	cnf, err = LoadConfig("")
	if err != nil {
		if fVerbose {
			log.Printf("No configuration file found.")
		}
	}

	// Logical
	if fDebug {
		fVerbose = true
		log.Printf("config: %#v", cnf)
	}

	// Various messages
	if fVerbose {
		if cnf.APIKey != "" {
			log.Printf("Found API key!")
		} else {
			log.Printf("No API key!")
		}

		if cnf.DefaultProbe != 0 {
			log.Printf("Found default probe: %d\n", cnf.DefaultProbe)
		}
	}

	// Check whether we have proxy authentication (from a separate config file)
	auth, err := setupProxyAuth()
	if err != nil {
		if fVerbose {
			log.Printf("Invalid or no proxy auth credentials")
		}
	}

	// If we want a logfile, open one for the API to log into
	if fLogfile != "" {
		mylog = openlog(fLogfile)
	}

	// Wondering whether to move to the Functional options pattern
	// cf. https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions
	client, err = atlas.NewClient(atlas.Config{
		APIKey:       cnf.APIKey,
		DefaultProbe: cnf.DefaultProbe,
		PoolSize:     cnf.PoolSize,
		ProxyAuth:    auth,
		Verbose:      fVerbose,
		Log:          mylog,
	})

	// No need to continue if this fails
	if err != nil {
		log.Fatalf("Error creating the Atlas client: %v", err)
	}

	if fWantMine {
		client.SetOption("mine", "true")
	}

	if fWant4 {
		cnf.WantAF = Want4
	}

	if fWant6 {
		cnf.WantAF = Want6
	}

	// Both are fine
	if fWant4 && fWant6 {
		cnf.WantAF = WantBoth
	}

	// So is neither — common case
	if !fWant4 && !fWant6 {
		cnf.WantAF = WantBoth
	}

	return nil
}

// main is the starting point (and everything)
func main() {
	cli.VersionFlag = cli.BoolFlag{Name: "version, V"}

	cli.VersionPrinter = func(c *cli.Context) {
		fmt.Printf("API wrapper: %s Atlas API: %s\n", c.App.Version, atlas.GetVersion())
	}

	app := cli.NewApp()
	app.Name = "atlas"
	app.Usage = "RIPE Atlas CLI interface"
	app.Author = "Ollivier Robert <roberto@keltia.net>"
	app.Version = atlasVersion
	//app.HideVersion = true

	// General flags
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "format,f",
			Usage:       "specify output format (NOT IMPLEMENTED)",
			Destination: &fFormat,
		},
		cli.BoolFlag{
			Name:        "debug,D",
			Usage:       "debug mode",
			Destination: &fDebug,
		},
		cli.BoolFlag{
			Name:        "verbose,v",
			Usage:       "verbose mode",
			Destination: &fVerbose,
		},
		cli.StringFlag{
			Name:        "fields,F",
			Usage:       "specify which fields are wanted",
			Destination: &fFieldList,
		},
		cli.StringFlag{
			Name:        "include,I",
			Usage:       "specify whether objects should be expanded",
			Destination: &fInclude,
		},
		cli.StringFlag{
			Name:        "logfile,L",
			Usage:       "specify a log file",
			Destination: &fLogfile,
		},
		cli.BoolFlag{
			Name:        "mine,M",
			Usage:       "limit output to my objects",
			Destination: &fWantMine,
		},
		cli.StringFlag{
			Name:        "opt-fields,O",
			Usage:       "specify which optional fields are wanted",
			Destination: &fOptFields,
		},
		cli.StringFlag{
			Name:        "page-size,P",
			Usage:       "page size for results",
			Destination: &fPageSize,
		},
		cli.StringFlag{
			Name:        "sort,S",
			Usage:       "sort results",
			Destination: &fSortOrder,
		},
		cli.BoolFlag{
			Name:        "6, ipv6",
			Usage:       "Only IPv6",
			Destination: &fWant6,
		},
		cli.BoolFlag{
			Name:        "4, ipv4",
			Usage:       "Only IPv4",
			Destination: &fWant4,
		},
	}

	// Ensure -4 & -6 are treated properly & initialization is done
	app.Before = finalcheck

	sort.Sort(ByAlphabet(cliCommands))
	app.Commands = cliCommands
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/urfave/cli"
	"log"
	"net/url"
	"strconv"
	"strings"
)

// ByAlphabet is for sorting
type ByAlphabet []cli.Command

func (a ByAlphabet) Len() int           { return len(a) }
func (a ByAlphabet) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAlphabet) Less(i, j int) bool { return a[i].Name < a[j].Name }

// checkGlobalFlags is the place to check global parameters
func checkGlobalFlags(o map[string]string) map[string]string {
	opts := o
	if fSortOrder != "" {
		opts["sort"] = fSortOrder
	}

	if fFieldList != "" {
		opts["fields"] = fFieldList
	}

	if fOptFields != "" {
		opts["optional_fields"] = fOptFields
	}

	if fFormat != "" && validateFormat(fFormat) {
		opts["format"] = fFormat
	}
	return opts
}

// validateFormat allows only supported formats
func validateFormat(fmt string) bool {
	f := strings.ToLower(fmt)
	if f == "json" || f == "xml" || f == "api" || f == "txt" || f == "jsonp" {
		return true
	}
	return false
}

func displayOptions(opts map[string]string) {
	log.Println("Options:")
	for key, val := range opts {
		log.Printf("  %s: %s", key, val)
	}
}

// analyzeTarget breaks up an url into its components
func analyzeTarget(target string) (proto, site, path string, port int) {
	uri, err := url.Parse(target)
	if err != nil {
		proto = ""
		site = ""
		path = ""
		port = 0
	} else {
		proto = uri.Scheme
		if proto == "https" {
			port = 443
		}

		// might be host:port
		sp := strings.Split(uri.Host, ":")
		if len(sp) == 2 {
			port, _ = strconv.Atoi(sp[1])
			site = sp[0]
		} else {
			site = uri.Host
		}

		path = uri.Path
		// Path can't be null
		if path == "" {
			path = "/"
		}
	}
	return
}
<commit_msg>Returns a copy to avoid side-effects.<commit_after>package main

import (
	"github.com/urfave/cli"
	"log"
	"net/url"
	"strconv"
	"strings"
)

// ByAlphabet is for sorting
type ByAlphabet []cli.Command

func (a ByAlphabet) Len() int           { return len(a) }
func (a ByAlphabet) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAlphabet) Less(i, j int) bool { return a[i].Name < a[j].Name }

// checkGlobalFlags is the place to check global parameters
func checkGlobalFlags(o map[string]string) (opts map[string]string) {
	opts = o
	if fSortOrder != "" {
		opts["sort"] = fSortOrder
	}

	if fFieldList != "" {
		opts["fields"] = fFieldList
	}

	if fOptFields != "" {
		opts["optional_fields"] = fOptFields
	}

	if fFormat != "" && validateFormat(fFormat) {
		opts["format"] = fFormat
	}
	return opts
}

// validateFormat allows only supported formats
func validateFormat(fmt string) bool {
	f := strings.ToLower(fmt)
	if f == "json" || f == "xml" || f == "api" || f == "txt" || f == "jsonp" {
		return true
	}
	return false
}

func displayOptions(opts map[string]string) {
	log.Println("Options:")
	for key, val := range opts {
		log.Printf("  %s: %s", key, val)
	}
}

// analyzeTarget breaks up an url into its components
func analyzeTarget(target string) (proto, site, path string, port int) {
	uri, err := url.Parse(target)
	if err != nil {
		proto = ""
		site = ""
		path = ""
		port = 0
	} else {
		proto = uri.Scheme
		if proto == "https" {
			port = 443
		}

		// might be host:port
		sp := strings.Split(uri.Host, ":")
		if len(sp) == 2 {
			port, _ = strconv.Atoi(sp[1])
			site = sp[0]
		} else {
			site = uri.Host
		}

		path = uri.Path
		// Path can't be null
		if path == "" {
			path = "/"
		}
	}
	return
}
<|endoftext|>