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

package push_test

import (
	"context"
	"fmt"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"go.opentelemetry.io/otel/api/kv"
	"go.opentelemetry.io/otel/api/label"
	"go.opentelemetry.io/otel/api/metric"
	"go.opentelemetry.io/otel/exporters/metric/test"
	export "go.opentelemetry.io/otel/sdk/export/metric"
	"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
	"go.opentelemetry.io/otel/sdk/metric/controller/push"
	controllerTest "go.opentelemetry.io/otel/sdk/metric/controller/test"
	"go.opentelemetry.io/otel/sdk/resource"
)

var testResource = resource.New(kv.String("R", "V"))

type testExporter struct {
	t         *testing.T
	lock      sync.Mutex
	exports   int
	records   []export.Record
	injectErr func(r export.Record) error
}

type testFixture struct {
	checkpointSet *test.CheckpointSet
	exporter      *testExporter
}

type testSelector struct{}

func newFixture(t *testing.T) testFixture {
	checkpointSet := test.NewCheckpointSet(testResource)

	exporter := &testExporter{
		t: t,
	}
	return testFixture{
		checkpointSet: checkpointSet,
		exporter:      exporter,
	}
}

func (testSelector) AggregatorFor(*metric.Descriptor) export.Aggregator {
	return sum.New()
}

func (e *testExporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error {
	e.lock.Lock()
	defer e.lock.Unlock()
	e.exports++
	var records []export.Record
	if err := checkpointSet.ForEach(func(r export.Record) error {
		if e.injectErr != nil {
			if err := e.injectErr(r); err != nil {
				return err
			}
		}
		records = append(records, r)
		return nil
	}); err != nil {
		return err
	}
	e.records = records
	return nil
}

func (e *testExporter) resetRecords() ([]export.Record, int) {
	e.lock.Lock()
	defer e.lock.Unlock()
	r := e.records
	e.records = nil
	return r, e.exports
}

func TestPushDoubleStop(t *testing.T) {
	fix := newFixture(t)
	p := push.New(testSelector{}, fix.exporter)
	p.Start()
	p.Stop()
	p.Stop()
}

func TestPushDoubleStart(t *testing.T) {
	fix := newFixture(t)
	p := push.New(testSelector{}, fix.exporter)
	p.Start()
	p.Start()
	p.Stop()
}

func TestPushTicker(t *testing.T) {
	fix := newFixture(t)

	p := push.New(
		testSelector{},
		fix.exporter,
		push.WithPeriod(time.Second),
		push.WithResource(testResource),
	)
	meter := p.Provider().Meter("name")

	mock := controllerTest.NewMockClock()
	p.SetClock(mock)

	ctx := context.Background()

	counter := metric.Must(meter).NewInt64Counter("counter")

	p.Start()

	counter.Add(ctx, 3)

	records, exports := fix.exporter.resetRecords()
	require.Equal(t, 0, exports)
	require.Equal(t, 0, len(records))

	mock.Add(time.Second)
	runtime.Gosched()

	records, exports = fix.exporter.resetRecords()
	require.Equal(t, 1, exports)
	require.Equal(t, 1, len(records))
	require.Equal(t, "counter", records[0].Descriptor().Name())
	require.Equal(t, "R=V", records[0].Resource().Encoded(label.DefaultEncoder()))

	sum, err := records[0].Aggregator().(aggregator.Sum).Sum()
	require.Equal(t, int64(3), sum.AsInt64())
	require.Nil(t, err)

	fix.checkpointSet.Reset()

	counter.Add(ctx, 7)

	mock.Add(time.Second)
	runtime.Gosched()

	records, exports = fix.exporter.resetRecords()
	require.Equal(t, 2, exports)
	require.Equal(t, 1, len(records))
	require.Equal(t, "counter", records[0].Descriptor().Name())
	require.Equal(t, "R=V", records[0].Resource().Encoded(label.DefaultEncoder()))

	sum, err = records[0].Aggregator().(aggregator.Sum).Sum()
	require.Equal(t, int64(7), sum.AsInt64())
	require.Nil(t, err)

	p.Stop()
}

func TestPushExportError(t *testing.T) {
	injector := func(name string, e error) func(r export.Record) error {
		return func(r export.Record) error {
			if r.Descriptor().Name() == name {
				return e
			}
			return nil
		}
	}
	var errAggregator = fmt.Errorf("unexpected error")
	var tests = []struct {
		name                string
		injectedError       error
		expectedDescriptors []string
		expectedError       error
	}{
		{"errNone", nil, []string{"counter1{R=V,X=Y}", "counter2{R=V,}"}, nil},
		{"errNoData", aggregator.ErrNoData, []string{"counter2{R=V,}"}, nil},
		{"errUnexpected", errAggregator, []string{}, errAggregator},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fix := newFixture(t)
			fix.exporter.injectErr = injector("counter1", tt.injectedError)

			p := push.New(
				testSelector{},
				fix.exporter,
				push.WithPeriod(time.Second),
				push.WithResource(testResource),
			)

			var err error
			var lock sync.Mutex
			p.SetErrorHandler(func(sdkErr error) {
				lock.Lock()
				defer lock.Unlock()
				err = sdkErr
			})

			mock := controllerTest.NewMockClock()
			p.SetClock(mock)

			ctx := context.Background()

			meter := p.Provider().Meter("name")
			counter1 := metric.Must(meter).NewInt64Counter("counter1")
			counter2 := metric.Must(meter).NewInt64Counter("counter2")

			p.Start()
			runtime.Gosched()

			counter1.Add(ctx, 3, kv.String("X", "Y"))
			counter2.Add(ctx, 5)

			require.Equal(t, 0, fix.exporter.exports)
			require.Nil(t, err)

			mock.Add(time.Second)
			runtime.Gosched()

			records, exports := fix.exporter.resetRecords()
			require.Equal(t, 1, exports)
			lock.Lock()
			if tt.expectedError == nil {
				require.NoError(t, err)
			} else {
				require.Error(t, err)
				require.Equal(t, tt.expectedError, err)
			}
			lock.Unlock()
			require.Equal(t, len(tt.expectedDescriptors), len(records))
			for _, r := range records {
				require.Contains(t, tt.expectedDescriptors,
					fmt.Sprintf("%s{%s,%s}",
						r.Descriptor().Name(),
						r.Resource().Encoded(label.DefaultEncoder()),
						r.Labels().Encoded(label.DefaultEncoder()),
					),
				)
			}

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

package push_test

import (
	"context"
	"fmt"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"go.opentelemetry.io/otel/api/global"
	"go.opentelemetry.io/otel/api/kv"
	"go.opentelemetry.io/otel/api/label"
	"go.opentelemetry.io/otel/api/metric"
	"go.opentelemetry.io/otel/exporters/metric/test"
	export "go.opentelemetry.io/otel/sdk/export/metric"
	"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
	"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
	"go.opentelemetry.io/otel/sdk/metric/controller/push"
	controllerTest "go.opentelemetry.io/otel/sdk/metric/controller/test"
	"go.opentelemetry.io/otel/sdk/resource"
)

var testResource = resource.New(kv.String("R", "V"))

type handler struct{ err error }

func (h *handler) Handle(err error) {
	h.err = err
}

func (h *handler) Reset() {
	h.err = nil
}

func (h *handler) Flush() error {
	err := h.err
	h.Reset()
	return err
}

var testHandler *handler

func init() {
	testHandler = new(handler)
	global.SetHandler(testHandler)
}

type testExporter struct {
	t         *testing.T
	lock      sync.Mutex
	exports   int
	records   []export.Record
	injectErr func(r export.Record) error
}

type testFixture struct {
	checkpointSet *test.CheckpointSet
	exporter      *testExporter
}

type testSelector struct{}

func newFixture(t *testing.T) testFixture {
	checkpointSet := test.NewCheckpointSet(testResource)

	exporter := &testExporter{
		t: t,
	}
	return testFixture{
		checkpointSet: checkpointSet,
		exporter:      exporter,
	}
}

func (testSelector) AggregatorFor(*metric.Descriptor) export.Aggregator {
	return sum.New()
}

func (e *testExporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error {
	e.lock.Lock()
	defer e.lock.Unlock()
	e.exports++
	var records []export.Record
	if err := checkpointSet.ForEach(func(r export.Record) error {
		if e.injectErr != nil {
			if err := e.injectErr(r); err != nil {
				return err
			}
		}
		records = append(records, r)
		return nil
	}); err != nil {
		return err
	}
	e.records = records
	return nil
}

func (e *testExporter) resetRecords() ([]export.Record, int) {
	e.lock.Lock()
	defer e.lock.Unlock()
	r := e.records
	e.records = nil
	return r, e.exports
}

func TestPushDoubleStop(t *testing.T) {
	fix := newFixture(t)
	p := push.New(testSelector{}, fix.exporter)
	p.Start()
	p.Stop()
	p.Stop()
}

func TestPushDoubleStart(t *testing.T) {
	fix := newFixture(t)
	p := push.New(testSelector{}, fix.exporter)
	p.Start()
	p.Start()
	p.Stop()
}

func TestPushTicker(t *testing.T) {
	fix := newFixture(t)

	p := push.New(
		testSelector{},
		fix.exporter,
		push.WithPeriod(time.Second),
		push.WithResource(testResource),
	)
	meter := p.Provider().Meter("name")

	mock := controllerTest.NewMockClock()
	p.SetClock(mock)

	ctx := context.Background()

	counter := metric.Must(meter).NewInt64Counter("counter")

	p.Start()

	counter.Add(ctx, 3)

	records, exports := fix.exporter.resetRecords()
	require.Equal(t, 0, exports)
	require.Equal(t, 0, len(records))

	mock.Add(time.Second)
	runtime.Gosched()

	records, exports = fix.exporter.resetRecords()
	require.Equal(t, 1, exports)
	require.Equal(t, 1, len(records))
	require.Equal(t, "counter", records[0].Descriptor().Name())
	require.Equal(t, "R=V", records[0].Resource().Encoded(label.DefaultEncoder()))

	sum, err := records[0].Aggregator().(aggregator.Sum).Sum()
	require.Equal(t, int64(3), sum.AsInt64())
	require.Nil(t, err)

	fix.checkpointSet.Reset()

	counter.Add(ctx, 7)

	mock.Add(time.Second)
	runtime.Gosched()

	records, exports = fix.exporter.resetRecords()
	require.Equal(t, 2, exports)
	require.Equal(t, 1, len(records))
	require.Equal(t, "counter", records[0].Descriptor().Name())
	require.Equal(t, "R=V", records[0].Resource().Encoded(label.DefaultEncoder()))

	sum, err = records[0].Aggregator().(aggregator.Sum).Sum()
	require.Equal(t, int64(7), sum.AsInt64())
	require.Nil(t, err)

	p.Stop()
}

func TestPushExportError(t *testing.T) {
	injector := func(name string, e error) func(r export.Record) error {
		return func(r export.Record) error {
			if r.Descriptor().Name() == name {
				return e
			}
			return nil
		}
	}
	var errAggregator = fmt.Errorf("unexpected error")
	var tests = []struct {
		name                string
		injectedError       error
		expectedDescriptors []string
		expectedError       error
	}{
		{"errNone", nil, []string{"counter1{R=V,X=Y}", "counter2{R=V,}"}, nil},
		{"errNoData", aggregator.ErrNoData, []string{"counter2{R=V,}"}, nil},
		{"errUnexpected", errAggregator, []string{}, errAggregator},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fix := newFixture(t)
			fix.exporter.injectErr = injector("counter1", tt.injectedError)

			p := push.New(
				testSelector{},
				fix.exporter,
				push.WithPeriod(time.Second),
				push.WithResource(testResource),
			)

			mock := controllerTest.NewMockClock()
			p.SetClock(mock)

			ctx := context.Background()

			meter := p.Provider().Meter("name")
			counter1 := metric.Must(meter).NewInt64Counter("counter1")
			counter2 := metric.Must(meter).NewInt64Counter("counter2")

			p.Start()
			runtime.Gosched()

			counter1.Add(ctx, 3, kv.String("X", "Y"))
			counter2.Add(ctx, 5)

			require.Equal(t, 0, fix.exporter.exports)
			require.Nil(t, testHandler.Flush())

			mock.Add(time.Second)
			runtime.Gosched()

			records, exports := fix.exporter.resetRecords()
			require.Equal(t, 1, exports)
			if tt.expectedError == nil {
				require.NoError(t, testHandler.Flush())
			} else {
				err := testHandler.Flush()
				require.Error(t, err)
				require.Equal(t, tt.expectedError, err)
			}
			require.Equal(t, len(tt.expectedDescriptors), len(records))
			for _, r := range records {
				require.Contains(t, tt.expectedDescriptors,
					fmt.Sprintf("%s{%s,%s}",
						r.Descriptor().Name(),
						r.Resource().Encoded(label.DefaultEncoder()),
						r.Labels().Encoded(label.DefaultEncoder()),
					),
				)
			}

			p.Stop()
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"flag"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"net/http"
)

var connectionString string
var dbUser string
var dbPass string
var dbName string
var db *sql.DB

func main() {
	flag.StringVar(&dbUser, "dbUser", "", "Database user")
	flag.StringVar(&dbPass, "dbPass", "", "Database pass")
	flag.StringVar(&dbName, "dbName", "", "Database name")

	flag.Parse()
	connectionString = fmt.Sprintf("%s:%s@/%s?charset=utf8&parseTime=True", dbUser, dbPass, dbName)
	var dbErr error
	db, dbErr = sql.Open("mysql", connectionString)
	if dbErr != nil {
		panic(dbErr)
	}
	defer db.Close()

	router := mux.NewRouter()
	DefineUserRoutes(router)

	//this has to be last or it will override ports
	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./")))
	http.Handle("/", router)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Added JWT basic validation.<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"github.com/auth0/go-jwt-middleware"
	"github.com/dgrijalva/jwt-go"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"net/http"
	"time"
)

var connectionString string
var dbUser string
var dbPass string
var dbName string
var db *sql.DB
var mySigningKey = []byte("secret")

func main() {
	flag.StringVar(&dbUser, "dbUser", "", "Database user")
	flag.StringVar(&dbPass, "dbPass", "", "Database pass")
	flag.StringVar(&dbName, "dbName", "", "Database name")

	flag.Parse()
	connectionString = fmt.Sprintf("%s:%s@/%s?charset=utf8&parseTime=True", dbUser, dbPass, dbName)
	var dbErr error
	db, dbErr = sql.Open("mysql", connectionString)
	if dbErr != nil {
		fmt.Println(dbErr.Error())
	}
	defer db.Close()

	router := mux.NewRouter()
	DefineUserRoutes(router)
	router.Handle("/test", jwtMiddleware.Handler(handleTest)).Methods("GET")
	router.HandleFunc("/get-token", GetTokenHandler).Methods("GET")

	//this has to be last or it will override ports
	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./")))
	http.Handle("/", router)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		panic(err)
	}
}

func GetTokenHandler(writer http.ResponseWriter, request *http.Request) {
	token := jwt.New(jwt.SigningMethodHS256)
	claims := token.Claims.(jwt.MapClaims)

	claims["admin"] = true
	claims["name"] = "Mo"
	claims["exp"] = time.Now().Add(time.Hour * 24).Unix()

	tokenString, _ := token.SignedString(mySigningKey)

	writer.Write([]byte(tokenString))
}

var handleTest = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
	writer.WriteHeader(200)
	writer.Write([]byte("Test Successful"))
})

var jwtMiddleware = jwtmiddleware.New(jwtmiddleware.Options{
	ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
		return mySigningKey, nil
	},

	SigningMethod: jwt.SigningMethodHS256,
})
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
    "io/ioutil"
    "net/http"
    "os"
    "time"

    "github.com/michaelklishin/rabbit-hole"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/Sirupsen/logrus"
)

const (
    namespace = "rabbitmq"
    configPath = "config.json"
)

var log = logrus.New()

// Listed available metrics
var (
    channelsTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "channels_total",
            Help:      "Total number of open channels.",
        },
    )
    connectionsTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "connections_total",
            Help:      "Total number of open connections.",
        },
    )
)

type Config struct {
    Nodes      *[]Node     `json:"nodes"`
    Port       string      `json:"port"`
    Interval   string      `json:"req_interval"`
}

type Node struct {
    Name       string      `json:"name"`
    Url        string      `json:"url"`
    Uname      string      `json:"uname"`
    Password   string      `json:"password"`
    Interval   string      `json:"req_interval,omitempty"`
}

func updateNodesStats(config *Config) {
    for _, node := range *config.Nodes {

        if len(node.Interval) == 0 {
            node.Interval = config.Interval
        }
        go runRequestLoop(node)
    }
}

func runRequestLoop(node Node) {
    for {
        rmqc, err := rabbithole.NewClient(node.Url, node.Uname, node.Password)

        updateMetrics(rmqc)

        dt, err := time.ParseDuration(node.Interval)
        if err != nil {
            log.Warningln(err)
            dt = 30 * time.Second
        }
        time.Sleep(dt)
    }
}

func updateMetrics(client *rabbithole.Client) {
    r1, _ := client.ListConnections()
    r2, _ := client.ListChannels()

    channelsTotal.Set(float64(len(r1)))
    connectionsTotal.Set(float64(len(r2)))
}

func newConfig(path string) (*Config, error) {
    var config Config

    file, err := ioutil.ReadFile(path)
    if err != nil {
        return nil, err
    }
    err = json.Unmarshal(file, &config)
    return &config, err
}

func main() {
    log.Out = os.Stdout
    config, _ := newConfig(configPath)
    updateNodesStats(config)

    http.Handle("/metrics", prometheus.Handler())
    log.Infof("Starting RabbitMQ exporter on port: %s.\n", config.Port)
    http.ListenAndServe(":" + config.Port, nil)
}

// Register metrics to Prometheus
func init() {
    prometheus.MustRegister(channelsTotal)
    prometheus.MustRegister(connectionsTotal)
}
<commit_msg>remove rabbit-hole dependency, add new metrics<commit_after>package main

import (
    "encoding/json"
    "io/ioutil"
    "net/http"
    "os"
    "time"
    "fmt"

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

const (
    namespace = "rabbitmq"
    configPath = "config.json"
)

var log = logrus.New()

// Listed available metrics
var (
    connectionsTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "connections_total",
            Help:      "Total number of open connections.",
        },
    )
    channelsTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "channels_total",
            Help:      "Total number of open channels.",
        },
    )
    queuesTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "queues_total",
            Help:      "Total number of queues in use.",
        },
    )
    consumersTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "consumers_total",
            Help:      "Total number of message consumers.",
        },
    )
    exchangesTotal = prometheus.NewGauge(
        prometheus.GaugeOpts{
            Namespace: namespace,
            Name:      "exchanges_total",
            Help:      "Total number of exchanges in use.",
        },
    )
)

type Config struct {
    Nodes      *[]Node     `json:"nodes"`
    Port       string      `json:"port"`
    Interval   string      `json:"req_interval"`
}

type Node struct {
    Name       string      `json:"name"`
    Url        string      `json:"url"`
    Uname      string      `json:"uname"`
    Password   string      `json:"password"`
    Interval   string      `json:"req_interval,omitempty"`
}

func unpackMetrics(d *json.Decoder) map[string]float64 {
    var output map[string]interface{}

    if err := d.Decode(&output); err != nil {
        fmt.Printf("Error : %s", err)
    }
    metrics := make(map[string]float64)

    for k, v := range output["object_totals"].(map[string]interface{}) {
        metrics[k] = v.(float64)
    }
    return metrics
}

func getOverview(hostname, username, password string) *json.Decoder {
    client := &http.Client{}
    req, err := http.NewRequest("GET", hostname + "/api/overview", nil)
    req.SetBasicAuth(username, password)

    resp, err := client.Do(req)

    if err != nil {
        fmt.Printf("Error : %s", err)
    }

    return json.NewDecoder(resp.Body)
}

func updateNodesStats(config *Config) {
    for _, node := range *config.Nodes {

        if len(node.Interval) == 0 {
            node.Interval = config.Interval
        }
        go runRequestLoop(node)
    }
}

func runRequestLoop(node Node) {
    for {
        decoder := getOverview(node.Url, node.Uname, node.Password)
        metrics := unpackMetrics(decoder)

        updateMetrics(metrics)

        dt, err := time.ParseDuration(node.Interval)
        if err != nil {
            log.Warningln(err)
            dt = 30 * time.Second
        }
        time.Sleep(dt)
    }
}

func updateMetrics(metrics map[string]float64) {
    channelsTotal.Set(metrics["channels"])
    connectionsTotal.Set(metrics["connections"])
    consumersTotal.Set(metrics["consumers"])
    queuesTotal.Set(metrics["queues"])
    exchangesTotal.Set(metrics["exchanges"])
}

func newConfig(path string) (*Config, error) {
    var config Config

    file, err := ioutil.ReadFile(path)
    if err != nil {
        return nil, err
    }
    err = json.Unmarshal(file, &config)
    return &config, err
}

func main() {
    log.Out = os.Stdout
    config, _ := newConfig(configPath)
    updateNodesStats(config)

    http.Handle("/metrics", prometheus.Handler())
    log.Infof("Starting RabbitMQ exporter on port: %s.\n", config.Port)
    http.ListenAndServe(":" + config.Port, nil)
}

// Register metrics to Prometheus
func init() {
    prometheus.MustRegister(channelsTotal)
    prometheus.MustRegister(connectionsTotal)
    prometheus.MustRegister(queuesTotal)
    prometheus.MustRegister(exchangesTotal)
    prometheus.MustRegister(consumersTotal)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"os"
)

func main() {
	args := os.Args[1:]
	if len(args) != 2 {
		fmt.Println("Usage: md5check [file] [provided md5 sum]")
		os.Exit(1)
	}

	filename := args[0]
	knownSum, err := hex.DecodeString(args[1])
	if err != nil {
		fmt.Println("Error decoding provided md5 hash: ", err)
		os.Exit(1)
	}

	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error reading input file: ", err)
		file.Close()
		os.Exit(1)
	}

	sum := md5.New()
	_, err = io.Copy(sum, file)
	if err != nil {
		fmt.Println("Error computing md5 sum of provided file: ", err)
		file.Close()
		os.Exit(1)
	}

	fileSum := sum.Sum(nil)

	if bytes.Compare(knownSum, fileSum) != 0 {
		fmt.Printf("MISMATCH! MD5 Sum of given file was: %x\n", fileSum)
		file.Close()
		os.Exit(1)
	} else {
		fmt.Println("OK")
		file.Close()
	}
}
<commit_msg>kept file open longer than necessary<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"os"
)

func main() {
	args := os.Args[1:]
	if len(args) != 2 {
		fmt.Println("Usage: md5check [file] [provided md5 sum]")
		os.Exit(1)
	}

	filename := args[0]
	knownSum, err := hex.DecodeString(args[1])
	if err != nil {
		fmt.Println("Error decoding provided md5 hash: ", err)
		os.Exit(1)
	}

	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error reading input file: ", err)
		file.Close()
		os.Exit(1)
	}

	sum := md5.New()
	_, err = io.Copy(sum, file)
	if err != nil {
		fmt.Println("Error computing md5 sum of provided file: ", err)
		file.Close()
		os.Exit(1)
	}

	file.Close()
	fileSum := sum.Sum(nil)

	if bytes.Compare(knownSum, fileSum) != 0 {
		fmt.Printf("MISMATCH! MD5 Sum of given file was: %x\n", fileSum)
		os.Exit(1)
	} else {
		fmt.Println("OK")
	}
}
<|endoftext|>


<commit_before>// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package pci

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/jaypipes/pcidb"

	"github.com/jaypipes/ghw/pkg/context"
	"github.com/jaypipes/ghw/pkg/linuxpath"
	pciaddr "github.com/jaypipes/ghw/pkg/pci/address"
	"github.com/jaypipes/ghw/pkg/topology"
	"github.com/jaypipes/ghw/pkg/util"
)

const (
	// found running `wc` against real linux systems
	modAliasExpectedLength = 54
)

func (i *Info) load() error {
	db, err := pcidb.New(pcidb.WithChroot(i.ctx.Chroot))
	if err != nil {
		return err
	}
	i.Classes = db.Classes
	i.Vendors = db.Vendors
	i.Products = db.Products
	i.Devices = i.ListDevices()
	return nil
}

func getDeviceModaliasPath(ctx *context.Context, pciAddr *pciaddr.Address) string {
	paths := linuxpath.New(ctx)
	return filepath.Join(
		paths.SysBusPciDevices,
		pciAddr.String(),
		"modalias",
	)
}

func getDeviceRevision(ctx *context.Context, pciAddr *pciaddr.Address) string {
	paths := linuxpath.New(ctx)
	revisionPath := filepath.Join(
		paths.SysBusPciDevices,
		pciAddr.String(),
		"revision",
	)

	if _, err := os.Stat(revisionPath); err != nil {
		return ""
	}
	revision, err := ioutil.ReadFile(revisionPath)
	if err != nil {
		return ""
	}
	return strings.TrimSpace(string(revision))
}

func getDeviceNUMANode(ctx *context.Context, pciAddr *pciaddr.Address) *topology.Node {
	paths := linuxpath.New(ctx)
	numaNodePath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "numa_node")

	if _, err := os.Stat(numaNodePath); err != nil {
		return nil
	}

	nodeIdx := util.SafeIntFromFile(ctx, numaNodePath)
	if nodeIdx == -1 {
		return nil
	}

	return &topology.Node{
		ID: nodeIdx,
	}
}

func getDeviceDriver(ctx *context.Context, pciAddr *pciaddr.Address) string {
	paths := linuxpath.New(ctx)
	driverPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "driver")

	if _, err := os.Stat(driverPath); err != nil {
		return ""
	}

	dest, err := os.Readlink(driverPath)
	if err != nil {
		return ""
	}
	return filepath.Base(dest)
}

type deviceModaliasInfo struct {
	vendorID     string
	productID    string
	subproductID string
	subvendorID  string
	classID      string
	subclassID   string
	progIfaceID  string
}

func parseModaliasFile(fp string) *deviceModaliasInfo {
	if _, err := os.Stat(fp); err != nil {
		return nil
	}
	data, err := ioutil.ReadFile(fp)
	if err != nil {
		return nil
	}

	return parseModaliasData(string(data))
}

func parseModaliasData(data string) *deviceModaliasInfo {
	// extra sanity check to avoid segfaults. We actually expect
	// the data to be exactly long `modAliasExpectedlength`, but
	// we will happily ignore any extra data we don't know how to
	// handle.
	if len(data) < modAliasExpectedLength {
		return nil
	}
	// The modalias file is an encoded file that looks like this:
	//
	// $ cat /sys/devices/pci0000\:00/0000\:00\:03.0/0000\:03\:00.0/modalias
	// pci:v000010DEd00001C82sv00001043sd00008613bc03sc00i00
	//
	// It is interpreted like so:
	//
	// pci: -- ignore
	// v000010DE -- PCI vendor ID
	// d00001C82 -- PCI device ID (the product/model ID)
	// sv00001043 -- PCI subsystem vendor ID
	// sd00008613 -- PCI subsystem device ID (subdevice product/model ID)
	// bc03 -- PCI base class
	// sc00 -- PCI subclass
	// i00 -- programming interface
	vendorID := strings.ToLower(data[9:13])
	productID := strings.ToLower(data[18:22])
	subvendorID := strings.ToLower(data[28:32])
	subproductID := strings.ToLower(data[38:42])
	classID := data[44:46]
	subclassID := data[48:50]
	progIfaceID := data[51:53]
	return &deviceModaliasInfo{
		vendorID:     vendorID,
		productID:    productID,
		subproductID: subproductID,
		subvendorID:  subvendorID,
		classID:      classID,
		subclassID:   subclassID,
		progIfaceID:  progIfaceID,
	}
}

// Returns a pointer to a pcidb.Vendor struct matching the supplied vendor
// ID string. If no such vendor ID string could be found, returns the
// pcidb.Vendor struct populated with "unknown" vendor Name attribute and
// empty Products attribute.
func findPCIVendor(info *Info, vendorID string) *pcidb.Vendor {
	vendor := info.Vendors[vendorID]
	if vendor == nil {
		return &pcidb.Vendor{
			ID:       vendorID,
			Name:     util.UNKNOWN,
			Products: []*pcidb.Product{},
		}
	}
	return vendor
}

// Returns a pointer to a pcidb.Product struct matching the supplied vendor
// and product ID strings. If no such product could be found, returns the
// pcidb.Product struct populated with "unknown" product Name attribute and
// empty Subsystems attribute.
func findPCIProduct(
	info *Info,
	vendorID string,
	productID string,
) *pcidb.Product {
	product := info.Products[vendorID+productID]
	if product == nil {
		return &pcidb.Product{
			ID:         productID,
			Name:       util.UNKNOWN,
			Subsystems: []*pcidb.Product{},
		}
	}
	return product
}

// Returns a pointer to a pcidb.Product struct matching the supplied vendor,
// product, subvendor and subproduct ID strings. If no such product could be
// found, returns the pcidb.Product struct populated with "unknown" product
// Name attribute and empty Subsystems attribute.
func findPCISubsystem(
	info *Info,
	vendorID string,
	productID string,
	subvendorID string,
	subproductID string,
) *pcidb.Product {
	product := info.Products[vendorID+productID]
	subvendor := info.Vendors[subvendorID]
	if subvendor != nil && product != nil {
		for _, p := range product.Subsystems {
			if p.ID == subproductID {
				return p
			}
		}
	}
	return &pcidb.Product{
		VendorID: subvendorID,
		ID:       subproductID,
		Name:     util.UNKNOWN,
	}
}

// Returns a pointer to a pcidb.Class struct matching the supplied class ID
// string. If no such class ID string could be found, returns the
// pcidb.Class struct populated with "unknown" class Name attribute and
// empty Subclasses attribute.
func findPCIClass(info *Info, classID string) *pcidb.Class {
	class := info.Classes[classID]
	if class == nil {
		return &pcidb.Class{
			ID:         classID,
			Name:       util.UNKNOWN,
			Subclasses: []*pcidb.Subclass{},
		}
	}
	return class
}

// Returns a pointer to a pcidb.Subclass struct matching the supplied class
// and subclass ID strings.  If no such subclass could be found, returns the
// pcidb.Subclass struct populated with "unknown" subclass Name attribute
// and empty ProgrammingInterfaces attribute.
func findPCISubclass(
	info *Info,
	classID string,
	subclassID string,
) *pcidb.Subclass {
	class := info.Classes[classID]
	if class != nil {
		for _, sc := range class.Subclasses {
			if sc.ID == subclassID {
				return sc
			}
		}
	}
	return &pcidb.Subclass{
		ID:                    subclassID,
		Name:                  util.UNKNOWN,
		ProgrammingInterfaces: []*pcidb.ProgrammingInterface{},
	}
}

// Returns a pointer to a pcidb.ProgrammingInterface struct matching the
// supplied class, subclass and programming interface ID strings.  If no such
// programming interface could be found, returns the
// pcidb.ProgrammingInterface struct populated with "unknown" Name attribute
func findPCIProgrammingInterface(
	info *Info,
	classID string,
	subclassID string,
	progIfaceID string,
) *pcidb.ProgrammingInterface {
	subclass := findPCISubclass(info, classID, subclassID)
	for _, pi := range subclass.ProgrammingInterfaces {
		if pi.ID == progIfaceID {
			return pi
		}
	}
	return &pcidb.ProgrammingInterface{
		ID:   progIfaceID,
		Name: util.UNKNOWN,
	}
}

// GetDevice returns a pointer to a Device struct that describes the PCI
// device at the requested address. If no such device could be found, returns nil.
func (info *Info) GetDevice(address string) *Device {
	// check cached data first
	if dev := info.lookupDevice(address); dev != nil {
		return dev
	}

	pciAddr := pciaddr.FromString(address)
	if pciAddr == nil {
		info.ctx.Warn("error parsing the pci address %q", address)
		return nil
	}

	// no cached data, let's get the information from system.
	fp := getDeviceModaliasPath(info.ctx, pciAddr)
	if fp == "" {
		info.ctx.Warn("error finding modalias info for device %q", address)
		return nil
	}

	modaliasInfo := parseModaliasFile(fp)
	if modaliasInfo == nil {
		info.ctx.Warn("error parsing modalias info for device %q", address)
		return nil
	}

	device := info.getDeviceFromModaliasInfo(address, modaliasInfo)
	device.Revision = getDeviceRevision(info.ctx, pciAddr)
	if info.arch == topology.ARCHITECTURE_NUMA {
		device.Node = getDeviceNUMANode(info.ctx, pciAddr)
	}
	device.Driver = getDeviceDriver(info.ctx, pciAddr)
	return device
}

// ParseDevice returns a pointer to a Device given its describing data.
// The PCI device obtained this way may not exist in the system;
// use GetDevice to get a *Device which is found in the system
func (info *Info) ParseDevice(address, modalias string) *Device {
	modaliasInfo := parseModaliasData(modalias)
	if modaliasInfo == nil {
		return nil
	}
	return info.getDeviceFromModaliasInfo(address, modaliasInfo)
}

func (info *Info) getDeviceFromModaliasInfo(address string, modaliasInfo *deviceModaliasInfo) *Device {
	vendor := findPCIVendor(info, modaliasInfo.vendorID)
	product := findPCIProduct(
		info,
		modaliasInfo.vendorID,
		modaliasInfo.productID,
	)
	subsystem := findPCISubsystem(
		info,
		modaliasInfo.vendorID,
		modaliasInfo.productID,
		modaliasInfo.subvendorID,
		modaliasInfo.subproductID,
	)
	class := findPCIClass(info, modaliasInfo.classID)
	subclass := findPCISubclass(
		info,
		modaliasInfo.classID,
		modaliasInfo.subclassID,
	)
	progIface := findPCIProgrammingInterface(
		info,
		modaliasInfo.classID,
		modaliasInfo.subclassID,
		modaliasInfo.progIfaceID,
	)

	return &Device{
		Address:              address,
		Vendor:               vendor,
		Subsystem:            subsystem,
		Product:              product,
		Class:                class,
		Subclass:             subclass,
		ProgrammingInterface: progIface,
	}
}

// ListDevices returns a list of pointers to Device structs present on the
// host system
// DEPRECATED. Will be removed in v1.0. Please use
// github.com/jaypipes/pcidb to explore PCIDB information
func (info *Info) ListDevices() []*Device {
	paths := linuxpath.New(info.ctx)
	devs := make([]*Device, 0)
	// We scan the /sys/bus/pci/devices directory which contains a collection
	// of symlinks. The names of the symlinks are all the known PCI addresses
	// for the host. For each address, we grab a *Device matching the
	// address and append to the returned array.
	links, err := ioutil.ReadDir(paths.SysBusPciDevices)
	if err != nil {
		info.ctx.Warn("failed to read /sys/bus/pci/devices")
		return nil
	}
	var dev *Device
	for _, link := range links {
		addr := link.Name()
		dev = info.GetDevice(addr)
		if dev == nil {
			info.ctx.Warn("failed to get device information for PCI address %s", addr)
		} else {
			devs = append(devs, dev)
		}
	}
	return devs
}
<commit_msg>pci: don't pass chroot to pcidb with snapshots<commit_after>// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package pci

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/jaypipes/pcidb"

	"github.com/jaypipes/ghw/pkg/context"
	"github.com/jaypipes/ghw/pkg/linuxpath"
	"github.com/jaypipes/ghw/pkg/option"
	pciaddr "github.com/jaypipes/ghw/pkg/pci/address"
	"github.com/jaypipes/ghw/pkg/topology"
	"github.com/jaypipes/ghw/pkg/util"
)

const (
	// found running `wc` against real linux systems
	modAliasExpectedLength = 54
)

func (i *Info) load() error {
	// when consuming snapshots - most notably, but not only, in tests,
	// the context pkg forces the chroot value to the unpacked snapshot root.
	// This is intentional, intentionally transparent and ghw is prepared to handle this case.
	// However, `pcidb` is not. It doesn't know about ghw snaphots, nor it should.
	// so we need to complicate things a bit. If the user explicitely supplied
	// a chroot option, then we should honor it all across the stack, and passing down
	// the chroot to pcidb is the right thing to do. If, however, the chroot was
	// implcitely set by snapshot support, then this must be consumed by ghw only.
	// In this case we should NOT pass it down to pcidb.
	chroot := i.ctx.Chroot
	if i.ctx.SnapshotPath != "" {
		chroot = option.DefaultChroot
	}
	db, err := pcidb.New(pcidb.WithChroot(chroot))
	if err != nil {
		return err
	}
	i.Classes = db.Classes
	i.Vendors = db.Vendors
	i.Products = db.Products
	i.Devices = i.ListDevices()
	return nil
}

func getDeviceModaliasPath(ctx *context.Context, pciAddr *pciaddr.Address) string {
	paths := linuxpath.New(ctx)
	return filepath.Join(
		paths.SysBusPciDevices,
		pciAddr.String(),
		"modalias",
	)
}

func getDeviceRevision(ctx *context.Context, pciAddr *pciaddr.Address) string {
	paths := linuxpath.New(ctx)
	revisionPath := filepath.Join(
		paths.SysBusPciDevices,
		pciAddr.String(),
		"revision",
	)

	if _, err := os.Stat(revisionPath); err != nil {
		return ""
	}
	revision, err := ioutil.ReadFile(revisionPath)
	if err != nil {
		return ""
	}
	return strings.TrimSpace(string(revision))
}

func getDeviceNUMANode(ctx *context.Context, pciAddr *pciaddr.Address) *topology.Node {
	paths := linuxpath.New(ctx)
	numaNodePath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "numa_node")

	if _, err := os.Stat(numaNodePath); err != nil {
		return nil
	}

	nodeIdx := util.SafeIntFromFile(ctx, numaNodePath)
	if nodeIdx == -1 {
		return nil
	}

	return &topology.Node{
		ID: nodeIdx,
	}
}

func getDeviceDriver(ctx *context.Context, pciAddr *pciaddr.Address) string {
	paths := linuxpath.New(ctx)
	driverPath := filepath.Join(paths.SysBusPciDevices, pciAddr.String(), "driver")

	if _, err := os.Stat(driverPath); err != nil {
		return ""
	}

	dest, err := os.Readlink(driverPath)
	if err != nil {
		return ""
	}
	return filepath.Base(dest)
}

type deviceModaliasInfo struct {
	vendorID     string
	productID    string
	subproductID string
	subvendorID  string
	classID      string
	subclassID   string
	progIfaceID  string
}

func parseModaliasFile(fp string) *deviceModaliasInfo {
	if _, err := os.Stat(fp); err != nil {
		return nil
	}
	data, err := ioutil.ReadFile(fp)
	if err != nil {
		return nil
	}

	return parseModaliasData(string(data))
}

func parseModaliasData(data string) *deviceModaliasInfo {
	// extra sanity check to avoid segfaults. We actually expect
	// the data to be exactly long `modAliasExpectedlength`, but
	// we will happily ignore any extra data we don't know how to
	// handle.
	if len(data) < modAliasExpectedLength {
		return nil
	}
	// The modalias file is an encoded file that looks like this:
	//
	// $ cat /sys/devices/pci0000\:00/0000\:00\:03.0/0000\:03\:00.0/modalias
	// pci:v000010DEd00001C82sv00001043sd00008613bc03sc00i00
	//
	// It is interpreted like so:
	//
	// pci: -- ignore
	// v000010DE -- PCI vendor ID
	// d00001C82 -- PCI device ID (the product/model ID)
	// sv00001043 -- PCI subsystem vendor ID
	// sd00008613 -- PCI subsystem device ID (subdevice product/model ID)
	// bc03 -- PCI base class
	// sc00 -- PCI subclass
	// i00 -- programming interface
	vendorID := strings.ToLower(data[9:13])
	productID := strings.ToLower(data[18:22])
	subvendorID := strings.ToLower(data[28:32])
	subproductID := strings.ToLower(data[38:42])
	classID := data[44:46]
	subclassID := data[48:50]
	progIfaceID := data[51:53]
	return &deviceModaliasInfo{
		vendorID:     vendorID,
		productID:    productID,
		subproductID: subproductID,
		subvendorID:  subvendorID,
		classID:      classID,
		subclassID:   subclassID,
		progIfaceID:  progIfaceID,
	}
}

// Returns a pointer to a pcidb.Vendor struct matching the supplied vendor
// ID string. If no such vendor ID string could be found, returns the
// pcidb.Vendor struct populated with "unknown" vendor Name attribute and
// empty Products attribute.
func findPCIVendor(info *Info, vendorID string) *pcidb.Vendor {
	vendor := info.Vendors[vendorID]
	if vendor == nil {
		return &pcidb.Vendor{
			ID:       vendorID,
			Name:     util.UNKNOWN,
			Products: []*pcidb.Product{},
		}
	}
	return vendor
}

// Returns a pointer to a pcidb.Product struct matching the supplied vendor
// and product ID strings. If no such product could be found, returns the
// pcidb.Product struct populated with "unknown" product Name attribute and
// empty Subsystems attribute.
func findPCIProduct(
	info *Info,
	vendorID string,
	productID string,
) *pcidb.Product {
	product := info.Products[vendorID+productID]
	if product == nil {
		return &pcidb.Product{
			ID:         productID,
			Name:       util.UNKNOWN,
			Subsystems: []*pcidb.Product{},
		}
	}
	return product
}

// Returns a pointer to a pcidb.Product struct matching the supplied vendor,
// product, subvendor and subproduct ID strings. If no such product could be
// found, returns the pcidb.Product struct populated with "unknown" product
// Name attribute and empty Subsystems attribute.
func findPCISubsystem(
	info *Info,
	vendorID string,
	productID string,
	subvendorID string,
	subproductID string,
) *pcidb.Product {
	product := info.Products[vendorID+productID]
	subvendor := info.Vendors[subvendorID]
	if subvendor != nil && product != nil {
		for _, p := range product.Subsystems {
			if p.ID == subproductID {
				return p
			}
		}
	}
	return &pcidb.Product{
		VendorID: subvendorID,
		ID:       subproductID,
		Name:     util.UNKNOWN,
	}
}

// Returns a pointer to a pcidb.Class struct matching the supplied class ID
// string. If no such class ID string could be found, returns the
// pcidb.Class struct populated with "unknown" class Name attribute and
// empty Subclasses attribute.
func findPCIClass(info *Info, classID string) *pcidb.Class {
	class := info.Classes[classID]
	if class == nil {
		return &pcidb.Class{
			ID:         classID,
			Name:       util.UNKNOWN,
			Subclasses: []*pcidb.Subclass{},
		}
	}
	return class
}

// Returns a pointer to a pcidb.Subclass struct matching the supplied class
// and subclass ID strings.  If no such subclass could be found, returns the
// pcidb.Subclass struct populated with "unknown" subclass Name attribute
// and empty ProgrammingInterfaces attribute.
func findPCISubclass(
	info *Info,
	classID string,
	subclassID string,
) *pcidb.Subclass {
	class := info.Classes[classID]
	if class != nil {
		for _, sc := range class.Subclasses {
			if sc.ID == subclassID {
				return sc
			}
		}
	}
	return &pcidb.Subclass{
		ID:                    subclassID,
		Name:                  util.UNKNOWN,
		ProgrammingInterfaces: []*pcidb.ProgrammingInterface{},
	}
}

// Returns a pointer to a pcidb.ProgrammingInterface struct matching the
// supplied class, subclass and programming interface ID strings.  If no such
// programming interface could be found, returns the
// pcidb.ProgrammingInterface struct populated with "unknown" Name attribute
func findPCIProgrammingInterface(
	info *Info,
	classID string,
	subclassID string,
	progIfaceID string,
) *pcidb.ProgrammingInterface {
	subclass := findPCISubclass(info, classID, subclassID)
	for _, pi := range subclass.ProgrammingInterfaces {
		if pi.ID == progIfaceID {
			return pi
		}
	}
	return &pcidb.ProgrammingInterface{
		ID:   progIfaceID,
		Name: util.UNKNOWN,
	}
}

// GetDevice returns a pointer to a Device struct that describes the PCI
// device at the requested address. If no such device could be found, returns nil.
func (info *Info) GetDevice(address string) *Device {
	// check cached data first
	if dev := info.lookupDevice(address); dev != nil {
		return dev
	}

	pciAddr := pciaddr.FromString(address)
	if pciAddr == nil {
		info.ctx.Warn("error parsing the pci address %q", address)
		return nil
	}

	// no cached data, let's get the information from system.
	fp := getDeviceModaliasPath(info.ctx, pciAddr)
	if fp == "" {
		info.ctx.Warn("error finding modalias info for device %q", address)
		return nil
	}

	modaliasInfo := parseModaliasFile(fp)
	if modaliasInfo == nil {
		info.ctx.Warn("error parsing modalias info for device %q", address)
		return nil
	}

	device := info.getDeviceFromModaliasInfo(address, modaliasInfo)
	device.Revision = getDeviceRevision(info.ctx, pciAddr)
	if info.arch == topology.ARCHITECTURE_NUMA {
		device.Node = getDeviceNUMANode(info.ctx, pciAddr)
	}
	device.Driver = getDeviceDriver(info.ctx, pciAddr)
	return device
}

// ParseDevice returns a pointer to a Device given its describing data.
// The PCI device obtained this way may not exist in the system;
// use GetDevice to get a *Device which is found in the system
func (info *Info) ParseDevice(address, modalias string) *Device {
	modaliasInfo := parseModaliasData(modalias)
	if modaliasInfo == nil {
		return nil
	}
	return info.getDeviceFromModaliasInfo(address, modaliasInfo)
}

func (info *Info) getDeviceFromModaliasInfo(address string, modaliasInfo *deviceModaliasInfo) *Device {
	vendor := findPCIVendor(info, modaliasInfo.vendorID)
	product := findPCIProduct(
		info,
		modaliasInfo.vendorID,
		modaliasInfo.productID,
	)
	subsystem := findPCISubsystem(
		info,
		modaliasInfo.vendorID,
		modaliasInfo.productID,
		modaliasInfo.subvendorID,
		modaliasInfo.subproductID,
	)
	class := findPCIClass(info, modaliasInfo.classID)
	subclass := findPCISubclass(
		info,
		modaliasInfo.classID,
		modaliasInfo.subclassID,
	)
	progIface := findPCIProgrammingInterface(
		info,
		modaliasInfo.classID,
		modaliasInfo.subclassID,
		modaliasInfo.progIfaceID,
	)

	return &Device{
		Address:              address,
		Vendor:               vendor,
		Subsystem:            subsystem,
		Product:              product,
		Class:                class,
		Subclass:             subclass,
		ProgrammingInterface: progIface,
	}
}

// ListDevices returns a list of pointers to Device structs present on the
// host system
// DEPRECATED. Will be removed in v1.0. Please use
// github.com/jaypipes/pcidb to explore PCIDB information
func (info *Info) ListDevices() []*Device {
	paths := linuxpath.New(info.ctx)
	devs := make([]*Device, 0)
	// We scan the /sys/bus/pci/devices directory which contains a collection
	// of symlinks. The names of the symlinks are all the known PCI addresses
	// for the host. For each address, we grab a *Device matching the
	// address and append to the returned array.
	links, err := ioutil.ReadDir(paths.SysBusPciDevices)
	if err != nil {
		info.ctx.Warn("failed to read /sys/bus/pci/devices")
		return nil
	}
	var dev *Device
	for _, link := range links {
		addr := link.Name()
		dev = info.GetDevice(addr)
		if dev == nil {
			info.ctx.Warn("failed to get device information for PCI address %s", addr)
		} else {
			devs = append(devs, dev)
		}
	}
	return devs
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

var (
	errFound = errors.New("found")
)

const (
	sp      = string(os.PathListSeparator)
	version = "0.0.1"
)

func main() {
	var (
		paths      []string
		searchDirs []string
	)

	searchProgramFiles := flag.Bool("p", false, "Search Program Files directories")
	showAll := flag.Bool("a", false, "Show all matches")
	help := flag.Bool("h", false, "Show this help message")
	printVersion := flag.Bool("v", false, "Print version and exit")
	flag.Usage = usage

	flag.Parse()

	commands := flag.Args()

	if *printVersion {
		fmt.Fprintf(os.Stdout, "%s %v, Copyright (C) 2014 Sol Touré.\n", filepath.Base(os.Args[0]), version)
		return
	}

	if *help || len(commands) == 0 {
		flag.Usage()
		return
	}

	//Add the current directory to the search path
	if currentDir, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil {
		paths = append(paths, currentDir)
	}

	if *searchProgramFiles {
		searchDirs = append(searchDirs, strings.Split(os.Getenv("ProgramFiles"), sp)...)
		searchDirs = append(searchDirs, strings.Split(os.Getenv("ProgramFiles(x86)"), sp)...)
	}

	paths = append(paths, strings.Split(os.Getenv("PATH"), sp)...)
	pathExt := map[string]struct{}{
		".com": struct{}{},
		".exe": struct{}{},
		".bat": struct{}{},
		".cmd": struct{}{},
	}

	for _, ext := range strings.Split(os.Getenv("PATHEXT"), sp) {
		pathExt[strings.ToLower(ext)] = struct{}{}
	}

	result := newResultMap()

	for _, p := range paths {
		for _, cmd := range commands {
			f := filepath.Join(p, cmd)
			if !result.hasKey(cmd) || *showAll {

				if len(filepath.Ext(cmd)) == 0 {
					for ext, _ := range pathExt {
						name := f + ext
						if (!result.hasKey(cmd) || *showAll) && isFile(name) && result.add(cmd, name) {
							fmt.Println(name)
						}
					}
				} else if (!result.hasKey(cmd) || *showAll) && isFile(f) && result.add(cmd, f) {
					fmt.Println(f)
				}
			}
		}
	}

	if *showAll || len(result) != len(commands) {
		for _, d := range searchDirs {
			walk(d, func(p string, info os.FileInfo, err error) error {
				if err != nil {
					return filepath.SkipDir
				}

				if info.IsDir() {
					return nil
				}

				for _, cmd := range commands {
					found := result.hasKey(cmd)
					addExtension := len(filepath.Ext(cmd)) == 0

					if !found || *showAll {
						base := filepath.Base(p)
						if _, ok := pathExt[strings.ToLower(filepath.Ext(p))]; ok {
							name := cmd
							if addExtension {
								name += filepath.Ext(p)
							}

							if strings.ToLower(base) == strings.ToLower(name) && result.add(cmd, p) {
								fmt.Println(p)
							}
						}
					}
					if !*showAll && len(commands) == len(result) {
						return errFound
					}
				}

				if !*showAll && len(commands) == len(result) {
					return errFound
				}

				return nil
			})
		}
	}

	for _, cmd := range commands {
		if _, found := result[cmd]; !found {
			fmt.Printf("\n%s: no %q in %v\n\n", filepath.Base(os.Args[0]), cmd, append(paths, searchDirs...))
		}
	}
	fmt.Printf("%v\n", result)
}

type resultMap map[string]map[string]struct{}

func newResultMap() resultMap {
	return make(map[string]map[string]struct{})
}

func (r resultMap) add(key, value string) bool {
	if _, ok := r[key]; ok {
		if _, ok := r[key][value]; ok {
			return !ok
		}
		r[key][value] = struct{}{}
		return ok
	}
	r[key] = make(map[string]struct{})
	r[key][value] = struct{}{}
	return true
}

func (r resultMap) hasKey(key string) bool {
	_, ok := r[key]
	return ok
}

func isFile(path string) bool {
	stat, err := os.Stat(path)
	return err == nil && !stat.IsDir()
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] COMMAND [...]\n", filepath.Base(os.Args[0]))
	flag.PrintDefaults()
}

type FileInfo struct {
	Path string
	os.FileInfo
}

func readdir(path string) ([]os.FileInfo, error) {
	f, err := os.Open(path)

	if err != nil {
		return nil, err
	}

	defer f.Close()

	return f.Readdir(-1)
}

func walk(path string, walkFn filepath.WalkFunc) error {
	var (
		stack   []*FileInfo
		current *FileInfo
	)

	info, err := os.Lstat(path)
	if err != nil || !info.IsDir() {
		return walkFn(path, info, err)
	}

	stack = append(stack, &FileInfo{path, info})

	for pos := len(stack) - 1; pos > -1; pos = len(stack) - 1 {
		current, stack = stack[pos], stack[:pos]

		if err := walkFn(current.Path, current, nil); err != nil {
			if err != filepath.SkipDir {
				return err
			}
			continue
		}

		infos, _ := readdir(current.Path)

		for _, info := range infos {
			sub := filepath.Join(current.Path, info.Name())

			if info.IsDir() {
				stack = append(stack, &FileInfo{sub, info})
			} else if err := walkFn(sub, info, nil); err != nil && err != filepath.SkipDir {
				return err
			}
		}
	}

	return nil
}
<commit_msg>Removed debug print<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

var (
	errFound = errors.New("found")
)

const (
	sp      = string(os.PathListSeparator)
	version = "0.0.1"
)

func main() {
	var (
		paths      []string
		searchDirs []string
	)

	searchProgramFiles := flag.Bool("p", false, "Search Program Files directories")
	showAll := flag.Bool("a", false, "Show all matches")
	help := flag.Bool("h", false, "Show this help message")
	printVersion := flag.Bool("v", false, "Print version and exit")
	flag.Usage = usage

	flag.Parse()

	commands := flag.Args()

	if *printVersion {
		fmt.Fprintf(os.Stdout, "%s %v, Copyright (C) 2014 Sol Touré.\n", filepath.Base(os.Args[0]), version)
		return
	}

	if *help || len(commands) == 0 {
		flag.Usage()
		return
	}

	//Add the current directory to the search path
	if currentDir, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil {
		paths = append(paths, currentDir)
	}

	if *searchProgramFiles {
		searchDirs = append(searchDirs, strings.Split(os.Getenv("ProgramFiles"), sp)...)
		searchDirs = append(searchDirs, strings.Split(os.Getenv("ProgramFiles(x86)"), sp)...)
	}

	paths = append(paths, strings.Split(os.Getenv("PATH"), sp)...)
	pathExt := map[string]struct{}{
		".com": struct{}{},
		".exe": struct{}{},
		".bat": struct{}{},
		".cmd": struct{}{},
	}

	for _, ext := range strings.Split(os.Getenv("PATHEXT"), sp) {
		pathExt[strings.ToLower(ext)] = struct{}{}
	}

	result := newResultMap()

	for _, p := range paths {
		for _, cmd := range commands {
			f := filepath.Join(p, cmd)
			if !result.hasKey(cmd) || *showAll {

				if len(filepath.Ext(cmd)) == 0 {
					for ext, _ := range pathExt {
						name := f + ext
						if (!result.hasKey(cmd) || *showAll) && isFile(name) && result.add(cmd, name) {
							fmt.Println(name)
						}
					}
				} else if (!result.hasKey(cmd) || *showAll) && isFile(f) && result.add(cmd, f) {
					fmt.Println(f)
				}
			}
		}
	}

	if *showAll || len(result) != len(commands) {
		for _, d := range searchDirs {
			walk(d, func(p string, info os.FileInfo, err error) error {
				if err != nil {
					return filepath.SkipDir
				}

				if info.IsDir() {
					return nil
				}

				for _, cmd := range commands {
					found := result.hasKey(cmd)
					addExtension := len(filepath.Ext(cmd)) == 0

					if !found || *showAll {
						base := filepath.Base(p)
						if _, ok := pathExt[strings.ToLower(filepath.Ext(p))]; ok {
							name := cmd
							if addExtension {
								name += filepath.Ext(p)
							}

							if strings.ToLower(base) == strings.ToLower(name) && result.add(cmd, p) {
								fmt.Println(p)
							}
						}
					}
					if !*showAll && len(commands) == len(result) {
						return errFound
					}
				}

				if !*showAll && len(commands) == len(result) {
					return errFound
				}

				return nil
			})
		}
	}

	for _, cmd := range commands {
		if _, found := result[cmd]; !found {
			fmt.Printf("\n%s: no %q in %v\n\n", filepath.Base(os.Args[0]), cmd, append(paths, searchDirs...))
		}
	}
}

type resultMap map[string]map[string]struct{}

func newResultMap() resultMap {
	return make(map[string]map[string]struct{})
}

func (r resultMap) add(key, value string) bool {
	if _, ok := r[key]; ok {
		if _, ok := r[key][value]; ok {
			return !ok
		}
		r[key][value] = struct{}{}
		return ok
	}
	r[key] = make(map[string]struct{})
	r[key][value] = struct{}{}
	return true
}

func (r resultMap) hasKey(key string) bool {
	_, ok := r[key]
	return ok
}

func isFile(path string) bool {
	stat, err := os.Stat(path)
	return err == nil && !stat.IsDir()
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] COMMAND [...]\n", filepath.Base(os.Args[0]))
	flag.PrintDefaults()
}

type FileInfo struct {
	Path string
	os.FileInfo
}

func readdir(path string) ([]os.FileInfo, error) {
	f, err := os.Open(path)

	if err != nil {
		return nil, err
	}

	defer f.Close()

	return f.Readdir(-1)
}

func walk(path string, walkFn filepath.WalkFunc) error {
	var (
		stack   []*FileInfo
		current *FileInfo
	)

	info, err := os.Lstat(path)
	if err != nil || !info.IsDir() {
		return walkFn(path, info, err)
	}

	stack = append(stack, &FileInfo{path, info})

	for pos := len(stack) - 1; pos > -1; pos = len(stack) - 1 {
		current, stack = stack[pos], stack[:pos]

		if err := walkFn(current.Path, current, nil); err != nil {
			if err != filepath.SkipDir {
				return err
			}
			continue
		}

		infos, _ := readdir(current.Path)

		for _, info := range infos {
			sub := filepath.Join(current.Path, info.Name())

			if info.IsDir() {
				stack = append(stack, &FileInfo{sub, info})
			} else if err := walkFn(sub, info, nil); err != nil && err != filepath.SkipDir {
				return err
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package runtime

// Note that the types provided in this file are not versioned and are intended to be
// safe to use from within all versions of every API object.

// TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type,
// like this:
// type MyAwesomeAPIObject struct {
//      runtime.TypeMeta    `json:",inline"`
//      ... // other fields
// }
// func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind
//
// TypeMeta is provided here for convenience. You may use it directly from this package or define
// your own with the same fields.
//
// +k8s:deepcopy-gen=false
// +protobuf=true
// +k8s:openapi-gen=true
type TypeMeta struct {
	// +optional
	APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty" protobuf:"bytes,1,opt,name=apiVersion"`
	// +optional
	Kind string `json:"kind,omitempty" yaml:"kind,omitempty" protobuf:"bytes,2,opt,name=kind"`
}

const (
	ContentTypeJSON     string = "application/json"
	ContentTypeYAML     string = "application/yaml"
	ContentTypeProtobuf string = "application/vnd.kubernetes.protobuf"
)

// RawExtension is used to hold extensions in external versions.
//
// To use this, make a field which has RawExtension as its type in your external, versioned
// struct, and Object in your internal struct. You also need to register your
// various plugin types.
//
// // Internal package:
// type MyAPIObject struct {
// 	runtime.TypeMeta `json:",inline"`
//	MyPlugin runtime.Object `json:"myPlugin"`
// }
// type PluginA struct {
//	AOption string `json:"aOption"`
// }
//
// // External package:
// type MyAPIObject struct {
// 	runtime.TypeMeta `json:",inline"`
//	MyPlugin runtime.RawExtension `json:"myPlugin"`
// }
// type PluginA struct {
//	AOption string `json:"aOption"`
// }
//
// // On the wire, the JSON will look something like this:
// {
//	"kind":"MyAPIObject",
//	"apiVersion":"v1",
//	"myPlugin": {
//		"kind":"PluginA",
//		"aOption":"foo",
//	},
// }
//
// So what happens? Decode first uses json or yaml to unmarshal the serialized data into
// your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.
// The next step is to copy (using pkg/conversion) into the internal struct. The runtime
// package's DefaultScheme has conversion functions installed which will unpack the
// JSON stored in RawExtension, turning it into the correct object type, and storing it
// in the Object. (TODO: In the case where the object is of an unknown type, a
// runtime.Unknown object will be created and stored.)
//
// +k8s:deepcopy-gen=true
// +protobuf=true
// +k8s:openapi-gen=true
type RawExtension struct {
	// Raw is the underlying serialization of this object.
	//
	// TODO: Determine how to detect ContentType and ContentEncoding of 'Raw' data.
	Raw []byte `protobuf:"bytes,1,opt,name=raw"`
	// Object can hold a representation of this extension - useful for working with versioned
	// structs.
	Object Object `json:"-"`
}

// Unknown allows api objects with unknown types to be passed-through. This can be used
// to deal with the API objects from a plug-in. Unknown objects still have functioning
// TypeMeta features-- kind, version, etc.
// TODO: Make this object have easy access to field based accessors and settors for
// metadata and field mutatation.
//
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +protobuf=true
// +k8s:openapi-gen=true
type Unknown struct {
	TypeMeta `json:",inline" protobuf:"bytes,1,opt,name=typeMeta"`
	// Raw will hold the complete serialized object which couldn't be matched
	// with a registered type. Most likely, nothing should be done with this
	// except for passing it through the system.
	Raw []byte `protobuf:"bytes,2,opt,name=raw"`
	// ContentEncoding is encoding used to encode 'Raw' data.
	// Unspecified means no encoding.
	ContentEncoding string `protobuf:"bytes,3,opt,name=contentEncoding"`
	// ContentType  is serialization method used to serialize 'Raw'.
	// Unspecified means ContentTypeJSON.
	ContentType string `protobuf:"bytes,4,opt,name=contentType"`
}

// VersionedObjects is used by Decoders to give callers a way to access all versions
// of an object during the decoding process.
//
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:deepcopy-gen=true
type VersionedObjects struct {
	// Objects is the set of objects retrieved during decoding, in order of conversion.
	// The 0 index is the object as serialized on the wire. If conversion has occurred,
	// other objects may be present. The right most object is the same as would be returned
	// by a normal Decode call.
	Objects []Object
}
<commit_msg>RawExtension.Raw json:"-"<commit_after>/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package runtime

// Note that the types provided in this file are not versioned and are intended to be
// safe to use from within all versions of every API object.

// TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type,
// like this:
// type MyAwesomeAPIObject struct {
//      runtime.TypeMeta    `json:",inline"`
//      ... // other fields
// }
// func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind
//
// TypeMeta is provided here for convenience. You may use it directly from this package or define
// your own with the same fields.
//
// +k8s:deepcopy-gen=false
// +protobuf=true
// +k8s:openapi-gen=true
type TypeMeta struct {
	// +optional
	APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty" protobuf:"bytes,1,opt,name=apiVersion"`
	// +optional
	Kind string `json:"kind,omitempty" yaml:"kind,omitempty" protobuf:"bytes,2,opt,name=kind"`
}

const (
	ContentTypeJSON     string = "application/json"
	ContentTypeYAML     string = "application/yaml"
	ContentTypeProtobuf string = "application/vnd.kubernetes.protobuf"
)

// RawExtension is used to hold extensions in external versions.
//
// To use this, make a field which has RawExtension as its type in your external, versioned
// struct, and Object in your internal struct. You also need to register your
// various plugin types.
//
// // Internal package:
// type MyAPIObject struct {
// 	runtime.TypeMeta `json:",inline"`
//	MyPlugin runtime.Object `json:"myPlugin"`
// }
// type PluginA struct {
//	AOption string `json:"aOption"`
// }
//
// // External package:
// type MyAPIObject struct {
// 	runtime.TypeMeta `json:",inline"`
//	MyPlugin runtime.RawExtension `json:"myPlugin"`
// }
// type PluginA struct {
//	AOption string `json:"aOption"`
// }
//
// // On the wire, the JSON will look something like this:
// {
//	"kind":"MyAPIObject",
//	"apiVersion":"v1",
//	"myPlugin": {
//		"kind":"PluginA",
//		"aOption":"foo",
//	},
// }
//
// So what happens? Decode first uses json or yaml to unmarshal the serialized data into
// your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.
// The next step is to copy (using pkg/conversion) into the internal struct. The runtime
// package's DefaultScheme has conversion functions installed which will unpack the
// JSON stored in RawExtension, turning it into the correct object type, and storing it
// in the Object. (TODO: In the case where the object is of an unknown type, a
// runtime.Unknown object will be created and stored.)
//
// +k8s:deepcopy-gen=true
// +protobuf=true
// +k8s:openapi-gen=true
type RawExtension struct {
	// Raw is the underlying serialization of this object.
	//
	// TODO: Determine how to detect ContentType and ContentEncoding of 'Raw' data.
	Raw []byte `json:"-" protobuf:"bytes,1,opt,name=raw"`
	// Object can hold a representation of this extension - useful for working with versioned
	// structs.
	Object Object `json:"-"`
}

// Unknown allows api objects with unknown types to be passed-through. This can be used
// to deal with the API objects from a plug-in. Unknown objects still have functioning
// TypeMeta features-- kind, version, etc.
// TODO: Make this object have easy access to field based accessors and settors for
// metadata and field mutatation.
//
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +protobuf=true
// +k8s:openapi-gen=true
type Unknown struct {
	TypeMeta `json:",inline" protobuf:"bytes,1,opt,name=typeMeta"`
	// Raw will hold the complete serialized object which couldn't be matched
	// with a registered type. Most likely, nothing should be done with this
	// except for passing it through the system.
	Raw []byte `protobuf:"bytes,2,opt,name=raw"`
	// ContentEncoding is encoding used to encode 'Raw' data.
	// Unspecified means no encoding.
	ContentEncoding string `protobuf:"bytes,3,opt,name=contentEncoding"`
	// ContentType  is serialization method used to serialize 'Raw'.
	// Unspecified means ContentTypeJSON.
	ContentType string `protobuf:"bytes,4,opt,name=contentType"`
}

// VersionedObjects is used by Decoders to give callers a way to access all versions
// of an object during the decoding process.
//
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:deepcopy-gen=true
type VersionedObjects struct {
	// Objects is the set of objects retrieved during decoding, in order of conversion.
	// The 0 index is the object as serialized on the wire. If conversion has occurred,
	// other objects may be present. The right most object is the same as would be returned
	// by a normal Decode call.
	Objects []Object
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
)

type Item struct {
	Title string
	URL   string
}

type Response struct {
	Data struct {
		Children []struct {
			Data Item
		}
	}
}

func main() {
	items, err := Get("golang")
	if err != nil {
		log.Fatalln(err)
	}
	for _, item := range items {
		fmt.Println(item)
	}
}

func Get(subreddit string) ([]Item, error) {
	url := fmt.Sprintf("http://reddit.com/r/%s.json", subreddit)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(resp.Status)
	}
	r := new(Response)
	if err = json.NewDecoder(resp.Body).Decode(r); err != nil {
		return nil, err
	}
	items := make([]Item, len(r.Data.Children))
	for i, child := range r.Data.Children {
		items[i] = child.Data
	}
	return items, nil
}
<commit_msg>Implement Stringer interface for Item type<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
)

type Item struct {
	Title string
	URL   string
}

func (i Item) String() string {
	return fmt.Sprintf("Title: %s\nURL: %s", i.Title, i.URL)
}

type Response struct {
	Data struct {
		Children []struct {
			Data Item
		}
	}
}

func main() {
	items, err := Get("golang")
	if err != nil {
		log.Fatalln(err)
	}
	for _, item := range items {
		fmt.Println(item)
	}
}

func Get(subreddit string) ([]Item, error) {
	url := fmt.Sprintf("http://reddit.com/r/%s.json", subreddit)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(resp.Status)
	}
	r := new(Response)
	if err = json.NewDecoder(resp.Body).Decode(r); err != nil {
		return nil, err
	}
	items := make([]Item, len(r.Data.Children))
	for i, child := range r.Data.Children {
		items[i] = child.Data
	}
	return items, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/csv"
	"encoding/json"
	"flag"
	"fmt"
	"hash/crc64"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/cloudflare/cloudflare-go"
	"github.com/gortc/ice"
	"github.com/gortc/sdp"
	"github.com/gortc/stun"
	"github.com/mssola/user_agent"
)

var (
	portHTTP = flag.Int("port", 3000, "http server port")
	hostHTTP = flag.String("host", "localhost", "http server host")
	portSTUN = flag.Int("port-stun", stun.DefaultPort, "UDP port")

	importPath = "gortc.io"
	repoPath   = "https://github.com/gortc"
)

var (
	bindingRequest = stun.MessageType{
		Method: stun.MethodBinding,
		Class:  stun.ClassRequest,
	}
	bindingSuccessResponse = stun.MessageType{
		Method: stun.MethodBinding,
		Class:  stun.ClassSuccessResponse,
	}
)

func processUDPPacket(addr net.Addr, b []byte, req, res *stun.Message) error {
	if !stun.IsMessage(b) {
		log.Println("packet from", addr, "is not STUN message")
		return nil
	}
	req.Raw = b
	if err := req.Decode(); err != nil {
		return err
	}
	if req.Type != bindingRequest {
		log.Println("stun: skipping", req.Type, "from", addr)
		return nil
	}
	log.Println("stun: got", req.Type)
	res.TransactionID = req.TransactionID
	res.Type = bindingSuccessResponse
	var (
		ip   net.IP
		port int
	)
	switch a := addr.(type) {
	case *net.UDPAddr:
		ip = a.IP
		port = a.Port
	default:
		panic(fmt.Sprintf("unknown addr: %v", addr))
	}
	stun.XORMappedAddress{
		IP:   ip,
		Port: port,
	}.AddTo(res)
	stun.NewSoftware("gortc.io/x/sdp example").AddTo(res)
	res.WriteHeader()
	messages.add(fmt.Sprintf("%s:%d", ip, port), req)
	return nil
}

type iceServerConfiguration struct {
	URLs []string `json:"urls"`
}

type iceConfiguration struct {
	Servers []iceServerConfiguration `json:"iceServers"`
}

func main() {
	flag.Parse()
	cf, err := cloudflare.New(
		os.Getenv("CF_API_KEY"),
		os.Getenv("CF_API_EMAIL"),
	)
	if err != nil {
		log.Fatal(err)
	}
	t, err := template.ParseFiles("static/index.html")
	if err != nil {
		log.Fatal(err)
	}
	log.SetFlags(log.Lshortfile)
	fs := http.FileServer(http.Dir("static"))
	var (
		s     *stats
		sLock sync.RWMutex
	)
	go func() {
		sLock.Lock()
		log.Println("gettings stats")
		s, err = getStats(false)
		if err != nil {
			log.Println("failed to get stats:", err)
		} else {
			log.Println("got stats")
		}
		sLock.Unlock()
	}()
	update := func() error {
		log.Println("updating stats")
		newStats, err := getStats(true)
		if err != nil {
			log.Println("failed to fetch stats:", err)
			return err
		}
		sLock.Lock()
		s = newStats
		sLock.Unlock()
		return nil
	}
	go func() {
		ticker := time.NewTicker(time.Second * 90)
		for range ticker.C {
			_ = update()
		}
	}()
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query()
		if q.Get("go-get") == "1" {
			redirect(w, r)
			return
		}
		if r.URL.Path != "/" {
			fs.ServeHTTP(w, r)
			return
		}
		w.Header().Add("Link", "</go-rtc.svg>; as=image; rel=preload")
		w.Header().Add("Link", "</jetbrains-variant-3.svg>; as=image; rel=preload")
		w.Header().Add("Link", "</css/main.css>; as=style; rel=preload")
		sLock.RLock()
		if err := t.Execute(w, s); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
		}
		sLock.RUnlock()
	})
	http.HandleFunc("/hook/"+os.Getenv("GITHUB_HOOK_SECRET"), func(writer http.ResponseWriter, request *http.Request) {
		start := time.Now()
		err := update()
		if err != nil {
			writer.WriteHeader(http.StatusInternalServerError)
			fmt.Println(writer, "failed:", err)
			return
		}
		writer.WriteHeader(http.StatusOK)
		fmt.Fprintln(writer, "updated in", time.Since(start))
		go func() {
			log.Println("purging cf cache")
			zoneID, err := cf.ZoneIDByName("gortc.io")
			if err != nil {
				log.Println("failed to get zone id:", err)
				return
			}
			res, err := cf.PurgeCache(zoneID, cloudflare.PurgeCacheRequest{
				Files: []string{
					"https://gortc.io/",
				},
			})
			if err != nil {
				log.Println("failed to purge cache:", err)
				return
			}
			if !res.Success {
				log.Println("failed to purge cache: not succeeded")
			} else {
				log.Println("purged cf cache")
			}
		}()
	})
	http.HandleFunc("/ice-configuration", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-type", "application/json")
		encoder := json.NewEncoder(w)
		origin := r.Header.Get("Origin")
		server := "stun:gortc.io"
		if len(origin) > 0 {
			u, err := url.Parse(origin)
			if err != nil {
				log.Printf("http: failed to parse origin %q: %s", origin, err)
			} else {
				if idx := strings.LastIndex(u.Host, ":"); idx > 0 {
					u.Host = u.Host[:idx]
				}
				server = fmt.Sprintf("stun:%s:%d", u.Host, *portSTUN)
				log.Printf("http: sending ice-server %q for origin %q", server, origin)
			}
		}
		if err := encoder.Encode(iceConfiguration{
			Servers: []iceServerConfiguration{
				{URLs: []string{server}},
			},
		}); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, "json encode:", err)
		}
	})

	mLog, err := os.OpenFile("packets.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatalln("Failed to open log:", err)
	}
	defer mLog.Close()
	csvLog := csv.NewWriter(mLog)

	http.HandleFunc("/x/sdp", func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()
		log.Println("http:", r.Method, "request from", r.RemoteAddr)
		if r.Method == http.MethodGet {
			http.Redirect(w, r, "/x/sdp/", http.StatusPermanentRedirect)
			return
		}
		s := sdp.Session{}
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Println("http: ReadAll body failed:", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		if s, err = sdp.DecodeSession(data, s); err != nil {
			log.Println("http: failed to decode sdp session:", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		for k, v := range s {
			fmt.Fprintf(w, `<p class="attribute">%02d %s</p>`+"\n", k, v)
			if v.Type != sdp.TypeAttribute {
				continue
			}
			if !bytes.HasPrefix(v.Value, []byte("candidate")) {
				continue
			}
			c := new(ice.Candidate)
			fmt.Fprintln(w, `<div class="stun-message">`)
			if err = ice.ParseAttribute(v.Value, c); err != nil {
				fmt.Fprintln(w, `<p class="error">failed to parse as candidate:`, err, "
")
				fmt.Fprintln(w, `</div>`)
				continue
			}
			fmt.Fprintln(w, "
parsed as candidate:", fmt.Sprintf("%+v", c), "
")
			if c.Type != ice.CandidateServerReflexive {
				fmt.Fprintln(w, `</div>`)
				continue
			}
			addr := fmt.Sprintf("%s:%d", c.ConnectionAddress, c.Port)
			m := messages.pop(addr)
			if m == nil {
				log.Println("http: no message for", addr, "in log")
				fmt.Fprintln(w, `<p class="warning">message from candidate not found in STUN log</p>`)
				fmt.Fprintln(w, `</div>`)
				continue
			}
			fmt.Fprintln(w, `<p class="success">message found in STUN log:`, m, `</p>`)
			for _, a := range m.Attributes {
				switch a.Type {
				case stun.AttrOrigin:
					fmt.Fprintf(w, "
STUN attribute %s: %q (len=%d)
", a.Type, a.Value, a.Length)
				default:
					fmt.Fprintf(w, "
STUN attribute %s: %v (len=%d)
", a.Type, a.Value, a.Length)
				}
			}
			var (
				b64             = base64.StdEncoding.EncodeToString(m.Raw)
				messageCRC64    = crc64.Checksum(m.Raw, crc64.MakeTable(crc64.ISO))
				clipID          = fmt.Sprintf("crc64-%d", messageCRC64)
				ua              = user_agent.New(r.Header.Get("User-agent"))
				bName, bVersion = ua.Browser()
			)
			if err = csvLog.Write([]string{
				addr,
				b64,
				fmt.Sprintf("%d", messageCRC64),
				bName,
				bVersion,
				ua.OS(),
			}); err != nil {
				log.Fatalln("log: failed to write:", err)
			}
			csvLog.Flush()
			fmt.Fprintln(w, `<p>dumped: <code id="`+clipID+`">stun-decode`, b64, `
				<button class="btn" data-clipboard-target="#`+clipID+`">copy</button>

`)
			fmt.Fprintln(w, `<p>crc64: <code>`, messageCRC64, `</code></p>`)
			fmt.Fprintln(w, `</div>`)
		}
	})

	var (
		addrSTUN = fmt.Sprintf(":%d", *portSTUN)
		addrHTTP = fmt.Sprintf("%s:%d", *hostHTTP, *portHTTP)
	)
	log.Println("Listening udp", addrSTUN)
	c, err := net.ListenPacket("udp", addrSTUN)
	if err != nil {
		log.Fatalf("Failed to bind udp on %s: %s", addrSTUN, err)
	}
	defer c.Close()

	// spawning storage garbage collector
	go messages.gc()

	// spawning STUN server
	go func(conn net.PacketConn) {
		log.Println("Started STUN server on", conn.LocalAddr())
		var (
			res = new(stun.Message)
			req = new(stun.Message)
			buf = make([]byte, 1024)
		)
		for {
			// ReadFrom c to buf
			n, addr, err := c.ReadFrom(buf)
			if err != nil {
				log.Fatalln("c.ReadFrom:", err)
			}
			log.Printf("udp: got packet len(%d) from %s", n, addr)
			// processing binding request
			if err = processUDPPacket(addr, buf[:n], req, res); err != nil {
				log.Println("failed to process UDP packet:", err, "from addr", addr)
			} else {
				log.Printf("stun: parsed message %q from %s", req, addr)
				if _, err = c.WriteTo(res.Raw, addr); err != nil {
					log.Println("failed to send packet:", err)
				}
			}
			res.Reset()
			req.Reset()
		}

	}(c)
	log.Println("Listening http", addrHTTP)
	log.Fatal(http.ListenAndServe(addrHTTP, nil))
}
<commit_msg>all: update redirector<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/csv"
	"encoding/json"
	"flag"
	"fmt"
	"hash/crc64"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/cloudflare/cloudflare-go"
	"github.com/gortc/ice"
	"github.com/gortc/sdp"
	"github.com/gortc/stun"
	"github.com/mssola/user_agent"
)

var (
	portHTTP = flag.Int("port", 3000, "http server port")
	hostHTTP = flag.String("host", "localhost", "http server host")
	portSTUN = flag.Int("port-stun", stun.DefaultPort, "UDP port")

	importPath = "gortc.io"
	repoPath   = "https://github.com/gortc"
)

var (
	bindingRequest = stun.MessageType{
		Method: stun.MethodBinding,
		Class:  stun.ClassRequest,
	}
	bindingSuccessResponse = stun.MessageType{
		Method: stun.MethodBinding,
		Class:  stun.ClassSuccessResponse,
	}
)

func processUDPPacket(addr net.Addr, b []byte, req, res *stun.Message) error {
	if !stun.IsMessage(b) {
		log.Println("packet from", addr, "is not STUN message")
		return nil
	}
	req.Raw = b
	if err := req.Decode(); err != nil {
		return err
	}
	if req.Type != bindingRequest {
		log.Println("stun: skipping", req.Type, "from", addr)
		return nil
	}
	log.Println("stun: got", req.Type)
	res.TransactionID = req.TransactionID
	res.Type = bindingSuccessResponse
	var (
		ip   net.IP
		port int
	)
	switch a := addr.(type) {
	case *net.UDPAddr:
		ip = a.IP
		port = a.Port
	default:
		panic(fmt.Sprintf("unknown addr: %v", addr))
	}
	stun.XORMappedAddress{
		IP:   ip,
		Port: port,
	}.AddTo(res)
	stun.NewSoftware("gortc.io/x/sdp example").AddTo(res)
	res.WriteHeader()
	messages.add(fmt.Sprintf("%s:%d", ip, port), req)
	return nil
}

type iceServerConfiguration struct {
	URLs []string `json:"urls"`
}

type iceConfiguration struct {
	Servers []iceServerConfiguration `json:"iceServers"`
}

func main() {
	flag.Parse()
	cf, err := cloudflare.New(
		os.Getenv("CF_API_KEY"),
		os.Getenv("CF_API_EMAIL"),
	)
	if err != nil {
		log.Fatal(err)
	}
	t, err := template.ParseFiles("static/index.html")
	if err != nil {
		log.Fatal(err)
	}
	log.SetFlags(log.Lshortfile)
	fs := http.FileServer(http.Dir("static"))
	var (
		s     *stats
		sLock sync.RWMutex
	)
	go func() {
		sLock.Lock()
		log.Println("gettings stats")
		s, err = getStats(false)
		if err != nil {
			log.Println("failed to get stats:", err)
		} else {
			log.Println("got stats")
		}
		sLock.Unlock()
	}()
	update := func() error {
		log.Println("updating stats")
		newStats, err := getStats(true)
		if err != nil {
			log.Println("failed to fetch stats:", err)
			return err
		}
		sLock.Lock()
		s = newStats
		sLock.Unlock()
		return nil
	}
	go func() {
		ticker := time.NewTicker(time.Second * 90)
		for range ticker.C {
			_ = update()
		}
	}()
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		q := r.URL.Query()
		if q.Get("go-get") == "1" {
			r.Host = "gortc.io"
			redirect(w, r)
			return
		}
		if r.URL.Path != "/" {
			fs.ServeHTTP(w, r)
			return
		}
		w.Header().Add("Link", "</go-rtc.svg>; as=image; rel=preload")
		w.Header().Add("Link", "</jetbrains-variant-3.svg>; as=image; rel=preload")
		w.Header().Add("Link", "</css/main.css>; as=style; rel=preload")
		sLock.RLock()
		if err := t.Execute(w, s); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
		}
		sLock.RUnlock()
	})
	http.HandleFunc("/hook/"+os.Getenv("GITHUB_HOOK_SECRET"), func(writer http.ResponseWriter, request *http.Request) {
		start := time.Now()
		err := update()
		if err != nil {
			writer.WriteHeader(http.StatusInternalServerError)
			fmt.Println(writer, "failed:", err)
			return
		}
		writer.WriteHeader(http.StatusOK)
		fmt.Fprintln(writer, "updated in", time.Since(start))
		go func() {
			log.Println("purging cf cache")
			zoneID, err := cf.ZoneIDByName("gortc.io")
			if err != nil {
				log.Println("failed to get zone id:", err)
				return
			}
			res, err := cf.PurgeCache(zoneID, cloudflare.PurgeCacheRequest{
				Files: []string{
					"https://gortc.io/",
				},
			})
			if err != nil {
				log.Println("failed to purge cache:", err)
				return
			}
			if !res.Success {
				log.Println("failed to purge cache: not succeeded")
			} else {
				log.Println("purged cf cache")
			}
		}()
	})
	http.HandleFunc("/ice-configuration", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Content-type", "application/json")
		encoder := json.NewEncoder(w)
		origin := r.Header.Get("Origin")
		server := "stun:gortc.io"
		if len(origin) > 0 {
			u, err := url.Parse(origin)
			if err != nil {
				log.Printf("http: failed to parse origin %q: %s", origin, err)
			} else {
				if idx := strings.LastIndex(u.Host, ":"); idx > 0 {
					u.Host = u.Host[:idx]
				}
				server = fmt.Sprintf("stun:%s:%d", u.Host, *portSTUN)
				log.Printf("http: sending ice-server %q for origin %q", server, origin)
			}
		}
		if err := encoder.Encode(iceConfiguration{
			Servers: []iceServerConfiguration{
				{URLs: []string{server}},
			},
		}); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, "json encode:", err)
		}
	})

	mLog, err := os.OpenFile("packets.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatalln("Failed to open log:", err)
	}
	defer mLog.Close()
	csvLog := csv.NewWriter(mLog)

	http.HandleFunc("/x/sdp", func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()
		log.Println("http:", r.Method, "request from", r.RemoteAddr)
		if r.Method == http.MethodGet {
			http.Redirect(w, r, "/x/sdp/", http.StatusPermanentRedirect)
			return
		}
		s := sdp.Session{}
		data, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Println("http: ReadAll body failed:", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		if s, err = sdp.DecodeSession(data, s); err != nil {
			log.Println("http: failed to decode sdp session:", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		for k, v := range s {
			fmt.Fprintf(w, `<p class="attribute">%02d %s</p>`+"\n", k, v)
			if v.Type != sdp.TypeAttribute {
				continue
			}
			if !bytes.HasPrefix(v.Value, []byte("candidate")) {
				continue
			}
			c := new(ice.Candidate)
			fmt.Fprintln(w, `<div class="stun-message">`)
			if err = ice.ParseAttribute(v.Value, c); err != nil {
				fmt.Fprintln(w, `<p class="error">failed to parse as candidate:`, err, "
")
				fmt.Fprintln(w, `</div>`)
				continue
			}
			fmt.Fprintln(w, "
parsed as candidate:", fmt.Sprintf("%+v", c), "
")
			if c.Type != ice.CandidateServerReflexive {
				fmt.Fprintln(w, `</div>`)
				continue
			}
			addr := fmt.Sprintf("%s:%d", c.ConnectionAddress, c.Port)
			m := messages.pop(addr)
			if m == nil {
				log.Println("http: no message for", addr, "in log")
				fmt.Fprintln(w, `<p class="warning">message from candidate not found in STUN log</p>`)
				fmt.Fprintln(w, `</div>`)
				continue
			}
			fmt.Fprintln(w, `<p class="success">message found in STUN log:`, m, `</p>`)
			for _, a := range m.Attributes {
				switch a.Type {
				case stun.AttrOrigin:
					fmt.Fprintf(w, "
STUN attribute %s: %q (len=%d)
", a.Type, a.Value, a.Length)
				default:
					fmt.Fprintf(w, "
STUN attribute %s: %v (len=%d)
", a.Type, a.Value, a.Length)
				}
			}
			var (
				b64             = base64.StdEncoding.EncodeToString(m.Raw)
				messageCRC64    = crc64.Checksum(m.Raw, crc64.MakeTable(crc64.ISO))
				clipID          = fmt.Sprintf("crc64-%d", messageCRC64)
				ua              = user_agent.New(r.Header.Get("User-agent"))
				bName, bVersion = ua.Browser()
			)
			if err = csvLog.Write([]string{
				addr,
				b64,
				fmt.Sprintf("%d", messageCRC64),
				bName,
				bVersion,
				ua.OS(),
			}); err != nil {
				log.Fatalln("log: failed to write:", err)
			}
			csvLog.Flush()
			fmt.Fprintln(w, `<p>dumped: <code id="`+clipID+`">stun-decode`, b64, `
				<button class="btn" data-clipboard-target="#`+clipID+`">copy</button>

`)
			fmt.Fprintln(w, `<p>crc64: <code>`, messageCRC64, `</code></p>`)
			fmt.Fprintln(w, `</div>`)
		}
	})

	var (
		addrSTUN = fmt.Sprintf(":%d", *portSTUN)
		addrHTTP = fmt.Sprintf("%s:%d", *hostHTTP, *portHTTP)
	)
	log.Println("Listening udp", addrSTUN)
	c, err := net.ListenPacket("udp", addrSTUN)
	if err != nil {
		log.Fatalf("Failed to bind udp on %s: %s", addrSTUN, err)
	}
	defer c.Close()

	// spawning storage garbage collector
	go messages.gc()

	// spawning STUN server
	go func(conn net.PacketConn) {
		log.Println("Started STUN server on", conn.LocalAddr())
		var (
			res = new(stun.Message)
			req = new(stun.Message)
			buf = make([]byte, 1024)
		)
		for {
			// ReadFrom c to buf
			n, addr, err := c.ReadFrom(buf)
			if err != nil {
				log.Fatalln("c.ReadFrom:", err)
			}
			log.Printf("udp: got packet len(%d) from %s", n, addr)
			// processing binding request
			if err = processUDPPacket(addr, buf[:n], req, res); err != nil {
				log.Println("failed to process UDP packet:", err, "from addr", addr)
			} else {
				log.Printf("stun: parsed message %q from %s", req, addr)
				if _, err = c.WriteTo(res.Raw, addr); err != nil {
					log.Println("failed to send packet:", err)
				}
			}
			res.Reset()
			req.Reset()
		}

	}(c)
	log.Println("Listening http", addrHTTP)
	log.Fatal(http.ListenAndServe(addrHTTP, nil))
}
<|endoftext|>


<commit_before>package main

func main() {
}<commit_msg>Basic server setup.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"io/ioutil"
	"github.com/benbjohnson/go-raft"
	"net"
	"net/http"
	"os"
	"path"
)

var logLevel string

func init() {
	flag.StringVar(&logLevel, "log-level", "", "log level (debug, trace)")
}

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	flag.Parse()
	switch logLevel {
	case "debug":
		raft.LogLevel = raft.Debug
	case "trace":
		raft.LogLevel = raft.Trace
	}

	var err error
	path := getPath()
	name, laddr := getName(path)

	// Setup new raft server.
	transporter := raft.NewHTTPTransporter("/raft")
	server, err := raft.NewServer(name, path, transporter, nil, nil)
	if err != nil {
		log.Fatalln(err)
	}
	server.StartFollower()

	// Setup HTTP server.
    mux := http.NewServeMux()
	transporter.Install(server, mux)
	http.Handle("/", mux)

	// Start server.
	fmt.Println(name)
	fmt.Println(path)
	fmt.Println("")
	log.Fatal(http.ListenAndServe(laddr, nil))
}

//--------------------------------------
// Utility
//--------------------------------------

// Retrieves the name and laddr of the server.
func getName(basepath string) (string, string) {
	var name string

	// Read name of server if it's already been set.
	if b, _ := ioutil.ReadFile(path.Join(basepath, "name")); len(b) > 0 {
		name = string(b)

	// Otherwise create a name based on the hostname and first available port.
	} else {
		hostname, _ := os.Hostname()
		port := 20000
		for {
			if listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)); err == nil {
				listener.Close()
				break
			} else {
				port++
			}
		}
		name = fmt.Sprintf("%s:%d", hostname, port)

		if err := ioutil.WriteFile(path.Join(basepath, "name"), []byte(name), 0644); err != nil {
			log.Fatalln(err)
		}
	}

	_, port, _ := net.SplitHostPort(name)
	return name, net.JoinHostPort("", port)
}

// Retrieves the path to save the log and configuration to. Uses the first
// parameter passed into the command line or creates a temporary directory if
// a path is not passed in.
func getPath() string {
	var path string
	if flag.NArg() == 0 {
		path, _ = ioutil.TempDir("", "go-raft-runner")
	} else {
		path = flag.Arg(0)
		if err := os.MkdirAll(path, 0744); err != nil {
			log.Fatalln(err)
		}
	}
	return path
}

<|endoftext|>


<commit_before>package fakes

import (
	"fmt"
	"time"
)

type Client struct {
	ExpectedCreateSnapshotErr error
	InvokedCreateSnapshot     []time.Duration

	ExpectedRDBPathErr error
	ExpectedRDBPath    string
	InvokedRDBPath     int

	Host string
	Port int
}

func (c *Client) Address() string {
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

func (c *Client) CreateSnapshot(timeout time.Duration) error {
	if c.InvokedCreateSnapshot == nil {
		c.InvokedCreateSnapshot = []time.Duration{}
	}
	c.InvokedCreateSnapshot = append(c.InvokedCreateSnapshot, timeout)
	return c.ExpectedCreateSnapshotErr
}

func (c *Client) Disconnect() error {
	return nil
}

func (c *Client) WaitUntilRedisNotLoading(timeoutMilliseconds int) error {
	return nil
}

func (c *Client) EnableAOF() error {
	return nil
}

func (c *Client) LastRDBSaveTime() (int64, error) {
	return 0, nil
}

func (c *Client) InfoField(fieldName string) (string, error) {
	return "", nil
}

func (c *Client) GetConfig(key string) (string, error) {
	return "", nil
}

func (c *Client) RDBPath() (string, error) {
	c.InvokedRDBPath++
	return c.ExpectedRDBPath, c.ExpectedRDBPathErr
}
<commit_msg>Fake client should not return address if host is not set<commit_after>package fakes

import (
	"fmt"
	"time"
)

type Client struct {
	ExpectedCreateSnapshotErr error
	InvokedCreateSnapshot     []time.Duration

	ExpectedRDBPathErr error
	ExpectedRDBPath    string
	InvokedRDBPath     int

	Host string
	Port int
}

func (c *Client) Address() string {
	if c.Host == "" {
		return ""
	}
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

func (c *Client) CreateSnapshot(timeout time.Duration) error {
	if c.InvokedCreateSnapshot == nil {
		c.InvokedCreateSnapshot = []time.Duration{}
	}
	c.InvokedCreateSnapshot = append(c.InvokedCreateSnapshot, timeout)
	return c.ExpectedCreateSnapshotErr
}

func (c *Client) Disconnect() error {
	return nil
}

func (c *Client) WaitUntilRedisNotLoading(timeoutMilliseconds int) error {
	return nil
}

func (c *Client) EnableAOF() error {
	return nil
}

func (c *Client) LastRDBSaveTime() (int64, error) {
	return 0, nil
}

func (c *Client) InfoField(fieldName string) (string, error) {
	return "", nil
}

func (c *Client) GetConfig(key string) (string, error) {
	return "", nil
}

func (c *Client) RDBPath() (string, error) {
	c.InvokedRDBPath++
	return c.ExpectedRDBPath, c.ExpectedRDBPathErr
}
<|endoftext|>