<commit_before>package plan

import (
	"fmt"
	"testing"

	stripe "github.com/stripe/stripe-go"
	"github.com/stripe/stripe-go/currency"
	. "github.com/stripe/stripe-go/utils"
)

func init() {
	stripe.Key = GetTestKey()
}

func TestPlanNew(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:            "test_plan",
		Name:          "Test Plan",
		Amount:        99,
		Currency:      currency.USD,
		Interval:      Month,
		IntervalCount: 3,
		TrialPeriod:   30,
		Statement:     "Test Plan",
	}

	target, err := New(planParams)

	if err != nil {
		t.Error(err)
	}

	if target.ID != planParams.ID {
		t.Errorf("ID %q does not match expected id %q\n", target.ID, planParams.ID)
	}

	if target.Name != planParams.Name {
		t.Errorf("Name %q does not match expected name %q\n", target.Name, planParams.Name)
	}

	if target.Amount != planParams.Amount {
		t.Errorf("Amount %v does not match expected amount %v\n", target.Amount, planParams.Amount)
	}

	if target.Currency != planParams.Currency {
		t.Errorf("Currency %q does not match expected currency %q\n", target.Currency, planParams.Currency)
	}

	if target.Interval != planParams.Interval {
		t.Errorf("Interval %q does not match expected interval %q\n", target.Interval, planParams.Interval)
	}

	if target.IntervalCount != planParams.IntervalCount {
		t.Errorf("Interval count %v does not match expected interval count %v\n", target.IntervalCount, planParams.IntervalCount)
	}

	if target.TrialPeriod != planParams.TrialPeriod {
		t.Errorf("Trial period %v does not match expected trial period %v\n", target.TrialPeriod, planParams.TrialPeriod)
	}

	if target.Statement != planParams.Statement {
		t.Errorf("Statement %q does not match expected statement %q\n", target.Statement, planParams.Statement)
	}

	Del(planParams.ID)
}

func TestPlanGet(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:       "test_plan",
		Name:     "Test Plan",
		Amount:   99,
		Currency: currency.USD,
		Interval: Month,
	}

	New(planParams)
	target, err := Get(planParams.ID, nil)

	if err != nil {
		t.Error(err)
	}

	if target.ID != planParams.ID {
		t.Errorf("Plan id %q does not match expected id %q\n", target.ID, planParams.ID)
	}

	Del(planParams.ID)
}

func TestPlanUpdate(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:            "test_plan",
		Name:          "Original Name",
		Amount:        99,
		Currency:      currency.USD,
		Interval:      Month,
		IntervalCount: 3,
		TrialPeriod:   30,
		Statement:     "Original Plan",
	}

	New(planParams)

	updatedPlan := &stripe.PlanParams{
		Name:      "Updated Name",
		Statement: "Updated Plan",
	}

	target, err := Update(planParams.ID, updatedPlan)

	if err != nil {
		t.Error(err)
	}

	if target.Name != updatedPlan.Name {
		t.Errorf("Name %q does not match expected name %q\n", target.Name, updatedPlan.Name)
	}

	if target.Statement != updatedPlan.Statement {
		t.Errorf("Statement %q does not match expected statement %q\n", target.Statement, updatedPlan.Statement)
	}

	Del(planParams.ID)
}

func TestPlanDel(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:       "test_plan",
		Name:     "Test Plan",
		Amount:   99,
		Currency: currency.USD,
		Interval: Month,
	}

	New(planParams)

	planDel, err := Del(planParams.ID)
	if err != nil {
		t.Error(err)
	}

	if !planDel.Deleted {
		t.Errorf("Plan id %q expected to be marked as deleted on the returned resource\n", planDel.ID)
	}
}

func TestPlanList(t *testing.T) {
	const runs = 3
	for i := 0; i < runs; i++ {
		planParams := &stripe.PlanParams{
			ID:       fmt.Sprintf("test_%v", i),
			Name:     fmt.Sprintf("test_%v", i),
			Amount:   99,
			Currency: currency.USD,
			Interval: Month,
		}

		New(planParams)
	}

	params := &stripe.PlanListParams{}
	params.Filters.AddFilter("limit", "", "1")

	plansChecked := 0
	i := List(params)
	for i.Next() && plansChecked < runs {
		target := i.Plan()

		if i.Meta() == nil {
			t.Error("No metadata returned")
		}

		if target.Amount != 99 {
			t.Errorf("Amount %v does not match expected value\n", target.Amount)
		}

		plansChecked += 1
	}
	if err := i.Err(); err != nil {
		t.Error(err)
	}

	for i := 0; i < runs; i++ {
		Del(fmt.Sprintf("test_%v", i))
	}
}
<commit_msg>update plan test to try and update trialperiod<commit_after>package plan

import (
	"fmt"
	"testing"

	stripe "github.com/stripe/stripe-go"
	"github.com/stripe/stripe-go/currency"
	. "github.com/stripe/stripe-go/utils"
)

func init() {
	stripe.Key = GetTestKey()
}

func TestPlanNew(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:            "test_plan",
		Name:          "Test Plan",
		Amount:        99,
		Currency:      currency.USD,
		Interval:      Month,
		IntervalCount: 3,
		TrialPeriod:   30,
		Statement:     "Test Plan",
	}

	target, err := New(planParams)

	if err != nil {
		t.Error(err)
	}

	if target.ID != planParams.ID {
		t.Errorf("ID %q does not match expected id %q\n", target.ID, planParams.ID)
	}

	if target.Name != planParams.Name {
		t.Errorf("Name %q does not match expected name %q\n", target.Name, planParams.Name)
	}

	if target.Amount != planParams.Amount {
		t.Errorf("Amount %v does not match expected amount %v\n", target.Amount, planParams.Amount)
	}

	if target.Currency != planParams.Currency {
		t.Errorf("Currency %q does not match expected currency %q\n", target.Currency, planParams.Currency)
	}

	if target.Interval != planParams.Interval {
		t.Errorf("Interval %q does not match expected interval %q\n", target.Interval, planParams.Interval)
	}

	if target.IntervalCount != planParams.IntervalCount {
		t.Errorf("Interval count %v does not match expected interval count %v\n", target.IntervalCount, planParams.IntervalCount)
	}

	if target.TrialPeriod != planParams.TrialPeriod {
		t.Errorf("Trial period %v does not match expected trial period %v\n", target.TrialPeriod, planParams.TrialPeriod)
	}

	if target.Statement != planParams.Statement {
		t.Errorf("Statement %q does not match expected statement %q\n", target.Statement, planParams.Statement)
	}

	Del(planParams.ID)
}

func TestPlanGet(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:       "test_plan",
		Name:     "Test Plan",
		Amount:   99,
		Currency: currency.USD,
		Interval: Month,
	}

	New(planParams)
	target, err := Get(planParams.ID, nil)

	if err != nil {
		t.Error(err)
	}

	if target.ID != planParams.ID {
		t.Errorf("Plan id %q does not match expected id %q\n", target.ID, planParams.ID)
	}

	Del(planParams.ID)
}

func TestPlanUpdate(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:            "test_plan",
		Name:          "Original Name",
		Amount:        99,
		Currency:      currency.USD,
		Interval:      Month,
		IntervalCount: 3,
		TrialPeriod:   30,
		Statement:     "Original Plan",
	}

	New(planParams)

	updatedPlan := &stripe.PlanParams{
		Name:        "Updated Name",
		Statement:   "Updated Plan",
		TrialPeriod: 15,
	}

	target, err := Update(planParams.ID, updatedPlan)

	if err != nil {
		t.Error(err)
	}

	if target.Name != updatedPlan.Name {
		t.Errorf("Name %q does not match expected name %q\n", target.Name, updatedPlan.Name)
	}

	if target.Statement != updatedPlan.Statement {
		t.Errorf("Statement %q does not match expected statement %q\n", target.Statement, updatedPlan.Statement)
	}

	if target.TrialPeriod != updatedPlan.TrialPeriod {
		t.Errorf("TrialPeriod %q does not match expected trial period %q\n", target.TrialPeriod, updatedPlan.TrialPeriod)
	}

	Del(planParams.ID)
}

func TestPlanDel(t *testing.T) {
	planParams := &stripe.PlanParams{
		ID:       "test_plan",
		Name:     "Test Plan",
		Amount:   99,
		Currency: currency.USD,
		Interval: Month,
	}

	New(planParams)

	planDel, err := Del(planParams.ID)
	if err != nil {
		t.Error(err)
	}

	if !planDel.Deleted {
		t.Errorf("Plan id %q expected to be marked as deleted on the returned resource\n", planDel.ID)
	}
}

func TestPlanList(t *testing.T) {
	const runs = 3
	for i := 0; i < runs; i++ {
		planParams := &stripe.PlanParams{
			ID:       fmt.Sprintf("test_%v", i),
			Name:     fmt.Sprintf("test_%v", i),
			Amount:   99,
			Currency: currency.USD,
			Interval: Month,
		}

		New(planParams)
	}

	params := &stripe.PlanListParams{}
	params.Filters.AddFilter("limit", "", "1")

	plansChecked := 0
	i := List(params)
	for i.Next() && plansChecked < runs {
		target := i.Plan()

		if i.Meta() == nil {
			t.Error("No metadata returned")
		}

		if target.Amount != 99 {
			t.Errorf("Amount %v does not match expected value\n", target.Amount)
		}

		plansChecked += 1
	}
	if err := i.Err(); err != nil {
		t.Error(err)
	}

	for i := 0; i < runs; i++ {
		Del(fmt.Sprintf("test_%v", i))
	}
}
<|endoftext|>


<commit_before>package requester

import (
	"strconv"
	"time"

	"github.com/nats-io/nats"
	"github.com/tylertreat/bench"
)

// NATSRequesterFactory implements RequesterFactory by creating a Requester
// which publishes messages to NATS and waits to receive them.
type NATSRequesterFactory struct {
	URL         string
	PayloadSize int
	Subject     string
}

// GetRequester returns a new Requester, called for each Benchmark connection.
func (n *NATSRequesterFactory) GetRequester(num uint64) bench.Requester {
	return &natsRequester{
		url:         n.URL,
		payloadSize: n.PayloadSize,
		subject:     n.Subject + "-" + strconv.FormatUint(num, 10),
	}
}

// natsRequester implements Requester by publishing a message to NATS and
// waiting to receive it.
type natsRequester struct {
	url         string
	payloadSize int
	subject     string
	conn        *nats.Conn
	sub         *nats.Subscription
	msg         []byte
}

// Setup prepares the Requester for benchmarking.
func (n *natsRequester) Setup() error {
	conn, err := nats.Connect(n.url)
	if err != nil {
		return err
	}
	sub, err := conn.SubscribeSync(n.subject)
	if err != nil {
		conn.Close()
		return err
	}
	n.conn = conn
	n.sub = sub
	n.msg = make([]byte, n.payloadSize)
	return nil
}

// Request performs a synchronous request to the system under test.
func (n *natsRequester) Request() error {
	if err := n.conn.Publish(n.subject, n.msg); err != nil {
		return err
	}
	_, err := n.sub.NextMsg(30 * time.Second)
	return err
}

// Teardown is called upon benchmark completion.
func (n *natsRequester) Teardown() error {
	if err := n.sub.Unsubscribe(); err != nil {
		return err
	}
	n.sub = nil
	n.conn.Close()
	n.conn = nil
	return nil
}
<commit_msg>Fix go-nats dependency<commit_after>package requester

import (
	"strconv"
	"time"

	"github.com/nats-io/go-nats"
	"github.com/tylertreat/bench"
)

// NATSRequesterFactory implements RequesterFactory by creating a Requester
// which publishes messages to NATS and waits to receive them.
type NATSRequesterFactory struct {
	URL         string
	PayloadSize int
	Subject     string
}

// GetRequester returns a new Requester, called for each Benchmark connection.
func (n *NATSRequesterFactory) GetRequester(num uint64) bench.Requester {
	return &natsRequester{
		url:         n.URL,
		payloadSize: n.PayloadSize,
		subject:     n.Subject + "-" + strconv.FormatUint(num, 10),
	}
}

// natsRequester implements Requester by publishing a message to NATS and
// waiting to receive it.
type natsRequester struct {
	url         string
	payloadSize int
	subject     string
	conn        *nats.Conn
	sub         *nats.Subscription
	msg         []byte
}

// Setup prepares the Requester for benchmarking.
func (n *natsRequester) Setup() error {
	conn, err := nats.Connect(n.url)
	if err != nil {
		return err
	}
	sub, err := conn.SubscribeSync(n.subject)
	if err != nil {
		conn.Close()
		return err
	}
	n.conn = conn
	n.sub = sub
	n.msg = make([]byte, n.payloadSize)
	return nil
}

// Request performs a synchronous request to the system under test.
func (n *natsRequester) Request() error {
	if err := n.conn.Publish(n.subject, n.msg); err != nil {
		return err
	}
	_, err := n.sub.NextMsg(30 * time.Second)
	return err
}

// Teardown is called upon benchmark completion.
func (n *natsRequester) Teardown() error {
	if err := n.sub.Unsubscribe(); err != nil {
		return err
	}
	n.sub = nil
	n.conn.Close()
	n.conn = nil
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	realos "os"

	"github.com/cloudfoundry/cli/plugin/fakes"
	. "github.com/cloudfoundry/cli/testhelpers/io"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type FakeOS struct {
	exitCalled                 bool
	exitCalledWithCode         int
	mkdirCalled                int
	mkdirCalledWithPath        string
	mkdirCalledWithMode        realos.FileMode
	removeCalled               int
	removeCalledWithPath       string
	symlinkCalled              int
	symlinkCalledWithTarget    string
	symlinkCalledWithSource    string
	readdirCalled              int
	readdirCalledWithPath      string
	readfileCalled             int
	readfileCalledWithPath     string
	writefileCalled            int
	writefileCalledWithPath    string
	writefileCalledWithContent []byte
	writefileCalledWithMode    realos.FileMode
	readdirShouldReturn        []realos.FileInfo
	readfileShouldReturn       []byte
}

func (os *FakeOS) Exit(code int) {
	os.exitCalled = true
	os.exitCalledWithCode = code
}

func (os *FakeOS) Mkdir(path string, mode realos.FileMode) {
	os.mkdirCalled++
	os.mkdirCalledWithPath = path
	os.mkdirCalledWithMode = mode
}

func (os *FakeOS) Remove(path string) {
	os.removeCalled++
	os.removeCalledWithPath = path
}

func (os *FakeOS) Symlink(target string, source string) error {
	os.symlinkCalled++
	os.symlinkCalledWithTarget = target
	os.symlinkCalledWithSource = source
	return nil
}

func (os *FakeOS) ReadDir(path string) ([]realos.FileInfo, error) {
	os.readdirCalled++
	os.readdirCalledWithPath = path
	return os.readdirShouldReturn, nil
}

func (os *FakeOS) ReadFile(path string) ([]byte, error) {
	os.readfileCalled++
	os.readfileCalledWithPath = path
	return os.readfileShouldReturn, nil
}

func (os *FakeOS) WriteFile(path string, content []byte, mode realos.FileMode) error {
	os.writefileCalled++
	os.writefileCalledWithPath = path
	os.writefileCalledWithContent = content
	os.writefileCalledWithMode = mode
	return nil
}

var _ = Describe("TargetsPlugin", func() {
	Describe("Command Syntax", func() {
		var fakeCliConnection *fakes.FakeCliConnection
		var targetsPlugin *TargetsPlugin
		var fakeOS FakeOS

		BeforeEach(func() {
			fakeOS = FakeOS{}
			os = &fakeOS
			fakeCliConnection = &fakes.FakeCliConnection{}
			targetsPlugin = &TargetsPlugin{}
		})

		It("displays usage when targets called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "targets"}))
		})

		It("displays usage when set-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when save-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when save-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when delete-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays usage when delete-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(true))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})
	})
})
<commit_msg>More tests<commit_after>package main

import (
	realos "os"

	"github.com/cloudfoundry/cli/plugin/fakes"
	. "github.com/cloudfoundry/cli/testhelpers/io"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type FakeOS struct {
	exitCalled                 int
	exitCalledWithCode         int
	mkdirCalled                int
	mkdirCalledWithPath        string
	mkdirCalledWithMode        realos.FileMode
	removeCalled               int
	removeCalledWithPath       string
	symlinkCalled              int
	symlinkCalledWithTarget    string
	symlinkCalledWithSource    string
	readdirCalled              int
	readdirCalledWithPath      string
	readfileCalled             int
	readfileCalledWithPath     string
	writefileCalled            int
	writefileCalledWithPath    string
	writefileCalledWithContent []byte
	writefileCalledWithMode    realos.FileMode
	readdirShouldReturn        []realos.FileInfo
	readfileShouldReturn       []byte
}

func (os *FakeOS) Exit(code int) {
	os.exitCalled++
	os.exitCalledWithCode = code
}

func (os *FakeOS) Mkdir(path string, mode realos.FileMode) {
	os.mkdirCalled++
	os.mkdirCalledWithPath = path
	os.mkdirCalledWithMode = mode
}

func (os *FakeOS) Remove(path string) {
	os.removeCalled++
	os.removeCalledWithPath = path
}

func (os *FakeOS) Symlink(target string, source string) error {
	os.symlinkCalled++
	os.symlinkCalledWithTarget = target
	os.symlinkCalledWithSource = source
	return nil
}

func (os *FakeOS) ReadDir(path string) ([]realos.FileInfo, error) {
	os.readdirCalled++
	os.readdirCalledWithPath = path
	return os.readdirShouldReturn, nil
}

func (os *FakeOS) ReadFile(path string) ([]byte, error) {
	os.readfileCalled++
	os.readfileCalledWithPath = path
	return os.readfileShouldReturn, nil
}

func (os *FakeOS) WriteFile(path string, content []byte, mode realos.FileMode) error {
	os.writefileCalled++
	os.writefileCalledWithPath = path
	os.writefileCalledWithContent = content
	os.writefileCalledWithMode = mode
	return nil
}

var _ = Describe("TargetsPlugin", func() {

	var fakeCliConnection *fakes.FakeCliConnection
	var targetsPlugin *TargetsPlugin
	var fakeOS FakeOS

	BeforeEach(func() {
		fakeOS = FakeOS{}
		os = &fakeOS
		fakeCliConnection = &fakes.FakeCliConnection{}
		targetsPlugin = newTargetsPlugin()
	})

	Describe("Command Syntax", func() {
		It("displays usage when targets called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "targets"}))
		})

		It("displays usage when set-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when set-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"set-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "set-target", "[-f]", "NAME"}))
		})

		It("displays usage when save-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when save-target called with unsupported option", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"save-target", "blah", "-k"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "save-target", "[-f]", "[NAME]"}))
		})

		It("displays usage when delete-target called with too few arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays usage when delete-target called with too many arguments", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"delete-target", "blah", "blah"})
			})
			Expect(fakeOS.exitCalled).To(Equal(1))
			Expect(fakeOS.exitCalledWithCode).To(Equal(1))
			Expect(output).To(ContainSubstrings([]string{"Usage:", "cf", "delete-target", "NAME"}))
		})

		It("displays proper first time message", func() {
			output := CaptureOutput(func() {
				targetsPlugin.Run(fakeCliConnection, []string{"targets"})
			})
			Expect(fakeOS.exitCalled).To(Equal(0))
			Expect(output).To(ContainSubstrings([]string{"No targets have been saved"}))
			Expect(output).To(ContainSubstrings([]string{"cf", "save-target", "NAME"}))
		})
	})

	Describe("Configuration File Manipulation", func() {

		It("creates the proper target directory", func() {
			Expect(fakeOS.mkdirCalled).To(Equal(1))
			Expect(fakeOS.mkdirCalledWithPath).To(HaveSuffix("/.cf/targets"))
		})

		It("properly saves first target", func() {
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "first"})
			Expect(fakeOS.writefileCalled).To(Equal(1))
			Expect(fakeOS.writefileCalledWithPath).To(HaveSuffix("/.cf/targets/first.config.json"))
			Expect(fakeOS.symlinkCalled).To(Equal(1))
			Expect(fakeOS.symlinkCalledWithSource).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalledWithTarget).To(HaveSuffix("/.cf/targets/first.config.json"))
		})

		It("properly saves second target", func() {
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "first"})
			targetsPlugin.Run(fakeCliConnection, []string{"save-target", "second"})
			Expect(fakeOS.writefileCalled).To(Equal(2))
			Expect(fakeOS.writefileCalledWithPath).To(HaveSuffix("/.cf/targets/second.config.json"))
			Expect(fakeOS.removeCalledWithPath).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalled).To(Equal(2))
			Expect(fakeOS.symlinkCalledWithSource).To(HaveSuffix("/.cf/targets/current"))
			Expect(fakeOS.symlinkCalledWithTarget).To(HaveSuffix("/.cf/targets/second.config.json"))
		})
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2015 OpsGenie. All rights reserved.
Use of this source code is governed by a Apache Software
license that can be found in the LICENSE file.
*/

//Package cfg reads configurations and provides configuration props to commands.
package cfg

import (
	"fmt"
	"github.com/ccding/go-config-reader/config"
	"os"
	"path/filepath"
)

const (
	confPath        = "LAMP_CONF_PATH"
	sep      string = string(filepath.Separator)
)

var lampConfig *config.Config

// Verbose is an exported variable to determine command is executing verbose mode or not.
var Verbose = false

func printVerboseMessage(message string) {
	if Verbose {
		fmt.Printf("%s\n", message)
	}
}

func lampHome() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fmt.Printf("Error occurred while getting lamp home path: %s\n", err.Error())
		return ""
	}
	return dir + sep
}

// LoadConfigFromGivenPath method reads configuration file from the given path.
func LoadConfigFromGivenPath(confPath string) {
	printVerboseMessage("Will read configuration from: \n--config " + confPath)
	load(confPath)
}

// LoadConfiguration method tries to find and read the configuration file some specific paths.
func LoadConfiguration() {
	confPath := os.Getenv(confPath)
	if confPath == "" {
		confPath = lampHome() + ".." + sep + "conf" + sep + "opsgenie-integration.conf"
		printVerboseMessage("LAMP_CONF_PATH environment variable is not set. Will try to read config from: \n" + confPath)
	}

	if _, err := os.Stat(confPath); os.IsNotExist(err) {
		confPath = lampHome() + "conf" + sep + "lamp.conf"
		printVerboseMessage("Could not find the file specified. Will try to read config from: \n" + confPath)
	}
	load(confPath)
}

func load(confPath string) {
	if _, err := os.Stat(confPath); !os.IsNotExist(err) {
		conf := config.NewConfig(confPath)
		conf.Read()
		lampConfig = conf
		configureLog()
	} else {
		printVerboseMessage("Could not read config file: " + err.Error())
	}
}

// Get method returns the configuration properties value according to the key.
func Get(key string) string {
	if lampConfig != nil {
		return lampConfig.Get("", key)
	}
	return ""
}

func configureLog() {
	level := Get("lamp.log.level")
	if level == "" {
		level = "warn"
		printVerboseMessage("Could not get log level from configuration, will use default \"warn\".")
	}
}
<commit_msg>Update default file config location<commit_after>/*
Copyright 2015 OpsGenie. All rights reserved.
Use of this source code is governed by a Apache Software
license that can be found in the LICENSE file.
*/

//Package cfg reads configurations and provides configuration props to commands.
package cfg

import (
	"fmt"
	"github.com/ccding/go-config-reader/config"
	"os"
	"path/filepath"
)

const (
	confPath        = "LAMP_CONF_PATH"
	sep      string = string(filepath.Separator)
)

var lampConfig *config.Config

// Verbose is an exported variable to determine command is executing verbose mode or not.
var Verbose = false

func printVerboseMessage(message string) {
	if Verbose {
		fmt.Printf("%s\n", message)
	}
}

func lampHome() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fmt.Printf("Error occurred while getting lamp home path: %s\n", err.Error())
		return ""
	}
	return dir + sep
}

// LoadConfigFromGivenPath method reads configuration file from the given path.
func LoadConfigFromGivenPath(confPath string) {
	printVerboseMessage("Will read configuration from: \n--config " + confPath)
	load(confPath)
}

// LoadConfiguration method tries to find and read the configuration file some specific paths.
func LoadConfiguration() {
	confPath := os.Getenv(confPath)
	if confPath == "" {
		confPath = lampHome() + ".." + sep + "conf" + sep + "opsgenie-integration.conf"
		printVerboseMessage("LAMP_CONF_PATH environment variable is not set. Will try to read config from: \n" + confPath)
	}

	if _, err := os.Stat(confPath); os.IsNotExist(err) {
		userHomeDir, _ := os.UserHomeDir()
		confPath = userHomeDir + sep + ".config" + sep + "lamp.conf"
		printVerboseMessage("Could not find the file specified. Will try to read config from: \n" + confPath)
	}
	load(confPath)
}

func load(confPath string) {
	if _, err := os.Stat(confPath); !os.IsNotExist(err) {
		conf := config.NewConfig(confPath)
		conf.Read()
		lampConfig = conf
		configureLog()
	} else {
		printVerboseMessage("Could not read config file: " + err.Error())
	}
}

// Get method returns the configuration properties value according to the key.
func Get(key string) string {
	if lampConfig != nil {
		return lampConfig.Get("", key)
	}
	return ""
}

func configureLog() {
	level := Get("lamp.log.level")
	if level == "" {
		level = "warn"
		printVerboseMessage("Could not get log level from configuration, will use default \"warn\".")
	}
}
<|endoftext|>


<commit_before>package goat

func IoWorker() {

}
<commit_msg>added IoWorker interface that requires read and write to be satisfied<commit_after>package goat

type IoWorker interface {
	Read(ResponseChan chan Response, request Request)
	Write(ResponseChan chan Response, request Request)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/go-chat-bot/bot"
)

type Kubebot struct {
	token    string
	admins   map[string]bool
	channels map[string]bool
	commands map[string]bool
}

const (
	forbiddenUserMessage     string = "%s - ⚠ kubectl forbidden for user @%s\n"
	forbiddenChannelMessage  string = "%s - ⚠ Channel %s forbidden for user @%s\n"
	forbiddenCommandMessage  string = "%s - ⚠ Command %s forbidden for user @%s\n"
	forbiddenUserResponse    string = "Sorry @%s, but you don't have permission to run this command :confused:"
	forbiddenChannelResponse string = "Sorry @%s, but I'm not allowed to run this command here :zipper_mouth_face:"
	forbiddenCommandResponse string = "Sorry @%s, but I cannot run this command. I'm allowed to run `%s`"
	okResponse               string = "Roger that!\n@%s, this is the response to your request:\n ```\n%s\n``` "
)

func kubectl(command *bot.Cmd) (msg string, err error) {
	t := time.Now()
	time := t.Format(time.RFC3339)
	nickname := command.User.Nick

	if !kb.admins[nickname] {
		fmt.Printf(forbiddenUserMessage, time, nickname)
		return fmt.Sprintf(forbiddenUserResponse, nickname), nil
	}

	if !kb.channels[command.Channel] {
		fmt.Printf(forbiddenChannelMessage, time, command.Channel, nickname)
		return fmt.Sprintf(forbiddenChannelResponse, nickname), nil
	}

	if len(command.Args) > 0 && !kb.commands[command.Args[0]] {
		fmt.Printf(forbiddenCommandMessage, time, kb.commands, nickname)
		return fmt.Sprintf(forbiddenCommandResponse, nickname, kb.commands), nil
	}

	output := execute("kubectl", command.Args...)

	return fmt.Sprintf(okResponse, nickname, output), nil
}

func init() {
	bot.RegisterCommand(
		"kubectl",
		"Kubectl Slack integration",
		"",
		kubectl)
}
<commit_msg>Added blacklist for commands' flags<commit_after>package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/go-chat-bot/bot"
)

type Kubebot struct {
	token    string
	admins   map[string]bool
	channels map[string]bool
	commands map[string]bool
}

const (
	forbiddenUserMessage     string = "%s - ⚠ kubectl forbidden for user @%s\n"
	forbiddenChannelMessage  string = "%s - ⚠ Channel %s forbidden for user @%s\n"
	forbiddenCommandMessage  string = "%s - ⚠ Command %s forbidden for user @%s\n"
	forbiddenFlagMessage     string = "%s - ⚠ Flag(s) %s forbidden for user @%s\n"
	forbiddenUserResponse    string = "Sorry @%s, but you don't have permission to run this command :confused:"
	forbiddenChannelResponse string = "Sorry @%s, but I'm not allowed to run this command here :zipper_mouth_face:"
	forbiddenCommandResponse string = "Sorry @%s, but I cannot run this command. I'm allowed to run `%s`"
	forbiddenFlagResponse    string = "Sorry @%s, but I'm not allowed to run one of your flags."
	okResponse               string = "Roger that!\n@%s, this is the response to your request:\n ```\n%s\n``` "
)

var (
	ignored = map[string]map[string]bool{
		"get": map[string]bool{
			"-f":           true,
			"--filename":   true,
			"-w":           true,
			"--watch":      true,
			"--watch-only": true,
		},
		"describe": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"create": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"replace": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"patch": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"delete": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"edit": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"apply": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"logs": map[string]bool{
			"-f":       true,
			"--follow": true,
		},
		"rolling-update": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"scale": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"attach": map[string]bool{
			"-i":      true,
			"--stdin": true,
			"-t":      true,
			"--tty":   true,
		},
		"exec": map[string]bool{
			"-i":      true,
			"--stdin": true,
			"-t":      true,
			"--tty":   true,
		},
		"run": map[string]bool{
			"--leave-stdin-open": true,
			"-i":                 true,
			"--stdin":            true,
			"--tty":              true,
		},
		"expose": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"autoscale": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"label": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"annotate": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
		"convert": map[string]bool{
			"-f":         true,
			"--filename": true,
		},
	}
)

func validateFlags(arguments ...string) error {
	if len(arguments) <= 1 {
		return nil
	}

	for i := 1; i < len(arguments); i++ {
		if ignored[arguments[0]][arguments[i]] {
			return errors.New(fmt.Sprintf("Error: %s is an invalid flag", arguments[i]))
		}

	}

	return nil
}

func kubectl(command *bot.Cmd) (msg string, err error) {
	t := time.Now()
	time := t.Format(time.RFC3339)
	nickname := command.User.Nick

	if !kb.admins[nickname] {
		fmt.Printf(forbiddenUserMessage, time, nickname)
		return fmt.Sprintf(forbiddenUserResponse, nickname), nil
	}

	if !kb.channels[command.Channel] {
		fmt.Printf(forbiddenChannelMessage, time, command.Channel, nickname)
		return fmt.Sprintf(forbiddenChannelResponse, nickname), nil
	}

	if len(command.Args) > 0 && !kb.commands[command.Args[0]] {
		fmt.Printf(forbiddenCommandMessage, time, command.Args, nickname)
		return fmt.Sprintf(forbiddenCommandResponse, nickname, kb.commands), nil
	}

	if err := validateFlags(command.Args...); err != nil {
		fmt.Printf(forbiddenFlagMessage, time, command.Args, nickname)
		return fmt.Sprintf(forbiddenFlagResponse, nickname), nil
	}

	output := execute("kubectl", command.Args...)

	return fmt.Sprintf(okResponse, nickname, output), nil
}

func init() {
	bot.RegisterCommand(
		"kubectl",
		"Kubectl Slack integration",
		"",
		kubectl)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors 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 unversioned

import (
	"fmt"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/runtime"
)

type EventExpansion interface {
	// CreateWithEventNamespace is the same as a Create, except that it sends the request to the event.Namespace.
	CreateWithEventNamespace(event *api.Event) (*api.Event, error)
	// UpdateWithEventNamespace is the same as a Update, except that it sends the request to the event.Namespace.
	UpdateWithEventNamespace(event *api.Event) (*api.Event, error)
	Patch(event *api.Event, data []byte) (*api.Event, error)
	// Search finds events about the specified object
	Search(objOrRef runtime.Object) (*api.EventList, error)
	// Returns the appropriate field selector based on the API version being used to communicate with the server.
	// The returned field selector can be used with List and Watch to filter desired events.
	GetFieldSelector(involvedObjectName, involvedObjectNamespace, involvedObjectKind, involvedObjectUID *string) fields.Selector
}

// CreateWithEventNamespace makes a new event. Returns the copy of the event the server returns,
// or an error. The namespace to create the event within is deduced from the
// event; it must either match this event client's namespace, or this event
// client must have been created with the "" namespace.
func (e *events) CreateWithEventNamespace(event *api.Event) (*api.Event, error) {
	if e.ns != "" && event.Namespace != e.ns {
		return nil, fmt.Errorf("can't create an event with namespace '%v' in namespace '%v'", event.Namespace, e.ns)
	}
	result := &api.Event{}
	err := e.client.Post().
		NamespaceIfScoped(event.Namespace, len(event.Namespace) > 0).
		Resource("events").
		Body(event).
		Do().
		Into(result)
	return result, err
}

// UpdateWithEventNamespace modifies an existing event. It returns the copy of the event that the server returns,
// or an error. The namespace and key to update the event within is deduced from the event. The
// namespace must either match this event client's namespace, or this event client must have been
// created with the "" namespace. Update also requires the ResourceVersion to be set in the event
// object.
func (e *events) UpdateWithEventNamespace(event *api.Event) (*api.Event, error) {
	result := &api.Event{}
	err := e.client.Put().
		NamespaceIfScoped(event.Namespace, len(event.Namespace) > 0).
		Resource("events").
		Name(event.Name).
		Body(event).
		Do().
		Into(result)
	return result, err
}

// Patch modifies an existing event. It returns the copy of the event that the server returns, or an
// error. The namespace and name of the target event is deduced from the incompleteEvent. The
// namespace must either match this event client's namespace, or this event client must have been
// created with the "" namespace.
func (e *events) Patch(incompleteEvent *api.Event, data []byte) (*api.Event, error) {
	result := &api.Event{}
	err := e.client.Patch(api.StrategicMergePatchType).
		NamespaceIfScoped(incompleteEvent.Namespace, len(incompleteEvent.Namespace) > 0).
		Resource("events").
		Name(incompleteEvent.Name).
		Body(data).
		Do().
		Into(result)
	return result, err
}

// Search finds events about the specified object. The namespace of the
// object must match this event's client namespace unless the event client
// was made with the "" namespace.
func (e *events) Search(objOrRef runtime.Object) (*api.EventList, error) {
	ref, err := api.GetReference(objOrRef)
	if err != nil {
		return nil, err
	}
	if e.ns != "" && ref.Namespace != e.ns {
		return nil, fmt.Errorf("won't be able to find any events of namespace '%v' in namespace '%v'", ref.Namespace, e.ns)
	}
	stringRefKind := string(ref.Kind)
	var refKind *string
	if stringRefKind != "" {
		refKind = &stringRefKind
	}
	stringRefUID := string(ref.UID)
	var refUID *string
	if stringRefUID != "" {
		refUID = &stringRefUID
	}
	fieldSelector := e.GetFieldSelector(&ref.Name, &ref.Namespace, refKind, refUID)
	return e.List(api.ListOptions{FieldSelector: fieldSelector})
}

// Returns the appropriate field selector based on the API version being used to communicate with the server.
// The returned field selector can be used with List and Watch to filter desired events.
func (e *events) GetFieldSelector(involvedObjectName, involvedObjectNamespace, involvedObjectKind, involvedObjectUID *string) fields.Selector {
	apiVersion := e.client.APIVersion().String()
	field := fields.Set{}
	if involvedObjectName != nil {
		field[GetInvolvedObjectNameFieldLabel(apiVersion)] = *involvedObjectName
	}
	if involvedObjectNamespace != nil {
		field["involvedObject.namespace"] = *involvedObjectNamespace
	}
	if involvedObjectKind != nil {
		field["involvedObject.kind"] = *involvedObjectKind
	}
	if involvedObjectUID != nil {
		field["involvedObject.uid"] = *involvedObjectUID
	}
	return field.AsSelector()
}

// Returns the appropriate field label to use for name of the involved object as per the given API version.
func GetInvolvedObjectNameFieldLabel(version string) string {
	return "involvedObject.name"
}
<commit_msg>add an adapter for EventSink<commit_after>/*
Copyright 2016 The Kubernetes Authors 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 unversioned

import (
	"fmt"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/runtime"
)

type EventExpansion interface {
	// CreateWithEventNamespace is the same as a Create, except that it sends the request to the event.Namespace.
	CreateWithEventNamespace(event *api.Event) (*api.Event, error)
	// UpdateWithEventNamespace is the same as a Update, except that it sends the request to the event.Namespace.
	UpdateWithEventNamespace(event *api.Event) (*api.Event, error)
	Patch(event *api.Event, data []byte) (*api.Event, error)
	// Search finds events about the specified object
	Search(objOrRef runtime.Object) (*api.EventList, error)
	// Returns the appropriate field selector based on the API version being used to communicate with the server.
	// The returned field selector can be used with List and Watch to filter desired events.
	GetFieldSelector(involvedObjectName, involvedObjectNamespace, involvedObjectKind, involvedObjectUID *string) fields.Selector
}

// CreateWithEventNamespace makes a new event. Returns the copy of the event the server returns,
// or an error. The namespace to create the event within is deduced from the
// event; it must either match this event client's namespace, or this event
// client must have been created with the "" namespace.
func (e *events) CreateWithEventNamespace(event *api.Event) (*api.Event, error) {
	if e.ns != "" && event.Namespace != e.ns {
		return nil, fmt.Errorf("can't create an event with namespace '%v' in namespace '%v'", event.Namespace, e.ns)
	}
	result := &api.Event{}
	err := e.client.Post().
		NamespaceIfScoped(event.Namespace, len(event.Namespace) > 0).
		Resource("events").
		Body(event).
		Do().
		Into(result)
	return result, err
}

// UpdateWithEventNamespace modifies an existing event. It returns the copy of the event that the server returns,
// or an error. The namespace and key to update the event within is deduced from the event. The
// namespace must either match this event client's namespace, or this event client must have been
// created with the "" namespace. Update also requires the ResourceVersion to be set in the event
// object.
func (e *events) UpdateWithEventNamespace(event *api.Event) (*api.Event, error) {
	result := &api.Event{}
	err := e.client.Put().
		NamespaceIfScoped(event.Namespace, len(event.Namespace) > 0).
		Resource("events").
		Name(event.Name).
		Body(event).
		Do().
		Into(result)
	return result, err
}

// Patch modifies an existing event. It returns the copy of the event that the server returns, or an
// error. The namespace and name of the target event is deduced from the incompleteEvent. The
// namespace must either match this event client's namespace, or this event client must have been
// created with the "" namespace.
func (e *events) Patch(incompleteEvent *api.Event, data []byte) (*api.Event, error) {
	result := &api.Event{}
	err := e.client.Patch(api.StrategicMergePatchType).
		NamespaceIfScoped(incompleteEvent.Namespace, len(incompleteEvent.Namespace) > 0).
		Resource("events").
		Name(incompleteEvent.Name).
		Body(data).
		Do().
		Into(result)
	return result, err
}

// Search finds events about the specified object. The namespace of the
// object must match this event's client namespace unless the event client
// was made with the "" namespace.
func (e *events) Search(objOrRef runtime.Object) (*api.EventList, error) {
	ref, err := api.GetReference(objOrRef)
	if err != nil {
		return nil, err
	}
	if e.ns != "" && ref.Namespace != e.ns {
		return nil, fmt.Errorf("won't be able to find any events of namespace '%v' in namespace '%v'", ref.Namespace, e.ns)
	}
	stringRefKind := string(ref.Kind)
	var refKind *string
	if stringRefKind != "" {
		refKind = &stringRefKind
	}
	stringRefUID := string(ref.UID)
	var refUID *string
	if stringRefUID != "" {
		refUID = &stringRefUID
	}
	fieldSelector := e.GetFieldSelector(&ref.Name, &ref.Namespace, refKind, refUID)
	return e.List(api.ListOptions{FieldSelector: fieldSelector})
}

// Returns the appropriate field selector based on the API version being used to communicate with the server.
// The returned field selector can be used with List and Watch to filter desired events.
func (e *events) GetFieldSelector(involvedObjectName, involvedObjectNamespace, involvedObjectKind, involvedObjectUID *string) fields.Selector {
	apiVersion := e.client.APIVersion().String()
	field := fields.Set{}
	if involvedObjectName != nil {
		field[GetInvolvedObjectNameFieldLabel(apiVersion)] = *involvedObjectName
	}
	if involvedObjectNamespace != nil {
		field["involvedObject.namespace"] = *involvedObjectNamespace
	}
	if involvedObjectKind != nil {
		field["involvedObject.kind"] = *involvedObjectKind
	}
	if involvedObjectUID != nil {
		field["involvedObject.uid"] = *involvedObjectUID
	}
	return field.AsSelector()
}

// Returns the appropriate field label to use for name of the involved object as per the given API version.
func GetInvolvedObjectNameFieldLabel(version string) string {
	return "involvedObject.name"
}

// TODO: This is a temporary arrangement and will be removed once all clients are moved to use the clientset.
type EventSinkImpl struct {
	Interface EventInterface
}

func (e *EventSinkImpl) Create(event *api.Event) (*api.Event, error) {
	return e.Interface.CreateWithEventNamespace(event)
}

func (e *EventSinkImpl) Update(event *api.Event) (*api.Event, error) {
	return e.Interface.UpdateWithEventNamespace(event)
}

func (e *EventSinkImpl) Patch(event *api.Event, data []byte) (*api.Event, error) {
	return e.Interface.Patch(event, data)
}
<|endoftext|>


<commit_before>package capn_test

import (
	"bytes"
	"fmt"
	"os"
	"testing"

	capn "github.com/glycerine/go-capnproto"
	air "github.com/glycerine/go-capnproto/aircraftlib"
	cv "github.com/smartystreets/goconvey/convey"
)

func ExampleAirplaneWrite() string {

	fname := "out.write_test.airplane.cpz"

	// make a brand new, empty segment (message)
	seg := capn.NewBuffer(nil)

	// If you want runtime-type identification, this is easily obtained. Just
	// wrap everything in a struct that contains a single anoymous union (e.g. struct Z).
	// Then always set a Z as the root object in you message/first segment.
	// The cost of the extra word of storage is usually worth it, as
	// then human readalbe output is easily obtained via a shell command such as
	//
	// $ cat binary.cpz | capnp decode aircraft.capnp Z
	//
	// If you need to conserve space, and know your content in advance, it
	// isn't necessary to use an anonymous union. Just supply the type name
	// in place of 'Z' in the decode command above.

	z := air.NewRootZ(seg) // root should be allocated first.
	// There must be only one root.

	// then non-root objects:
	aircraft := air.NewAircraft(seg)
	b737 := air.NewB737(seg)
	planebase := air.NewPlaneBase(seg)

	// how to create a list. Requires a cast at the moment.
	homes := air.NewAirportList(seg, 2)
	uint16list := capn.UInt16List(homes) // cast to the underlying type
	uint16list.Set(0, air.AIRPORT_JFK)
	uint16list.Set(1, air.AIRPORT_LAX)

	// set the primitive fields
	planebase.SetCanFly(true)
	planebase.SetName("Henrietta")
	planebase.SetRating(100)

	// set a list field
	planebase.SetHomes(homes)

	// wire up the pointers between objects
	b737.SetBase(planebase)
	aircraft.SetB737(b737)
	z.SetAircraft(aircraft)

	// ready to write

	// example of writing to memory
	buf := bytes.Buffer{}
	seg.WriteTo(&buf)

	// example of writing to file. Just use WriteTo().
	// We could have used SegToFile(seg, fname) from
	// util_test.go intead, but this makes it clear how easy it is.
	file, err := os.Create(fname)
	defer file.Close()
	if err != nil {
		panic(err)
	}
	seg.WriteTo(file)

	// readback and view that file in human readable format. Defined in util_test.go
	text, err := CapnFileToText(fname, "aircraftlib/aircraft.capnp", "")
	if err != nil {
		panic(err)
	}
	fmt.Printf("here is our aircraft:\n")
	fmt.Printf("%s\n", text)

	return text
}

func TestAircraftWrite(t *testing.T) {

	observedText := ExampleAirplaneWrite()
	expectedText := `(aircraft = (b737 = (base = (name = "Henrietta", homes = [jfk, lax], rating = 100, canFly = true, capacity = 0, maxSpeed = 0))))`

	cv.Convey("When we run the ExampleAirplaneWrite() function in write_test.go", t, func() {
		cv.Convey("Then we should see the human readable B737 example struct we expect", func() {
			cv.So(observedText, cv.ShouldEqual, expectedText)
		})
	})

}
<commit_msg>fill in more planebase fields in the write example<commit_after>package capn_test

import (
	"bytes"
	"fmt"
	"os"
	"testing"

	capn "github.com/glycerine/go-capnproto"
	air "github.com/glycerine/go-capnproto/aircraftlib"
	cv "github.com/smartystreets/goconvey/convey"
)

func ExampleAirplaneWrite() string {

	fname := "out.write_test.airplane.cpz"

	// make a brand new, empty segment (message)
	seg := capn.NewBuffer(nil)

	// If you want runtime-type identification, this is easily obtained. Just
	// wrap everything in a struct that contains a single anoymous union (e.g. struct Z).
	// Then always set a Z as the root object in you message/first segment.
	// The cost of the extra word of storage is usually worth it, as
	// then human readalbe output is easily obtained via a shell command such as
	//
	// $ cat binary.cpz | capnp decode aircraft.capnp Z
	//
	// If you need to conserve space, and know your content in advance, it
	// isn't necessary to use an anonymous union. Just supply the type name
	// in place of 'Z' in the decode command above.

	z := air.NewRootZ(seg) // root should be allocated first.
	// There must be only one root.

	// then non-root objects:
	aircraft := air.NewAircraft(seg)
	b737 := air.NewB737(seg)
	planebase := air.NewPlaneBase(seg)

	// how to create a list. Requires a cast at the moment.
	homes := air.NewAirportList(seg, 2)
	uint16list := capn.UInt16List(homes) // cast to the underlying type
	uint16list.Set(0, air.AIRPORT_JFK)
	uint16list.Set(1, air.AIRPORT_LAX)

	// set the primitive fields
	planebase.SetCanFly(true)
	planebase.SetName("Henrietta")
	planebase.SetRating(100)
	planebase.SetMaxSpeed(876) // km/hr
	// if we don't set capacity, it will get the default value, in this case 0.
	//planebase.SetCapacity(26020) // Liters fuel

	// set a list field
	planebase.SetHomes(homes)

	// wire up the pointers between objects
	b737.SetBase(planebase)
	aircraft.SetB737(b737)
	z.SetAircraft(aircraft)

	// ready to write

	// example of writing to memory
	buf := bytes.Buffer{}
	seg.WriteTo(&buf)

	// example of writing to file. Just use WriteTo().
	// We could have used SegToFile(seg, fname) from
	// util_test.go intead, but this makes it clear how easy it is.
	file, err := os.Create(fname)
	defer file.Close()
	if err != nil {
		panic(err)
	}
	seg.WriteTo(file)

	// readback and view that file in human readable format. Defined in util_test.go
	text, err := CapnFileToText(fname, "aircraftlib/aircraft.capnp", "")
	if err != nil {
		panic(err)
	}
	fmt.Printf("here is our aircraft:\n")
	fmt.Printf("%s\n", text)

	return text
}

func TestAircraftWrite(t *testing.T) {

	observedText := ExampleAirplaneWrite()
	expectedText := `(aircraft = (b737 = (base = (name = "Henrietta", homes = [jfk, lax], rating = 100, canFly = true, capacity = 0, maxSpeed = 876))))`

	cv.Convey("When we run the ExampleAirplaneWrite() function in write_test.go", t, func() {
		cv.Convey("Then we should see the human readable B737 example struct we expect", func() {
			cv.So(observedText, cv.ShouldEqual, expectedText)
		})
	})

}
<|endoftext|>


<commit_before>package device

import (
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/xshellinc/go-virtualbox"
	"github.com/xshellinc/iotit/device/config"
	"github.com/xshellinc/iotit/repo"
	"github.com/xshellinc/iotit/vbox"
	"github.com/xshellinc/tools/dialogs"
	"github.com/xshellinc/tools/lib/help"
	"gopkg.in/urfave/cli.v1"
)

// Flasher is an entity for flashing different devices
type Flasher interface {
	Flash() error
	Configure() error
	Write() error
}

// flasher contains virtualbox machine, ssh connection, repository, currently selected device and image name
type flasher struct {
	Quiet   bool
	vbox    *virtualbox.Machine
	conf    *vbox.Config
	devRepo *repo.DeviceMapping
	CLI     *cli.Context

	img     string
	folder  string
	device  string
	mounted bool
}

var retries = 0

// validates given image path and downloads image archive to os tmp folder
func (d *flasher) DownloadImage() (fileName, filePath string, err error) {
	wg := &sync.WaitGroup{}
	if !help.ValidURL(d.devRepo.Image.URL) {
		// local file path given
		filePath = d.devRepo.Image.URL
		fileName = filepath.Base(filePath)
		log.WithField("path", filePath).WithField("name", fileName).Info("Custom image")
		if !help.Exists(filePath) {
			fmt.Println("[-] Image location is neither valid URL nor existing file. Aborting.")
			return "", "", errors.New("Invalid image location")
		}
		fmt.Println("[+] Using local image file for ", d.device)
		return fileName, filePath, err
	}
	// download image over http
	fmt.Println("[+] Starting download", d.devRepo.Image.Title)
	log.WithField("url", d.devRepo.Image.URL).WithField("dir", d.devRepo.Dir()).Debug("download")
	name, bar, err := help.DownloadFromUrlWithAttemptsAsync(d.devRepo.Image.URL, d.devRepo.Dir(), 3, wg)
	if err != nil {
		return "", "", err
	}
	fileName = name
	filePath = filepath.Join(d.devRepo.Dir(), fileName)

	bar.Prefix(fmt.Sprintf("[+] Download %-15s", fileName))
	bar.Start()
	wg.Wait()
	bar.Finish()
	time.Sleep(time.Second * 2)

	return fileName, filePath, err
}

// Prepare method inits virtualbox, downloads os image and uploads it into vm
func (d *flasher) Prepare() error {
	log.Debug("Prepare")
	if err := vbox.CheckVBInstalled(); err != nil {
		return err
	}

	d.conf = vbox.NewConfig(d.device)
	log.Debug("Configuring virtual box")
	var err error
	if d.vbox, err = d.conf.GetVbox(d.device, d.Quiet); err != nil {
		return err
	}
	log.WithField("name", d.vbox.Name).Info("Selected profile")

	if d.vbox.State != virtualbox.Running {
		fmt.Printf(`[+] Using virtual machine
	Name - `+dialogs.PrintColored("%s")+`
	Description - `+dialogs.PrintColored("%s")+"\n", d.vbox.Name, d.vbox.Description)

		if err := d.startVM(); err != nil {
			return err
		}
	}

	help.DeleteHost(filepath.Join(help.UserHomeDir(), ".ssh", "known_hosts"), "localhost")

	return d.prepareImage()
}

func (d *flasher) startVM() error {
	job := help.NewBackgroundJob()
	if err := d.vbox.Start(); err != nil {
		return err
	}

	go func() {
		ticker := time.NewTicker(15 * time.Second)
		defer ticker.Stop()
		defer job.Close()
		for {
			select {
			case <-ticker.C:
				_, eut, err := d.conf.SSH.Run("whoami")
				if err == nil && strings.TrimSpace(eut) == "" {
					return
				}
			case <-time.After(180 * time.Second):
				job.Error(errors.New("Cannot connect to vbox via ssh"))
			}
		}
	}()

	if err := help.WaitJobAndSpin("Starting", job); err != nil {
		return err
	}

	time.Sleep(time.Second)
	return nil
}

func (d *flasher) prepareImage() error {
	var (
		fileName, filePath string
	)

	if fn, fp, err := d.DownloadImage(); err == nil {
		fileName = fn
		filePath = fp
	} else {
		return err
	}

	if err := d.uploadImage(fileName, filePath); err != nil {
		return err
	}

	if d.img == "" {
		if err := d.extractImage(fileName); err != nil {
			return err
		}
	}

	return nil
}

func (d *flasher) uploadImage(fileName, filePath string) error {
	if _, eut, err := d.conf.SSH.Run("ls " + config.TmpDir + fileName); err != nil || len(strings.TrimSpace(eut)) > 0 {
		fmt.Printf("[+] Uploading %s to virtual machine\n", fileName)
		if err := d.conf.SSH.Scp(filePath, config.TmpDir); err != nil {
			return err
		}
	}
	log.Debug("Image already exists inside VM")
	log.WithField("path", filePath).WithField("name", fileName).Info("Image")
	return nil
}

func (d *flasher) extractImage(fileName string) error {
	if strings.HasSuffix(fileName, ".img") {
		d.img = fileName
		return nil
	}

	if strings.HasSuffix(fileName, ".zip") {
		if files, err := help.GetZipFiles(help.AddPathSuffix(runtime.GOOS, d.devRepo.Dir(), fileName)); err == nil && len(files) == 1 {
			if _, eut, err := d.conf.SSH.Run("ls " + config.TmpDir + files[0].Name); err == nil && len(strings.TrimSpace(eut)) == 0 {
				log.Debug("Image file already extracted")
				d.img = files[0].Name
			}
		}
	}
	d.conf.SSH.Run("rm -rf " + config.TmpDir + "*.img")
	fmt.Printf("[+] Extracting %s \n", fileName)
	command := fmt.Sprintf(help.GetExtractCommand(fileName), help.AddPathSuffix("unix", config.TmpDir, fileName), config.TmpDir)
	log.WithField("command", command).Debug("Extracting an image...")
	d.conf.SSH.SetTimer(help.SshExtendedCommandTimeout)
	out, eut, err := d.conf.SSH.Run(command)
	out = strings.TrimSpace(out)
	eut = strings.TrimSpace(eut)
	if err != nil || len(eut) > 0 {
		log.WithField("err", eut).Debug("Extract error")
		if eut == "unzip: crc error" || strings.Contains(eut, "unzip: corrupted data") {
			filePath := filepath.Join(d.devRepo.Dir(), fileName)
			if help.Exists(filePath) && retries < 2 {
				fmt.Println("[-] CRC error, re-trying...")
				log.WithField("filePath", filePath).Info("Trying to download and extract image again")
				help.DeleteFile(filePath)
				d.conf.SSH.Run("rm " + config.TmpDir + fileName)
				retries++
				return d.prepareImage()
			}
		}
		fmt.Println("[-] ", eut)
		return err
	}
	log.Debug(out)
	for _, raw := range strings.Split(out, " ") {
		s := strings.TrimSpace(raw)
		if s != "" && strings.HasSuffix(s, ".img") {
			d.img = s
			return nil
		}
	}
	if out, _, err := d.conf.SSH.Run("ls -1 " + config.TmpDir); err == nil && len(strings.TrimSpace(out)) > 0 {
		log.WithField("command", "ls -1").Debug(out)
		for _, raw := range strings.Split(strings.TrimSpace(out), "\n") {
			s := strings.TrimSpace(raw)
			if d.folder == "" {
				d.folder = s
			}
			if s != "" && strings.HasSuffix(s, ".img") {
				d.img = s
				return nil
			}
		}
	}
	return nil
}

// Configure is a generic mock method
func (d *flasher) Configure() error {
	fmt.Println("Mock, nothing to configure")
	return nil
}

// Write is a generic method
func (d *flasher) Write() error {
	fmt.Println("Mock, nothing to write")
	return nil
}

// Flash configures and flashes image
func (d *flasher) Flash() error {
	fmt.Println("Mock, nothing to flash")
	return nil
}

// Done prints out final success message
func (d *flasher) Done() error {
	if err := d.conf.Stop(d.Quiet); err != nil {
		log.Error(err)
	}
	fmt.Println("\t\t ...                      .................    ..                ")
	fmt.Println("\t\t ...                      .................   ....    ...        ")
	fmt.Println("\t\t ...                             ....                 ...        ")
	fmt.Println("\t\t ...          .....              ....                 ...        ")
	fmt.Println("\t\t ...       ...........           ....         ...     .......... ")
	fmt.Println("\t\t ...      ...       ...          ....         ...     ...        ")
	fmt.Println("\t\t ...     ...         ...         ....         ...     ...        ")
	fmt.Println("\t\t ...     ...         ...         ....         ...     ...        ")
	fmt.Println("\t\t ...     ...         ...         ....         ...     ...        ")
	fmt.Println("\t\t ...     ....       ....         ....         ...      ...       ")
	fmt.Println("\t\t ...      .....   .....          ....         ...      ....   .. ")
	fmt.Println("\t\t ...         .......             ....         ...        ....... ")
	fmt.Println("\n\t\t Flashing Complete!")
	fmt.Println("\t\t If you have any questions or suggestions feel free to make an issue at https://github.com/xshellinc/iotit/issues/ or tweet us @isaax_iot")
	return nil
}
<commit_msg>Add ability to verify sha256 hash of the image<commit_after>package device

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/xshellinc/go-virtualbox"
	"github.com/xshellinc/iotit/device/config"
	"github.com/xshellinc/iotit/repo"
	"github.com/xshellinc/iotit/vbox"
	"github.com/xshellinc/tools/dialogs"
	"github.com/xshellinc/tools/lib/help"
	"gopkg.in/urfave/cli.v1"
)

// Flasher is an entity for flashing different devices
type Flasher interface {
	Flash() error
	Configure() error
	Write() error
}

// flasher contains virtualbox machine, ssh connection, repository, currently selected device and image name
type flasher struct {
	Quiet   bool
	vbox    *virtualbox.Machine
	conf    *vbox.Config
	devRepo *repo.DeviceMapping
	CLI     *cli.Context

	img     string
	folder  string
	device  string
	mounted bool
}

var retries = 0

// validates given image path and downloads image archive to os tmp folder
func (d *flasher) DownloadImage() (fileName, filePath string, err error) {
	wg := &sync.WaitGroup{}
	if !help.ValidURL(d.devRepo.Image.URL) {
		// local file path given
		filePath = d.devRepo.Image.URL
		fileName = filepath.Base(filePath)
		log.WithField("path", filePath).WithField("name", fileName).Info("Custom image")
		if !help.Exists(filePath) {
			fmt.Println("[-] Image location is neither valid URL nor existing file. Aborting.")
			return "", "", errors.New("Invalid image location")
		}
		fmt.Println("[+] Using local image file for ", d.device)
		return fileName, filePath, err
	}
	// download image over http
	fmt.Println("[+] Starting download", d.devRepo.Image.Title)
	log.WithField("url", d.devRepo.Image.URL).WithField("dir", d.devRepo.Dir()).Debug("download")
	name, bar, err := help.DownloadFromUrlWithAttemptsAsync(d.devRepo.Image.URL, d.devRepo.Dir(), 3, wg)
	if err != nil {
		return "", "", err
	}
	fileName = name
	filePath = filepath.Join(d.devRepo.Dir(), fileName)

	bar.Prefix(fmt.Sprintf("[+] Download %-15s", fileName))
	bar.Start()
	wg.Wait()
	bar.Finish()
	time.Sleep(time.Second * 2)
	return fileName, filePath, err
}

// Prepare method inits virtualbox, downloads os image and uploads it into vm
func (d *flasher) Prepare() error {
	log.Debug("Prepare")
	if err := vbox.CheckVBInstalled(); err != nil {
		return err
	}

	d.conf = vbox.NewConfig(d.device)
	log.Debug("Configuring virtual box")
	var err error
	if d.vbox, err = d.conf.GetVbox(d.device, d.Quiet); err != nil {
		return err
	}
	log.WithField("name", d.vbox.Name).Info("Selected profile")

	if d.vbox.State != virtualbox.Running {
		fmt.Printf(`[+] Using virtual machine
	Name - `+dialogs.PrintColored("%s")+`
	Description - `+dialogs.PrintColored("%s")+"\n", d.vbox.Name, d.vbox.Description)

		if err := d.startVM(); err != nil {
			return err
		}
	}

	help.DeleteHost(filepath.Join(help.UserHomeDir(), ".ssh", "known_hosts"), "localhost")

	return d.prepareImage()
}

func (d *flasher) startVM() error {
	job := help.NewBackgroundJob()
	if err := d.vbox.Start(); err != nil {
		return err
	}

	go func() {
		ticker := time.NewTicker(15 * time.Second)
		defer ticker.Stop()
		defer job.Close()
		for {
			select {
			case <-ticker.C:
				_, eut, err := d.conf.SSH.Run("whoami")
				if err == nil && strings.TrimSpace(eut) == "" {
					return
				}
			case <-time.After(180 * time.Second):
				job.Error(errors.New("Cannot connect to vbox via ssh"))
			}
		}
	}()

	if err := help.WaitJobAndSpin("Starting", job); err != nil {
		return err
	}

	time.Sleep(time.Second)
	return nil
}

func (d *flasher) prepareImage() error {
	var (
		fileName, filePath string
	)

	if fn, fp, err := d.DownloadImage(); err == nil {
		fileName = fn
		filePath = fp
	} else {
		return err
	}

	if len(d.devRepo.Image.Hash) > 0 {
		fmt.Println("[+] Calculating sha256 of the downloaded image")
		f, err := os.Open(filePath)
		if err != nil {
			return err
		}
		defer f.Close()

		h := sha256.New()
		if _, err := io.Copy(h, f); err != nil {
			return err
		}
		hash := fmt.Sprintf("%x", h.Sum(nil))
		log.WithField("sum", hash).WithField("hash", d.devRepo.Image.Hash).Debug("comparing")
		if hash != d.devRepo.Image.Hash {
			return fmt.Errorf("wrong sha256 hash")
		}
		fmt.Println("[+] SHA256 verified")
	}

	if err := d.uploadImage(fileName, filePath); err != nil {
		return err
	}

	if d.img == "" {
		if err := d.extractImage(fileName); err != nil {
			return err
		}
	}

	return nil
}

func (d *flasher) uploadImage(fileName, filePath string) error {
	if _, eut, err := d.conf.SSH.Run("ls " + config.TmpDir + fileName); err != nil || len(strings.TrimSpace(eut)) > 0 {
		fmt.Printf("[+] Uploading %s to virtual machine\n", fileName)
		if err := d.conf.SSH.Scp(filePath, config.TmpDir); err != nil {
			return err
		}
	}
	log.Debug("Image already exists inside VM")
	log.WithField("path", filePath).WithField("name", fileName).Info("Image")
	return nil
}

func (d *flasher) extractImage(fileName string) error {
	if strings.HasSuffix(fileName, ".img") {
		d.img = fileName
		return nil
	}

	if strings.HasSuffix(fileName, ".zip") {
		if files, err := help.GetZipFiles(help.AddPathSuffix(runtime.GOOS, d.devRepo.Dir(), fileName)); err == nil && len(files) == 1 {
			if _, eut, err := d.conf.SSH.Run("ls " + config.TmpDir + files[0].Name); err == nil && len(strings.TrimSpace(eut)) == 0 {
				log.Debug("Image file already extracted")
				d.img = files[0].Name
			}
		}
	}
	d.conf.SSH.Run("rm -rf " + config.TmpDir + "*.img")
	fmt.Printf("[+] Extracting %s \n", fileName)
	command := fmt.Sprintf(help.GetExtractCommand(fileName), help.AddPathSuffix("unix", config.TmpDir, fileName), config.TmpDir)
	log.WithField("command", command).Debug("Extracting an image...")
	d.conf.SSH.SetTimer(help.SshExtendedCommandTimeout)
	out, eut, err := d.conf.SSH.Run(command)
	out = strings.TrimSpace(out)
	eut = strings.TrimSpace(eut)
	if err != nil || len(eut) > 0 {
		log.WithField("err", eut).Debug("Extract error")
		if eut == "unzip: crc error" || strings.Contains(eut, "unzip: corrupted data") {
			filePath := filepath.Join(d.devRepo.Dir(), fileName)
			if help.Exists(filePath) && retries < 2 {
				fmt.Println("[-] CRC error, re-trying...")
				log.WithField("filePath", filePath).Info("Trying to download and extract image again")
				help.DeleteFile(filePath)
				d.conf.SSH.Run("rm " + config.TmpDir + fileName)
				retries++
				return d.prepareImage()
			}
		}
		fmt.Println("[-] ", eut)
		return err
	}
	log.Debug(out)
	for _, raw := range strings.Split(out, " ") {
		s := strings.TrimSpace(raw)
		if s != "" && strings.HasSuffix(s, ".img") {
			d.img = s
			return nil
		}
	}
	if out, _, err := d.conf.SSH.Run("ls -1 " + config.TmpDir); err == nil && len(strings.TrimSpace(out)) > 0 {
		log.WithField("command", "ls -1").Debug(out)
		for _, raw := range strings.Split(strings.TrimSpace(out), "\n") {
			s := strings.TrimSpace(raw)
			if d.folder == "" {
				d.folder = s
			}
			if s != "" && strings.HasSuffix(s, ".img") {
				d.img = s
				return nil
			}
		}
	}
	return nil
}

// Configure is a generic mock method
func (d *flasher) Configure() error {
	fmt.Println("Mock, nothing to configure")
	return nil
}

// Write is a generic method
func (d *flasher) Write() error {
	fmt.Println("Mock, nothing to write")
	return nil
}

// Flash configures and flashes image
func (d *flasher) Flash() error {
	fmt.Println("Mock, nothing to flash")
	return nil
}

// Done prints out final success message
func (d *flasher) Done() error {
	if err := d.conf.Stop(d.Quiet); err != nil {
		log.Error(err)
	}
	fmt.Println("\t\t ...                      .................    ..                ")
	fmt.Println("\t\t ...                      .................   ....    ...        ")
	fmt.Println("\t\t ...                             ....                 ...        ")
	fmt.Println("\t\t ...          .....              ....                 ...        ")
	fmt.Println("\t\t ...       ...........           ....         ...     .......... ")
	fmt.Println("\t\t ...      ...       ...          ....         ...     ...        ")
	fmt.Println("\t\t ...     ...         ...         ....         ...     ...        ")
	fmt.Println("\t\t ...     ...         ...         ....         ...     ...        ")
	fmt.Println("\t\t ...     ...         ...         ....         ...     ...        ")
	fmt.Println("\t\t ...     ....       ....         ....         ...      ...       ")
	fmt.Println("\t\t ...      .....   .....          ....         ...      ....   .. ")
	fmt.Println("\t\t ...         .......             ....         ...        ....... ")
	fmt.Println("\n\t\t Flashing Complete!")
	fmt.Println("\t\t If you have any questions or suggestions feel free to make an issue at https://github.com/xshellinc/iotit/issues/ or tweet us @isaax_iot")
	return nil
}
<|endoftext|>


<commit_before>package splunkstream

import (
	"testing"
)

func TestNewClient(t *testing.T) {
	_, err := NewClient("localhost:8089", &Config{})

	if err != nil {
		t.Error(err)
	}
}
<commit_msg>Fix test for new config options<commit_after>package splunkstream

import (
	"testing"
)

func TestNewClient(t *testing.T) {
	_, err := NewClient(&Config{})

	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math"
	"time"

	// "github.com/davecgh/go-spew/spew"
	"github.com/lucasb-eyer/go-colorful"
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/clock"
	"github.com/ninjasphere/sphere-go-led-controller/ui"
	"github.com/ninjasphere/sphere-go-led-controller/util"
	"github.com/ninjasphere/usvc-lib/config"
)

var enableACPane = config.Bool(true, "ac.enable")
var tempAdjustInterval = config.Duration(time.Millisecond*300, "ac.airwheel.interval")
var airWheelReset = config.Duration(time.Millisecond*500, "ac.airwheel.reset")
var tapTimeout = config.Duration(time.Millisecond*500, "ac.tap.timeout")

var (
	red, _   = colorful.Hex("#fd9720")
	blue, _  = colorful.Hex("#45aff9")
	green, _ = colorful.Hex("#45f96b")
)

type ACPane struct {
	mode        string
	images      map[string]util.Image
	targetTemp  int
	currentTemp int
	flash       bool

	thermostat *ninja.ServiceClient
	acstat     *ninja.ServiceClient

	lastTempAdjust   time.Time
	lastAirWheelTime time.Time
	lastAirWheel     *uint8
	countSinceLast   int
	ignoringTap      bool
	ignoreTapTimer   *time.Timer
}

func NewACPane(conn *ninja.Connection) *ACPane {

	pane := &ACPane{
		mode:        "off",
		flash:       false,
		targetTemp:  666,
		currentTemp: 666,
		images: map[string]util.Image{
			"off":  util.LoadImage(util.ResolveImagePath("fan.gif")),
			"fan":  util.LoadImage(util.ResolveImagePath("fan-on.gif")),
			"cool": util.LoadImage(util.ResolveImagePath("fan-on-blue.gif")),
			"heat": util.LoadImage(util.ResolveImagePath("fan-on-red.gif")),
		},
	}

	pane.ignoreTapTimer = time.AfterFunc(0, func() {
		pane.ignoringTap = false
	})

	listening := make(map[string]bool)

	isFlash := func(s string) bool {
		return s == "SUSPENDED"
	}

	onState := func(protocol, event string, cb func(params *json.RawMessage)) {
		ui.GetChannelServicesContinuous("aircon", protocol, func(thing *model.Thing) bool {
			return true
		}, func(devices []*ninja.ServiceClient, err error) {
			if err != nil {
				log.Infof("Failed to update %s device: %s", protocol, err)
			} else {
				log.Infof("Got %d %s devices", len(devices), protocol)

				for _, device := range devices {

					log.Debugf("Checking %s device %s", protocol, device.Topic)

					if _, ok := listening[device.Topic]; !ok {

						// New device
						log.Infof("Got new %s device: %s", protocol, device.Topic)

						if protocol == "thermostat" {
							pane.thermostat = device
						}

						if protocol == "acstat" {
							pane.acstat = device
						}

						if protocol == "demandcontrol" {

							// only need to query for the initial notification - listener will pick up the rest

							go func() {
								var state StateChangeNotification
								err := device.Call("get", nil, &state, time.Second*10)
								if err != nil {
									log.Errorf("Failed to fetch state from demand control channel: %s", err)
								}

								// spew.Dump("Got demand state", state)

								pane.flash = isFlash(state.State)
							}()
						}

						listening[device.Topic] = true

						device.OnEvent(event, func(params *json.RawMessage, values map[string]string) bool {
							cb(params)

							return true
						})
					}
				}
			}
		})
	}

	onState("temperature", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal temp from %s error:%s", *params, err)
		}

		pane.currentTemp = int(temp)

		log.Infof("Got the temp %d", pane.currentTemp)
	})

	onState("thermostat", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal thermostat from %s error:%s", *params, err)
		}

		pane.targetTemp = int(temp)

		log.Infof("Got the thermostat %d", pane.targetTemp)
	})

	onState("acstat", "state", func(params *json.RawMessage) {
		var state channels.ACState
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal acstat from %s error:%s", *params, err)
		}

		pane.mode = *state.Mode

		log.Infof("Got the ac mode %d", pane.mode)
	})

	onState("demandcontrol", "state", func(params *json.RawMessage) {

		// spew.Dump("demand/controlstate", params)

		var state StateChangeNotification
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal demandstat state from %s error:%s", *params, err)
		}

		pane.flash = isFlash(state.State)

		log.Infof("Got the demandcontrol state %d", pane.mode)
	})

	go ui.StartSearchTasks(conn)

	return pane
}

type StateChangeNotification struct {
	State string `json:"state"` // the current state of the controller
}

func (p *ACPane) KeepAwake() bool {
	return false
}

func (p *ACPane) Locked() bool {
	return false
}

func (p *ACPane) IsEnabled() bool {
	return enableACPane
}

func (p *ACPane) Gesture(gesture *gestic.GestureMessage) {

	if p.acstat != nil && !p.ignoringTap && gesture.Tap.Active() {
		log.Infof("AC tap!")

		p.ignoringTap = true
		p.ignoreTapTimer.Reset(tapTimeout)

		switch p.mode {
		case "off":
			p.mode = "fan"
		case "fan":
			p.mode = "cool"
		case "cool":
			p.mode = "heat"
		case "heat":
			p.mode = "off"
		}

		p.acstat.Call("set", channels.ACState{
			Mode: &p.mode,
		}, nil, 0)

	}

	if p.thermostat != nil && p.targetTemp != 666 && p.lastAirWheel == nil || gesture.AirWheel.Counter != int(*p.lastAirWheel) {

		if time.Since(p.lastAirWheelTime) > airWheelReset {
			p.lastAirWheel = nil
		}

		if p.countSinceLast > gesture.AirWheel.CountSinceLast {
			p.lastAirWheel = nil
		}

		p.countSinceLast = gesture.AirWheel.CountSinceLast

		p.lastAirWheelTime = time.Now()

		log.Debugf("Airwheel: %d", gesture.AirWheel.Counter)

		if p.lastAirWheel != nil {
			offset := int(gesture.AirWheel.Counter) - int(*p.lastAirWheel)

			if offset > 30 {
				offset -= 255
			}

			if offset < -30 {
				offset += 255
			}

			log.Debugf("Airwheel New: %d Offset: %d Last: %d", gesture.AirWheel.Counter, offset, *p.lastAirWheel)

			log.Debugf("Current temp %f", p.targetTemp)

			log.Debugf("Temp offset %f", float64(offset)/255.0)

			if time.Since(p.lastTempAdjust) < tempAdjustInterval {
				log.Debugf("Temp rate limited")
			} else {
				p.lastTempAdjust = time.Now()
				log.Debugf("Temp NOT rate limited")
				if offset > 0 {
					p.targetTemp += 1
				} else {
					p.targetTemp -= 1
				}

				p.thermostat.Call("set", p.targetTemp, nil, 0)

			}

		}

		val := uint8(gesture.AirWheel.Counter)
		p.lastAirWheel = &val
	}
}

func (p *ACPane) Render() (*image.RGBA, error) {

	img := image.NewRGBA(image.Rect(0, 0, 16, 16))

	drawText := func(text string, col color.Color, x, y int) {
		clock.Font.DrawString(img, x, y, text, col)
	}

	draw.Draw(img, img.Bounds(), p.images[p.mode].GetNextFrame(), img.Bounds().Min, draw.Src)

	targetCol := green
	if p.flash {
		//drawText("$", green, 0, 11)
		h, s, v := targetCol.Hsv()
		targetCol = colorful.Hsv(sinTime(h), s, v)
	}

	if p.currentTemp != 666 {

		if p.currentTemp > p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), red, 0, 11)
		} else if p.currentTemp < p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), blue, 0, 11)
		}

	}

	if p.targetTemp != 666 {
		drawText(fmt.Sprintf("%02d", p.targetTemp), targetCol, 9, 11)
	}

	return img, nil
}

func (p *ACPane) IsDirty() bool {
	return true
}

func sinTime(x float64) float64 {
	t := float64(time.Now().Nanosecond()/int(time.Millisecond)) / 1000.0
	t = math.Sin((t - 0.5) * (2 * math.Pi))
	t = (t + 1) / 2
	return x * t
}
<commit_msg>fix nil pointer<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math"
	"time"

	// "github.com/davecgh/go-spew/spew"
	"github.com/lucasb-eyer/go-colorful"
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/clock"
	"github.com/ninjasphere/sphere-go-led-controller/ui"
	"github.com/ninjasphere/sphere-go-led-controller/util"
	"github.com/ninjasphere/usvc-lib/config"
)

var enableACPane = config.Bool(true, "ac.enable")
var tempAdjustInterval = config.Duration(time.Millisecond*300, "ac.airwheel.interval")
var airWheelReset = config.Duration(time.Millisecond*500, "ac.airwheel.reset")
var tapTimeout = config.Duration(time.Millisecond*500, "ac.tap.timeout")

var (
	red, _   = colorful.Hex("#fd9720")
	blue, _  = colorful.Hex("#45aff9")
	green, _ = colorful.Hex("#45f96b")
)

type ACPane struct {
	mode        string
	images      map[string]util.Image
	targetTemp  int
	currentTemp int
	flash       bool

	thermostat *ninja.ServiceClient
	acstat     *ninja.ServiceClient

	lastTempAdjust   time.Time
	lastAirWheelTime time.Time
	lastAirWheel     *uint8
	countSinceLast   int
	ignoringTap      bool
	ignoreTapTimer   *time.Timer
}

func NewACPane(conn *ninja.Connection) *ACPane {

	pane := &ACPane{
		mode:        "off",
		flash:       false,
		targetTemp:  666,
		currentTemp: 666,
		images: map[string]util.Image{
			"off":  util.LoadImage(util.ResolveImagePath("fan.gif")),
			"fan":  util.LoadImage(util.ResolveImagePath("fan-on.gif")),
			"cool": util.LoadImage(util.ResolveImagePath("fan-on-blue.gif")),
			"heat": util.LoadImage(util.ResolveImagePath("fan-on-red.gif")),
		},
	}

	pane.ignoreTapTimer = time.AfterFunc(0, func() {
		pane.ignoringTap = false
	})

	listening := make(map[string]bool)

	isFlash := func(s string) bool {
		return s == "SUSPENDED"
	}

	onState := func(protocol, event string, cb func(params *json.RawMessage)) {
		ui.GetChannelServicesContinuous("aircon", protocol, func(thing *model.Thing) bool {
			return true
		}, func(devices []*ninja.ServiceClient, err error) {
			if err != nil {
				log.Infof("Failed to update %s device: %s", protocol, err)
			} else {
				log.Infof("Got %d %s devices", len(devices), protocol)

				for _, device := range devices {

					log.Debugf("Checking %s device %s", protocol, device.Topic)

					if _, ok := listening[device.Topic]; !ok {

						// New device
						log.Infof("Got new %s device: %s", protocol, device.Topic)

						if protocol == "thermostat" {
							pane.thermostat = device
						}

						if protocol == "acstat" {
							pane.acstat = device
						}

						if protocol == "demandcontrol" {

							// only need to query for the initial notification - listener will pick up the rest

							go func() {
								var state StateChangeNotification
								err := device.Call("get", nil, &state, time.Second*10)
								if err != nil {
									log.Errorf("Failed to fetch state from demand control channel: %s", err)
								}

								// spew.Dump("Got demand state", state)

								pane.flash = isFlash(state.State)
							}()
						}

						listening[device.Topic] = true

						device.OnEvent(event, func(params *json.RawMessage, values map[string]string) bool {
							cb(params)

							return true
						})
					}
				}
			}
		})
	}

	onState("temperature", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal temp from %s error:%s", *params, err)
		}

		pane.currentTemp = int(temp)

		log.Infof("Got the temp %d", pane.currentTemp)
	})

	onState("thermostat", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal thermostat from %s error:%s", *params, err)
		}

		pane.targetTemp = int(temp)

		log.Infof("Got the thermostat %d", pane.targetTemp)
	})

	onState("acstat", "state", func(params *json.RawMessage) {
		var state channels.ACState
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal acstat from %s error:%s", *params, err)
		}

		pane.mode = *state.Mode

		log.Infof("Got the ac mode %d", pane.mode)
	})

	onState("demandcontrol", "state", func(params *json.RawMessage) {

		// spew.Dump("demand/controlstate", params)

		var state StateChangeNotification
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal demandstat state from %s error:%s", *params, err)
		}

		pane.flash = isFlash(state.State)

		log.Infof("Got the demandcontrol state %d", pane.mode)
	})

	go ui.StartSearchTasks(conn)

	return pane
}

type StateChangeNotification struct {
	State string `json:"state"` // the current state of the controller
}

func (p *ACPane) KeepAwake() bool {
	return false
}

func (p *ACPane) Locked() bool {
	return false
}

func (p *ACPane) IsEnabled() bool {
	return enableACPane
}

func (p *ACPane) Gesture(gesture *gestic.GestureMessage) {

	if p.acstat != nil && !p.ignoringTap && gesture.Tap.Active() {
		log.Infof("AC tap!")

		p.ignoringTap = true
		p.ignoreTapTimer.Reset(tapTimeout)

		switch p.mode {
		case "off":
			p.mode = "fan"
		case "fan":
			p.mode = "cool"
		case "cool":
			p.mode = "heat"
		case "heat":
			p.mode = "off"
		}

		p.acstat.Call("set", channels.ACState{
			Mode: &p.mode,
		}, nil, 0)

	}

	if (p.thermostat != nil && p.targetTemp != 666 && p.lastAirWheel == nil) || (p.lastAirWheel != nil && gesture.AirWheel.Counter != int(*p.lastAirWheel)) {

		if time.Since(p.lastAirWheelTime) > airWheelReset {
			p.lastAirWheel = nil
		}

		if p.countSinceLast > gesture.AirWheel.CountSinceLast {
			p.lastAirWheel = nil
		}

		p.countSinceLast = gesture.AirWheel.CountSinceLast

		p.lastAirWheelTime = time.Now()

		log.Debugf("Airwheel: %d", gesture.AirWheel.Counter)

		if p.lastAirWheel != nil {
			offset := int(gesture.AirWheel.Counter) - int(*p.lastAirWheel)

			if offset > 30 {
				offset -= 255
			}

			if offset < -30 {
				offset += 255
			}

			log.Debugf("Airwheel New: %d Offset: %d Last: %d", gesture.AirWheel.Counter, offset, *p.lastAirWheel)

			log.Debugf("Current temp %f", p.targetTemp)

			log.Debugf("Temp offset %f", float64(offset)/255.0)

			if time.Since(p.lastTempAdjust) < tempAdjustInterval {
				log.Debugf("Temp rate limited")
			} else {
				p.lastTempAdjust = time.Now()
				log.Debugf("Temp NOT rate limited")
				if offset > 0 {
					p.targetTemp += 1
				} else {
					p.targetTemp -= 1
				}

				p.thermostat.Call("set", p.targetTemp, nil, 0)

			}

		}

		val := uint8(gesture.AirWheel.Counter)
		p.lastAirWheel = &val
	}
}

func (p *ACPane) Render() (*image.RGBA, error) {

	img := image.NewRGBA(image.Rect(0, 0, 16, 16))

	drawText := func(text string, col color.Color, x, y int) {
		clock.Font.DrawString(img, x, y, text, col)
	}

	draw.Draw(img, img.Bounds(), p.images[p.mode].GetNextFrame(), img.Bounds().Min, draw.Src)

	targetCol := green
	if p.flash {
		//drawText("$", green, 0, 11)
		h, s, v := targetCol.Hsv()
		targetCol = colorful.Hsv(sinTime(h), s, v)
	}

	if p.currentTemp != 666 {

		if p.currentTemp > p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), red, 0, 11)
		} else if p.currentTemp < p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), blue, 0, 11)
		}

	}

	if p.targetTemp != 666 {
		drawText(fmt.Sprintf("%02d", p.targetTemp), targetCol, 9, 11)
	}

	return img, nil
}

func (p *ACPane) IsDirty() bool {
	return true
}

func sinTime(x float64) float64 {
	t := float64(time.Now().Nanosecond()/int(time.Millisecond)) / 1000.0
	t = math.Sin((t - 0.5) * (2 * math.Pi))
	t = (t + 1) / 2
	return x * t
}
<|endoftext|>


<commit_before>package digests

import (
	"encoding/json"
	"fmt"
	"math"
	"strings"

	"github.com/ion-channel/ionic/scanner"
	"github.com/ion-channel/ionic/scans"
)

var (
	// ErrFailedValueAssertion is the error returned when the value for
	// constructing a new digest does not convert to the digest data type
	// specified
	ErrFailedValueAssertion = fmt.Errorf("failed to assert value for given data type")

	// ErrUnsupportedType is the error returned when the data type for
	// constructing the digest is not a valid type
	ErrUnsupportedType = fmt.Errorf("unsupported digest data type")
)

// Digest is only the important parts for display
type Digest struct {
	Index          int             `json:"index"`
	Title          string          `json:"title"`
	Data           json.RawMessage `json:"data"`
	SourceData     json.RawMessage `json:"source_data"`
	ScanID         string          `json:"scan_id"`
	RuleID         string          `json:"rule_id"`
	RulesetID      string          `json:"ruleset_id"`
	Evaluated      bool            `json:"evaluated"`
	Pending        bool            `json:"pending"`
	Passed         bool            `json:"passed"`
	PassedMessage  string          `json:"passed_message"`
	Warning        bool            `json:"warning"`
	WarningMessage string          `json:"warning_message,omitempty"`
	Errored        bool            `json:"errored"`
	ErroredMessage string          `json:"errored_message,omitempty"`

	singularTitle string
	pluralTitle   string
}

type boolean struct {
	Bool bool `json:"bool"`
}

type chars struct {
	Chars string `json:"chars"`
}

type count struct {
	Count int `json:"count"`
}

type list struct {
	List []string `json:"list"`
}

type percent struct {
	Percent float64 `json:"percent"`
}

// String returns a JSON formatted string of the digest object
func (d Digest) String() string {
	b, err := json.Marshal(d)
	if err != nil {
		return fmt.Sprintf("failed to format user: %v", err.Error())
	}
	return string(b)
}

// NewDigest takes a scan status, ordering index, and a singular and plural
// version of the digest title. The status is leveraged to determine the first
// levels of a digest status. The index provides an ordered location if a list
// of Digests are sorted. The singular and plural titles are used to display as
// singular title if data is added that warrants a singular title, and otherwise
// will favor a plural title. A newly constructed Digest with the appropriate
// settings is returned.
func NewDigest(status *scanner.ScanStatus, index int, singular, plural string) *Digest {
	// We have other states now
	errored := false
	pending := true
	erroredMsg := "evaluation not received"

	if status != nil {
		switch status.Status {
		case scanner.AnalysisStatusErrored:
			errored = true
			erroredMsg = status.Message
			pending = false
		case scanner.AnalysisStatusQueued, scanner.AnalysisStatusAnalyzing:
			pending = true
		case scanner.AnalysisStatusFinished, scanner.AnalysisStatusFailed:
			pending = false
		default:
			errored = true
			pending = true
			erroredMsg = "Unknown status"
		}
	}

	d := &Digest{
		Index:          index,
		Pending:        pending,
		Errored:        errored,
		ErroredMessage: erroredMsg,
		Title:          plural,
		singularTitle:  singular,
		pluralTitle:    plural,
	}

	if status != nil {
		d.ScanID = status.ID
	}

	return d
}

// AppendEval takes an evaluation, data type, and value to interleave into the
// digest. It will also use the data to determine whether or not to show it's
// singular or plural title. It returns an error if the data type does not match
// the value provided.
func (d *Digest) AppendEval(eval *scans.Evaluation, dataType string, value interface{}) error {
	var data []byte
	var err error
	title := d.pluralTitle

	switch strings.ToLower(dataType) {
	case "bool", "boolean":
		b, ok := value.(bool)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(boolean{b})
	case "chars":
		c, ok := value.(string)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(chars{c})
	case "count":
		c, ok := value.(int)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(count{c})
		if c == 1 {
			title = d.singularTitle
		}

		// Counts less than 0 are an indicator of error state. Record where the
		// information came from, but do not overwrite errors and other states.
		if c < 0 {
			d.ScanID = eval.ID
			d.RuleID = eval.RuleID
			d.RulesetID = eval.RulesetID

			return nil
		}
	case "list":
		l, ok := value.([]string)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(&list{l})
		if len(l) == 1 {
			title = d.singularTitle
		}
	case "percent":
		p, ok := value.(float64)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(percent{math.Round(p*100) / 100})
	default:
		return ErrUnsupportedType
	}

	if err != nil {
		return fmt.Errorf("failed to marshal digest data: %v", err.Error())
	}

	d.Data = data
	d.Title = title
	d.Errored = false
	d.ErroredMessage = ""
	d.ScanID = eval.ID
	d.RuleID = eval.RuleID
	d.RulesetID = eval.RulesetID
	d.Evaluated = (strings.ToLower(eval.Type) != "not evaluated")
	d.Passed = eval.Passed
	d.PassedMessage = eval.Description

	return nil
}

// MarshalSourceData takes a data struct, generates json
// and sets the source data on the digest
func (d *Digest) MarshalSourceData(data interface{}, t string) error {
	r := scans.TranslatedResults{
		Type: t,
		Data: data,
	}
	b, err := json.Marshal(&r)
	if err != nil {
		return fmt.Errorf("failed to marshal digest data: %v", err.Error())
	}
	d.SourceData = b
	return nil
}

// UseSingularTitle forcibly sets the title to be singular
func (d *Digest) UseSingularTitle() {
	d.Title = d.singularTitle
}

// UsePluralTitle forcibly sets the title to be plural
func (d *Digest) UsePluralTitle() {
	d.Title = d.pluralTitle
}
<commit_msg>Trigger analysis<commit_after>package digests

import (
	"encoding/json"
	"fmt"
	"math"
	"strings"

	"github.com/ion-channel/ionic/scanner"
	"github.com/ion-channel/ionic/scans"
)

var (
	// ErrFailedValueAssertion is the error returned when the value for
	// constructing a new digest does not convert to the digest data type
	// specified
	ErrFailedValueAssertion = fmt.Errorf("failed to assert value for given data type")

	// ErrUnsupportedType is the error returned when the data type for
	// constructing the digest is not a valid type
	ErrUnsupportedType = fmt.Errorf("unsupported digest data type")
)

// Digest is only the important parts for display
type Digest struct {
	Index          int             `json:"index"`
	Title          string          `json:"title"`
	Data           json.RawMessage `json:"data"`
	SourceData     json.RawMessage `json:"source_data"`
	ScanID         string          `json:"scan_id"`
	RuleID         string          `json:"rule_id"`
	RulesetID      string          `json:"ruleset_id"`
	Evaluated      bool            `json:"evaluated"`
	Pending        bool            `json:"pending"`
	Passed         bool            `json:"passed"`
	PassedMessage  string          `json:"passed_message"`
	Warning        bool            `json:"warning"`
	WarningMessage string          `json:"warning_message,omitempty"`
	Errored        bool            `json:"errored"`
	ErroredMessage string          `json:"errored_message,omitempty"`

	singularTitle string
	pluralTitle   string
}

type boolean struct {
	Bool bool `json:"bool"`
}

type chars struct {
	Chars string `json:"chars"`
}

type count struct {
	Count int `json:"count"`
}

type list struct {
	List []string `json:"list"`
}

type percent struct {
	Percent float64 `json:"percent"`
}

// String returns a JSON formatted string of the digest object
func (d Digest) String() string {
	b, err := json.Marshal(d)
	if err != nil {
		return fmt.Sprintf("failed to format user: %v", err.Error())
	}
	return string(b)
}

// NewDigest takes a scan status, ordering index, and a singular and plural
// version of the digest title. The status is leveraged to determine the first
// levels of a digest status. The index provides an ordered location if a list
// of Digests are sorted. The singular and plural titles are used to display as
// singular title if data is added that warrants a singular title, and otherwise
// will favor a plural title. A newly constructed Digest with the appropriate
// settings is returned.
func NewDigest(status *scanner.ScanStatus, index int, singular, plural string) *Digest {
	// We have other states now
	errored := false
	pending := true
	erroredMsg := "evaluation not received"

	if status != nil {
		switch status.Status {
		case scanner.AnalysisStatusErrored:
			errored = true
			erroredMsg = status.Message
			pending = false
		case scanner.AnalysisStatusQueued, scanner.AnalysisStatusAnalyzing:
			pending = true
		case scanner.AnalysisStatusFinished, scanner.AnalysisStatusFailed:
			pending = false
		default:
			errored = true
			pending = true
			erroredMsg = "Unknown status"
		}
	}

	d := &Digest{
		Index:          index,
		Pending:        pending,
		Errored:        errored,
		ErroredMessage: erroredMsg,
		Title:          plural,
		singularTitle:  singular,
		pluralTitle:    plural,
	}

	if status != nil {
		d.ScanID = status.ID
	}

	return d
}

// AppendEval takes an evaluation, data type, and value to interleave into the
// digest. It will also use the data to determine whether or not to show it's
// singular or plural title. It returns an error if the data type does not match
// the value provided.
func (d *Digest) AppendEval(eval *scans.Evaluation, dataType string, value interface{}) error {
	var data []byte
	var err error
	title := d.pluralTitle

	switch strings.ToLower(dataType) {
	case "bool", "boolean":
		b, ok := value.(bool)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(boolean{b})
	case "chars":
		c, ok := value.(string)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(chars{c})
	case "count":
		c, ok := value.(int)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(count{c})
		if c == 1 {
			title = d.singularTitle
		}

		// Counts less than 0 are an indicator of error state. Record where the
		// information came from, but do not overwrite errors and other states.
		if c < 0 {
			d.ScanID = eval.ID
			d.RuleID = eval.RuleID
			d.RulesetID = eval.RulesetID

			return nil
		}
	case "list":
		l, ok := value.([]string)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(&list{l})
		if len(l) == 1 {
			title = d.singularTitle
		}
	case "percent":
		p, ok := value.(float64)
		if !ok {
			return ErrFailedValueAssertion
		}

		data, err = json.Marshal(percent{math.Round(p*100) / 100})
	default:
		return ErrUnsupportedType
	}

	if err != nil {
		return fmt.Errorf("failed to marshal digest data: %v", err.Error())
	}

	d.Data = data
	d.Title = title
	d.Errored = false
	d.ErroredMessage = ""
	d.ScanID = eval.ID
	d.RuleID = eval.RuleID
	d.RulesetID = eval.RulesetID
	d.Evaluated = (strings.ToLower(eval.Type) != "not evaluated")
	d.Passed = eval.Passed
	d.PassedMessage = eval.Description

	return nil
}

// MarshalSourceData takes a data struct, generates json
// and sets the source data on the digest
func (d *Digest) MarshalSourceData(data interface{}, t string) error {
	r := scans.TranslatedResults{
		Type: t,
		Data: data,
	}
	b, err := json.Marshal(&r)
	if err != nil {
		return fmt.Errorf("failed to marshal digest data: %v", err.Error())
	}
	d.SourceData = b

	return nil
}

// UseSingularTitle forcibly sets the title to be singular
func (d *Digest) UseSingularTitle() {
	d.Title = d.singularTitle
}

// UsePluralTitle forcibly sets the title to be plural
func (d *Digest) UsePluralTitle() {
	d.Title = d.pluralTitle
}
<|endoftext|>