<commit_before>package driver

import (
	"context"

	"github.com/pkg/errors"

	"github.com/ory/x/errorsx"

	"github.com/ory/fosite"
	foauth2 "github.com/ory/fosite/handler/oauth2"

	"github.com/ory/x/logrusx"

	"github.com/ory/hydra/persistence"

	prometheus "github.com/ory/x/prometheusx"

	"github.com/ory/x/dbal"
	"github.com/ory/x/healthx"

	"github.com/ory/hydra/client"
	"github.com/ory/hydra/consent"
	"github.com/ory/hydra/driver/config"
	"github.com/ory/hydra/jwk"
	"github.com/ory/hydra/oauth2"
	"github.com/ory/hydra/x"
)

type Registry interface {
	dbal.Driver

	Init(ctx context.Context) error

	WithConfig(c *config.Provider) Registry
	WithLogger(l *logrusx.Logger) Registry

	Config() *config.Provider
	persistence.Provider
	x.RegistryLogger
	x.RegistryWriter
	x.RegistryCookieStore
	client.Registry
	consent.Registry
	jwk.Registry
	oauth2.Registry
	PrometheusManager() *prometheus.MetricsManager
	x.TracingProvider

	RegisterRoutes(admin *x.RouterAdmin, public *x.RouterPublic)
	ClientHandler() *client.Handler
	KeyHandler() *jwk.Handler
	ConsentHandler() *consent.Handler
	OAuth2Handler() *oauth2.Handler
	HealthHandler() *healthx.Handler

	OAuth2HMACStrategy() *foauth2.HMACSHAStrategy
	WithOAuth2Provider(f fosite.OAuth2Provider)
	WithConsentStrategy(c consent.Strategy)
}

func NewRegistryFromDSN(ctx context.Context, c *config.Provider, l *logrusx.Logger) (Registry, error) {
	driver, err := dbal.GetDriverFor(c.DSN())
	if err != nil {
		return nil, errorsx.WithStack(err)
	}

	registry, ok := driver.(Registry)
	if !ok {
		return nil, errors.Errorf("driver of type %T does not implement interface Registry", driver)
	}

	registry = registry.WithLogger(l).WithConfig(c)

	if err := registry.Init(ctx); err != nil {
		return nil, err
	}

	return registry, nil
}

func CallRegistry(ctx context.Context, r Registry) {
	r.ClientValidator()
	r.ClientManager()
	r.ClientHasher()
	r.ConsentManager()
	r.ConsentStrategy()
	r.SubjectIdentifierAlgorithm()
	r.KeyManager()
	r.KeyGenerators()
	r.KeyCipher()
	r.OAuth2Storage()
	r.OAuth2Provider()
	r.AudienceStrategy()
	r.ScopeStrategy()
	r.AccessTokenJWTStrategy()
	r.OpenIDJWTStrategy()
	r.OpenIDConnectRequestValidator()
	r.PrometheusManager()
	r.Tracer(ctx)
}
<commit_msg>fix: version info nil on version api endpoint (#2894)<commit_after>package driver

import (
	"context"

	"github.com/pkg/errors"

	"github.com/ory/x/errorsx"

	"github.com/ory/fosite"
	foauth2 "github.com/ory/fosite/handler/oauth2"

	"github.com/ory/x/logrusx"

	"github.com/ory/hydra/persistence"

	prometheus "github.com/ory/x/prometheusx"

	"github.com/ory/x/dbal"
	"github.com/ory/x/healthx"

	"github.com/ory/hydra/client"
	"github.com/ory/hydra/consent"
	"github.com/ory/hydra/driver/config"
	"github.com/ory/hydra/jwk"
	"github.com/ory/hydra/oauth2"
	"github.com/ory/hydra/x"
)

type Registry interface {
	dbal.Driver

	Init(ctx context.Context) error

	WithBuildInfo(v, h, d string) Registry
	WithConfig(c *config.Provider) Registry
	WithLogger(l *logrusx.Logger) Registry

	Config() *config.Provider
	persistence.Provider
	x.RegistryLogger
	x.RegistryWriter
	x.RegistryCookieStore
	client.Registry
	consent.Registry
	jwk.Registry
	oauth2.Registry
	PrometheusManager() *prometheus.MetricsManager
	x.TracingProvider

	RegisterRoutes(admin *x.RouterAdmin, public *x.RouterPublic)
	ClientHandler() *client.Handler
	KeyHandler() *jwk.Handler
	ConsentHandler() *consent.Handler
	OAuth2Handler() *oauth2.Handler
	HealthHandler() *healthx.Handler

	OAuth2HMACStrategy() *foauth2.HMACSHAStrategy
	WithOAuth2Provider(f fosite.OAuth2Provider)
	WithConsentStrategy(c consent.Strategy)
}

func NewRegistryFromDSN(ctx context.Context, c *config.Provider, l *logrusx.Logger) (Registry, error) {
	driver, err := dbal.GetDriverFor(c.DSN())
	if err != nil {
		return nil, errorsx.WithStack(err)
	}

	registry, ok := driver.(Registry)
	if !ok {
		return nil, errors.Errorf("driver of type %T does not implement interface Registry", driver)
	}

	registry = registry.WithLogger(l).WithConfig(c).WithBuildInfo(config.Version, config.Commit, config.Date)

	if err := registry.Init(ctx); err != nil {
		return nil, err
	}

	return registry, nil
}

func CallRegistry(ctx context.Context, r Registry) {
	r.ClientValidator()
	r.ClientManager()
	r.ClientHasher()
	r.ConsentManager()
	r.ConsentStrategy()
	r.SubjectIdentifierAlgorithm()
	r.KeyManager()
	r.KeyGenerators()
	r.KeyCipher()
	r.OAuth2Storage()
	r.OAuth2Provider()
	r.AudienceStrategy()
	r.ScopeStrategy()
	r.AccessTokenJWTStrategy()
	r.OpenIDJWTStrategy()
	r.OpenIDConnectRequestValidator()
	r.PrometheusManager()
	r.Tracer(ctx)
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 exec

import (
	"context"
	"fmt"
	"io"
	"reflect"
	"time"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/coder"
	"github.com/apache/beam/sdks/go/pkg/beam/core/typex"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/ioutilx"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/reflectx"
)

// StreamID represents the information needed to identify a data stream.
type StreamID struct {
	Port   graph.Port
	Target graph.Target
	InstID string
}

func (id StreamID) String() string {
	return fmt.Sprintf("S:%v:[%v:%v]:%v", id.Port.URL, id.Target.ID, id.Target.Name, id.InstID)
}

// DataReader is the interface for reading data elements from a particular stream.
type DataReader interface {
	OpenRead(ctx context.Context, id StreamID) (io.ReadCloser, error)
}

// DataWriter is the interface for writing data elements to a particular stream.
type DataWriter interface {
	OpenWrite(ctx context.Context, id StreamID) (io.WriteCloser, error)
}

// DataManager manages external data byte streams. Each data stream can be
// opened by one consumer only.
type DataManager interface {
	DataReader
	DataWriter
}

// NOTE(herohde) 4/30/2017: The main complication is CoGBK results, which have
// nested streams. Hence, a simple read-one-element-at-a-time approach doesn't
// work, because each "element" can be too large to fit into memory. Instead,
// we handle the top GBK/CoGBK layer in the processing node directly.

// ElementEncoder handles FullValue serialization to a byte stream. The encoder
// can be reused, even if an error is encountered.
type ElementEncoder interface {
	// Encode serializes the given value to the writer.
	Encode(FullValue, io.Writer) error
}

// ElementDecoder handles FullValue deserialization from a byte stream. The decoder
// can be reused, even if an error is encountered.
type ElementDecoder interface {
	// Decode deserializes a value from the given reader.
	Decode(io.Reader) (FullValue, error)
}

// MakeElementEncoder returns a ElementCoder for the given coder. It panics
// if given a coder with stream types, such as GBK.
func MakeElementEncoder(c *coder.Coder) ElementEncoder {
	switch c.Kind {
	case coder.Bytes:
		return &bytesEncoder{}

	case coder.VarInt:
		return &varIntEncoder{}

	case coder.Custom:
		return &customEncoder{
			t:   c.Custom.Type,
			enc: makeEncoder(c.Custom.Enc.Fn),
		}

	case coder.KV:
		return &kvEncoder{
			fst: MakeElementEncoder(c.Components[0]),
			snd: MakeElementEncoder(c.Components[1]),
		}

	default:
		panic(fmt.Sprintf("Unexpected coder: %v", c))
	}
}

// MakeElementDecoder returns a ElementDecoder for the given coder. It panics
// if given a coder with stream types, such as GBK.
func MakeElementDecoder(c *coder.Coder) ElementDecoder {
	switch c.Kind {
	case coder.Bytes:
		return &bytesDecoder{}

	case coder.VarInt:
		return &varIntDecoder{}

	case coder.Custom:
		return &customDecoder{
			t:   c.Custom.Type,
			dec: makeDecoder(c.Custom.Dec.Fn),
		}

	case coder.KV:
		return &kvDecoder{
			fst: MakeElementDecoder(c.Components[0]),
			snd: MakeElementDecoder(c.Components[1]),
		}

	default:
		panic(fmt.Sprintf("Unexpected coder: %v", c))
	}
}

type bytesEncoder struct{}

func (*bytesEncoder) Encode(val FullValue, w io.Writer) error {
	// Encoding: size (varint) + raw data
	var data []byte
	switch v := val.Elm.(type) {
	case []byte:
		data = v
	case string:
		data = []byte(v)
	default:
		return fmt.Errorf("received unknown value type: want []byte or string, got %T", v)
	}

	if err := coder.EncodeVarInt((int32)(size), w); err != nil {
		return err
	}
	_, err := w.Write(data)
	return err
}

type bytesDecoder struct{}

func (*bytesDecoder) Decode(r io.Reader) (FullValue, error) {
	// Encoding: size (varint) + raw data

	size, err := coder.DecodeVarInt(r)
	if err != nil {
		return FullValue{}, err
	}
	data, err := ioutilx.ReadN(r, (int)(size))
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: data}, nil
}

type varIntEncoder struct{}

func (*varIntEncoder) Encode(val FullValue, w io.Writer) error {
	// Encoding: beam varint

	n := Convert(val.Elm, reflectx.Int32).(int32) // Convert needed?
	return coder.EncodeVarInt(n, w)
}

type varIntDecoder struct{}

func (*varIntDecoder) Decode(r io.Reader) (FullValue, error) {
	// Encoding: beam varint

	n, err := coder.DecodeVarInt(r)
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: n}, nil
}

type customEncoder struct {
	t   reflect.Type
	enc Encoder
}

func (c *customEncoder) Encode(val FullValue, w io.Writer) error {
	// (1) Call encode

	data, err := c.enc.Encode(c.t, val.Elm)
	if err != nil {
		return err
	}

	// (2) Add length prefix

	size := len(data)
	if err := coder.EncodeVarInt((int32)(size), w); err != nil {
		return err
	}
	_, err = w.Write(data)
	return err
}

type customDecoder struct {
	t   reflect.Type
	dec Decoder
}

func (c *customDecoder) Decode(r io.Reader) (FullValue, error) {
	// (1) Read length-prefixed encoded data

	size, err := coder.DecodeVarInt(r)
	if err != nil {
		return FullValue{}, err
	}
	data, err := ioutilx.ReadN(r, (int)(size))
	if err != nil {
		return FullValue{}, err
	}

	// (2) Call decode

	val, err := c.dec.Decode(c.t, data)
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: val}, err
}

type kvEncoder struct {
	fst, snd ElementEncoder
}

func (c *kvEncoder) Encode(val FullValue, w io.Writer) error {
	if err := c.fst.Encode(FullValue{Elm: val.Elm}, w); err != nil {
		return err
	}
	return c.snd.Encode(FullValue{Elm: val.Elm2}, w)

}

type kvDecoder struct {
	fst, snd ElementDecoder
}

func (c *kvDecoder) Decode(r io.Reader) (FullValue, error) {
	key, err := c.fst.Decode(r)
	if err != nil {
		return FullValue{}, err
	}
	value, err := c.snd.Decode(r)
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: key.Elm, Elm2: value.Elm}, nil

}

// TODO(herohde) 4/7/2017: actually handle windows.

// EncodeWindowedValueHeader uses the supplied coder to serialize a windowed value header.
func EncodeWindowedValueHeader(c *coder.Coder, t typex.EventTime, w io.Writer) error {
	// Encoding: Timestamp, Window, Pane (header) + Element

	if err := coder.EncodeEventTime(t, w); err != nil {
		return err
	}
	if err := coder.EncodeInt32(1, w); err != nil { // #windows
		return err
	}
	// Ignore GlobalWindow, for now. It encoded into the empty string.

	_, err := w.Write([]byte{0xf}) // NO_FIRING pane
	return err
}

// DecodeWindowedValueHeader uses the supplied coder to deserialize a windowed value header.
func DecodeWindowedValueHeader(c *coder.Coder, r io.Reader) (typex.EventTime, error) {
	// Encoding: Timestamp, Window, Pane (header) + Element

	t, err := coder.DecodeEventTime(r)
	if err != nil {
		return typex.EventTime(time.Time{}), err
	}
	if _, err := coder.DecodeInt32(r); err != nil { // #windows
		return typex.EventTime(time.Time{}), err
	}
	if _, err := ioutilx.ReadN(r, 1); err != nil { // NO_FIRING pane
		return typex.EventTime(time.Time{}), err
	}
	return t, nil
}
<commit_msg>Update coder.go<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 exec

import (
	"context"
	"fmt"
	"io"
	"reflect"
	"time"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/coder"
	"github.com/apache/beam/sdks/go/pkg/beam/core/typex"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/ioutilx"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/reflectx"
)

// StreamID represents the information needed to identify a data stream.
type StreamID struct {
	Port   graph.Port
	Target graph.Target
	InstID string
}

func (id StreamID) String() string {
	return fmt.Sprintf("S:%v:[%v:%v]:%v", id.Port.URL, id.Target.ID, id.Target.Name, id.InstID)
}

// DataReader is the interface for reading data elements from a particular stream.
type DataReader interface {
	OpenRead(ctx context.Context, id StreamID) (io.ReadCloser, error)
}

// DataWriter is the interface for writing data elements to a particular stream.
type DataWriter interface {
	OpenWrite(ctx context.Context, id StreamID) (io.WriteCloser, error)
}

// DataManager manages external data byte streams. Each data stream can be
// opened by one consumer only.
type DataManager interface {
	DataReader
	DataWriter
}

// NOTE(herohde) 4/30/2017: The main complication is CoGBK results, which have
// nested streams. Hence, a simple read-one-element-at-a-time approach doesn't
// work, because each "element" can be too large to fit into memory. Instead,
// we handle the top GBK/CoGBK layer in the processing node directly.

// ElementEncoder handles FullValue serialization to a byte stream. The encoder
// can be reused, even if an error is encountered.
type ElementEncoder interface {
	// Encode serializes the given value to the writer.
	Encode(FullValue, io.Writer) error
}

// ElementDecoder handles FullValue deserialization from a byte stream. The decoder
// can be reused, even if an error is encountered.
type ElementDecoder interface {
	// Decode deserializes a value from the given reader.
	Decode(io.Reader) (FullValue, error)
}

// MakeElementEncoder returns a ElementCoder for the given coder. It panics
// if given a coder with stream types, such as GBK.
func MakeElementEncoder(c *coder.Coder) ElementEncoder {
	switch c.Kind {
	case coder.Bytes:
		return &bytesEncoder{}

	case coder.VarInt:
		return &varIntEncoder{}

	case coder.Custom:
		return &customEncoder{
			t:   c.Custom.Type,
			enc: makeEncoder(c.Custom.Enc.Fn),
		}

	case coder.KV:
		return &kvEncoder{
			fst: MakeElementEncoder(c.Components[0]),
			snd: MakeElementEncoder(c.Components[1]),
		}

	default:
		panic(fmt.Sprintf("Unexpected coder: %v", c))
	}
}

// MakeElementDecoder returns a ElementDecoder for the given coder. It panics
// if given a coder with stream types, such as GBK.
func MakeElementDecoder(c *coder.Coder) ElementDecoder {
	switch c.Kind {
	case coder.Bytes:
		return &bytesDecoder{}

	case coder.VarInt:
		return &varIntDecoder{}

	case coder.Custom:
		return &customDecoder{
			t:   c.Custom.Type,
			dec: makeDecoder(c.Custom.Dec.Fn),
		}

	case coder.KV:
		return &kvDecoder{
			fst: MakeElementDecoder(c.Components[0]),
			snd: MakeElementDecoder(c.Components[1]),
		}

	default:
		panic(fmt.Sprintf("Unexpected coder: %v", c))
	}
}

type bytesEncoder struct{}

func (*bytesEncoder) Encode(val FullValue, w io.Writer) error {
	// Encoding: size (varint) + raw data
	var data []byte
	switch v := val.Elm.(type) {
	case []byte:
		data = v
	case string:
		data = []byte(v)
	default:
		return fmt.Errorf("received unknown value type: want []byte or string, got %T", v)
	}
	size := len(data)

	if err := coder.EncodeVarInt((int32)(size), w); err != nil {
		return err
	}
	_, err := w.Write(data)
	return err
}

type bytesDecoder struct{}

func (*bytesDecoder) Decode(r io.Reader) (FullValue, error) {
	// Encoding: size (varint) + raw data

	size, err := coder.DecodeVarInt(r)
	if err != nil {
		return FullValue{}, err
	}
	data, err := ioutilx.ReadN(r, (int)(size))
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: data}, nil
}

type varIntEncoder struct{}

func (*varIntEncoder) Encode(val FullValue, w io.Writer) error {
	// Encoding: beam varint

	n := Convert(val.Elm, reflectx.Int32).(int32) // Convert needed?
	return coder.EncodeVarInt(n, w)
}

type varIntDecoder struct{}

func (*varIntDecoder) Decode(r io.Reader) (FullValue, error) {
	// Encoding: beam varint

	n, err := coder.DecodeVarInt(r)
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: n}, nil
}

type customEncoder struct {
	t   reflect.Type
	enc Encoder
}

func (c *customEncoder) Encode(val FullValue, w io.Writer) error {
	// (1) Call encode

	data, err := c.enc.Encode(c.t, val.Elm)
	if err != nil {
		return err
	}

	// (2) Add length prefix

	size := len(data)
	if err := coder.EncodeVarInt((int32)(size), w); err != nil {
		return err
	}
	_, err = w.Write(data)
	return err
}

type customDecoder struct {
	t   reflect.Type
	dec Decoder
}

func (c *customDecoder) Decode(r io.Reader) (FullValue, error) {
	// (1) Read length-prefixed encoded data

	size, err := coder.DecodeVarInt(r)
	if err != nil {
		return FullValue{}, err
	}
	data, err := ioutilx.ReadN(r, (int)(size))
	if err != nil {
		return FullValue{}, err
	}

	// (2) Call decode

	val, err := c.dec.Decode(c.t, data)
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: val}, err
}

type kvEncoder struct {
	fst, snd ElementEncoder
}

func (c *kvEncoder) Encode(val FullValue, w io.Writer) error {
	if err := c.fst.Encode(FullValue{Elm: val.Elm}, w); err != nil {
		return err
	}
	return c.snd.Encode(FullValue{Elm: val.Elm2}, w)

}

type kvDecoder struct {
	fst, snd ElementDecoder
}

func (c *kvDecoder) Decode(r io.Reader) (FullValue, error) {
	key, err := c.fst.Decode(r)
	if err != nil {
		return FullValue{}, err
	}
	value, err := c.snd.Decode(r)
	if err != nil {
		return FullValue{}, err
	}
	return FullValue{Elm: key.Elm, Elm2: value.Elm}, nil

}

// TODO(herohde) 4/7/2017: actually handle windows.

// EncodeWindowedValueHeader uses the supplied coder to serialize a windowed value header.
func EncodeWindowedValueHeader(c *coder.Coder, t typex.EventTime, w io.Writer) error {
	// Encoding: Timestamp, Window, Pane (header) + Element

	if err := coder.EncodeEventTime(t, w); err != nil {
		return err
	}
	if err := coder.EncodeInt32(1, w); err != nil { // #windows
		return err
	}
	// Ignore GlobalWindow, for now. It encoded into the empty string.

	_, err := w.Write([]byte{0xf}) // NO_FIRING pane
	return err
}

// DecodeWindowedValueHeader uses the supplied coder to deserialize a windowed value header.
func DecodeWindowedValueHeader(c *coder.Coder, r io.Reader) (typex.EventTime, error) {
	// Encoding: Timestamp, Window, Pane (header) + Element

	t, err := coder.DecodeEventTime(r)
	if err != nil {
		return typex.EventTime(time.Time{}), err
	}
	if _, err := coder.DecodeInt32(r); err != nil { // #windows
		return typex.EventTime(time.Time{}), err
	}
	if _, err := ioutilx.ReadN(r, 1); err != nil { // NO_FIRING pane
		return typex.EventTime(time.Time{}), err
	}
	return t, nil
}
<|endoftext|>


<commit_before>package capabilities

import (
	"net/http"
	"os"
	"strings"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"context"
	"google.golang.org/api/container/v1"
	"sync"
	"io/ioutil"
	"io"
	"encoding/json"
	"fmt"
	"github.com/sirupsen/logrus"
)

const (
	defaultCredentialEnv = "GOOGLE_APPLICATION_CREDENTIALS"
)

var mutex = &sync.Mutex{}

func NewGKECapabilitiesHandler() *gkeVersionHandler {
	return &gkeVersionHandler{}
}

type gkeVersionHandler struct {
}

type errorResponse struct {
	Error string `json:"error"`
}

type requestBody struct {
	Key       string `json:"key"`
	Zone      string `json:"zone"`
	ProjectId string `json:"projectId"`
}

func (g *gkeVersionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodPost {
		writer.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	writer.Header().Set("Content-Type", "application/json")

	raw, err := ioutil.ReadAll(req.Body)

	fmt.Println(string(raw))

	var body requestBody
	err = json.Unmarshal(raw, &body)

	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("cannot parse request body: "+err.Error()))
		return
	}

	key := body.Key
	projectId := body.ProjectId
	zone := body.Zone

	if projectId == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid projectId"))
		return
	}

	if zone == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid zone"))
		return
	}

	if key == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid key"))
		return
	}

	client, err := g.getServiceClient(context.Background(), key)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	result, err := client.Projects.Zones.GetServerconfig(projectId, zone).Do()

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	serialized, err := json.Marshal(result)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	writer.Write(serialized)
}

func (g *gkeVersionHandler) handleErr(writer http.ResponseWriter, originalErr error) {
	resp := errorResponse{originalErr.Error()}

	asJson, err := json.Marshal(resp)

	if err != nil {
		logrus.Error("error while marshalling error message '" + originalErr.Error() + "' error was '" + err.Error() + "'")
		writer.Write([]byte(err.Error()))
		return
	}

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

func (g *gkeVersionHandler) getServiceClient(ctx context.Context, credentialContent string) (*container.Service, error) {
	// The google SDK has no sane way to pass in a TokenSource give all the different types (user, service account, etc)
	// So we actually set an environment variable and then unset it
	mutex.Lock()
	locked := true
	setEnv := false
	cleanup := func() {
		if setEnv {
			os.Unsetenv(defaultCredentialEnv)
		}

		if locked {
			mutex.Unlock()
			locked = false
		}
	}
	defer cleanup()

	file, err := ioutil.TempFile("", "credential-file")
	if err != nil {
		return nil, err
	}
	defer os.Remove(file.Name())
	defer file.Close()

	if _, err := io.Copy(file, strings.NewReader(credentialContent)); err != nil {
		return nil, err
	}

	setEnv = true
	os.Setenv(defaultCredentialEnv, file.Name())

	ts, err := google.DefaultTokenSource(ctx, container.CloudPlatformScope)
	if err != nil {
		return nil, err
	}

	// Unlocks
	cleanup()

	client := oauth2.NewClient(ctx, ts)
	service, err := container.New(client)

	if err != nil {
		return nil, err
	}
	return service, nil
}
<commit_msg>Changing request body to be more in line with cluster creation request<commit_after>package capabilities

import (
	"net/http"
	"os"
	"strings"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"context"
	"google.golang.org/api/container/v1"
	"sync"
	"io/ioutil"
	"io"
	"encoding/json"
	"fmt"
	"github.com/sirupsen/logrus"
)

const (
	defaultCredentialEnv = "GOOGLE_APPLICATION_CREDENTIALS"
)

var mutex = &sync.Mutex{}

func NewGKECapabilitiesHandler() *gkeVersionHandler {
	return &gkeVersionHandler{}
}

type gkeVersionHandler struct {
}

type errorResponse struct {
	Error string `json:"error"`
}

type requestBody struct {
	Credentials string `json:"credentials"`
	Zone        string `json:"zone"`
	ProjectId   string `json:"projectId"`
}

func (g *gkeVersionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodPost {
		writer.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	writer.Header().Set("Content-Type", "application/json")

	raw, err := ioutil.ReadAll(req.Body)

	var body requestBody
	err = json.Unmarshal(raw, &body)

	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("cannot parse request body: "+err.Error()))
		return
	}

	credentials := body.Credentials
	projectId := body.ProjectId
	zone := body.Zone

	if projectId == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid projectId"))
		return
	}

	if zone == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid zone"))
		return
	}

	if credentials == "" {
		writer.WriteHeader(http.StatusBadRequest)
		g.handleErr(writer, fmt.Errorf("invalid credentials"))
		return
	}

	client, err := g.getServiceClient(context.Background(), credentials)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	result, err := client.Projects.Zones.GetServerconfig(projectId, zone).Do()

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	serialized, err := json.Marshal(result)

	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		g.handleErr(writer, err)
		return
	}

	writer.Write(serialized)
}

func (g *gkeVersionHandler) handleErr(writer http.ResponseWriter, originalErr error) {
	resp := errorResponse{originalErr.Error()}

	asJson, err := json.Marshal(resp)

	if err != nil {
		logrus.Error("error while marshalling error message '" + originalErr.Error() + "' error was '" + err.Error() + "'")
		writer.Write([]byte(err.Error()))
		return
	}

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

func (g *gkeVersionHandler) getServiceClient(ctx context.Context, credentialContent string) (*container.Service, error) {
	// The google SDK has no sane way to pass in a TokenSource give all the different types (user, service account, etc)
	// So we actually set an environment variable and then unset it
	mutex.Lock()
	locked := true
	setEnv := false
	cleanup := func() {
		if setEnv {
			os.Unsetenv(defaultCredentialEnv)
		}

		if locked {
			mutex.Unlock()
			locked = false
		}
	}
	defer cleanup()

	file, err := ioutil.TempFile("", "credential-file")
	if err != nil {
		return nil, err
	}
	defer os.Remove(file.Name())
	defer file.Close()

	if _, err := io.Copy(file, strings.NewReader(credentialContent)); err != nil {
		return nil, err
	}

	setEnv = true
	os.Setenv(defaultCredentialEnv, file.Name())

	ts, err := google.DefaultTokenSource(ctx, container.CloudPlatformScope)
	if err != nil {
		return nil, err
	}

	// Unlocks
	cleanup()

	client := oauth2.NewClient(ctx, ts)
	service, err := container.New(client)

	if err != nil {
		return nil, err
	}
	return service, nil
}
<|endoftext|>


<commit_before>package images

import (
	"io/ioutil"
	"testing"
)

func TestXYZ(t *testing.T) {
	fname := "sample1.jpg"

	dat, _ := ioutil.ReadFile(fname)

	fixed_data := FixJpgOrientation(dat)

	ioutil.WriteFile("fixed1.jpg", fixed_data, 0644)

}
<commit_msg>fix build issue<commit_after>package images

import (
	"io/ioutil"
	"os"
	"testing"
)

func TestXYZ(t *testing.T) {
	fname := "sample1.jpg"

	dat, _ := ioutil.ReadFile(fname)

	fixed_data := FixJpgOrientation(dat)

	ioutil.WriteFile("fixed1.jpg", fixed_data, 0644)

	os.Remove("fixed1.jpg")

}
<|endoftext|>


<commit_before>package databaseService

import (
	"database/sql"
	"errors"
	_ "github.com/go-sql-driver/mysql"
	"github.com/hacksoc-manchester/www/helpers/validator"
	"log"
	"os"
)

type userEntry struct {
	FirstName            string
	LastName             string
	Email                string
	SubscribedToArticles bool
	SubscribedToEvents   bool
}

var db *sql.DB

func init() {

	if os.Getenv("MYSQL_CONNECTION_STRING") == "" {
		log.Println("MYSQL_CONNECTION_STRING unassigned. Signing up will not work!")
		return
	}

	db, _ = sql.Open("mysql", os.Getenv("MYSQL_CONNECTION_STRING"))

	if err := db.Ping(); err != nil {
		panic(err)
	}
}

// CreateUser adds the specified entry to the User table.
func CreateUser(firstName, lastName, email string, subscribedToArticles, subscribedToEvents bool) error {
	if !validator.IsValidName(firstName) {
		return errors.New(`Name "` + firstName + `" is not valid.`)
	}

	if !validator.IsValidName(lastName) {
		return errors.New(`Name "` + lastName + `" is not valid.`)
	}

	if !validator.IsValidEmail(email) {
		return errors.New(`Email "` + email + `" is not valid.`)
	}

	stmt, _ := db.Prepare(`insert into User(FirstName, LastName, Email, SubscribedToArticles, SubscribedToEvents)
		values (?, ?, ?, ?, ?)`)

	if _, err := stmt.Exec(firstName, lastName, email, subscribedToArticles, subscribedToEvents); err != nil {
		return errors.New(`Email "` + email + `" is already subscribed.`)
	}

	return nil
}

// GetUser retrieves the user with the specified email.
func GetUser(email string) (*userEntry, error) {
	stmt, _ := db.Prepare("select * from User where Email = ?")
	user := new(userEntry)
	err := stmt.QueryRow(email).Scan(
		&user.FirstName,
		&user.LastName,
		&user.Email,
		&user.SubscribedToArticles,
		&user.SubscribedToEvents)

	if err != nil {
		return nil, errors.New(`Could not find user with email "` + email + `".`)
	}

	return user, nil
}

// ExistsUser determines whether a user with the specified email exists.
func ExistsUser(email string) bool {
	user, _ := GetUser(email)

	return user != nil
}

// DeleteUser removes the specified entry from the User table.
func DeleteUser(email string) error {
	stmt, _ := db.Prepare("delete from User where Email = ?")

	if _, err := stmt.Exec(email); err != nil {
		return errors.New(`Could not delete user with email "` + email + `".`)
	}

	return nil
}
<commit_msg>Traceback of err.Error not err<commit_after>package databaseService

import (
	"database/sql"
	"errors"
	_ "github.com/go-sql-driver/mysql"
	"github.com/hacksoc-manchester/www/helpers/validator"
	"log"
	"os"
)

type userEntry struct {
	FirstName            string
	LastName             string
	Email                string
	SubscribedToArticles bool
	SubscribedToEvents   bool
}

var db *sql.DB

func init() {

	if os.Getenv("MYSQL_CONNECTION_STRING") == "" {
		log.Println("MYSQL_CONNECTION_STRING unassigned. Signing up will not work!")
		return
	}

	db, _ = sql.Open("mysql", os.Getenv("MYSQL_CONNECTION_STRING"))

	if err := db.Ping(); err != nil {
		panic(err.Error())
	}
}

// CreateUser adds the specified entry to the User table.
func CreateUser(firstName, lastName, email string, subscribedToArticles, subscribedToEvents bool) error {
	if !validator.IsValidName(firstName) {
		return errors.New(`Name "` + firstName + `" is not valid.`)
	}

	if !validator.IsValidName(lastName) {
		return errors.New(`Name "` + lastName + `" is not valid.`)
	}

	if !validator.IsValidEmail(email) {
		return errors.New(`Email "` + email + `" is not valid.`)
	}

	stmt, _ := db.Prepare(`insert into User(FirstName, LastName, Email, SubscribedToArticles, SubscribedToEvents)
		values (?, ?, ?, ?, ?)`)

	if _, err := stmt.Exec(firstName, lastName, email, subscribedToArticles, subscribedToEvents); err != nil {
		return errors.New(`Email "` + email + `" is already subscribed.`)
	}

	return nil
}

// GetUser retrieves the user with the specified email.
func GetUser(email string) (*userEntry, error) {
	stmt, _ := db.Prepare("select * from User where Email = ?")
	user := new(userEntry)
	err := stmt.QueryRow(email).Scan(
		&user.FirstName,
		&user.LastName,
		&user.Email,
		&user.SubscribedToArticles,
		&user.SubscribedToEvents)

	if err != nil {
		return nil, errors.New(`Could not find user with email "` + email + `".`)
	}

	return user, nil
}

// ExistsUser determines whether a user with the specified email exists.
func ExistsUser(email string) bool {
	user, _ := GetUser(email)

	return user != nil
}

// DeleteUser removes the specified entry from the User table.
func DeleteUser(email string) error {
	stmt, _ := db.Prepare("delete from User where Email = ?")

	if _, err := stmt.Exec(email); err != nil {
		return errors.New(`Could not delete user with email "` + email + `".`)
	}

	return nil
}
<|endoftext|>


<commit_before>package aero_test

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"syscall"
	"testing"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/http/client"
	"github.com/stretchr/testify/assert"
)

const helloWorld = "Hello World"

func TestApplicationGet(t *testing.T) {
	app := aero.New()

	// Register route
	app.Get("/", func(ctx *aero.Context) string {
		return ctx.Text(helloWorld)
	})

	// Get response
	response := getResponse(app, "/")

	// Verify response
	assert.Equal(t, http.StatusOK, response.Code)
	assert.Equal(t, helloWorld, response.Body.String())
}

func TestApplicationPost(t *testing.T) {
	app := aero.New()

	// Register route
	app.Post("/", func(ctx *aero.Context) string {
		return ctx.Text(helloWorld)
	})

	// Get response
	request, _ := http.NewRequest("POST", "/", nil)
	response := httptest.NewRecorder()
	app.Handler().ServeHTTP(response, request)

	// Verify response
	assert.Equal(t, http.StatusOK, response.Code)
	assert.Equal(t, helloWorld, response.Body.String())
}

func TestApplicationRewrite(t *testing.T) {
	app := aero.New()

	// Register route
	app.Get("/hello", func(ctx *aero.Context) string {
		return ctx.Text(helloWorld)
	})

	// Rewrite route
	app.Rewrite(func(ctx *aero.RewriteContext) {
		if ctx.URI() == "/" {
			ctx.SetURI("/hello")
			return
		}
	})

	// Get response
	response := getResponse(app, "/")

	// Verify response
	assert.Equal(t, http.StatusOK, response.Code)
	assert.Equal(t, helloWorld, response.Body.String())
}

func TestApplicationLoadConfig(t *testing.T) {
	app := aero.New()
	workingDirectory, _ := os.Getwd()

	os.Chdir("test")
	app.Load()
	os.Chdir(workingDirectory)

	assert.Equal(t, "Test title", app.Config.Title)
}

func TestApplicationRun(t *testing.T) {
	app := aero.New()

	// When frontpage is requested, kill the server
	app.Get("/", func(ctx *aero.Context) string {
		return ctx.HTML(helloWorld)
	})

	// When the server is started, we request the frontpage
	app.OnStart(func() {
		client.Get(fmt.Sprintf("http://localhost:%d/", app.Config.Ports.HTTP)).End()
		syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
	})

	// When the server ends, check elapsed time
	app.OnEnd(func() {
		elapsed := time.Since(app.StartTime())
		assert.True(t, elapsed < 2*time.Second)
	})

	// Run
	app.Run()
}

func TestApplicationRunHTTPS(t *testing.T) {
	app := aero.New()
	app.Security.Load("test/fullchain.pem", "test/privkey.pem")

	// Register route
	app.Get("/", func(ctx *aero.Context) string {
		return ctx.HTML(helloWorld)
	})

	// When the server is started, we request the frontpage
	app.OnStart(func() {
		client.Get(fmt.Sprintf("http://localhost:%d/", app.Config.Ports.HTTP)).End()
		client.Get(fmt.Sprintf("https://localhost:%d/", app.Config.Ports.HTTPS)).End()

		go func() {
			syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
		}()
	})

	// Run
	app.Run()
}

// getResponse sends a request to the server and returns the response.
func getResponse(app *aero.Application, route string) *httptest.ResponseRecorder {
	// Create request
	request, _ := http.NewRequest("GET", route, nil)
	request.Header.Set("Accept-Encoding", "gzip")

	// Get response
	response := httptest.NewRecorder()
	app.Handler().ServeHTTP(response, request)

	return response
}
<commit_msg>Add a test for failing port bindings<commit_after>package aero_test

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"syscall"
	"testing"
	"time"

	"github.com/aerogo/aero"
	"github.com/aerogo/http/client"
	"github.com/stretchr/testify/assert"
)

const helloWorld = "Hello World"

func TestApplicationGet(t *testing.T) {
	app := aero.New()

	// Register route
	app.Get("/", func(ctx *aero.Context) string {
		return ctx.Text(helloWorld)
	})

	// Get response
	response := getResponse(app, "/")

	// Verify response
	assert.Equal(t, http.StatusOK, response.Code)
	assert.Equal(t, helloWorld, response.Body.String())
}

func TestApplicationPost(t *testing.T) {
	app := aero.New()

	// Register route
	app.Post("/", func(ctx *aero.Context) string {
		return ctx.Text(helloWorld)
	})

	// Get response
	request, _ := http.NewRequest("POST", "/", nil)
	response := httptest.NewRecorder()
	app.Handler().ServeHTTP(response, request)

	// Verify response
	assert.Equal(t, http.StatusOK, response.Code)
	assert.Equal(t, helloWorld, response.Body.String())
}

func TestApplicationRewrite(t *testing.T) {
	app := aero.New()

	// Register route
	app.Get("/hello", func(ctx *aero.Context) string {
		return ctx.Text(helloWorld)
	})

	// Rewrite route
	app.Rewrite(func(ctx *aero.RewriteContext) {
		if ctx.URI() == "/" {
			ctx.SetURI("/hello")
			return
		}
	})

	// Get response
	response := getResponse(app, "/")

	// Verify response
	assert.Equal(t, http.StatusOK, response.Code)
	assert.Equal(t, helloWorld, response.Body.String())
}

func TestApplicationLoadConfig(t *testing.T) {
	app := aero.New()
	workingDirectory, _ := os.Getwd()

	os.Chdir("test")
	app.Load()
	os.Chdir(workingDirectory)

	assert.Equal(t, "Test title", app.Config.Title)
}

func TestApplicationRun(t *testing.T) {
	app := aero.New()

	// When frontpage is requested, kill the server
	app.Get("/", func(ctx *aero.Context) string {
		return ctx.HTML(helloWorld)
	})

	// When the server is started, we request the frontpage
	app.OnStart(func() {
		client.Get(fmt.Sprintf("http://localhost:%d/", app.Config.Ports.HTTP)).End()
		syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
	})

	// When the server ends, check elapsed time
	app.OnEnd(func() {
		elapsed := time.Since(app.StartTime())
		assert.True(t, elapsed < 2*time.Second)
	})

	// Run
	app.Run()
}

func TestApplicationRunHTTPS(t *testing.T) {
	app := aero.New()
	app.Security.Load("test/fullchain.pem", "test/privkey.pem")

	// Register route
	app.Get("/", func(ctx *aero.Context) string {
		return ctx.HTML(helloWorld)
	})

	// When the server is started, we request the frontpage
	app.OnStart(func() {
		client.Get(fmt.Sprintf("http://localhost:%d/", app.Config.Ports.HTTP)).End()
		client.Get(fmt.Sprintf("https://localhost:%d/", app.Config.Ports.HTTPS)).End()

		go func() {
			syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
		}()
	})

	// Run
	app.Run()
}

func TestApplicationUnavailablePort(t *testing.T) {
	defer func() {
		r := recover()
		assert.NotNil(t, r)
		assert.Contains(t, r.(error).Error(), "bind: permission denied")
	}()

	app := aero.New()
	app.Config.Ports.HTTP = 80
	app.Config.Ports.HTTPS = 443
	app.ListenAndServe()
}

// getResponse sends a request to the server and returns the response.
func getResponse(app *aero.Application, route string) *httptest.ResponseRecorder {
	// Create request
	request, _ := http.NewRequest("GET", route, nil)
	request.Header.Set("Accept-Encoding", "gzip")

	// Get response
	response := httptest.NewRecorder()
	app.Handler().ServeHTTP(response, request)

	return response
}
<|endoftext|>


<commit_before>package presenter

import (
	"io"

	"github.com/anchore/imgbom/imgbom/pkg"
	json_dirs "github.com/anchore/imgbom/imgbom/presenter/json/dirs"
	json_imgs "github.com/anchore/imgbom/imgbom/presenter/json/imgs"
	text_dirs "github.com/anchore/imgbom/imgbom/presenter/text/dirs"
	text_imgs "github.com/anchore/imgbom/imgbom/presenter/text/imgs"
	"github.com/anchore/stereoscope/pkg/image"
)

type Presenter interface {
	Present(io.Writer) error
}

func GetImgPresenter(option Option, img *image.Image, catalog *pkg.Catalog) Presenter {
	switch option {
	case JSONPresenter:
		return json_imgs.NewPresenter(img, catalog)
	case TextPresenter:
		return text_imgs.NewPresenter(img, catalog)
	default:
		return nil
	}
}

func GetDirPresenter(option Option, path string, catalog *pkg.Catalog) Presenter {
	switch option {
	case JSONPresenter:
		return json_dirs.NewPresenter(catalog, path)
	case TextPresenter:
		return text_dirs.NewPresenter(catalog, path)
	default:
		return nil
	}
}
<commit_msg>presenter: use new scoped source<commit_after>package presenter

import (
	"io"

	"github.com/anchore/imgbom/imgbom/pkg"
	json_dirs "github.com/anchore/imgbom/imgbom/presenter/json/dirs"
	json_imgs "github.com/anchore/imgbom/imgbom/presenter/json/imgs"
	text_dirs "github.com/anchore/imgbom/imgbom/presenter/text/dirs"
	text_imgs "github.com/anchore/imgbom/imgbom/presenter/text/imgs"
	"github.com/anchore/imgbom/imgbom/scope"
)

type Presenter interface {
	Present(io.Writer) error
}

// GetPresenter returns a presenter for images or directories
func GetPresenter(option Option, s scope.Scope, catalog *pkg.Catalog) Presenter {
	src := s.Source()

	switch src.(type) {
	case scope.DirSource:
		return GetDirPresenter(option, s, catalog)
	case scope.ImageSource:
		return GetImgPresenter(option, s, catalog)
	default:
		return nil
	}
}

// GetImgPresenter returns a Json or Text presenter for images
func GetImgPresenter(option Option, s scope.Scope, c *pkg.Catalog) Presenter {
	src := s.Source()
	img := src.(scope.ImageSource).Img
	switch option {
	case JSONPresenter:
		return json_imgs.NewPresenter(img, c)
	case TextPresenter:
		return text_imgs.NewPresenter(img, c)
	default:
		return nil
	}
}

// GetDirPresenter returns a Json or Text presenter for directories
func GetDirPresenter(option Option, s scope.Scope, c *pkg.Catalog) Presenter {
	src := s.Source()
	path := src.(scope.DirSource).Path
	switch option {
	case JSONPresenter:
		return json_dirs.NewPresenter(c, path)
	case TextPresenter:
		return text_dirs.NewPresenter(c, path)
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package backend

import (
	"bytes"
	"context"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/go-redis/redis/v7"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

// Errors.
var (
	ErrAsyncTimeout = errors.New("async timeout")
)

// Client defines the backend client interface.
type Client interface {
	// GetSenderID returns the SenderID.
	GetSenderID() string
	// GetReceiverID returns the ReceiverID.
	GetReceiverID() string
	// IsAsync returns a bool indicating if the client is async.
	IsAsync() bool
	// GetRandomTransactionID returns a random transaction id.
	GetRandomTransactionID() uint32
	// JoinReq method.
	JoinReq(context.Context, JoinReqPayload) (JoinAnsPayload, error)
	// RejoinReq method.
	RejoinReq(context.Context, RejoinReqPayload) (RejoinAnsPayload, error)
	// PRStartReq method.
	PRStartReq(context.Context, PRStartReqPayload) (PRStartAnsPayload, error)
	// PRStopReq method.
	PRStopReq(context.Context, PRStopReqPayload) (PRStopAnsPayload, error)
	// XmitDataReq method.
	XmitDataReq(context.Context, XmitDataReqPayload) (XmitDataAnsPayload, error)
	// ProfileReq method.
	ProfileReq(context.Context, ProfileReqPayload) (ProfileAnsPayload, error)
	// HomeNSReq method.
	HomeNSReq(context.Context, HomeNSReqPayload) (HomeNSAnsPayload, error)
	// SendAnswer sends the async answer.
	SendAnswer(context.Context, Answer) error
	// HandleAnswer handles an async answer.
	HandleAnswer(context.Context, Answer) error
}

// ClientConfig holds the backend client configuration.
type ClientConfig struct {
	SenderID   string
	ReceiverID string
	Server     string
	CACert     string
	TLSCert    string
	TLSKey     string

	// RedisClient holds the optional Redis database client. When set the client
	// will use the aysnc protocol scheme. In this case the client will wait
	// AsyncTimeout before returning a timeout error.
	RedisClient redis.UniversalClient

	// AsyncTimeout defines the async timeout. This must be set when RedisClient
	// is set.
	AsyncTimeout time.Duration

	// Logger holds a Logger instance.
	Logger *log.Logger
}

// NewClient creates a new Client.
func NewClient(config ClientConfig) (Client, error) {
	httpClient := http.DefaultClient

	if config.CACert != "" || config.TLSCert != "" || config.TLSKey != "" {
		tlsConfig := &tls.Config{}

		if config.CACert != "" {
			rawCACert, err := ioutil.ReadFile(config.CACert)
			if err != nil {
				return nil, errors.Wrap(err, "read ca cert error")
			}

			caCertPool := x509.NewCertPool()
			if !caCertPool.AppendCertsFromPEM(rawCACert) {
				return nil, errors.New("append ca cert to pool error")
			}

			tlsConfig.RootCAs = caCertPool
		}

		if config.TLSCert != "" || config.TLSKey != "" {
			cert, err := tls.LoadX509KeyPair(config.TLSCert, config.TLSKey)
			if err != nil {
				return nil, errors.Wrap(err, "load x509 keypair error")
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}

		httpClient = &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: tlsConfig,
			},
		}
	}

	if config.Logger == nil {
		config.Logger = &log.Logger{
			Out: ioutil.Discard,
		}
	}

	return &client{
		log:             config.Logger,
		server:          config.Server,
		httpClient:      httpClient,
		senderID:        config.SenderID,
		receiverID:      config.ReceiverID,
		protocolVersion: ProtocolVersion1_0,
		redisClient:     config.RedisClient,
		asyncTimeout:    config.AsyncTimeout,
	}, nil

}

type client struct {
	log             *log.Logger
	server          string
	httpClient      *http.Client
	protocolVersion string
	senderID        string
	receiverID      string
	redisClient     redis.UniversalClient
	asyncTimeout    time.Duration
}

func (c *client) GetSenderID() string {
	return c.senderID
}

func (c *client) GetReceiverID() string {
	return c.receiverID
}

func (c *client) IsAsync() bool {
	return c.redisClient != nil
}

func (c *client) JoinReq(ctx context.Context, pl JoinReqPayload) (JoinAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = JoinReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans JoinAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) RejoinReq(ctx context.Context, pl RejoinReqPayload) (RejoinAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = RejoinReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans RejoinAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) PRStartReq(ctx context.Context, pl PRStartReqPayload) (PRStartAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = PRStartReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans PRStartAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) PRStopReq(ctx context.Context, pl PRStopReqPayload) (PRStopAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = PRStopReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans PRStopAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) XmitDataReq(ctx context.Context, pl XmitDataReqPayload) (XmitDataAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = XmitDataReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans XmitDataAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) ProfileReq(ctx context.Context, pl ProfileReqPayload) (ProfileAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = ProfileReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans ProfileAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) HomeNSReq(ctx context.Context, pl HomeNSReqPayload) (HomeNSAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = HomeNSReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans HomeNSAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) request(ctx context.Context, pl Request, ans Answer) error {
	b, err := json.Marshal(pl)
	if err != nil {
		return errors.Wrap(err, "json marshal error")
	}

	responseChan := make(chan []byte, 1)
	errorChan := make(chan error, 1)

	// Setup async subscriber to receive response. Please note that we have to do
	// this before making the request, as the response might come in, before the
	// request has returned.
	if c.IsAsync() {
		key := c.getAsyncKey(pl.GetBasePayload().TransactionID)

		go func() {
			bb, err := c.readAsync(ctx, key)
			if err != nil {
				errorChan <- err
			} else {
				responseChan <- bb
			}
		}()
	}

	req, err := http.NewRequestWithContext(ctx, "POST", c.server, bytes.NewReader(b))
	if err != nil {
		return errors.Wrap(err, "new request error")
	}
	req.Header.Add("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "http post error")
	}
	defer resp.Body.Close()

	// If async is not used, the http response contains the API response payload.
	if !c.IsAsync() {
		bb, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			errorChan <- err
		} else {
			responseChan <- bb
		}
	}

	select {
	case err := <-errorChan:
		return err
	case bb := <-responseChan:
		if err := json.Unmarshal(bb, ans); err != nil {
			return errors.Wrap(err, "unmarshal response error")
		}
	}

	c.log.WithFields(log.Fields{
		"protocol_version": pl.GetBasePayload().ProtocolVersion,
		"sender_id":        pl.GetBasePayload().SenderID,
		"receiver_id":      pl.GetBasePayload().ReceiverID,
		"transaction_id":   pl.GetBasePayload().TransactionID,
		"message_type":     pl.GetBasePayload().MessageType,
		"result_code":      ans.GetBasePayload().Result.ResultCode,
	}).Info("lorawan/backend: finished backend api call")

	return nil
}

func (c *client) HandleAnswer(ctx context.Context, pl Answer) error {
	if !c.IsAsync() {
		return errors.New("async is not configured")
	}

	b, err := json.Marshal(pl)
	if err != nil {
		return errors.Wrap(err, "marshal answer error")
	}

	err = c.redisClient.Publish(c.getAsyncKey(pl.GetBasePayload().TransactionID), b).Err()
	if err != nil {
		return errors.Wrap(err, "publish answer error")
	}

	return nil
}

func (c *client) SendAnswer(ctx context.Context, pl Answer) error {
	b, err := json.Marshal(pl)
	if err != nil {
		return errors.Wrap(err, "json marshal error")
	}

	// TODO add context for cancellation
	resp, err := c.httpClient.Post(c.server, "application/json", bytes.NewReader(b))
	if err != nil {
		return errors.Wrap(err, "http post error")
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		bb, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return errors.Wrap(err, "read body error")
		}
		return fmt.Errorf("expected: 200, got: %d (%s)", resp.StatusCode, string(bb))
	}

	return nil
}

func (c *client) GetRandomTransactionID() uint32 {
	b := make([]byte, 4)
	rand.Read(b)
	return binary.LittleEndian.Uint32(b)
}

func (c *client) getAsyncKey(id uint32) string {
	return fmt.Sprintf("lora:backend:async:%d", id)
}

func (c *client) readAsync(ctx context.Context, key string) ([]byte, error) {
	sub := c.redisClient.Subscribe(key)
	defer sub.Close()

	ch := sub.Channel()

	select {
	case msg := <-ch:
		return []byte(msg.Payload), nil
	case <-time.After(c.asyncTimeout):
		return nil, ErrAsyncTimeout
	}
}
<commit_msg>Add debug log on new client.<commit_after>package backend

import (
	"bytes"
	"context"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/go-redis/redis/v7"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
)

// Errors.
var (
	ErrAsyncTimeout = errors.New("async timeout")
)

// Client defines the backend client interface.
type Client interface {
	// GetSenderID returns the SenderID.
	GetSenderID() string
	// GetReceiverID returns the ReceiverID.
	GetReceiverID() string
	// IsAsync returns a bool indicating if the client is async.
	IsAsync() bool
	// GetRandomTransactionID returns a random transaction id.
	GetRandomTransactionID() uint32
	// JoinReq method.
	JoinReq(context.Context, JoinReqPayload) (JoinAnsPayload, error)
	// RejoinReq method.
	RejoinReq(context.Context, RejoinReqPayload) (RejoinAnsPayload, error)
	// PRStartReq method.
	PRStartReq(context.Context, PRStartReqPayload) (PRStartAnsPayload, error)
	// PRStopReq method.
	PRStopReq(context.Context, PRStopReqPayload) (PRStopAnsPayload, error)
	// XmitDataReq method.
	XmitDataReq(context.Context, XmitDataReqPayload) (XmitDataAnsPayload, error)
	// ProfileReq method.
	ProfileReq(context.Context, ProfileReqPayload) (ProfileAnsPayload, error)
	// HomeNSReq method.
	HomeNSReq(context.Context, HomeNSReqPayload) (HomeNSAnsPayload, error)
	// SendAnswer sends the async answer.
	SendAnswer(context.Context, Answer) error
	// HandleAnswer handles an async answer.
	HandleAnswer(context.Context, Answer) error
}

// ClientConfig holds the backend client configuration.
type ClientConfig struct {
	SenderID   string
	ReceiverID string
	Server     string
	CACert     string
	TLSCert    string
	TLSKey     string

	// RedisClient holds the optional Redis database client. When set the client
	// will use the aysnc protocol scheme. In this case the client will wait
	// AsyncTimeout before returning a timeout error.
	RedisClient redis.UniversalClient

	// AsyncTimeout defines the async timeout. This must be set when RedisClient
	// is set.
	AsyncTimeout time.Duration

	// Logger holds a Logger instance.
	Logger *log.Logger
}

// NewClient creates a new Client.
func NewClient(config ClientConfig) (Client, error) {
	httpClient := http.DefaultClient

	if config.CACert != "" || config.TLSCert != "" || config.TLSKey != "" {
		tlsConfig := &tls.Config{}

		if config.CACert != "" {
			rawCACert, err := ioutil.ReadFile(config.CACert)
			if err != nil {
				return nil, errors.Wrap(err, "read ca cert error")
			}

			caCertPool := x509.NewCertPool()
			if !caCertPool.AppendCertsFromPEM(rawCACert) {
				return nil, errors.New("append ca cert to pool error")
			}

			tlsConfig.RootCAs = caCertPool
		}

		if config.TLSCert != "" || config.TLSKey != "" {
			cert, err := tls.LoadX509KeyPair(config.TLSCert, config.TLSKey)
			if err != nil {
				return nil, errors.Wrap(err, "load x509 keypair error")
			}
			tlsConfig.Certificates = []tls.Certificate{cert}
		}

		httpClient = &http.Client{
			Transport: &http.Transport{
				TLSClientConfig: tlsConfig,
			},
		}
	}

	if config.Logger == nil {
		config.Logger = &log.Logger{
			Out: ioutil.Discard,
		}
	}

	config.Logger.WithFields(log.Fields{
		"server":      config.Server,
		"ca_cert":     config.CACert,
		"tls_cert":    config.TLSCert,
		"tls_key":     config.TLSKey,
		"sender_id":   config.SenderID,
		"receiver_id": config.ReceiverID,
	}).Debug("lorawan/backend: new backend client")

	return &client{
		log:             config.Logger,
		server:          config.Server,
		httpClient:      httpClient,
		senderID:        config.SenderID,
		receiverID:      config.ReceiverID,
		protocolVersion: ProtocolVersion1_0,
		redisClient:     config.RedisClient,
		asyncTimeout:    config.AsyncTimeout,
	}, nil

}

type client struct {
	log             *log.Logger
	server          string
	httpClient      *http.Client
	protocolVersion string
	senderID        string
	receiverID      string
	redisClient     redis.UniversalClient
	asyncTimeout    time.Duration
}

func (c *client) GetSenderID() string {
	return c.senderID
}

func (c *client) GetReceiverID() string {
	return c.receiverID
}

func (c *client) IsAsync() bool {
	return c.redisClient != nil
}

func (c *client) JoinReq(ctx context.Context, pl JoinReqPayload) (JoinAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = JoinReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans JoinAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) RejoinReq(ctx context.Context, pl RejoinReqPayload) (RejoinAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = RejoinReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans RejoinAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) PRStartReq(ctx context.Context, pl PRStartReqPayload) (PRStartAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = PRStartReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans PRStartAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) PRStopReq(ctx context.Context, pl PRStopReqPayload) (PRStopAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = PRStopReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans PRStopAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) XmitDataReq(ctx context.Context, pl XmitDataReqPayload) (XmitDataAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = XmitDataReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans XmitDataAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) ProfileReq(ctx context.Context, pl ProfileReqPayload) (ProfileAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = ProfileReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans ProfileAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) HomeNSReq(ctx context.Context, pl HomeNSReqPayload) (HomeNSAnsPayload, error) {
	pl.BasePayload.ProtocolVersion = c.protocolVersion
	pl.BasePayload.SenderID = c.senderID
	pl.BasePayload.ReceiverID = c.receiverID
	pl.BasePayload.MessageType = HomeNSReq
	if pl.BasePayload.TransactionID == 0 {
		pl.BasePayload.TransactionID = c.GetRandomTransactionID()
	}

	var ans HomeNSAnsPayload

	if err := c.request(ctx, pl, &ans); err != nil {
		return ans, err
	}

	if ans.Result.ResultCode != Success {
		return ans, fmt.Errorf("response error, code: %s, description: %s", ans.Result.ResultCode, ans.Result.Description)
	}

	return ans, nil
}

func (c *client) request(ctx context.Context, pl Request, ans Answer) error {
	b, err := json.Marshal(pl)
	if err != nil {
		return errors.Wrap(err, "json marshal error")
	}

	responseChan := make(chan []byte, 1)
	errorChan := make(chan error, 1)

	// Setup async subscriber to receive response. Please note that we have to do
	// this before making the request, as the response might come in, before the
	// request has returned.
	if c.IsAsync() {
		key := c.getAsyncKey(pl.GetBasePayload().TransactionID)

		go func() {
			bb, err := c.readAsync(ctx, key)
			if err != nil {
				errorChan <- err
			} else {
				responseChan <- bb
			}
		}()
	}

	req, err := http.NewRequestWithContext(ctx, "POST", c.server, bytes.NewReader(b))
	if err != nil {
		return errors.Wrap(err, "new request error")
	}
	req.Header.Add("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "http post error")
	}
	defer resp.Body.Close()

	// If async is not used, the http response contains the API response payload.
	if !c.IsAsync() {
		bb, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			errorChan <- err
		} else {
			responseChan <- bb
		}
	}

	select {
	case err := <-errorChan:
		return err
	case bb := <-responseChan:
		if err := json.Unmarshal(bb, ans); err != nil {
			return errors.Wrap(err, "unmarshal response error")
		}
	}

	c.log.WithFields(log.Fields{
		"protocol_version": pl.GetBasePayload().ProtocolVersion,
		"sender_id":        pl.GetBasePayload().SenderID,
		"receiver_id":      pl.GetBasePayload().ReceiverID,
		"transaction_id":   pl.GetBasePayload().TransactionID,
		"message_type":     pl.GetBasePayload().MessageType,
		"result_code":      ans.GetBasePayload().Result.ResultCode,
	}).Info("lorawan/backend: finished backend api call")

	return nil
}

func (c *client) HandleAnswer(ctx context.Context, pl Answer) error {
	if !c.IsAsync() {
		return errors.New("async is not configured")
	}

	b, err := json.Marshal(pl)
	if err != nil {
		return errors.Wrap(err, "marshal answer error")
	}

	err = c.redisClient.Publish(c.getAsyncKey(pl.GetBasePayload().TransactionID), b).Err()
	if err != nil {
		return errors.Wrap(err, "publish answer error")
	}

	return nil
}

func (c *client) SendAnswer(ctx context.Context, pl Answer) error {
	b, err := json.Marshal(pl)
	if err != nil {
		return errors.Wrap(err, "json marshal error")
	}

	// TODO add context for cancellation
	resp, err := c.httpClient.Post(c.server, "application/json", bytes.NewReader(b))
	if err != nil {
		return errors.Wrap(err, "http post error")
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		bb, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return errors.Wrap(err, "read body error")
		}
		return fmt.Errorf("expected: 200, got: %d (%s)", resp.StatusCode, string(bb))
	}

	return nil
}

func (c *client) GetRandomTransactionID() uint32 {
	b := make([]byte, 4)
	rand.Read(b)
	return binary.LittleEndian.Uint32(b)
}

func (c *client) getAsyncKey(id uint32) string {
	return fmt.Sprintf("lora:backend:async:%d", id)
}

func (c *client) readAsync(ctx context.Context, key string) ([]byte, error) {
	sub := c.redisClient.Subscribe(key)
	defer sub.Close()

	ch := sub.Channel()

	select {
	case msg := <-ch:
		return []byte(msg.Payload), nil
	case <-time.After(c.asyncTimeout):
		return nil, ErrAsyncTimeout
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 functional

import (
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/coreos/fleet/functional/platform"
	"github.com/coreos/fleet/functional/util"
)

// Start three machines and test template units based on machines Metadata
func TestTemplatesWithSpecifiersInMetadata(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	members, err := platform.CreateNClusterMembers(cluster, 3)
	if err != nil {
		t.Fatal(err)
	}
	m0 := members[0]
	_, err = cluster.WaitForNMachines(m0, 3)
	if err != nil {
		t.Fatal(err)
	}

	// Submit one template
	if stdout, stderr, err := cluster.Fleetctl(m0, "submit", "fixtures/units/metadata@.service"); err != nil {
		t.Fatalf("Unable to submit metadata@.service template: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
	}

	// Start units based on template in backward order
	for i := len(members) - 1; i >= 0; i-- {
		if stdout, stderr, err := cluster.Fleetctl(m0, "start", fmt.Sprintf("fixtures/units/metadata@smoke%s.service", members[i].ID())); err != nil {
			t.Fatalf("Unable to start template based unit: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
		}
	}

	_, err = cluster.WaitForNActiveUnits(m0, 3)
	if err != nil {
		t.Fatal(err)
	}

	ndesired := 3
	if stdout, stderr, err := cluster.Fleetctl(m0, "list-units", "--no-legend", "--full", "--fields", "unit,active,machine"); err != nil {
		t.Fatalf("Unable to get submited units: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
	} else {
		stdout = strings.TrimSpace(stdout)
		if err != nil {
			t.Fatalf("Failed to parse stdout: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
		}

		lines := strings.Split(stdout, "\n")
		allStates := util.ParseUnitStates(lines)
		active := util.FilterActiveUnits(allStates)
		nactive := len(active)
		if nactive != ndesired {
			t.Fatalf("Failed to get %d active units: \nstdout: %s\nstderr: %s", ndesired, stdout, stderr)
		}

		for _, state := range active {
			re := regexp.MustCompile(`@([^.]*)`)
			desiredMachine := re.FindStringSubmatch(state.Name)
			if len(desiredMachine) < 2 {
				t.Fatalf("Cannot parse state.Name (%v): \nstdout: %s\nstderr: %s", state.Name, stdout, stderr)
			}
			currentMachine := fmt.Sprintf("smoke%s", state.Machine)
			if desiredMachine[1] != currentMachine {
				t.Fatalf("Template (%s) has been scheduled on wrong machine (%s): \nstdout: %s\nstderr: %s", state.Name, currentMachine, stdout, stderr)
			}
		}
	}

	if stdout, stderr, err := cluster.Fleetctl(m0, "start", "--block-attempts=20", "fixtures/units/metadata@invalid.service"); err == nil {
		t.Fatalf("metadata@invalid unit should not be scheduled: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
	}
}
<commit_msg>tests: several improvements in "template + metadata" test<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 functional

import (
	"fmt"
	"regexp"
	"strings"
	"testing"

	"github.com/coreos/fleet/functional/platform"
	"github.com/coreos/fleet/functional/util"
)

// Start three machines and test template units based on machines Metadata
func TestTemplatesWithSpecifiersInMetadata(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	members, err := platform.CreateNClusterMembers(cluster, 3)
	if err != nil {
		t.Fatal(err)
	}
	m0 := members[0]
	_, err = cluster.WaitForNMachines(m0, 3)
	if err != nil {
		t.Fatal(err)
	}

	// Submit one template
	if stdout, stderr, err := cluster.Fleetctl(m0, "submit", "fixtures/units/metadata@.service"); err != nil {
		t.Fatalf("Unable to submit metadata@.service template: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
	}

	// Start units based on template in backward order
	for i := len(members) - 1; i >= 0; i-- {
		if stdout, stderr, err := cluster.Fleetctl(m0, "start", fmt.Sprintf("fixtures/units/metadata@smoke%s.service", members[i].ID())); err != nil {
			t.Fatalf("Unable to start template based unit: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
		}
	}

	_, err = cluster.WaitForNActiveUnits(m0, 3)
	if err != nil {
		t.Fatal(err)
	}

	stdout, stderr, err := cluster.Fleetctl(m0, "list-units", "--no-legend", "--full", "--fields", "unit,active,machine")
	if err != nil {
		t.Fatalf("Unable to get submitted units: \nstdout: %s\nstderr: %s\nerr: %v", stdout, stderr, err)
	}

	ndesired := 3
	stdout = strings.TrimSpace(stdout)
	lines := strings.Split(stdout, "\n")
	allStates := util.ParseUnitStates(lines)
	active := util.FilterActiveUnits(allStates)
	nactive := len(active)
	if nactive != ndesired {
		t.Fatalf("Failed to get %d active units: \nstdout: %s\nstderr: %s", ndesired, stdout, stderr)
	}

	for _, state := range active {
		re := regexp.MustCompile(`@([^.]*)`)
		desiredMachine := re.FindStringSubmatch(state.Name)
		if len(desiredMachine) < 2 {
			t.Fatalf("Cannot parse state.Name (%v): \nstdout: %s\nstderr: %s", state.Name, stdout, stderr)
		}
		currentMachine := fmt.Sprintf("smoke%s", state.Machine)
		if desiredMachine[1] != currentMachine {
			t.Fatalf("Template (%s) has been scheduled on wrong machine (%s): \nstdout: %s\nstderr: %s", state.Name, currentMachine, stdout, stderr)
		}
	}

	if stdout, stderr, err := cluster.Fleetctl(m0, "start", "--block-attempts=20", "fixtures/units/metadata@invalid.service"); err == nil {
		t.Fatalf("metadata@invalid unit should not be scheduled: \nstdout: %s\nstderr: %s", stdout, stderr)
	}
}
<|endoftext|>


<commit_before>package main

import (
  "log"
  "github.com/gorilla/mux"
  "net/http"
  "net/http/httputil"
  "net/url"
  "github.com/fhs/gompd/mpd"
  "github.com/ascherkus/go-id3/src/id3"
  "os"
  "fmt"
  "encoding/json"
  "strconv"
  "io"
  //"os/exec"
  "./turbo-wookie"
)

// TODO: consider if global is really the best idea, or if we should
//       make some classes, or something...
var mpd_conn *mpd.Client
var config *map[string]string

func main() {
  // setup our global MPD connection
  mpd_conn = mpdConnect("localhost:6600")
  defer mpd_conn.Close()

  // create a new mux router for our server.
  r := mux.NewRouter()

  // requests to `/stream` are proxied to the MPD httpd.
  r.HandleFunc("/stream",
    httputil.NewSingleHostReverseProxy(
      &url.URL{
        Scheme:"http",
        Host: "localhost:8000",
        Path: "/",
      }).ServeHTTP)

  r.HandleFunc("/songs", listSongs)
  r.HandleFunc("/current", getCurrentSong)
  r.HandleFunc("/upcoming", getUpcomingSongs)
  r.HandleFunc("/add", addSong)

  // This MUST go last! It takes precedence over any after it, meaning
  // the server will try to serve a file, which most likely doesn't exist,
  // and will 404.
  //
  // serve up the frontend files.
  r.PathPrefix("/").Handler(http.FileServer(http.Dir("../frontend/turbo_wookie/web")))

  // sit, waiting, like a hunter, spying on its prey.
  log.Println("Starting server on port 9000")
  http.ListenAndServe(":9000", r)
}

/********************
  Handler Functions
 ********************/

// return all songs known to MPD to the client.
func listSongs(w http.ResponseWriter, r *http.Request) {
  // get all files from MPD
  mpdfiles, err := mpd_conn.GetFiles()
  if err != nil {
    MPDReconnect()

    mpdfiles, err = mpd_conn.GetFiles()

    if err != nil {
      error(w, "Couldn't get a list of files...", err)
      return
    }
  }

  // create a slice of id3.File s
  //files := make([]*id3.File, 0)
  files := make([]*TBFile, 0)

  for _, song := range mpdfiles {
    // grab the file on the filesystem
    file, err := os.Open("mpd/music/" + song)
    if err != nil {
      error(w, "Couldn't open file: " + song, err)
      return
    }

    // add the current file to our slice
    id3_file := id3Read(file, song)
    files = append(files, id3_file)
  }

  // send the json to the client.
  fmt.Fprintf(w, jsoniffy(files))
}

// Return a JSON representation of the currently playing song.
func getCurrentSong(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  fmt.Fprintf(w, jsoniffy(currentSong))
}

func getUpcomingSongs(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  pos, err := strconv.Atoi(currentSong["Pos"])
  if err != nil {
    error(w, "Couldn't turn current song's position to int", err)
    return
  }

  playlist, err := mpd_conn.PlaylistInfo(-1, -1)
  if err != nil {
    MPDReconnect()
    playlist, err = mpd_conn.PlaylistInfo(-1, -1)

    if err != nil {
      error(w, "Couldn't get the current playlist", err)
      return
    }
  }

  upcoming := playlist[pos + 1:]

  fmt.Fprintf(w, jsoniffy(upcoming))
}

func addSong(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  song, ok := r.Form["song"]
  if !ok {
    jsonError(w, "No song given", nil)
    return
  }

  err := mpd_conn.Add(song[0])
  ue := "unexpected response: "

  if err != nil {
    if err.Error()[:len(ue)] == ue {
      log.Println("add UE song error:", err)
      jsonError(w, "Unknown song", err)
      return
    }

    log.Println("add song error:", err)
    MPDReconnect()
    err = mpd_conn.Add(song[0])

    if err != nil {
      jsonError(w, "Couldn't add song to playlist.", err)
      return
    }
  }

  m := make(map[string]string)
  m["note"] = "Added song: " + song[0]
  fmt.Fprintf(w, jsoniffy(m))
}

/*******************
  Helper Functions
********************/

// Connect to MPD's control channel, and set the global mpd_conn to it.
func mpdConnect(url string) *mpd.Client {
  conn, err := mpd.Dial("tcp", url)

  // if we can't connect to MPD everything's fucked, nothing's going to work
  // kill all humans, and die, respectfully, after explaining what the issue
  // is.
  if err != nil {
    log.Println("\n\nServer quiting because it can't connect to MPD");
    log.Println(err)

    return nil
  }

  return conn
}

// helper struct; used to hold some ID3 info, plus an MPD file path.
type TBFile struct {id3.File; FilePath string;}

// helper method, returns a pointer to one of our helper structs (see above).
func id3Read(reader io.Reader, filePath string) *TBFile {
  id3File := id3.Read(reader)

  file := new(TBFile)
  file.Header = id3File.Header
  file.Name = id3File.Name
  file.Artist = id3File.Artist
  file.Album = id3File.Album
  file.Year = id3File.Year
  file.FilePath = filePath

  return file
}

func MPDReconnect() {
  mpd_conn.Close()
  mpd_conn = mpdConnect("localhost:6600")
}

// turn anything into JSON.
func jsoniffy(v interface {}) string {
  obj, err := json.MarshalIndent(v, "", "  ")
  if err != nil {
    log.Print("Couldn't turn something into JSON: ", v)
    log.Fatal(err)
  }

  return string(obj)
}

func error(w http.ResponseWriter, message string, err interface{Error() string;}) {
  log.Println("An error occured; telling the client.")
  log.Println("Message:", message)
  log.Println("Error:", err, "\n\n")

  fmt.Fprintf(w, message + "\n")
}

func jsonError(w http.ResponseWriter, message string, err interface{Error() string;}) {
  m := make(map[string]string)
  m["error"] = message
  error(w, jsoniffy(m), err)
}

<commit_msg>server didn't run...<commit_after>package main

import (
  "log"
  "github.com/gorilla/mux"
  "net/http"
  "net/http/httputil"
  "net/url"
  "github.com/fhs/gompd/mpd"
  "github.com/ascherkus/go-id3/src/id3"
  "os"
  "fmt"
  "encoding/json"
  "strconv"
  "io"
  //"os/exec"
  //"./turbo-wookie"
)

// TODO: consider if global is really the best idea, or if we should
//       make some classes, or something...
var mpd_conn *mpd.Client
var config *map[string]string

func main() {
  // setup our global MPD connection
  mpd_conn = mpdConnect("localhost:6600")
  defer mpd_conn.Close()

  // create a new mux router for our server.
  r := mux.NewRouter()

  // requests to `/stream` are proxied to the MPD httpd.
  r.HandleFunc("/stream",
    httputil.NewSingleHostReverseProxy(
      &url.URL{
        Scheme:"http",
        Host: "localhost:8000",
        Path: "/",
      }).ServeHTTP)

  r.HandleFunc("/songs", listSongs)
  r.HandleFunc("/current", getCurrentSong)
  r.HandleFunc("/upcoming", getUpcomingSongs)
  r.HandleFunc("/add", addSong)

  // This MUST go last! It takes precedence over any after it, meaning
  // the server will try to serve a file, which most likely doesn't exist,
  // and will 404.
  //
  // serve up the frontend files.
  r.PathPrefix("/").Handler(http.FileServer(http.Dir("../frontend/turbo_wookie/web")))

  // sit, waiting, like a hunter, spying on its prey.
  log.Println("Starting server on port 9000")
  http.ListenAndServe(":9000", r)
}

/********************
  Handler Functions
 ********************/

// return all songs known to MPD to the client.
func listSongs(w http.ResponseWriter, r *http.Request) {
  // get all files from MPD
  mpdfiles, err := mpd_conn.GetFiles()
  if err != nil {
    MPDReconnect()

    mpdfiles, err = mpd_conn.GetFiles()

    if err != nil {
      error(w, "Couldn't get a list of files...", err)
      return
    }
  }

  // create a slice of id3.File s
  //files := make([]*id3.File, 0)
  files := make([]*TBFile, 0)

  for _, song := range mpdfiles {
    // grab the file on the filesystem
    file, err := os.Open("mpd/music/" + song)
    if err != nil {
      error(w, "Couldn't open file: " + song, err)
      return
    }

    // add the current file to our slice
    id3_file := id3Read(file, song)
    files = append(files, id3_file)
  }

  // send the json to the client.
  fmt.Fprintf(w, jsoniffy(files))
}

// Return a JSON representation of the currently playing song.
func getCurrentSong(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  fmt.Fprintf(w, jsoniffy(currentSong))
}

func getUpcomingSongs(w http.ResponseWriter, r *http.Request) {
  currentSong, err := mpd_conn.CurrentSong()
  if err != nil {
    MPDReconnect()

    currentSong, err = mpd_conn.CurrentSong()

    if err != nil {
      error(w, "Couldn't get current song info for upcoming list", err)
      return
    }
  }

  pos, err := strconv.Atoi(currentSong["Pos"])
  if err != nil {
    error(w, "Couldn't turn current song's position to int", err)
    return
  }

  playlist, err := mpd_conn.PlaylistInfo(-1, -1)
  if err != nil {
    MPDReconnect()
    playlist, err = mpd_conn.PlaylistInfo(-1, -1)

    if err != nil {
      error(w, "Couldn't get the current playlist", err)
      return
    }
  }

  upcoming := playlist[pos + 1:]

  fmt.Fprintf(w, jsoniffy(upcoming))
}

func addSong(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  song, ok := r.Form["song"]
  if !ok {
    jsonError(w, "No song given", nil)
    return
  }

  err := mpd_conn.Add(song[0])
  ue := "unexpected response: "

  if err != nil {
    if err.Error()[:len(ue)] == ue {
      log.Println("add UE song error:", err)
      jsonError(w, "Unknown song", err)
      return
    }

    log.Println("add song error:", err)
    MPDReconnect()
    err = mpd_conn.Add(song[0])

    if err != nil {
      jsonError(w, "Couldn't add song to playlist.", err)
      return
    }
  }

  m := make(map[string]string)
  m["note"] = "Added song: " + song[0]
  fmt.Fprintf(w, jsoniffy(m))
}

/*******************
  Helper Functions
********************/

// Connect to MPD's control channel, and set the global mpd_conn to it.
func mpdConnect(url string) *mpd.Client {
  conn, err := mpd.Dial("tcp", url)

  // if we can't connect to MPD everything's fucked, nothing's going to work
  // kill all humans, and die, respectfully, after explaining what the issue
  // is.
  if err != nil {
    log.Println("\n\nServer quiting because it can't connect to MPD");
    log.Println(err)

    return nil
  }

  return conn
}

// helper struct; used to hold some ID3 info, plus an MPD file path.
type TBFile struct {id3.File; FilePath string;}

// helper method, returns a pointer to one of our helper structs (see above).
func id3Read(reader io.Reader, filePath string) *TBFile {
  id3File := id3.Read(reader)

  file := new(TBFile)
  file.Header = id3File.Header
  file.Name = id3File.Name
  file.Artist = id3File.Artist
  file.Album = id3File.Album
  file.Year = id3File.Year
  file.FilePath = filePath

  return file
}

func MPDReconnect() {
  mpd_conn.Close()
  mpd_conn = mpdConnect("localhost:6600")
}

// turn anything into JSON.
func jsoniffy(v interface {}) string {
  obj, err := json.MarshalIndent(v, "", "  ")
  if err != nil {
    log.Print("Couldn't turn something into JSON: ", v)
    log.Fatal(err)
  }

  return string(obj)
}

func error(w http.ResponseWriter, message string, err interface{Error() string;}) {
  log.Println("An error occured; telling the client.")
  log.Println("Message:", message)
  log.Println("Error:", err, "\n\n")

  fmt.Fprintf(w, message + "\n")
}

func jsonError(w http.ResponseWriter, message string, err interface{Error() string;}) {
  m := make(map[string]string)
  m["error"] = message
  error(w, jsoniffy(m), err)
}

<|endoftext|>