<commit_before>/*
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * Copyright 2017 OpsVision Solutions
 *
 * 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 signalfx

import (
	"bytes"
	"fmt"
	"github.com/intelsdi-x/snap-plugin-lib-go/v1/plugin"
	"github.com/signalfx/golib/datapoint"
	"github.com/signalfx/golib/sfxclient"
	"golang.org/x/net/context"
	"log"
	"os"
	"strings"
)

const (
	NS_VENDOR = "opsvision"
	NS_PLUGIN = "signalfx"
	VERSION   = 1
)

var fileHandle *os.File

type SignalFx struct {
	initialized bool
	token       string
	hostname    string
	namespace   string
}

// Constructor
func New() *SignalFx {
	return new(SignalFx)
}

func (s *SignalFx) init() error {
	s.initialized = true

	return nil
}

/**
 * Returns the configPolicy for the plugin
 */
func (s *SignalFx) GetConfigPolicy() (plugin.ConfigPolicy, error) {
	policy := plugin.NewConfigPolicy()

	// The SignalFx token
	policy.AddNewStringRule([]string{NS_VENDOR, NS_PLUGIN},
		"token",
		true)

	// The hostname to use (defaults to local hostname)
	policy.AddNewStringRule([]string{NS_VENDOR, NS_PLUGIN},
		"hostname",
		false)

	return *policy, nil
}

/**
 * Publish metrics to SignalFx using the TOKEN found in the config
 */
func (s *SignalFx) Publish(mts []plugin.Metric, cfg plugin.Config) error {
	// Make sure we've initialized
	if !s.initialized {
		s.init()
	}

	// Set the output file
	f, err := os.OpenFile("/tmp/signalfx-plugin.debug", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	defer f.Close()

	log.SetOutput(f)
	log.Printf("Inside publisher")

	// Fetch the token
	token, err := cfg.GetString("token")
	if err != nil {
		return err
	}
	s.token = token

	// Attempt to set the hostname
	hostname, err := cfg.GetString("hostname")
	if err != nil {
		hostname, err = os.Hostname()
		if err != nil {
			hostname = "localhost"
		}
	}
	s.hostname = hostname

	// Iterate over the supplied metrics
	for _, m := range mts {
		var buffer bytes.Buffer

		// Convert the namespace to dot notation
		fmt.Fprintf(&buffer, "snap.%s", strings.Join(m.Namespace.Strings(), "."))
		s.namespace = buffer.String()

		// Do some type conversion and send the data
		switch v := m.Data.(type) {
		case uint:
			s.sendIntValue(int64(v))
		case uint32:
			s.sendIntValue(int64(v))
		case uint64:
			s.sendIntValue(int64(v))
		case int:
			s.sendIntValue(int64(v))
		case int32:
			s.sendIntValue(int64(v))
		case int64:
			s.sendIntValue(int64(v))
		case float32:
			s.sendFloatValue(float64(v))
		case float64:
			s.sendFloatValue(float64(v))
		default:
			fmt.Printf("Ignoring %T: %v\n", v, v)
		}
	}

	return nil
}

/**
 *
 */
func (s *SignalFx) sendIntValue(value int64) {
	client := sfxclient.NewHTTPDatapointSink()
	client.AuthToken = s.token
	ctx := context.Background()
	client.AddDatapoints(ctx, []*datapoint.Datapoint{
		sfxclient.Gauge(s.namespace, map[string]string{
			"host": s.hostname,
		}, value),
	})
}

/**
 *
 */
func (s *SignalFx) sendFloatValue(value float64) {
	client := sfxclient.NewHTTPDatapointSink()
	client.AuthToken = s.token
	ctx := context.Background()
	client.AddDatapoints(ctx, []*datapoint.Datapoint{
		sfxclient.GaugeF(s.namespace, map[string]string{
			"host": s.hostname,
		}, value),
	})
}
<commit_msg>Tweaked logging<commit_after>/*
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * Copyright 2017 OpsVision Solutions
 *
 * 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 signalfx

import (
	"bytes"
	"fmt"
	"github.com/intelsdi-x/snap-plugin-lib-go/v1/plugin"
	"github.com/signalfx/golib/datapoint"
	"github.com/signalfx/golib/sfxclient"
	"golang.org/x/net/context"
	"log"
	"os"
	"strings"
)

const (
	NS_VENDOR = "opsvision"
	NS_PLUGIN = "signalfx"
	VERSION   = 1
)

var fileHandle *os.File

type SignalFx struct {
	initialized bool
	token       string
	hostname    string
	namespace   string
}

// Constructor
func New() *SignalFx {
	return new(SignalFx)
}

func (s *SignalFx) init() error {
	s.initialized = true

	return nil
}

/**
 * Returns the configPolicy for the plugin
 */
func (s *SignalFx) GetConfigPolicy() (plugin.ConfigPolicy, error) {
	policy := plugin.NewConfigPolicy()

	// The SignalFx token
	policy.AddNewStringRule([]string{NS_VENDOR, NS_PLUGIN},
		"token",
		true)

	// The hostname to use (defaults to local hostname)
	policy.AddNewStringRule([]string{NS_VENDOR, NS_PLUGIN},
		"hostname",
		false)

	return *policy, nil
}

/**
 * Publish metrics to SignalFx using the TOKEN found in the config
 */
func (s *SignalFx) Publish(mts []plugin.Metric, cfg plugin.Config) error {
	// Make sure we've initialized
	if !s.initialized {
		s.init()
	}

	// Set the output file
	f, err := os.OpenFile("/tmp/signalfx-plugin.debug", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	defer f.Close()

	// Set logging output for debugging
	log.SetOutput(f)

	// Fetch the token
	token, err := cfg.GetString("token")
	if err != nil {
		return err
	}
	s.token = token

	// Attempt to set the hostname
	hostname, err := cfg.GetString("hostname")
	if err != nil {
		hostname, err = os.Hostname()
		if err != nil {
			hostname = "localhost"
		}
	}
	s.hostname = hostname

	// Iterate over the supplied metrics
	for _, m := range mts {
		var buffer bytes.Buffer

		// Convert the namespace to dot notation
		fmt.Fprintf(&buffer, "snap.%s", strings.Join(m.Namespace.Strings(), "."))
		s.namespace = buffer.String()

		// Do some type conversion and send the data
		switch v := m.Data.(type) {
		case uint:
			s.sendIntValue(int64(v))
		case uint32:
			s.sendIntValue(int64(v))
		case uint64:
			s.sendIntValue(int64(v))
		case int:
			s.sendIntValue(int64(v))
		case int32:
			s.sendIntValue(int64(v))
		case int64:
			s.sendIntValue(int64(v))
		case float32:
			s.sendFloatValue(float64(v))
		case float64:
			s.sendFloatValue(float64(v))
		default:
			fmt.Printf("Ignoring %T: %v\n", v, v)
		}
	}

	return nil
}

/**
 *
 */
func (s *SignalFx) sendIntValue(value int64) {
	log.Printf("%s -> %v", s.namespace, value)

	client := sfxclient.NewHTTPDatapointSink()
	client.AuthToken = s.token
	ctx := context.Background()
	client.AddDatapoints(ctx, []*datapoint.Datapoint{
		sfxclient.Gauge(s.namespace, map[string]string{
			"host": s.hostname,
		}, value),
	})
}

/**
 *
 */
func (s *SignalFx) sendFloatValue(value float64) {
	log.Printf("%s -> %v", s.namespace, value)

	client := sfxclient.NewHTTPDatapointSink()
	client.AuthToken = s.token
	ctx := context.Background()
	client.AddDatapoints(ctx, []*datapoint.Datapoint{
		sfxclient.GaugeF(s.namespace, map[string]string{
			"host": s.hostname,
		}, value),
	})
}
<|endoftext|>


<commit_before>package routing

import (
	"net/http"

	"github.com/asaintgenis/catapi/utils"
	"github.com/gorilla/mux"
)

//NewRouter create a new router
func NewRouter() *mux.Router {

	router := mux.NewRouter().StrictSlash(true)
	for _, route := range routes {

		var handler http.Handler
		handler = route.HandlerFunc
		handler = utils.Logger(handler, route.Name)
		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)
	}

	router.Handle("/", http.FileServer(http.Dir("./web/")))

	return router
}
<commit_msg>front is now expose to /static/<commit_after>package routing

import (
	"net/http"

	"github.com/asaintgenis/catapi/utils"
	"github.com/gorilla/mux"
)

//NewRouter create a new router
func NewRouter() *mux.Router {

	router := mux.NewRouter().StrictSlash(true)
	for _, route := range routes {

		var handler http.Handler
		handler = route.HandlerFunc
		handler = utils.Logger(handler, route.Name)
		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)
	}

	router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./web/"))))

	return router
}
<|endoftext|>


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

package rpc

import (
	"testing"

	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/leaktest"
	"github.com/cockroachdb/cockroach/util/stop"
	"github.com/gogo/protobuf/proto"
)

func TestRemoteOffsetString(t *testing.T) {
	defer leaktest.AfterTest(t)()
	ro := RemoteOffset{
		Offset:      -501584461,
		Uncertainty: 351698,
		MeasuredAt:  1430348776127420269,
	}
	expStr := "off=-501.584461ms, err=351.698µs, at=2015-04-29 23:06:16.127420269 +0000 UTC"
	if str := ro.String(); str != expStr {
		t.Errorf("expected %s; got %s", expStr, str)
	}
}

func TestHeartbeatReply(t *testing.T) {
	defer leaktest.AfterTest(t)()
	manual := hlc.NewManualClock(5)
	clock := hlc.NewClock(manual.UnixNano)
	heartbeat := &HeartbeatService{
		clock:              clock,
		remoteClockMonitor: newRemoteClockMonitor(clock),
	}

	request := &PingRequest{
		Ping: "testPing",
	}
	response, err := heartbeat.Ping(context.Background(), request)
	if err != nil {
		t.Fatal(err)
	}

	if response.Pong != request.Ping {
		t.Errorf("expected %s to be equal to %s", response.Pong, request.Ping)
	}

	if response.ServerTime != 5 {
		t.Errorf("expected server time 5, instead %d", response.ServerTime)
	}
}

func TestManualHeartbeat(t *testing.T) {
	defer leaktest.AfterTest(t)()
	manual := hlc.NewManualClock(5)
	clock := hlc.NewClock(manual.UnixNano)
	manualHeartbeat := &ManualHeartbeatService{
		clock:              clock,
		remoteClockMonitor: newRemoteClockMonitor(clock),
		ready:              make(chan struct{}, 1),
	}
	regularHeartbeat := &HeartbeatService{
		clock:              clock,
		remoteClockMonitor: newRemoteClockMonitor(clock),
	}

	request := &PingRequest{
		Ping: "testManual",
	}
	manualHeartbeat.ready <- struct{}{}
	ctx := context.Background()
	regularResponse, err := regularHeartbeat.Ping(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	manualResponse, err := manualHeartbeat.Ping(ctx, request)
	if err != nil {
		t.Fatal(err)
	}

	// Ensure that the response is the same as with a normal heartbeat.
	if manualResponse.Pong != regularResponse.Pong {
		t.Errorf("expected pong %s, instead %s",
			manualResponse.Pong, regularResponse.Pong)
	}
	if manualResponse.ServerTime != regularResponse.ServerTime {
		t.Errorf("expected ServerTime %d, instead %d",
			manualResponse.ServerTime, regularResponse.ServerTime)
	}
}

func TestUpdateOffsetOnHeartbeat(t *testing.T) {
	defer leaktest.AfterTest(t)()

	stopper := stop.NewStopper()
	defer stopper.Stop()

	ctx := newNodeTestContext(nil, stopper)

	_, ln := newTestServer(t, ctx, false)
	remoteAddr := ln.Addr().String()
	ctx.RemoteClocks.mu.Lock()
	ctx.RemoteClocks.mu.offsets[remoteAddr] = RemoteOffset{
		Offset:      10,
		Uncertainty: 5,
		MeasuredAt:  20,
	}
	ctx.RemoteClocks.mu.Unlock()
	// Create a client and set its remote offset. On first heartbeat,
	// it will update the server's remote clocks map.
	_, err := ctx.GRPCDial(remoteAddr)
	if err != nil {
		t.Fatal(err)
	}

	ctx.RemoteClocks.mu.Lock()
	o := ctx.RemoteClocks.mu.offsets[remoteAddr]
	ctx.RemoteClocks.mu.Unlock()
	expServerOffset := RemoteOffset{Offset: -10, Uncertainty: 5, MeasuredAt: 20}
	if proto.Equal(&o, &expServerOffset) {
		t.Errorf("expected updated offset %v, instead %v", expServerOffset, o)
	}
	ln.Close()

	// Remove the offset from RemoteClocks and close the connection from the
	// remote end. A new offset for the server should not be added to the clock
	// monitor.
	ctx.RemoteClocks.mu.Lock()
	delete(ctx.RemoteClocks.mu.offsets, remoteAddr)
	ln.Close()
	ctx.RemoteClocks.mu.Unlock()

	ctx.RemoteClocks.mu.Lock()
	if offset, ok := ctx.RemoteClocks.mu.offsets[remoteAddr]; ok {
		t.Errorf("unexpected updated offset: %v", offset)
	}
	ctx.RemoteClocks.mu.Unlock()
}
<commit_msg>rpc: remove nonsense test<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Kathy Spradlin (kathyspradlin@gmail.com)

package rpc

import (
	"testing"

	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/leaktest"
)

func TestRemoteOffsetString(t *testing.T) {
	defer leaktest.AfterTest(t)()
	ro := RemoteOffset{
		Offset:      -501584461,
		Uncertainty: 351698,
		MeasuredAt:  1430348776127420269,
	}
	expStr := "off=-501.584461ms, err=351.698µs, at=2015-04-29 23:06:16.127420269 +0000 UTC"
	if str := ro.String(); str != expStr {
		t.Errorf("expected %s; got %s", expStr, str)
	}
}

func TestHeartbeatReply(t *testing.T) {
	defer leaktest.AfterTest(t)()
	manual := hlc.NewManualClock(5)
	clock := hlc.NewClock(manual.UnixNano)
	heartbeat := &HeartbeatService{
		clock:              clock,
		remoteClockMonitor: newRemoteClockMonitor(clock),
	}

	request := &PingRequest{
		Ping: "testPing",
	}
	response, err := heartbeat.Ping(context.Background(), request)
	if err != nil {
		t.Fatal(err)
	}

	if response.Pong != request.Ping {
		t.Errorf("expected %s to be equal to %s", response.Pong, request.Ping)
	}

	if response.ServerTime != 5 {
		t.Errorf("expected server time 5, instead %d", response.ServerTime)
	}
}

func TestManualHeartbeat(t *testing.T) {
	defer leaktest.AfterTest(t)()
	manual := hlc.NewManualClock(5)
	clock := hlc.NewClock(manual.UnixNano)
	manualHeartbeat := &ManualHeartbeatService{
		clock:              clock,
		remoteClockMonitor: newRemoteClockMonitor(clock),
		ready:              make(chan struct{}, 1),
	}
	regularHeartbeat := &HeartbeatService{
		clock:              clock,
		remoteClockMonitor: newRemoteClockMonitor(clock),
	}

	request := &PingRequest{
		Ping: "testManual",
	}
	manualHeartbeat.ready <- struct{}{}
	ctx := context.Background()
	regularResponse, err := regularHeartbeat.Ping(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	manualResponse, err := manualHeartbeat.Ping(ctx, request)
	if err != nil {
		t.Fatal(err)
	}

	// Ensure that the response is the same as with a normal heartbeat.
	if manualResponse.Pong != regularResponse.Pong {
		t.Errorf("expected pong %s, instead %s",
			manualResponse.Pong, regularResponse.Pong)
	}
	if manualResponse.ServerTime != regularResponse.ServerTime {
		t.Errorf("expected ServerTime %d, instead %d",
			manualResponse.ServerTime, regularResponse.ServerTime)
	}
}
<|endoftext|>


<commit_before>package test

import (
	"strconv"
	"testing"
	"time"

	"github.com/vkuznet/transfer2go/core"
)

// Compare 1k files of source with the same 1k files of destination
func TestCompareThousand(t *testing.T) {
	var sourceCatalog []core.CatalogEntry
	destinationCatalog := make([]core.CatalogEntry, 1000)
	dataset := "/a/b/c"
	block := "/a/b/c#123"
	for i := 0; i < 1000; i++ {
		rec := core.CatalogEntry{Dataset: dataset, Block: block, Lfn: block + "-" + dataset + "file" + strconv.Itoa(i) + ".root"}
		sourceCatalog = append(sourceCatalog, rec)
	}
	copy(destinationCatalog[:], sourceCatalog)
	start := time.Now()
	records := core.CompareRecords(sourceCatalog, destinationCatalog)
	elapsed := time.Since(start)
	t.Log("For 1k took %s", elapsed)
	if records != nil {
		t.Errorf("Incorrect Match for 1k files: %d", len(records))
	}
}

// Compare 10k files of source with the same 10k files of destination
func TestCompareTenThousand(t *testing.T) {
	var sourceCatalog []core.CatalogEntry
	destinationCatalog := make([]core.CatalogEntry, 10000)
	dataset := "/a/b/c"
	block := "/a/b/c#123"
	for i := 0; i < 10000; i++ {
		rec := core.CatalogEntry{Dataset: dataset, Block: block, Lfn: block + "-" + dataset + "file" + strconv.Itoa(i) + ".root"}
		sourceCatalog = append(sourceCatalog, rec)
	}
	copy(destinationCatalog[:], sourceCatalog)
	start := time.Now()
	records := core.CompareRecords(sourceCatalog, destinationCatalog)
	elapsed := time.Since(start)
	t.Log("For 10k took %s", elapsed)
	if records != nil {
		t.Errorf("Incorrect Match for 1k files: %d", len(records))
	}
}

// Compare 1k files of source with the distinct 10k files of destination
func TestCompareTenThousandUncommon(t *testing.T) {
	var sourceCatalog []core.CatalogEntry
	var destinationCatalog []core.CatalogEntry
	dataset := "/a/b/c"
	block := "/a/b/c#123"
	for i := 0; i < 10000; i++ {
		rec := core.CatalogEntry{Dataset: dataset, Block: block, Lfn: block + "-" + dataset + "file" + strconv.Itoa(i) + ".root"}
		sourceCatalog = append(sourceCatalog, rec)
	}
	start := time.Now()
	records := core.CompareRecords(sourceCatalog, destinationCatalog)
	elapsed := time.Since(start)
	t.Log("For 10k uncommon took %s", elapsed)
	if records != nil {
		t.Log("Uncommon files: %d", len(records))
	}
}
<commit_msg>Changes in print command<commit_after>package test

import (
	"strconv"
	"testing"
	"time"

	"github.com/vkuznet/transfer2go/core"
)

// Compare 1k files of source with the same 1k files of destination
func TestCompareThousand(t *testing.T) {
	var sourceCatalog []core.CatalogEntry
	destinationCatalog := make([]core.CatalogEntry, 1000)
	dataset := "/a/b/c"
	block := "/a/b/c#123"
	for i := 0; i < 1000; i++ {
		rec := core.CatalogEntry{Dataset: dataset, Block: block, Lfn: block + "-" + dataset + "file" + strconv.Itoa(i) + ".root"}
		sourceCatalog = append(sourceCatalog, rec)
	}
	copy(destinationCatalog[:], sourceCatalog)
	start := time.Now()
	records := core.CompareRecords(sourceCatalog, destinationCatalog)
	elapsed := time.Since(start)
	t.Log("For 1k it took", elapsed)
	if records != nil {
		t.Errorf("Incorrect Match for 1k files: %d", len(records))
	}
}

// Compare 10k files of source with the same 10k files of destination
func TestCompareTenThousand(t *testing.T) {
	var sourceCatalog []core.CatalogEntry
	destinationCatalog := make([]core.CatalogEntry, 10000)
	dataset := "/a/b/c"
	block := "/a/b/c#123"
	for i := 0; i < 10000; i++ {
		rec := core.CatalogEntry{Dataset: dataset, Block: block, Lfn: block + "-" + dataset + "file" + strconv.Itoa(i) + ".root"}
		sourceCatalog = append(sourceCatalog, rec)
	}
	copy(destinationCatalog[:], sourceCatalog)
	start := time.Now()
	records := core.CompareRecords(sourceCatalog, destinationCatalog)
	elapsed := time.Since(start)
	t.Log("For 10k it took", elapsed)
	if records != nil {
		t.Errorf("Incorrect Match for 1k files: %d", len(records))
	}
}

// Compare 1k files of source with the distinct 10k files of destination
func TestCompareTenThousandUncommon(t *testing.T) {
	var sourceCatalog []core.CatalogEntry
	var destinationCatalog []core.CatalogEntry
	dataset := "/a/b/c"
	block := "/a/b/c#123"
	for i := 0; i < 10000; i++ {
		rec := core.CatalogEntry{Dataset: dataset, Block: block, Lfn: block + "-" + dataset + "file" + strconv.Itoa(i) + ".root"}
		sourceCatalog = append(sourceCatalog, rec)
	}
	start := time.Now()
	records := core.CompareRecords(sourceCatalog, destinationCatalog)
	elapsed := time.Since(start)
	t.Log("For 10k uncommon it took", elapsed)
	if records != nil {
		t.Log("Need to transfer total files:", len(records))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"syscall"

	"github.com/ansel1/merry"
	"github.com/dickeyxxx/golock"
)

func init() {
	Topics = append(Topics, &Topic{
		Name:        "plugins",
		Description: "manage plugins",
		Commands: CommandSet{
			{
				Topic:            "plugins",
				Hidden:           true,
				Description:      "Lists installed plugins",
				DisableAnalytics: true,
				Flags: []Flag{
					{Name: "core", Description: "show core plugins"},
				},
				Help: `
Example:
  $ heroku plugins`,

				Run: pluginsList,
			},
			{
				Topic:        "plugins",
				Command:      "install",
				Hidden:       true,
				VariableArgs: true,
				Description:  "Installs a plugin into the CLI",
				Help: `Install a Heroku plugin

  Example:
  $ heroku plugins:install heroku-production-status`,

				Run: pluginsInstall,
			},
			{
				Topic:       "plugins",
				Command:     "link",
				Description: "Links a local plugin into CLI",
				Args:        []Arg{{Name: "path", Optional: true}},
				Help: `Links a local plugin into CLI.
	This is useful when developing plugins locally.
	It simply symlinks the specified path into the plugins directory
	and parses the plugin.

	You will need to run it again if you change any of the plugin metadata.

  Example:
	$ heroku plugins:link .`,

				Run: pluginsLink,
			},
			{
				Topic:       "plugins",
				Command:     "uninstall",
				Hidden:      true,
				Args:        []Arg{{Name: "name"}},
				Description: "Uninstalls a plugin from the CLI",
				Help: `Uninstalls a Heroku plugin

  Example:
  $ heroku plugins:uninstall heroku-production-status`,

				Run: pluginsUninstall,
			},
		},
	})
}

func pluginsList(ctx *Context) {
	var names []string
	for _, plugin := range UserPlugins.Plugins() {
		symlinked := ""
		if UserPlugins.isPluginSymlinked(plugin.Name) {
			symlinked = " (symlinked)"
		}
		names = append(names, fmt.Sprintf("%s %s%s", plugin.Name, plugin.Version, symlinked))
	}
	if ctx.Flags["core"] != nil {
		UserPluginNames := UserPlugins.PluginNames()
		for _, plugin := range CorePlugins.Plugins() {
			if contains(UserPluginNames, plugin.Name) {
				continue
			}
			names = append(names, fmt.Sprintf("%s %s (core)", plugin.Name, plugin.Version))
		}
	}
	sort.Strings(names)
	for _, plugin := range names {
		Println(plugin)
	}
}
func pluginsInstall(ctx *Context) {
	plugins := ctx.Args.([]string)
	if len(plugins) == 0 {
		ExitWithMessage("Must specify a plugin name.\nUSAGE: heroku plugins:install heroku-debug")
	}
	toinstall := make([]string, 0, len(plugins))
	core := CorePlugins.PluginNames()
	for _, plugin := range plugins {
		if contains(core, strings.Split(plugin, "@")[0]) {
			Warn("Not installing " + plugin + " because it is already installed as a core plugin.")
			continue
		}
		toinstall = append(toinstall, plugin)
	}
	if len(toinstall) == 0 {
		Exit(1)
	}
	action("Installing "+plural("plugin", len(toinstall))+" "+strings.Join(toinstall, " "), "done", func() {
		err := UserPlugins.InstallPlugins(toinstall...)
		if err != nil {
			if strings.Contains(err.Error(), "no such package available") {
				ExitWithMessage("Plugin not found")
			}
			must(err)
		}
	})
}

func pluginsLink(ctx *Context) {
	path := ctx.Args.(map[string]string)["path"]
	if path == "" {
		path = "."
	}
	path, err := filepath.Abs(path)
	must(err)
	_, err = os.Stat(path)
	must(err)
	name := filepath.Base(path)
	action("Symlinking "+name, "done", func() {
		newPath := UserPlugins.pluginPath(name)
		os.Remove(newPath)
		os.RemoveAll(newPath)
		os.MkdirAll(filepath.Dir(newPath), 0755)
		err = os.Symlink(path, newPath)
		must(err)
		plugin, err := UserPlugins.ParsePlugin(name)
		must(err)
		if name != plugin.Name {
			path = newPath
			newPath = UserPlugins.pluginPath(plugin.Name)
			os.Remove(newPath)
			os.RemoveAll(newPath)
			os.Rename(path, newPath)
		}
		UserPlugins.addToCache(plugin)
	})
}

func pluginsUninstall(ctx *Context) {
	name := ctx.Args.(map[string]string)["name"]
	if !contains(UserPlugins.PluginNames(), name) {
		must(errors.New(name + " is not installed"))
	}
	Errf("Uninstalling plugin %s...", name)
	must(UserPlugins.RemovePackages(name))
	UserPlugins.removeFromCache(name)
	Errln(" done")
}

// Plugins represents either core or user plugins
type Plugins struct {
	Path    string
	plugins []*Plugin
}

// CorePlugins are built in plugins
var CorePlugins = &Plugins{Path: filepath.Join(AppDir, "lib")}

// UserPlugins are user-installable plugins
var UserPlugins = &Plugins{Path: filepath.Join(DataHome, "plugins")}

// Plugin represents a javascript plugin
type Plugin struct {
	Name     string     `json:"name"`
	Version  string     `json:"version"`
	Topics   TopicSet   `json:"topics"`
	Topic    *Topic     `json:"topic"`
	Commands CommandSet `json:"commands"`
}

// Commands lists all the commands of the plugins
func (p *Plugins) Commands() (commands CommandSet) {
	for _, plugin := range p.Plugins() {
		for _, command := range plugin.Commands {
			command.Run = p.runFn(plugin, command.Topic, command.Command)
			commands = append(commands, command)
		}
	}
	return
}

// Topics gets all the plugin's topics
func (p *Plugins) Topics() (topics TopicSet) {
	for _, plugin := range p.Plugins() {
		if plugin.Topic != nil {
			topics = append(topics, plugin.Topic)
		}
		topics = append(topics, plugin.Topics...)
	}
	return
}

func (p *Plugins) runFn(plugin *Plugin, topic, command string) func(ctx *Context) {
	return func(ctx *Context) {
		p.readLockPlugin(plugin.Name)
		ctx.Dev = p.isPluginSymlinked(plugin.Name)
		ctxJSON, err := json.Marshal(ctx)
		must(err)
		args, err := json.Marshal(Args)
		must(err)
		title, _ := json.Marshal("heroku " + strings.Join(Args[1:], " "))

		script := fmt.Sprintf(`'use strict'
process.argv = %s
let pluginName = '%s'
let pluginVersion = '%s'
let topic = '%s'
let command = '%s'
process.title = %s
let ctx = %s
ctx.version = ctx.version + ' ' + pluginName + '/' + pluginVersion + ' node-' + process.version
process.chdir(ctx.cwd)
if (command === '') { command = null }
let plugin = require(pluginName)
let cmd = plugin.commands.filter((c) => c.topic === topic && c.command == command)[0]
cmd.run(ctx)
`, args, plugin.Name, plugin.Version, topic, command, string(title), ctxJSON)

		// swallow sigint since the plugin will handle it
		swallowSigint = true

		currentAnalyticsCommand.Plugin = plugin.Name
		currentAnalyticsCommand.Version = plugin.Version
		currentAnalyticsCommand.Language = fmt.Sprintf("node/" + NodeVersion)

		cmd, done := p.RunScript(script)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		done()
		Exit(getExitCode(err))
	}
}

func getExitCode(err error) int {
	switch e := err.(type) {
	case nil:
		return 0
	case *exec.ExitError:
		status, ok := e.Sys().(syscall.WaitStatus)
		if !ok {
			must(err)
		}
		return status.ExitStatus()
	}
	must(err)
	return -1
}

// ParsePlugin requires the plugin's node module
// to get the commands and metadata
func (p *Plugins) ParsePlugin(name string) (*Plugin, error) {
	script := `
	var plugin = require('` + name + `');
	var pjson  = require('` + name + `/package.json');

	plugin.name    = pjson.name;
	plugin.version = pjson.version;

	console.log(JSON.stringify(plugin))`
	cmd, done := p.RunScript(script)
	cmd.Stderr = Stderr
	output, err := cmd.Output()
	done()

	if err != nil {
		return nil, merry.Errorf("Error installing plugin %s", name)
	}
	var plugin Plugin
	err = json.Unmarshal(output, &plugin)
	if err != nil {
		return nil, fmt.Errorf("Error parsing plugin: %s\n%s\n%s", name, err, string(output))
	}
	if len(plugin.Commands) == 0 {
		return nil, fmt.Errorf("Invalid plugin. No commands found.")
	}
	for _, command := range plugin.Commands {
		if command == nil {
			continue
		}
		command.Plugin = plugin.Name
		command.Help = strings.TrimSpace(command.Help)
	}
	return &plugin, nil
}

// PluginNames lists all the plugin names
func (p *Plugins) PluginNames() []string {
	plugins := p.Plugins()
	names := make([]string, 0, len(plugins))
	for _, plugin := range plugins {
		names = append(names, plugin.Name)
	}
	return names
}

// PluginNamesNotSymlinked lists all the plugin names that are not symlinked
func (p *Plugins) PluginNamesNotSymlinked() []string {
	plugins := p.PluginNames()
	names := make([]string, 0, len(plugins))
	for _, plugin := range plugins {
		if !p.isPluginSymlinked(plugin) {
			names = append(names, plugin)
		}
	}
	return names
}

func (p *Plugins) isPluginSymlinked(plugin string) bool {
	path := filepath.Join(p.modulesPath(), plugin)
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	return fi.Mode()&os.ModeSymlink != 0
}

func contains(arr []string, s string) bool {
	for _, a := range arr {
		if a == s {
			return true
		}
	}
	return false
}

// InstallPlugins installs plugins
func (p *Plugins) InstallPlugins(names ...string) error {
	for _, name := range names {
		p.lockPlugin(name)
	}
	defer func() {
		for _, name := range names {
			p.unlockPlugin(name)
		}
	}()
	err := p.installPackages(names...)
	if err != nil {
		return err
	}
	plugins := make([]*Plugin, len(names))
	for i, name := range names {
		plugin, err := p.ParsePlugin(name)
		if err != nil {
			return err
		}
		plugins[i] = plugin
	}
	p.addToCache(plugins...)
	return nil
}

// directory location of plugin
func (p *Plugins) pluginPath(plugin string) string {
	return filepath.Join(p.Path, "node_modules", plugin)
}

// name of lockfile
func (p *Plugins) lockfile(name string) string {
	return filepath.Join(p.Path, name+".updating")
}

// lock a plugin for reading
func (p *Plugins) readLockPlugin(name string) {
	locked, err := golock.IsLocked(p.lockfile(name))
	LogIfError(err)
	if locked {
		p.lockPlugin(name)
		p.unlockPlugin(name)
	}
}

// lock a plugin for writing
func (p *Plugins) lockPlugin(name string) {
	os.MkdirAll(filepath.Dir(p.lockfile(name)), 0755)
	LogIfError(golock.Lock(p.lockfile(name)))
}

// unlock a plugin
func (p *Plugins) unlockPlugin(name string) {
	LogIfError(golock.Unlock(p.lockfile(name)))
}

// Update updates the plugins
func (p *Plugins) Update() {
	plugins := p.PluginNamesNotSymlinked()
	if len(plugins) == 0 {
		return
	}
	packages, err := p.OutdatedPackages(plugins...)
	WarnIfError(err)
	if len(packages) > 0 {
		action("heroku-cli: Updating plugins", "", func() {
			for name, version := range packages {
				p.lockPlugin(name)
				WarnIfError(p.installPackages(name + "@" + version))
				plugin, err := p.ParsePlugin(name)
				WarnIfError(err)
				p.addToCache(plugin)
				p.unlockPlugin(name)
			}
		})
		Errf(" done. Updated %d %s.\n", len(packages), plural("package", len(packages)))
	}
}

func (p *Plugins) addToCache(plugins ...*Plugin) {
	contains := func(name string) int {
		for i, plugin := range p.plugins {
			if plugin.Name == name {
				return i
			}
		}
		return -1
	}
	for _, plugin := range plugins {
		// find or replace
		i := contains(plugin.Name)
		if i == -1 {
			p.plugins = append(p.plugins, plugin)
		} else {
			p.plugins[i] = plugin
		}
	}
	p.saveCache()
}

func (p *Plugins) removeFromCache(name string) {
	for i, plugin := range p.plugins {
		if plugin.Name == name {
			p.plugins = append(p.plugins[:i], p.plugins[i+1:]...)
		}
	}
	p.saveCache()
}

func (p *Plugins) saveCache() {
	if err := saveJSON(p.plugins, p.cachePath()); err != nil {
		must(err)
	}
}

// Plugins reads the cache file into the struct
func (p *Plugins) Plugins() []*Plugin {
	if p.plugins == nil {
		p.plugins = []*Plugin{}
		if exists, _ := FileExists(p.cachePath()); !exists {
			return p.plugins
		}
		f, err := os.Open(p.cachePath())
		if err != nil {
			LogIfError(err)
			return p.plugins
		}
		err = json.NewDecoder(f).Decode(&p.plugins)
		WarnIfError(err)
	}
	return p.plugins
}

func (p *Plugins) cachePath() string {
	return filepath.Join(p.Path, "plugins.json")
}
<commit_msg>do not bubble up plugin not installed message to rollbar<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"syscall"

	"github.com/ansel1/merry"
	"github.com/dickeyxxx/golock"
)

func init() {
	Topics = append(Topics, &Topic{
		Name:        "plugins",
		Description: "manage plugins",
		Commands: CommandSet{
			{
				Topic:            "plugins",
				Hidden:           true,
				Description:      "Lists installed plugins",
				DisableAnalytics: true,
				Flags: []Flag{
					{Name: "core", Description: "show core plugins"},
				},
				Help: `
Example:
  $ heroku plugins`,

				Run: pluginsList,
			},
			{
				Topic:        "plugins",
				Command:      "install",
				Hidden:       true,
				VariableArgs: true,
				Description:  "Installs a plugin into the CLI",
				Help: `Install a Heroku plugin

  Example:
  $ heroku plugins:install heroku-production-status`,

				Run: pluginsInstall,
			},
			{
				Topic:       "plugins",
				Command:     "link",
				Description: "Links a local plugin into CLI",
				Args:        []Arg{{Name: "path", Optional: true}},
				Help: `Links a local plugin into CLI.
	This is useful when developing plugins locally.
	It simply symlinks the specified path into the plugins directory
	and parses the plugin.

	You will need to run it again if you change any of the plugin metadata.

  Example:
	$ heroku plugins:link .`,

				Run: pluginsLink,
			},
			{
				Topic:       "plugins",
				Command:     "uninstall",
				Hidden:      true,
				Args:        []Arg{{Name: "name"}},
				Description: "Uninstalls a plugin from the CLI",
				Help: `Uninstalls a Heroku plugin

  Example:
  $ heroku plugins:uninstall heroku-production-status`,

				Run: pluginsUninstall,
			},
		},
	})
}

func pluginsList(ctx *Context) {
	var names []string
	for _, plugin := range UserPlugins.Plugins() {
		symlinked := ""
		if UserPlugins.isPluginSymlinked(plugin.Name) {
			symlinked = " (symlinked)"
		}
		names = append(names, fmt.Sprintf("%s %s%s", plugin.Name, plugin.Version, symlinked))
	}
	if ctx.Flags["core"] != nil {
		UserPluginNames := UserPlugins.PluginNames()
		for _, plugin := range CorePlugins.Plugins() {
			if contains(UserPluginNames, plugin.Name) {
				continue
			}
			names = append(names, fmt.Sprintf("%s %s (core)", plugin.Name, plugin.Version))
		}
	}
	sort.Strings(names)
	for _, plugin := range names {
		Println(plugin)
	}
}
func pluginsInstall(ctx *Context) {
	plugins := ctx.Args.([]string)
	if len(plugins) == 0 {
		ExitWithMessage("Must specify a plugin name.\nUSAGE: heroku plugins:install heroku-debug")
	}
	toinstall := make([]string, 0, len(plugins))
	core := CorePlugins.PluginNames()
	for _, plugin := range plugins {
		if contains(core, strings.Split(plugin, "@")[0]) {
			Warn("Not installing " + plugin + " because it is already installed as a core plugin.")
			continue
		}
		toinstall = append(toinstall, plugin)
	}
	if len(toinstall) == 0 {
		Exit(1)
	}
	action("Installing "+plural("plugin", len(toinstall))+" "+strings.Join(toinstall, " "), "done", func() {
		err := UserPlugins.InstallPlugins(toinstall...)
		if err != nil {
			if strings.Contains(err.Error(), "no such package available") {
				ExitWithMessage("Plugin not found")
			}
			must(err)
		}
	})
}

func pluginsLink(ctx *Context) {
	path := ctx.Args.(map[string]string)["path"]
	if path == "" {
		path = "."
	}
	path, err := filepath.Abs(path)
	must(err)
	_, err = os.Stat(path)
	must(err)
	name := filepath.Base(path)
	action("Symlinking "+name, "done", func() {
		newPath := UserPlugins.pluginPath(name)
		os.Remove(newPath)
		os.RemoveAll(newPath)
		os.MkdirAll(filepath.Dir(newPath), 0755)
		err = os.Symlink(path, newPath)
		must(err)
		plugin, err := UserPlugins.ParsePlugin(name)
		must(err)
		if name != plugin.Name {
			path = newPath
			newPath = UserPlugins.pluginPath(plugin.Name)
			os.Remove(newPath)
			os.RemoveAll(newPath)
			os.Rename(path, newPath)
		}
		UserPlugins.addToCache(plugin)
	})
}

func pluginsUninstall(ctx *Context) {
	name := ctx.Args.(map[string]string)["name"]
	if !contains(UserPlugins.PluginNames(), name) {
		ExitWithMessage("%s is not installed", name)
	}
	Errf("Uninstalling plugin %s...", name)
	must(UserPlugins.RemovePackages(name))
	UserPlugins.removeFromCache(name)
	Errln(" done")
}

// Plugins represents either core or user plugins
type Plugins struct {
	Path    string
	plugins []*Plugin
}

// CorePlugins are built in plugins
var CorePlugins = &Plugins{Path: filepath.Join(AppDir, "lib")}

// UserPlugins are user-installable plugins
var UserPlugins = &Plugins{Path: filepath.Join(DataHome, "plugins")}

// Plugin represents a javascript plugin
type Plugin struct {
	Name     string     `json:"name"`
	Version  string     `json:"version"`
	Topics   TopicSet   `json:"topics"`
	Topic    *Topic     `json:"topic"`
	Commands CommandSet `json:"commands"`
}

// Commands lists all the commands of the plugins
func (p *Plugins) Commands() (commands CommandSet) {
	for _, plugin := range p.Plugins() {
		for _, command := range plugin.Commands {
			command.Run = p.runFn(plugin, command.Topic, command.Command)
			commands = append(commands, command)
		}
	}
	return
}

// Topics gets all the plugin's topics
func (p *Plugins) Topics() (topics TopicSet) {
	for _, plugin := range p.Plugins() {
		if plugin.Topic != nil {
			topics = append(topics, plugin.Topic)
		}
		topics = append(topics, plugin.Topics...)
	}
	return
}

func (p *Plugins) runFn(plugin *Plugin, topic, command string) func(ctx *Context) {
	return func(ctx *Context) {
		p.readLockPlugin(plugin.Name)
		ctx.Dev = p.isPluginSymlinked(plugin.Name)
		ctxJSON, err := json.Marshal(ctx)
		must(err)
		args, err := json.Marshal(Args)
		must(err)
		title, _ := json.Marshal("heroku " + strings.Join(Args[1:], " "))

		script := fmt.Sprintf(`'use strict'
process.argv = %s
let pluginName = '%s'
let pluginVersion = '%s'
let topic = '%s'
let command = '%s'
process.title = %s
let ctx = %s
ctx.version = ctx.version + ' ' + pluginName + '/' + pluginVersion + ' node-' + process.version
process.chdir(ctx.cwd)
if (command === '') { command = null }
let plugin = require(pluginName)
let cmd = plugin.commands.filter((c) => c.topic === topic && c.command == command)[0]
cmd.run(ctx)
`, args, plugin.Name, plugin.Version, topic, command, string(title), ctxJSON)

		// swallow sigint since the plugin will handle it
		swallowSigint = true

		currentAnalyticsCommand.Plugin = plugin.Name
		currentAnalyticsCommand.Version = plugin.Version
		currentAnalyticsCommand.Language = fmt.Sprintf("node/" + NodeVersion)

		cmd, done := p.RunScript(script)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		done()
		Exit(getExitCode(err))
	}
}

func getExitCode(err error) int {
	switch e := err.(type) {
	case nil:
		return 0
	case *exec.ExitError:
		status, ok := e.Sys().(syscall.WaitStatus)
		if !ok {
			must(err)
		}
		return status.ExitStatus()
	}
	must(err)
	return -1
}

// ParsePlugin requires the plugin's node module
// to get the commands and metadata
func (p *Plugins) ParsePlugin(name string) (*Plugin, error) {
	script := `
	var plugin = require('` + name + `');
	var pjson  = require('` + name + `/package.json');

	plugin.name    = pjson.name;
	plugin.version = pjson.version;

	console.log(JSON.stringify(plugin))`
	cmd, done := p.RunScript(script)
	cmd.Stderr = Stderr
	output, err := cmd.Output()
	done()

	if err != nil {
		return nil, merry.Errorf("Error installing plugin %s", name)
	}
	var plugin Plugin
	err = json.Unmarshal(output, &plugin)
	if err != nil {
		return nil, fmt.Errorf("Error parsing plugin: %s\n%s\n%s", name, err, string(output))
	}
	if len(plugin.Commands) == 0 {
		return nil, fmt.Errorf("Invalid plugin. No commands found.")
	}
	for _, command := range plugin.Commands {
		if command == nil {
			continue
		}
		command.Plugin = plugin.Name
		command.Help = strings.TrimSpace(command.Help)
	}
	return &plugin, nil
}

// PluginNames lists all the plugin names
func (p *Plugins) PluginNames() []string {
	plugins := p.Plugins()
	names := make([]string, 0, len(plugins))
	for _, plugin := range plugins {
		names = append(names, plugin.Name)
	}
	return names
}

// PluginNamesNotSymlinked lists all the plugin names that are not symlinked
func (p *Plugins) PluginNamesNotSymlinked() []string {
	plugins := p.PluginNames()
	names := make([]string, 0, len(plugins))
	for _, plugin := range plugins {
		if !p.isPluginSymlinked(plugin) {
			names = append(names, plugin)
		}
	}
	return names
}

func (p *Plugins) isPluginSymlinked(plugin string) bool {
	path := filepath.Join(p.modulesPath(), plugin)
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	return fi.Mode()&os.ModeSymlink != 0
}

func contains(arr []string, s string) bool {
	for _, a := range arr {
		if a == s {
			return true
		}
	}
	return false
}

// InstallPlugins installs plugins
func (p *Plugins) InstallPlugins(names ...string) error {
	for _, name := range names {
		p.lockPlugin(name)
	}
	defer func() {
		for _, name := range names {
			p.unlockPlugin(name)
		}
	}()
	err := p.installPackages(names...)
	if err != nil {
		return err
	}
	plugins := make([]*Plugin, len(names))
	for i, name := range names {
		plugin, err := p.ParsePlugin(name)
		if err != nil {
			return err
		}
		plugins[i] = plugin
	}
	p.addToCache(plugins...)
	return nil
}

// directory location of plugin
func (p *Plugins) pluginPath(plugin string) string {
	return filepath.Join(p.Path, "node_modules", plugin)
}

// name of lockfile
func (p *Plugins) lockfile(name string) string {
	return filepath.Join(p.Path, name+".updating")
}

// lock a plugin for reading
func (p *Plugins) readLockPlugin(name string) {
	locked, err := golock.IsLocked(p.lockfile(name))
	LogIfError(err)
	if locked {
		p.lockPlugin(name)
		p.unlockPlugin(name)
	}
}

// lock a plugin for writing
func (p *Plugins) lockPlugin(name string) {
	os.MkdirAll(filepath.Dir(p.lockfile(name)), 0755)
	LogIfError(golock.Lock(p.lockfile(name)))
}

// unlock a plugin
func (p *Plugins) unlockPlugin(name string) {
	LogIfError(golock.Unlock(p.lockfile(name)))
}

// Update updates the plugins
func (p *Plugins) Update() {
	plugins := p.PluginNamesNotSymlinked()
	if len(plugins) == 0 {
		return
	}
	packages, err := p.OutdatedPackages(plugins...)
	WarnIfError(err)
	if len(packages) > 0 {
		action("heroku-cli: Updating plugins", "", func() {
			for name, version := range packages {
				p.lockPlugin(name)
				WarnIfError(p.installPackages(name + "@" + version))
				plugin, err := p.ParsePlugin(name)
				WarnIfError(err)
				p.addToCache(plugin)
				p.unlockPlugin(name)
			}
		})
		Errf(" done. Updated %d %s.\n", len(packages), plural("package", len(packages)))
	}
}

func (p *Plugins) addToCache(plugins ...*Plugin) {
	contains := func(name string) int {
		for i, plugin := range p.plugins {
			if plugin.Name == name {
				return i
			}
		}
		return -1
	}
	for _, plugin := range plugins {
		// find or replace
		i := contains(plugin.Name)
		if i == -1 {
			p.plugins = append(p.plugins, plugin)
		} else {
			p.plugins[i] = plugin
		}
	}
	p.saveCache()
}

func (p *Plugins) removeFromCache(name string) {
	for i, plugin := range p.plugins {
		if plugin.Name == name {
			p.plugins = append(p.plugins[:i], p.plugins[i+1:]...)
		}
	}
	p.saveCache()
}

func (p *Plugins) saveCache() {
	if err := saveJSON(p.plugins, p.cachePath()); err != nil {
		must(err)
	}
}

// Plugins reads the cache file into the struct
func (p *Plugins) Plugins() []*Plugin {
	if p.plugins == nil {
		p.plugins = []*Plugin{}
		if exists, _ := FileExists(p.cachePath()); !exists {
			return p.plugins
		}
		f, err := os.Open(p.cachePath())
		if err != nil {
			LogIfError(err)
			return p.plugins
		}
		err = json.NewDecoder(f).Decode(&p.plugins)
		WarnIfError(err)
	}
	return p.plugins
}

func (p *Plugins) cachePath() string {
	return filepath.Join(p.Path, "plugins.json")
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"kubevirt.io/kubevirt/tools/util"

	k8sv1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sfield "k8s.io/apimachinery/pkg/util/validation/field"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/kubevirt/pkg/testutils"
	validating_webhook "kubevirt.io/kubevirt/pkg/virt-api/webhooks/validating-webhook/admitters"
	virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
	"kubevirt.io/kubevirt/tools/vms-generator/utils"
)

func main() {
	flag.StringVar(&utils.DockerPrefix, "container-prefix", utils.DockerPrefix, "")
	flag.StringVar(&utils.DockerTag, "container-tag", utils.DockerTag, "")
	genDir := flag.String("generated-vms-dir", "", "")
	flag.Parse()

	config, _, _, _ := testutils.NewFakeClusterConfig(&k8sv1.ConfigMap{
		Data: map[string]string{
			// Required to validate DataVolume usage
			virtconfig.FeatureGatesKey:                   "DataVolumes,LiveMigration,SRIOV,GPU,HostDisk",
			virtconfig.PermitSlirpInterface:              "true",
			virtconfig.PermitBridgeInterfaceOnPodNetwork: "true",
		},
	})
	var priorityClasses = map[string]*schedulingv1.PriorityClass{
		utils.Preemtible:    utils.GetPreemtible(),
		utils.NonPreemtible: utils.GetNonPreemtible(),
	}

	var vms = map[string]*v1.VirtualMachine{
		utils.VmCirros:           utils.GetVMCirros(),
		utils.VmAlpineMultiPvc:   utils.GetVMMultiPvc(),
		utils.VmAlpineDataVolume: utils.GetVMDataVolume(),
		utils.VMPriorityClass:    utils.GetVMPriorityClass(),
	}

	var vmis = map[string]*v1.VirtualMachineInstance{
		utils.VmiEphemeral:         utils.GetVMIEphemeral(),
		utils.VmiMigratable:        utils.GetVMIMigratable(),
		utils.VmiFlavorSmall:       utils.GetVMIFlavorSmall(),
		utils.VmiSata:              utils.GetVMISata(),
		utils.VmiFedora:            utils.GetVMIEphemeralFedora(),
		utils.VmiSecureBoot:        utils.GetVMISecureBoot(),
		utils.VmiAlpineEFI:         utils.GetVMIAlpineEFI(),
		utils.VmiNoCloud:           utils.GetVMINoCloud(),
		utils.VmiPVC:               utils.GetVMIPvc(),
		utils.VmiBlockPVC:          utils.GetVMIBlockPvc(),
		utils.VmiWindows:           utils.GetVMIWindows(),
		utils.VmiSlirp:             utils.GetVMISlirp(),
		utils.VmiSRIOV:             utils.GetVMISRIOV(),
		utils.VmiWithHookSidecar:   utils.GetVMIWithHookSidecar(),
		utils.VmiMultusPtp:         utils.GetVMIMultusPtp(),
		utils.VmiMultusMultipleNet: utils.GetVMIMultusMultipleNet(),
		utils.VmiMasquerade:        utils.GetVMIMasquerade(),
		utils.VmiHostDisk:          utils.GetVMIHostDisk(),
		utils.VmiGPU:               utils.GetVMIGPU(),
	}

	var vmireplicasets = map[string]*v1.VirtualMachineInstanceReplicaSet{
		utils.VmiReplicaSetCirros: utils.GetVMIReplicaSetCirros(),
	}

	var vmipresets = map[string]*v1.VirtualMachineInstancePreset{
		utils.VmiPresetSmall: utils.GetVMIPresetSmall(),
	}

	var migrations = map[string]*v1.VirtualMachineInstanceMigration{
		utils.VmiMigration: utils.GetVMIMigration(),
	}

	var templates = map[string]*utils.Template{
		utils.VmTemplateFedora:  utils.GetTemplateFedora(),
		utils.VmTemplateRHEL7:   utils.GetTemplateRHEL7(),
		utils.VmTemplateWindows: utils.GetTemplateWindows(),
	}

	handleError := func(err error) {
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			panic(err)
		}
	}

	handleCauses := func(causes []metav1.StatusCause, name string, objType string) {
		if len(causes) > 0 {
			for _, cause := range causes {
				fmt.Fprintf(
					os.Stderr,
					"Failed to validate %s spec: failed to admit yaml for %s: %s at %s: %s\n",
					objType, name, cause.Type, cause.Field, cause.Message)
			}
			panic(fmt.Errorf("Failed to admit %s of type %s", name, objType))
		}
	}

	dumpObject := func(name string, obj interface{}) error {

		filename := filepath.Join(*genDir, fmt.Sprintf("%s.yaml", name))
		file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			return fmt.Errorf("Failed to open file %v, %v", filename, err)
		}
		defer file.Close()

		util.MarshallObject(obj, file)

		return nil
	}

	// Having no generics is lots of fun
	for name, obj := range vms {
		causes := validating_webhook.ValidateVirtualMachineSpec(k8sfield.NewPath("spec"), &obj.Spec, config)
		handleCauses(causes, name, "vm")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range vmis {
		causes := validating_webhook.ValidateVirtualMachineInstanceSpec(k8sfield.NewPath("spec"), &obj.Spec, config)
		handleCauses(causes, name, "vmi")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range vmireplicasets {
		causes := validating_webhook.ValidateVMIRSSpec(k8sfield.NewPath("spec"), &obj.Spec, config)
		handleCauses(causes, name, "vmi replica set")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range vmipresets {
		causes := validating_webhook.ValidateVMIPresetSpec(k8sfield.NewPath("spec"), &obj.Spec)
		handleCauses(causes, name, "vmi preset")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range migrations {
		causes := validating_webhook.ValidateVirtualMachineInstanceMigrationSpec(k8sfield.NewPath("spec"), &obj.Spec)
		handleCauses(causes, name, "vmi preset")
		handleError(dumpObject(name, *obj))
	}

	// TODO:(ihar) how to validate templates?
	for name, obj := range templates {
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range priorityClasses {
		handleError(dumpObject(name, *obj))
	}
}
<commit_msg>Pass account name to comply to VM creation admission control.<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"kubevirt.io/kubevirt/tools/util"

	k8sv1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sfield "k8s.io/apimachinery/pkg/util/validation/field"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/kubevirt/pkg/testutils"
	validating_webhook "kubevirt.io/kubevirt/pkg/virt-api/webhooks/validating-webhook/admitters"
	virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
	"kubevirt.io/kubevirt/tools/vms-generator/utils"
)

func main() {
	flag.StringVar(&utils.DockerPrefix, "container-prefix", utils.DockerPrefix, "")
	flag.StringVar(&utils.DockerTag, "container-tag", utils.DockerTag, "")
	genDir := flag.String("generated-vms-dir", "", "")
	flag.Parse()

	config, _, _, _ := testutils.NewFakeClusterConfig(&k8sv1.ConfigMap{
		Data: map[string]string{
			// Required to validate DataVolume usage
			virtconfig.FeatureGatesKey:                   "DataVolumes,LiveMigration,SRIOV,GPU,HostDisk",
			virtconfig.PermitSlirpInterface:              "true",
			virtconfig.PermitBridgeInterfaceOnPodNetwork: "true",
		},
	})
	var priorityClasses = map[string]*schedulingv1.PriorityClass{
		utils.Preemtible:    utils.GetPreemtible(),
		utils.NonPreemtible: utils.GetNonPreemtible(),
	}

	var vms = map[string]*v1.VirtualMachine{
		utils.VmCirros:           utils.GetVMCirros(),
		utils.VmAlpineMultiPvc:   utils.GetVMMultiPvc(),
		utils.VmAlpineDataVolume: utils.GetVMDataVolume(),
		utils.VMPriorityClass:    utils.GetVMPriorityClass(),
	}

	var vmis = map[string]*v1.VirtualMachineInstance{
		utils.VmiEphemeral:         utils.GetVMIEphemeral(),
		utils.VmiMigratable:        utils.GetVMIMigratable(),
		utils.VmiFlavorSmall:       utils.GetVMIFlavorSmall(),
		utils.VmiSata:              utils.GetVMISata(),
		utils.VmiFedora:            utils.GetVMIEphemeralFedora(),
		utils.VmiSecureBoot:        utils.GetVMISecureBoot(),
		utils.VmiAlpineEFI:         utils.GetVMIAlpineEFI(),
		utils.VmiNoCloud:           utils.GetVMINoCloud(),
		utils.VmiPVC:               utils.GetVMIPvc(),
		utils.VmiBlockPVC:          utils.GetVMIBlockPvc(),
		utils.VmiWindows:           utils.GetVMIWindows(),
		utils.VmiSlirp:             utils.GetVMISlirp(),
		utils.VmiSRIOV:             utils.GetVMISRIOV(),
		utils.VmiWithHookSidecar:   utils.GetVMIWithHookSidecar(),
		utils.VmiMultusPtp:         utils.GetVMIMultusPtp(),
		utils.VmiMultusMultipleNet: utils.GetVMIMultusMultipleNet(),
		utils.VmiMasquerade:        utils.GetVMIMasquerade(),
		utils.VmiHostDisk:          utils.GetVMIHostDisk(),
		utils.VmiGPU:               utils.GetVMIGPU(),
	}

	var vmireplicasets = map[string]*v1.VirtualMachineInstanceReplicaSet{
		utils.VmiReplicaSetCirros: utils.GetVMIReplicaSetCirros(),
	}

	var vmipresets = map[string]*v1.VirtualMachineInstancePreset{
		utils.VmiPresetSmall: utils.GetVMIPresetSmall(),
	}

	var migrations = map[string]*v1.VirtualMachineInstanceMigration{
		utils.VmiMigration: utils.GetVMIMigration(),
	}

	var templates = map[string]*utils.Template{
		utils.VmTemplateFedora:  utils.GetTemplateFedora(),
		utils.VmTemplateRHEL7:   utils.GetTemplateRHEL7(),
		utils.VmTemplateWindows: utils.GetTemplateWindows(),
	}

	handleError := func(err error) {
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			panic(err)
		}
	}

	handleCauses := func(causes []metav1.StatusCause, name string, objType string) {
		if len(causes) > 0 {
			for _, cause := range causes {
				fmt.Fprintf(
					os.Stderr,
					"Failed to validate %s spec: failed to admit yaml for %s: %s at %s: %s\n",
					objType, name, cause.Type, cause.Field, cause.Message)
			}
			panic(fmt.Errorf("Failed to admit %s of type %s", name, objType))
		}
	}

	dumpObject := func(name string, obj interface{}) error {

		filename := filepath.Join(*genDir, fmt.Sprintf("%s.yaml", name))
		file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
		if err != nil {
			return fmt.Errorf("Failed to open file %v, %v", filename, err)
		}
		defer file.Close()

		util.MarshallObject(obj, file)

		return nil
	}

	// Having no generics is lots of fun
	for name, obj := range vms {
		causes := validating_webhook.ValidateVirtualMachineSpec(k8sfield.NewPath("spec"), &obj.Spec, config, "user-account")
		handleCauses(causes, name, "vm")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range vmis {
		causes := validating_webhook.ValidateVirtualMachineInstanceSpec(k8sfield.NewPath("spec"), &obj.Spec, config)
		handleCauses(causes, name, "vmi")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range vmireplicasets {
		causes := validating_webhook.ValidateVMIRSSpec(k8sfield.NewPath("spec"), &obj.Spec, config)
		handleCauses(causes, name, "vmi replica set")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range vmipresets {
		causes := validating_webhook.ValidateVMIPresetSpec(k8sfield.NewPath("spec"), &obj.Spec)
		handleCauses(causes, name, "vmi preset")
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range migrations {
		causes := validating_webhook.ValidateVirtualMachineInstanceMigrationSpec(k8sfield.NewPath("spec"), &obj.Spec)
		handleCauses(causes, name, "vmi preset")
		handleError(dumpObject(name, *obj))
	}

	// TODO:(ihar) how to validate templates?
	for name, obj := range templates {
		handleError(dumpObject(name, *obj))
	}

	for name, obj := range priorityClasses {
		handleError(dumpObject(name, *obj))
	}
}
<|endoftext|>


<commit_before>package btconn

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
)

var errInvalidProtocol = errors.New("invalid protocol")

var pstr = [19]byte{'B', 'i', 't', 'T', 'o', 'r', 'r', 'e', 'n', 't', ' ', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l'}

func writeHandshake(w io.Writer, ih [20]byte, id [20]byte, extensions [8]byte) error {
	var h = struct {
		Pstrlen    byte
		Pstr       [len(pstr)]byte
		Extensions [8]byte
		InfoHash   [20]byte
		PeerID     [20]byte
	}{
		Pstrlen:    byte(len(pstr)),
		Pstr:       pstr,
		Extensions: extensions,
		InfoHash:   ih,
		PeerID:     id,
	}
	return binary.Write(w, binary.BigEndian, h)
}

func readHandshake1(r io.Reader) (extensions [8]byte, ih [20]byte, err error) {
	var pstrLen byte
	err = binary.Read(r, binary.BigEndian, &pstrLen)
	if err != nil {
		return
	}
	if pstrLen != byte(len(pstr)) {
		err = errInvalidProtocol
		return
	}

	pstr := make([]byte, pstrLen)
	_, err = io.ReadFull(r, pstr)
	if err != nil {
		return
	}
	if !bytes.Equal(pstr, pstr) {
		err = errInvalidProtocol
		return
	}

	_, err = io.ReadFull(r, extensions[:])
	if err != nil {
		return
	}

	_, err = io.ReadFull(r, ih[:])
	return
}

func readHandshake2(r io.Reader) (id [20]byte, err error) {
	_, err = io.ReadFull(r, id[:])
	return
}
<commit_msg>fix handshake bug<commit_after>package btconn

import (
	"encoding/binary"
	"errors"
	"io"
)

var errInvalidProtocol = errors.New("invalid protocol")

var pstr = [20]byte{19, 'B', 'i', 't', 'T', 'o', 'r', 'r', 'e', 'n', 't', ' ', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l'}

func writeHandshake(w io.Writer, ih [20]byte, id [20]byte, extensions [8]byte) error {
	var h = struct {
		Pstr       [20]byte
		Extensions [8]byte
		InfoHash   [20]byte
		PeerID     [20]byte
	}{
		Pstr:       pstr,
		Extensions: extensions,
		InfoHash:   ih,
		PeerID:     id,
	}
	return binary.Write(w, binary.BigEndian, h)
}

func readHandshake1(r io.Reader) (extensions [8]byte, ih [20]byte, err error) {
	_, err = io.ReadFull(r, ih[:])
	if err != nil {
		return
	}
	if ih != pstr {
		err = errInvalidProtocol
		return
	}
	_, err = io.ReadFull(r, extensions[:])
	if err != nil {
		return
	}
	_, err = io.ReadFull(r, ih[:])
	return
}

func readHandshake2(r io.Reader) (id [20]byte, err error) {
	_, err = io.ReadFull(r, id[:])
	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"strings"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/elb"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccAWSLoadBalancerListenerPolicy_basic(t *testing.T) {
	rChar := acctest.RandStringFromCharSet(6, acctest.CharSetAlpha)
	lbName := rChar
	mcName := rChar
	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSLoadBalancerListenerPolicyDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSLoadBalancerListenerPolicyConfig_basic0(lbName, mcName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"),
					testAccCheckAWSLoadBalancerListenerPolicyState(lbName, int64(80), mcName, true),
				),
			},
			{
				Config: testAccAWSLoadBalancerListenerPolicyConfig_basic1(lbName, mcName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"),
					testAccCheckAWSLoadBalancerListenerPolicyState(lbName, int64(80), mcName, true),
				),
			},
			{
				Config: testAccAWSLoadBalancerListenerPolicyConfig_basic2(lbName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSLoadBalancerListenerPolicyState(lbName, int64(80), mcName, false),
				),
			},
		},
	})
}

func policyInListenerPolicies(str string, list []string) bool {
	for _, v := range list {
		if v == str {
			return true
		}
	}
	return false
}

func testAccCheckAWSLoadBalancerListenerPolicyDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).elbconn

	for _, rs := range s.RootModule().Resources {
		switch {
		case rs.Type == "aws_load_balancer_policy":
			loadBalancerName, policyName := resourceAwsLoadBalancerListenerPoliciesParseId(rs.Primary.ID)
			out, err := conn.DescribeLoadBalancerPolicies(
				&elb.DescribeLoadBalancerPoliciesInput{
					LoadBalancerName: aws.String(loadBalancerName),
					PolicyNames:      []*string{aws.String(policyName)},
				})
			if err != nil {
				if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") {
					continue
				}
				return err
			}
			if len(out.PolicyDescriptions) > 0 {
				return fmt.Errorf("Policy still exists")
			}
		case rs.Type == "aws_load_listener_policy":
			loadBalancerName, _ := resourceAwsLoadBalancerListenerPoliciesParseId(rs.Primary.ID)
			out, err := conn.DescribeLoadBalancers(
				&elb.DescribeLoadBalancersInput{
					LoadBalancerNames: []*string{aws.String(loadBalancerName)},
				})
			if err != nil {
				if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "LoadBalancerNotFound") {
					continue
				}
				return err
			}
			policyNames := []string{}
			for k := range rs.Primary.Attributes {
				if strings.HasPrefix(k, "policy_names.") && strings.HasSuffix(k, ".name") {
					value_key := fmt.Sprintf("%s.value", strings.TrimSuffix(k, ".name"))
					policyNames = append(policyNames, rs.Primary.Attributes[value_key])
				}
			}
			for _, policyName := range policyNames {
				for _, listener := range out.LoadBalancerDescriptions[0].ListenerDescriptions {
					policyStrings := []string{}
					for _, pol := range listener.PolicyNames {
						policyStrings = append(policyStrings, *pol)
					}
					if policyInListenerPolicies(policyName, policyStrings) {
						return fmt.Errorf("Policy still exists and is assigned")
					}
				}
			}
		default:
			continue
		}
	}
	return nil
}

func testAccCheckAWSLoadBalancerListenerPolicyState(loadBalancerName string, loadBalancerListenerPort int64, loadBalancerListenerPolicyName string, assigned bool) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		elbconn := testAccProvider.Meta().(*AWSClient).elbconn

		loadBalancerDescription, err := elbconn.DescribeLoadBalancers(&elb.DescribeLoadBalancersInput{
			LoadBalancerNames: []*string{aws.String(loadBalancerName)},
		})
		if err != nil {
			return err
		}

		for _, listener := range loadBalancerDescription.LoadBalancerDescriptions[0].ListenerDescriptions {
			if *listener.Listener.LoadBalancerPort != loadBalancerListenerPort {
				continue
			}
			policyStrings := []string{}
			for _, pol := range listener.PolicyNames {
				policyStrings = append(policyStrings, *pol)
			}
			if policyInListenerPolicies(loadBalancerListenerPolicyName, policyStrings) != assigned {
				if assigned {
					return fmt.Errorf("Policy no longer assigned %s not in %+v", loadBalancerListenerPolicyName, policyStrings)
				} else {
					return fmt.Errorf("Policy exists and is assigned")
				}
			}
		}

		return nil
	}
}

func testAccAWSLoadBalancerListenerPolicyConfig_basic0(lbName, mcName string) string {
	return fmt.Sprintf(`
resource "aws_elb" "test-lb" {
  name               = "%s"
  availability_zones = ["us-west-2a"]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    Name = "tf-acc-test"
  }
}

resource "aws_load_balancer_policy" "magic-cookie-sticky" {
  load_balancer_name = aws_elb.test-lb.name
  policy_name        = "%s"
  policy_type_name   = "AppCookieStickinessPolicyType"

  policy_attribute {
    name  = "CookieName"
    value = "magic_cookie"
  }
}

resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" {
  load_balancer_name = aws_elb.test-lb.name
  load_balancer_port = 80

  policy_names = [
    aws_load_balancer_policy.magic-cookie-sticky.policy_name,
  ]
}
`, lbName, mcName)
}

func testAccAWSLoadBalancerListenerPolicyConfig_basic1(lbName, mcName string) string {
	return fmt.Sprintf(`
resource "aws_elb" "test-lb" {
  name               = "%s"
  availability_zones = ["us-west-2a"]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    Name = "tf-acc-test"
  }
}

resource "aws_load_balancer_policy" "magic-cookie-sticky" {
  load_balancer_name = aws_elb.test-lb.name
  policy_name        = "%s"
  policy_type_name   = "AppCookieStickinessPolicyType"

  policy_attribute {
    name  = "CookieName"
    value = "unicorn_cookie"
  }
}

resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" {
  load_balancer_name = aws_elb.test-lb.name
  load_balancer_port = 80

  policy_names = [
    aws_load_balancer_policy.magic-cookie-sticky.policy_name,
  ]
}
`, lbName, mcName)
}

func testAccAWSLoadBalancerListenerPolicyConfig_basic2(lbName string) string {
	return fmt.Sprintf(`
resource "aws_elb" "test-lb" {
  name               = "%s"
  availability_zones = ["us-west-2a"]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    Name = "tf-acc-test"
  }
}
`, lbName)
}
<commit_msg>tests/provider: Fix hardcoded (lb listener policy)<commit_after>package aws

import (
	"fmt"
	"strings"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/elb"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccAWSLoadBalancerListenerPolicy_basic(t *testing.T) {
	rChar := acctest.RandStringFromCharSet(6, acctest.CharSetAlpha)
	lbName := rChar
	mcName := rChar
	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSLoadBalancerListenerPolicyDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccAWSLoadBalancerListenerPolicyConfig_basic0(lbName, mcName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"),
					testAccCheckAWSLoadBalancerListenerPolicyState(lbName, int64(80), mcName, true),
				),
			},
			{
				Config: testAccAWSLoadBalancerListenerPolicyConfig_basic1(lbName, mcName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSLoadBalancerPolicyState("aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"),
					testAccCheckAWSLoadBalancerListenerPolicyState(lbName, int64(80), mcName, true),
				),
			},
			{
				Config: testAccAWSLoadBalancerListenerPolicyConfig_basic2(lbName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSLoadBalancerListenerPolicyState(lbName, int64(80), mcName, false),
				),
			},
		},
	})
}

func policyInListenerPolicies(str string, list []string) bool {
	for _, v := range list {
		if v == str {
			return true
		}
	}
	return false
}

func testAccCheckAWSLoadBalancerListenerPolicyDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).elbconn

	for _, rs := range s.RootModule().Resources {
		switch {
		case rs.Type == "aws_load_balancer_policy":
			loadBalancerName, policyName := resourceAwsLoadBalancerListenerPoliciesParseId(rs.Primary.ID)
			out, err := conn.DescribeLoadBalancerPolicies(
				&elb.DescribeLoadBalancerPoliciesInput{
					LoadBalancerName: aws.String(loadBalancerName),
					PolicyNames:      []*string{aws.String(policyName)},
				})
			if err != nil {
				if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") {
					continue
				}
				return err
			}
			if len(out.PolicyDescriptions) > 0 {
				return fmt.Errorf("Policy still exists")
			}
		case rs.Type == "aws_load_listener_policy":
			loadBalancerName, _ := resourceAwsLoadBalancerListenerPoliciesParseId(rs.Primary.ID)
			out, err := conn.DescribeLoadBalancers(
				&elb.DescribeLoadBalancersInput{
					LoadBalancerNames: []*string{aws.String(loadBalancerName)},
				})
			if err != nil {
				if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "LoadBalancerNotFound") {
					continue
				}
				return err
			}
			policyNames := []string{}
			for k := range rs.Primary.Attributes {
				if strings.HasPrefix(k, "policy_names.") && strings.HasSuffix(k, ".name") {
					value_key := fmt.Sprintf("%s.value", strings.TrimSuffix(k, ".name"))
					policyNames = append(policyNames, rs.Primary.Attributes[value_key])
				}
			}
			for _, policyName := range policyNames {
				for _, listener := range out.LoadBalancerDescriptions[0].ListenerDescriptions {
					policyStrings := []string{}
					for _, pol := range listener.PolicyNames {
						policyStrings = append(policyStrings, *pol)
					}
					if policyInListenerPolicies(policyName, policyStrings) {
						return fmt.Errorf("Policy still exists and is assigned")
					}
				}
			}
		default:
			continue
		}
	}
	return nil
}

func testAccCheckAWSLoadBalancerListenerPolicyState(loadBalancerName string, loadBalancerListenerPort int64, loadBalancerListenerPolicyName string, assigned bool) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		elbconn := testAccProvider.Meta().(*AWSClient).elbconn

		loadBalancerDescription, err := elbconn.DescribeLoadBalancers(&elb.DescribeLoadBalancersInput{
			LoadBalancerNames: []*string{aws.String(loadBalancerName)},
		})
		if err != nil {
			return err
		}

		for _, listener := range loadBalancerDescription.LoadBalancerDescriptions[0].ListenerDescriptions {
			if *listener.Listener.LoadBalancerPort != loadBalancerListenerPort {
				continue
			}
			policyStrings := []string{}
			for _, pol := range listener.PolicyNames {
				policyStrings = append(policyStrings, *pol)
			}
			if policyInListenerPolicies(loadBalancerListenerPolicyName, policyStrings) != assigned {
				if assigned {
					return fmt.Errorf("Policy no longer assigned %s not in %+v", loadBalancerListenerPolicyName, policyStrings)
				} else {
					return fmt.Errorf("Policy exists and is assigned")
				}
			}
		}

		return nil
	}
}

func testAccAWSLoadBalancerListenerPolicyConfig_basic0(lbName, mcName string) string {
	return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(`
resource "aws_elb" "test-lb" {
  name               = "%s"
  availability_zones = [data.aws_availability_zones.available.names[0]]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    Name = "tf-acc-test"
  }
}

resource "aws_load_balancer_policy" "magic-cookie-sticky" {
  load_balancer_name = aws_elb.test-lb.name
  policy_name        = "%s"
  policy_type_name   = "AppCookieStickinessPolicyType"

  policy_attribute {
    name  = "CookieName"
    value = "magic_cookie"
  }
}

resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" {
  load_balancer_name = aws_elb.test-lb.name
  load_balancer_port = 80

  policy_names = [
    aws_load_balancer_policy.magic-cookie-sticky.policy_name,
  ]
}
`, lbName, mcName))
}

func testAccAWSLoadBalancerListenerPolicyConfig_basic1(lbName, mcName string) string {
	return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(`
resource "aws_elb" "test-lb" {
  name               = "%s"
  availability_zones = [data.aws_availability_zones.available.names[0]]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    Name = "tf-acc-test"
  }
}

resource "aws_load_balancer_policy" "magic-cookie-sticky" {
  load_balancer_name = aws_elb.test-lb.name
  policy_name        = "%s"
  policy_type_name   = "AppCookieStickinessPolicyType"

  policy_attribute {
    name  = "CookieName"
    value = "unicorn_cookie"
  }
}

resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" {
  load_balancer_name = aws_elb.test-lb.name
  load_balancer_port = 80

  policy_names = [
    aws_load_balancer_policy.magic-cookie-sticky.policy_name,
  ]
}
`, lbName, mcName))
}

func testAccAWSLoadBalancerListenerPolicyConfig_basic2(lbName string) string {
	return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(`
resource "aws_elb" "test-lb" {
  name               = "%s"
  availability_zones = [data.aws_availability_zones.available.names[0]]

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    Name = "tf-acc-test"
  }
}
`, lbName))
}
<|endoftext|>


<commit_before>package class

type AttributeType uint8

type baseAttribute struct {
	attrType  AttributeType
	NameIndex ConstPoolIndex
	Length    uint16
}

// field_info, may single
// ACC_STATIC only
type ConstantValue struct {
	baseAttribute
	Index ConstPoolIndex
}

// method_info, single
// not if native or abstract
type Code struct {
	baseAttribute

	MaxStackSize   uint16
	MaxLocalsCount uint16

	CodeLength uint32
	Code       []uint8

	ExceptionsCount uint16
	Exceptions      []struct {
		StartPC   uint16
		EndPC     uint16
		HandlerPC uint16
		// may be zero, then used for finally
		CatchType ConstPoolIndex
	}

	// only LineNumberTable, LocalVariableTable, LocalVariableTypeTable
	AttributesCount uint16
	Attributes
}

type StackMapTable struct {
	baseAttribute
}

type Exceptions struct {
	baseAttribute
}

type InnerClasses struct {
	baseAttribute
}

type EnclosingMethod struct {
	baseAttribute
}

type Synthetic struct {
	baseAttribute
}

type Signature struct {
	baseAttribute
}

type SourceFile struct {
	baseAttribute
}

type SourceDebugExtension struct {
	baseAttribute
}

type LineNumberTable struct {
	baseAttribute
}

type LocalVariableTable struct {
	baseAttribute
}

type LocalVariableTypeTable struct {
	baseAttribute
}

type Deprecated struct {
	baseAttribute
}

type RuntimeVisibleAnnotations struct {
	baseAttribute
}

type RuntimeInvisibleAnnotations struct {
	baseAttribute
}

type RuntimeVisibleParameterAnnotations struct {
	baseAttribute
}

type RuntimeInvisibleParameterAnnotations struct {
	baseAttribute
}

type AnnotationDefault struct {
	baseAttribute
}

type BootstrapMethods struct {
	baseAttribute
}
<commit_msg>Added attributes: LineNumberTable,SourceDebugExtension,SourceFile,Signature,Synthetic,EnclosingMethod,InnerClasses,Exceptions.<commit_after>package class

type AttributeType uint8

type baseAttribute struct {
	attrType  AttributeType
	NameIndex ConstPoolIndex
	Length    uint16
}

// field_info, may single
// ACC_STATIC only
type ConstantValue struct {
	baseAttribute
	Index ConstPoolIndex
}

// method_info, single
// not if native or abstract
type Code struct {
	baseAttribute

	MaxStackSize   uint16
	MaxLocalsCount uint16

	CodeLength uint32
	Code       []uint8

	ExceptionsCount uint16
	Exceptions      []struct {
		StartPC   uint16
		EndPC     uint16
		HandlerPC uint16
		// may be zero, then used for finally
		CatchType ConstPoolIndex
	}

	// only LineNumberTable, LocalVariableTable,
	// LocalVariableTypeTable, StackMapTable
	AttributesCount uint16
	Attributes
}

type StackMapTable struct {
	baseAttribute
}

// method_info, may single
type Exceptions struct {
	baseAttribute
	ExceptionsCount uint16
	Exceptions      []ConstPoolIndex
}

// ClassFile, may single
type InnerClasses struct {
	baseAttribute

	ClassesCount uint16
	Classes      []struct {
		InnerClassIndex  ConstPoolIndex
		OuterClassIndex  ConstPoolIndex
		InnerName        ConstPoolIndex
		InnerAccessFlags NestedClassAccessFlag
	}
}

// ClassFile, may single
// iff local class or anonymous class
type EnclosingMethod struct {
	baseAttribute
	ClassIndex  ConstPoolIndex
	MethodIndex ConstPoolIndex
}

// ClassFile, method_info or field_info, may single
// if compiler generated
// instead maybe: ACC_SYNTHETIC
type Synthetic baseAttribute

// ClassFile, field_info, or method_info, may single
type Signature struct {
	baseAttribute
	SignatureIndex ConstPoolIndex
}

// ClassFile, may single
type SourceFile struct {
	baseAttribute
	SourceFileIndex ConstPoolIndex
}

// ClassFile, may single
type SourceDebugExtension struct {
	baseAttribute
	DebugExtension string
}

// Code, may multiple
type LineNumberTable struct {
	baseAttribute
	TableLength uint16
	Table       []struct {
		StartPC    uint16
		LineNumber uint16
	}
}

type LocalVariableTable struct {
	baseAttribute
}

type LocalVariableTypeTable struct {
	baseAttribute
}

type Deprecated struct {
	baseAttribute
}

type RuntimeVisibleAnnotations struct {
	baseAttribute
}

type RuntimeInvisibleAnnotations struct {
	baseAttribute
}

type RuntimeVisibleParameterAnnotations struct {
	baseAttribute
}

type RuntimeInvisibleParameterAnnotations struct {
	baseAttribute
}

type AnnotationDefault struct {
	baseAttribute
}

type BootstrapMethods struct {
	baseAttribute
}
<|endoftext|>


<commit_before>package jsonrpc

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"sync"

	"github.com/cenkalti/rpc2"
)

type jsonCodec struct {
	dec *json.Decoder // for reading JSON values
	enc *json.Encoder // for writing JSON values
	c   io.Closer

	// temporary work space
	msg            message
	serverRequest  serverRequest
	clientRequest  clientRequest
	clientResponse clientResponse

	// JSON-RPC clients can use arbitrary json values as request IDs.
	// Package rpc expects uint64 request IDs.
	// We assign uint64 sequence numbers to incoming requests
	// but save the original request ID in the pending map.
	// When rpc responds, we use the sequence number in
	// the response to find the original request ID.
	mutext  sync.Mutex // protects seq, pending
	pending map[uint64]*json.RawMessage
	seq     uint64
}

func NewJSONCodec(conn io.ReadWriteCloser) rpc2.Codec {
	return &jsonCodec{
		dec:     json.NewDecoder(conn),
		enc:     json.NewEncoder(conn),
		c:       conn,
		pending: make(map[uint64]*json.RawMessage),
	}
}

type clientRequest struct {
	Method string         `json:"method"`
	Params [1]interface{} `json:"params"`
	Id     *uint64        `json:"id"`
}
type serverRequest struct {
	Method string           `json:"method"`
	Params *json.RawMessage `json:"params"`
	Id     *json.RawMessage `json:"id"`
}

type clientResponse struct {
	Id     uint64           `json:"id"`
	Result *json.RawMessage `json:"result"`
	Error  interface{}      `json:"error"`
}
type serverResponse struct {
	Id     *json.RawMessage `json:"id"`
	Result interface{}      `json:"result"`
	Error  interface{}      `json:"error"`
}

type message struct {
	Method string           `json:"method"`
	Params *json.RawMessage `json:"params"`
	Id     *json.RawMessage `json:"id"`
	Result *json.RawMessage `json:"result"`
	Error  interface{}      `json:"error"`
}

func (c *jsonCodec) ReadHeader(req *rpc2.Request, resp *rpc2.Response) error {
	c.msg = message{}
	if err := c.dec.Decode(&c.msg); err != nil {
		return err
	}

	if c.msg.Method != "" {
		// server request
		c.serverRequest.Id = c.msg.Id
		c.serverRequest.Method = c.msg.Method
		c.serverRequest.Params = c.msg.Params

		req.Method = c.serverRequest.Method

		// JSON request id can be any JSON value;
		// RPC package expects uint64.  Translate to
		// internal uint64 and save JSON on the side.
		if c.serverRequest.Id == nil {
			// Notification
		} else {
			c.mutext.Lock()
			c.seq++
			c.pending[c.seq] = c.serverRequest.Id
			c.serverRequest.Id = nil
			req.Seq = c.seq
			c.mutext.Unlock()
		}

		return nil

	} else if c.msg.Result != nil {
		// client response
		// c.clientResponse.Id = msg.Id // TODO fix
		err := json.Unmarshal([]byte(*c.msg.Id), &c.clientResponse.Id)
		if err != nil {
			return err
		}
		c.clientResponse.Result = c.msg.Result
		c.clientResponse.Error = c.msg.Error

		resp.Error = ""
		resp.Seq = c.clientResponse.Id
		if c.clientResponse.Error != nil || c.clientResponse.Result == nil {
			x, ok := c.clientResponse.Error.(string)
			if !ok {
				return fmt.Errorf("invalid error %v", c.clientResponse.Error)
			}
			if x == "" {
				x = "unspecified error"
			}
			resp.Error = x
		}
		return nil
	}
	return errors.New("cannot determine message type")
}

var errMissingParams = errors.New("jsonrpc: request body missing params")

func (c *jsonCodec) ReadRequestBody(x interface{}) error {
	if x == nil {
		return nil
	}
	if c.serverRequest.Params == nil {
		return errMissingParams
	}
	// JSON params is array value.
	// RPC params is struct.
	// Unmarshal into array containing struct for now.
	// Should think about making RPC more general.
	var params [1]interface{}
	params[0] = x
	return json.Unmarshal(*c.serverRequest.Params, &params)

}

func (c *jsonCodec) ReadResponseBody(x interface{}) error {
	if x == nil {
		return nil
	}
	return json.Unmarshal(*c.clientResponse.Result, x)
}

func (c *jsonCodec) WriteRequest(r *rpc2.Request, param interface{}) error {
	c.clientRequest.Method = r.Method
	c.clientRequest.Params[0] = param
	if r.Seq == 0 {
		// Notification
		c.clientRequest.Id = nil
	} else {
		seq := r.Seq
		c.clientRequest.Id = &seq
	}
	return c.enc.Encode(&c.clientRequest)
}

var null = json.RawMessage([]byte("null"))

func (c *jsonCodec) WriteResponse(r *rpc2.Response, x interface{}) error {
	var resp serverResponse
	c.mutext.Lock()
	b, ok := c.pending[r.Seq]
	if !ok {
		c.mutext.Unlock()
		return errors.New("invalid sequence number in response")
	}
	delete(c.pending, r.Seq)
	c.mutext.Unlock()

	if b == nil {
		// Invalid request so no id.  Use JSON null.
		b = &null
	}
	resp.Id = b
	resp.Result = x
	if r.Error == "" {
		resp.Error = nil
	} else {
		resp.Error = r.Error
	}
	return c.enc.Encode(resp)

}

func (c *jsonCodec) Close() error {
	return c.c.Close()
}
<commit_msg>update comments<commit_after>package jsonrpc

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"sync"

	"github.com/cenkalti/rpc2"
)

type jsonCodec struct {
	dec *json.Decoder // for reading JSON values
	enc *json.Encoder // for writing JSON values
	c   io.Closer

	// temporary work space
	msg            message
	serverRequest  serverRequest
	clientRequest  clientRequest
	clientResponse clientResponse

	// JSON-RPC clients can use arbitrary json values as request IDs.
	// Package rpc expects uint64 request IDs.
	// We assign uint64 sequence numbers to incoming requests
	// but save the original request ID in the pending map.
	// When rpc responds, we use the sequence number in
	// the response to find the original request ID.
	mutext  sync.Mutex // protects seq, pending
	pending map[uint64]*json.RawMessage
	seq     uint64
}

func NewJSONCodec(conn io.ReadWriteCloser) rpc2.Codec {
	return &jsonCodec{
		dec:     json.NewDecoder(conn),
		enc:     json.NewEncoder(conn),
		c:       conn,
		pending: make(map[uint64]*json.RawMessage),
	}
}

type clientRequest struct {
	Method string         `json:"method"`
	Params [1]interface{} `json:"params"`
	Id     *uint64        `json:"id"`
}
type serverRequest struct {
	Method string           `json:"method"`
	Params *json.RawMessage `json:"params"`
	Id     *json.RawMessage `json:"id"`
}

type clientResponse struct {
	Id     uint64           `json:"id"`
	Result *json.RawMessage `json:"result"`
	Error  interface{}      `json:"error"`
}
type serverResponse struct {
	Id     *json.RawMessage `json:"id"`
	Result interface{}      `json:"result"`
	Error  interface{}      `json:"error"`
}

type message struct {
	Method string           `json:"method"`
	Params *json.RawMessage `json:"params"`
	Id     *json.RawMessage `json:"id"`
	Result *json.RawMessage `json:"result"`
	Error  interface{}      `json:"error"`
}

func (c *jsonCodec) ReadHeader(req *rpc2.Request, resp *rpc2.Response) error {
	c.msg = message{}
	if err := c.dec.Decode(&c.msg); err != nil {
		return err
	}

	if c.msg.Method != "" {
		// We are server and read a request from client.
		c.serverRequest.Id = c.msg.Id
		c.serverRequest.Method = c.msg.Method
		c.serverRequest.Params = c.msg.Params

		req.Method = c.serverRequest.Method

		// JSON request id can be any JSON value;
		// RPC package expects uint64.  Translate to
		// internal uint64 and save JSON on the side.
		if c.serverRequest.Id == nil {
			// Notification
		} else {
			c.mutext.Lock()
			c.seq++
			c.pending[c.seq] = c.serverRequest.Id
			c.serverRequest.Id = nil
			req.Seq = c.seq
			c.mutext.Unlock()
		}

		return nil

	} else if c.msg.Result != nil {
		// We are client and read a response from server.
		// c.clientResponse.Id = msg.Id // TODO fix
		err := json.Unmarshal([]byte(*c.msg.Id), &c.clientResponse.Id)
		if err != nil {
			return err
		}
		c.clientResponse.Result = c.msg.Result
		c.clientResponse.Error = c.msg.Error

		resp.Error = ""
		resp.Seq = c.clientResponse.Id
		if c.clientResponse.Error != nil || c.clientResponse.Result == nil {
			x, ok := c.clientResponse.Error.(string)
			if !ok {
				return fmt.Errorf("invalid error %v", c.clientResponse.Error)
			}
			if x == "" {
				x = "unspecified error"
			}
			resp.Error = x
		}
		return nil
	}
	return errors.New("cannot determine message type")
}

var errMissingParams = errors.New("jsonrpc: request body missing params")

func (c *jsonCodec) ReadRequestBody(x interface{}) error {
	if x == nil {
		return nil
	}
	if c.serverRequest.Params == nil {
		return errMissingParams
	}
	// JSON params is array value.
	// RPC params is struct.
	// Unmarshal into array containing struct for now.
	// Should think about making RPC more general.
	var params [1]interface{}
	params[0] = x
	return json.Unmarshal(*c.serverRequest.Params, &params)

}

func (c *jsonCodec) ReadResponseBody(x interface{}) error {
	if x == nil {
		return nil
	}
	return json.Unmarshal(*c.clientResponse.Result, x)
}

func (c *jsonCodec) WriteRequest(r *rpc2.Request, param interface{}) error {
	c.clientRequest.Method = r.Method
	c.clientRequest.Params[0] = param
	if r.Seq == 0 {
		// Notification
		c.clientRequest.Id = nil
	} else {
		seq := r.Seq
		c.clientRequest.Id = &seq
	}
	return c.enc.Encode(&c.clientRequest)
}

var null = json.RawMessage([]byte("null"))

func (c *jsonCodec) WriteResponse(r *rpc2.Response, x interface{}) error {
	var resp serverResponse
	c.mutext.Lock()
	b, ok := c.pending[r.Seq]
	if !ok {
		c.mutext.Unlock()
		return errors.New("invalid sequence number in response")
	}
	delete(c.pending, r.Seq)
	c.mutext.Unlock()

	if b == nil {
		// Invalid request so no id.  Use JSON null.
		b = &null
	}
	resp.Id = b
	resp.Result = x
	if r.Error == "" {
		resp.Error = nil
	} else {
		resp.Error = r.Error
	}
	return c.enc.Encode(resp)

}

func (c *jsonCodec) Close() error {
	return c.c.Close()
}
<|endoftext|>