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

// Package hlc implements the Hybrid Logical Clock outlined in
// "Logical Physical Clocks and Consistent Snapshots in Globally
// Distributed Databases", available online at
// http://www.cse.buffalo.edu/tech-reports/2014-04.pdf.
package hlc

import (
	"sync"
	"sync/atomic"
	"time"

	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util/log"
)

// TODO(Tobias): Figure out if it would make sense to save some
// history of the physical clock and react if it jumps backwards
// repeatedly. This is expected during NTP updates, but may
// indicate a broken clock in some cases.

// Clock is a hybrid logical clock. Objects of this
// type model causality while maintaining a relation
// to physical time. Roughly speaking, timestamps
// consist of the largest wall clock time among all
// events, and a logical clock that ticks whenever
// an event happens in the future of the local physical
// clock.
// The data structure is thread safe and thus can safely
// be shared by multiple goroutines.
//
// See NewClock for details.
type Clock struct {
	physicalClock func() int64
	// Clock contains a mutex used to lock the below
	// fields while methods operate on them.
	sync.Mutex
	state roachpb.Timestamp
	// MaxOffset specifies how far ahead of the physical
	// clock (and cluster time) the wall time can be.
	// See SetMaxOffset.
	maxOffset time.Duration
}

// ManualClock is a convenience type to facilitate
// creating a hybrid logical clock whose physical clock
// is manually controlled. ManualClock is thread safe.
type ManualClock struct {
	nanos int64
}

// NewManualClock returns a new instance, initialized with
// specified timestamp.
func NewManualClock(nanos int64) *ManualClock {
	return &ManualClock{nanos: nanos}
}

// UnixNano returns the underlying manual clock's timestamp.
func (m *ManualClock) UnixNano() int64 {
	return atomic.LoadInt64(&m.nanos)
}

// Increment atomically increments the manual clock's timestamp.
func (m *ManualClock) Increment(incr int64) {
	atomic.AddInt64(&m.nanos, incr)
}

// Set atomically sets the manual clock's timestamp.
func (m *ManualClock) Set(nanos int64) {
	atomic.StoreInt64(&m.nanos, nanos)
}

// UnixNano returns the local machine's physical nanosecond
// unix epoch timestamp as a convenience to create a HLC via
// c := hlc.NewClock(hlc.UnixNano).
func UnixNano() int64 {
	return time.Now().UnixNano()
}

// NewClock creates a new hybrid logical clock associated
// with the given physical clock, initializing both wall time
// and logical time with zero.
//
// The physical clock is typically given by the wall time
// of the local machine in unix epoch nanoseconds, using
// hlc.UnixNano. This is not a requirement.
func NewClock(physicalClock func() int64) *Clock {
	return &Clock{
		physicalClock: physicalClock,
	}
}

// SetMaxOffset sets the maximal offset of the physical clock from the cluster.
// It is used to set the max offset a call to Update may cause and to ensure
// an upperbound on timestamp WallTime in transactions. A well-chosen value is
// large enough to ignore a reasonable amount of clock skew but will prevent
// ill-configured nodes from dramatically skewing the wall time of the clock
// into the future.
//
// A value of zero disables all safety features.
// The default value for a new instance is zero.
func (c *Clock) SetMaxOffset(delta time.Duration) {
	c.Lock()
	defer c.Unlock()
	c.maxOffset = delta
}

// MaxOffset returns the maximal offset allowed.
// A value of 0 means offset checking is disabled.
// See SetMaxOffset for details.
func (c *Clock) MaxOffset() time.Duration {
	c.Lock()
	defer c.Unlock()
	return c.maxOffset
}

// Timestamp returns a copy of the clock's current timestamp,
// without performing a clock adjustment.
func (c *Clock) Timestamp() roachpb.Timestamp {
	c.Lock()
	defer c.Unlock()
	return c.timestamp()
}

// timestamp returns the state as a timestamp, without
// a lock on the clock's state, for internal usage.
func (c *Clock) timestamp() roachpb.Timestamp {
	return roachpb.Timestamp{
		WallTime: c.state.WallTime,
		Logical:  c.state.Logical,
	}
}

// Now returns a timestamp associated with an event from
// the local machine that may be sent to other members
// of the distributed network. This is the counterpart
// of Update, which is passed a timestamp received from
// another member of the distributed network.
func (c *Clock) Now() roachpb.Timestamp {
	c.Lock()
	defer c.Unlock()

	physicalClock := c.physicalClock()
	if c.state.WallTime >= physicalClock {
		// The wall time is ahead, so the logical clock ticks.
		c.state.Logical++
	} else {
		// Use the physical clock, and reset the logical one.
		c.state.WallTime = physicalClock
		c.state.Logical = 0
	}
	return c.timestamp()
}

// PhysicalNow returns the local wall time. It corresponds to the physicalClock
// provided at instantiation. For a timestamp value, use Now() instead.
func (c *Clock) PhysicalNow() int64 {
	wallTime := c.physicalClock()
	return wallTime
}

// PhysicalTime returns a time.Time struct using the local wall time.
func (c *Clock) PhysicalTime() time.Time {
	return time.Unix(0, c.PhysicalNow())
}

// Update takes a hybrid timestamp, usually originating from
// an event received from another member of a distributed
// system. The clock is updated and the hybrid timestamp
// associated to the receipt of the event returned.
// An error may only occur if offset checking is active and
// the remote timestamp was rejected due to clock offset,
// in which case the state of the clock will not have been
// altered.
// To timestamp events of local origin, use Now instead.
func (c *Clock) Update(rt roachpb.Timestamp) roachpb.Timestamp {
	c.Lock()
	defer c.Unlock()
	physicalClock := c.physicalClock()

	if physicalClock > c.state.WallTime && physicalClock > rt.WallTime {
		// Our physical clock is ahead of both wall times. It is used
		// as the new wall time and the logical clock is reset.
		c.state.WallTime = physicalClock
		c.state.Logical = 0
		return c.timestamp()
	}

	// In the remaining cases, our physical clock plays no role
	// as it is behind the local and remote wall times. Instead,
	// the logical clock comes into play.
	if rt.WallTime > c.state.WallTime {
		if c.maxOffset.Nanoseconds() > 0 &&
			rt.WallTime-physicalClock > c.maxOffset.Nanoseconds() {
			// The remote wall time is too far ahead to be trustworthy.
			log.Errorf("Remote wall time offsets from local physical clock: %d (%dns ahead)",
				rt.WallTime, rt.WallTime-physicalClock)
		}
		// The remote clock is ahead of ours, and we update
		// our own logical clock with theirs.
		c.state.WallTime = rt.WallTime
		c.state.Logical = rt.Logical + 1
	} else if c.state.WallTime > rt.WallTime {
		// Our wall time is larger, so it remains but we tick
		// the logical clock.
		c.state.Logical++
	} else {
		// Both wall times are equal, and the larger logical
		// clock is used for the update.
		if rt.Logical > c.state.Logical {
			c.state.Logical = rt.Logical
		}
		c.state.Logical++
	}
	return c.timestamp()
}
<commit_msg>hlc: print a better message and change to warning<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Tobias Schottdorf (tobias.schottdorf@gmail.com)

// Package hlc implements the Hybrid Logical Clock outlined in
// "Logical Physical Clocks and Consistent Snapshots in Globally
// Distributed Databases", available online at
// http://www.cse.buffalo.edu/tech-reports/2014-04.pdf.
package hlc

import (
	"sync"
	"sync/atomic"
	"time"

	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util/log"
)

// TODO(Tobias): Figure out if it would make sense to save some
// history of the physical clock and react if it jumps backwards
// repeatedly. This is expected during NTP updates, but may
// indicate a broken clock in some cases.

// Clock is a hybrid logical clock. Objects of this
// type model causality while maintaining a relation
// to physical time. Roughly speaking, timestamps
// consist of the largest wall clock time among all
// events, and a logical clock that ticks whenever
// an event happens in the future of the local physical
// clock.
// The data structure is thread safe and thus can safely
// be shared by multiple goroutines.
//
// See NewClock for details.
type Clock struct {
	physicalClock func() int64
	// Clock contains a mutex used to lock the below
	// fields while methods operate on them.
	sync.Mutex
	state roachpb.Timestamp
	// MaxOffset specifies how far ahead of the physical
	// clock (and cluster time) the wall time can be.
	// See SetMaxOffset.
	maxOffset time.Duration
}

// ManualClock is a convenience type to facilitate
// creating a hybrid logical clock whose physical clock
// is manually controlled. ManualClock is thread safe.
type ManualClock struct {
	nanos int64
}

// NewManualClock returns a new instance, initialized with
// specified timestamp.
func NewManualClock(nanos int64) *ManualClock {
	return &ManualClock{nanos: nanos}
}

// UnixNano returns the underlying manual clock's timestamp.
func (m *ManualClock) UnixNano() int64 {
	return atomic.LoadInt64(&m.nanos)
}

// Increment atomically increments the manual clock's timestamp.
func (m *ManualClock) Increment(incr int64) {
	atomic.AddInt64(&m.nanos, incr)
}

// Set atomically sets the manual clock's timestamp.
func (m *ManualClock) Set(nanos int64) {
	atomic.StoreInt64(&m.nanos, nanos)
}

// UnixNano returns the local machine's physical nanosecond
// unix epoch timestamp as a convenience to create a HLC via
// c := hlc.NewClock(hlc.UnixNano).
func UnixNano() int64 {
	return time.Now().UnixNano()
}

// NewClock creates a new hybrid logical clock associated
// with the given physical clock, initializing both wall time
// and logical time with zero.
//
// The physical clock is typically given by the wall time
// of the local machine in unix epoch nanoseconds, using
// hlc.UnixNano. This is not a requirement.
func NewClock(physicalClock func() int64) *Clock {
	return &Clock{
		physicalClock: physicalClock,
	}
}

// SetMaxOffset sets the maximal offset of the physical clock from the cluster.
// It is used to set the max offset a call to Update may cause and to ensure
// an upperbound on timestamp WallTime in transactions. A well-chosen value is
// large enough to ignore a reasonable amount of clock skew but will prevent
// ill-configured nodes from dramatically skewing the wall time of the clock
// into the future.
//
// A value of zero disables all safety features.
// The default value for a new instance is zero.
func (c *Clock) SetMaxOffset(delta time.Duration) {
	c.Lock()
	defer c.Unlock()
	c.maxOffset = delta
}

// MaxOffset returns the maximal offset allowed.
// A value of 0 means offset checking is disabled.
// See SetMaxOffset for details.
func (c *Clock) MaxOffset() time.Duration {
	c.Lock()
	defer c.Unlock()
	return c.maxOffset
}

// Timestamp returns a copy of the clock's current timestamp,
// without performing a clock adjustment.
func (c *Clock) Timestamp() roachpb.Timestamp {
	c.Lock()
	defer c.Unlock()
	return c.timestamp()
}

// timestamp returns the state as a timestamp, without
// a lock on the clock's state, for internal usage.
func (c *Clock) timestamp() roachpb.Timestamp {
	return roachpb.Timestamp{
		WallTime: c.state.WallTime,
		Logical:  c.state.Logical,
	}
}

// Now returns a timestamp associated with an event from
// the local machine that may be sent to other members
// of the distributed network. This is the counterpart
// of Update, which is passed a timestamp received from
// another member of the distributed network.
func (c *Clock) Now() roachpb.Timestamp {
	c.Lock()
	defer c.Unlock()

	physicalClock := c.physicalClock()
	if c.state.WallTime >= physicalClock {
		// The wall time is ahead, so the logical clock ticks.
		c.state.Logical++
	} else {
		// Use the physical clock, and reset the logical one.
		c.state.WallTime = physicalClock
		c.state.Logical = 0
	}
	return c.timestamp()
}

// PhysicalNow returns the local wall time. It corresponds to the physicalClock
// provided at instantiation. For a timestamp value, use Now() instead.
func (c *Clock) PhysicalNow() int64 {
	wallTime := c.physicalClock()
	return wallTime
}

// PhysicalTime returns a time.Time struct using the local wall time.
func (c *Clock) PhysicalTime() time.Time {
	return time.Unix(0, c.PhysicalNow())
}

// Update takes a hybrid timestamp, usually originating from
// an event received from another member of a distributed
// system. The clock is updated and the hybrid timestamp
// associated to the receipt of the event returned.
// An error may only occur if offset checking is active and
// the remote timestamp was rejected due to clock offset,
// in which case the state of the clock will not have been
// altered.
// To timestamp events of local origin, use Now instead.
func (c *Clock) Update(rt roachpb.Timestamp) roachpb.Timestamp {
	c.Lock()
	defer c.Unlock()
	physicalClock := c.physicalClock()

	if physicalClock > c.state.WallTime && physicalClock > rt.WallTime {
		// Our physical clock is ahead of both wall times. It is used
		// as the new wall time and the logical clock is reset.
		c.state.WallTime = physicalClock
		c.state.Logical = 0
		return c.timestamp()
	}

	// In the remaining cases, our physical clock plays no role
	// as it is behind the local and remote wall times. Instead,
	// the logical clock comes into play.
	if rt.WallTime > c.state.WallTime {
		offset := time.Duration(rt.WallTime-physicalClock) * time.Nanosecond
		if c.maxOffset > 0 && offset > c.maxOffset {
			log.Warningf("remote wall time is too far ahead (%s) to be trustworthy - updating anyway", offset)
		}
		// The remote clock is ahead of ours, and we update
		// our own logical clock with theirs.
		c.state.WallTime = rt.WallTime
		c.state.Logical = rt.Logical + 1
	} else if c.state.WallTime > rt.WallTime {
		// Our wall time is larger, so it remains but we tick
		// the logical clock.
		c.state.Logical++
	} else {
		// Both wall times are equal, and the larger logical
		// clock is used for the update.
		if rt.Logical > c.state.Logical {
			c.state.Logical = rt.Logical
		}
		c.state.Logical++
	}
	return c.timestamp()
}
<|endoftext|>


<commit_before>// Copyright 2018 Istio 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 server

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/time/rate"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"

	mcp "istio.io/api/mcp/v1alpha1"
	"istio.io/pkg/ctrlz"
	"istio.io/pkg/ctrlz/fw"
	"istio.io/pkg/log"
	"istio.io/pkg/probe"
	"istio.io/pkg/version"

	"istio.io/istio/galley/pkg/meshconfig"
	"istio.io/istio/galley/pkg/metadata"
	kubeMeta "istio.io/istio/galley/pkg/metadata/kube"
	"istio.io/istio/galley/pkg/runtime"
	"istio.io/istio/galley/pkg/runtime/groups"
	"istio.io/istio/galley/pkg/runtime/resource"
	"istio.io/istio/galley/pkg/source/fs"
	kubeSource "istio.io/istio/galley/pkg/source/kube"
	"istio.io/istio/galley/pkg/source/kube/builtin"
	"istio.io/istio/galley/pkg/source/kube/client"
	"istio.io/istio/galley/pkg/source/kube/dynamic/converter"
	"istio.io/istio/galley/pkg/source/kube/schema"
	"istio.io/istio/galley/pkg/source/kube/schema/check"
	configz "istio.io/istio/pkg/mcp/configz/server"
	"istio.io/istio/pkg/mcp/creds"
	"istio.io/istio/pkg/mcp/monitoring"
	mcprate "istio.io/istio/pkg/mcp/rate"
	"istio.io/istio/pkg/mcp/server"
	"istio.io/istio/pkg/mcp/snapshot"
	"istio.io/istio/pkg/mcp/source"
)

var scope = log.RegisterScope("server", "Galley server debugging", 0)

// Server is the main entry point into the Galley code.
type Server struct {
	serveWG       sync.WaitGroup
	grpcServer    *grpc.Server
	processor     *runtime.Processor
	mcpSource     *source.Server
	reporter      monitoring.Reporter
	listenerMutex sync.Mutex
	listener      net.Listener
	controlZ      *ctrlz.Server
	stopCh        chan struct{}
	callOut       *callout
}

type patchTable struct {
	newKubeFromConfigFile       func(string) (client.Interfaces, error)
	verifyResourceTypesPresence func(client.Interfaces, []schema.ResourceSpec) ([]schema.ResourceSpec, error)
	findSupportedResources      func(client.Interfaces, []schema.ResourceSpec) ([]schema.ResourceSpec, error)
	newSource                   func(client.Interfaces, time.Duration, *schema.Instance, *converter.Config) (runtime.Source, error)
	netListen                   func(network, address string) (net.Listener, error)
	newMeshConfigCache          func(path string) (meshconfig.Cache, error)
	mcpMetricReporter           func(string) monitoring.Reporter
	fsNew                       func(string, *schema.Instance, *converter.Config) (runtime.Source, error)
}

func defaultPatchTable() patchTable {
	return patchTable{
		newKubeFromConfigFile:       client.NewKubeFromConfigFile,
		verifyResourceTypesPresence: check.ResourceTypesPresence,
		findSupportedResources:      check.FindSupportedResourceSchemas,
		newSource:                   kubeSource.New,
		netListen:                   net.Listen,
		mcpMetricReporter:           func(prefix string) monitoring.Reporter { return monitoring.NewStatsContext(prefix) },
		newMeshConfigCache:          func(path string) (meshconfig.Cache, error) { return meshconfig.NewCacheFromFile(path) },
		fsNew:                       fs.New,
	}
}

// New returns a new instance of a Server.
func New(a *Args) (*Server, error) {
	return newServer(a, defaultPatchTable())
}

func newServer(a *Args, p patchTable) (*Server, error) {
	var err error
	s := &Server{}

	defer func() {
		// If returns with error, need to close the server.
		if err != nil {
			_ = s.Close()
		}
	}()

	mesh, err := p.newMeshConfigCache(a.MeshConfigFile)
	if err != nil {
		return nil, err
	}
	converterCfg := &converter.Config{
		Mesh:         mesh,
		DomainSuffix: a.DomainSuffix,
	}

	sourceSchema := getSourceSchema(a)

	var src runtime.Source
	if a.ConfigPath != "" {
		src, err = p.fsNew(a.ConfigPath, sourceSchema, converterCfg)
		if err != nil {
			return nil, err
		}
	} else {
		k, err := p.newKubeFromConfigFile(a.KubeConfig)
		if err != nil {
			return nil, err
		}
		var found []schema.ResourceSpec

		if !a.DisableResourceReadyCheck {
			found, err = p.verifyResourceTypesPresence(k, sourceSchema.All())
		} else {
			found, err = p.findSupportedResources(k, sourceSchema.All())
		}
		if err != nil {
			return nil, err
		}
		sourceSchema = schema.New(found...)
		src, err = p.newSource(k, a.ResyncPeriod, sourceSchema, converterCfg)
		if err != nil {
			return nil, err
		}
	}

	types := getMCPTypes(a)

	processorCfg := runtime.Config{
		DomainSuffix:             a.DomainSuffix,
		Mesh:                     mesh,
		Schema:                   types,
		SynthesizeServiceEntries: a.EnableServiceDiscovery,
	}
	distributor := snapshot.New(groups.IndexFunction)
	s.processor = runtime.NewProcessor(src, distributor, &processorCfg)

	var grpcOptions []grpc.ServerOption
	grpcOptions = append(grpcOptions,
		grpc.MaxConcurrentStreams(uint32(a.MaxConcurrentStreams)),
		grpc.MaxRecvMsgSize(int(a.MaxReceivedMessageSize)),
		grpc.InitialWindowSize(int32(a.InitialWindowSize)),
		grpc.InitialConnWindowSize(int32(a.InitialConnectionWindowSize)),
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Timeout:               a.KeepAlive.Timeout,
			Time:                  a.KeepAlive.Time,
			MaxConnectionAge:      a.KeepAlive.MaxServerConnectionAge,
			MaxConnectionAgeGrace: a.KeepAlive.MaxServerConnectionAgeGrace,
		}))

	s.stopCh = make(chan struct{})
	var checker source.AuthChecker = server.NewAllowAllChecker()
	if !a.Insecure {
		checker, err = watchAccessList(s.stopCh, a.AccessListFile)
		if err != nil {
			return nil, err
		}

		watcher, err := creds.PollFiles(s.stopCh, a.CredentialOptions)
		if err != nil {
			return nil, err
		}
		credentials := creds.CreateForServer(watcher)

		grpcOptions = append(grpcOptions, grpc.Creds(credentials))
	}
	grpc.EnableTracing = a.EnableGRPCTracing
	s.grpcServer = grpc.NewServer(grpcOptions...)

	s.reporter = p.mcpMetricReporter("galley/mcp/source")

	options := &source.Options{
		Watcher:            distributor,
		Reporter:           s.reporter,
		CollectionsOptions: source.CollectionOptionsFromSlice(types.Collections()),
		ConnRateLimiter:    mcprate.NewRateLimiter(time.Second, 100), // TODO(Nino-K): https://github.com/istio/istio/issues/12074
	}

	if a.SinkAddress != "" {
		s.callOut, err = newCallout(a.SinkAddress, a.SinkAuthMode, a.SinkMeta, options)
		if err != nil {
			s.callOut = nil
			scope.Fatalf("Callout could not be initialized: %v", err)
		}
	}

	serverOptions := &source.ServerOptions{
		AuthChecker: checker,
		RateLimiter: rate.NewLimiter(rate.Every(time.Second), 100), // TODO(Nino-K): https://github.com/istio/istio/issues/12074
	}

	s.mcpSource = source.NewServer(options, serverOptions)

	// get the network stuff setup
	network := "tcp"
	var address string
	idx := strings.Index(a.APIAddress, "://")
	if idx < 0 {
		address = a.APIAddress
	} else {
		network = a.APIAddress[:idx]
		address = a.APIAddress[idx+3:]
	}

	if s.listener, err = p.netListen(network, address); err != nil {
		return nil, fmt.Errorf("unable to listen: %v", err)
	}

	mcp.RegisterResourceSourceServer(s.grpcServer, s.mcpSource)

	s.controlZ, _ = ctrlz.Run(a.IntrospectionOptions, []fw.Topic{configz.CreateTopic(distributor)})

	return s, nil
}

func getSourceSchema(a *Args) *schema.Instance {
	b := schema.NewBuilder()
	for _, spec := range kubeMeta.Types.All() {

		if a.EnableServiceDiscovery {
			// TODO: IsBuiltIn is a proxy for types needed for service discovery
			if builtin.IsBuiltIn(spec.Kind) {
				b.Add(spec)
				continue
			}
		}

		if !isKindExcluded(a, spec.Kind) {
			b.Add(spec)
		}
	}
	return b.Build()
}

func isKindExcluded(a *Args, kind string) bool {
	for _, excludedKind := range a.ExcludedResourceKinds {
		if kind == excludedKind {
			return true
		}
	}
	return false
}

func getMCPTypes(a *Args) *resource.Schema {
	b := resource.NewSchemaBuilder()
	b.RegisterSchema(metadata.Types)
	b.Register(
		"istio/mesh/v1alpha1/MeshConfig",
		"type.googleapis.com/istio.mesh.v1alpha1.MeshConfig")

	for _, k := range a.ExcludedResourceKinds {
		spec := kubeMeta.Types.Get(k)
		if spec != nil {
			b.UnregisterInfo(spec.Target)
		}
	}

	return b.Build()
}

// Run enables Galley to start receiving gRPC requests on its main API port.
func (s *Server) Run() {
	s.serveWG.Add(1)
	go func() {
		defer s.serveWG.Done()
		err := s.processor.Start()
		if err != nil {
			scope.Errorf("Galley Server unexpectedly terminated: %v", err)
			return
		}

		l := s.getListener()
		if l != nil {
			// start serving
			err = s.grpcServer.Serve(l)
			if err != nil {
				scope.Errorf("Galley Server unexpectedly terminated: %v", err)
			}
		}
	}()
	if s.callOut != nil {
		s.serveWG.Add(1)
		go func() {
			defer s.serveWG.Done()
			s.callOut.Run()
		}()
	}
}

func (s *Server) getListener() net.Listener {
	s.listenerMutex.Lock()
	defer s.listenerMutex.Unlock()
	return s.listener
}

// Address returns the Address of the MCP service.
func (s *Server) Address() net.Addr {
	l := s.getListener()
	if l == nil {
		return nil
	}
	return l.Addr()
}

// Close cleans up resources used by the server.
func (s *Server) Close() error {
	if s.stopCh != nil {
		close(s.stopCh)
		s.stopCh = nil
	}

	if s.grpcServer != nil {
		s.grpcServer.GracefulStop()
	}

	if s.controlZ != nil {
		s.controlZ.Close()
	}

	if s.processor != nil {
		s.processor.Stop()
	}

	s.listenerMutex.Lock()
	if s.listener != nil {
		_ = s.listener.Close()
		s.listener = nil
	}
	s.listenerMutex.Unlock()

	if s.reporter != nil {
		_ = s.reporter.Close()
	}

	if s.callOut != nil {
		s.callOut.Close()
	}

	if s.grpcServer != nil || s.callOut != nil {
		s.serveWG.Wait()
	}

	// final attempt to purge buffered logs
	_ = log.Sync()

	return nil
}

//RunServer start Galley Server mode
func RunServer(sa *Args, livenessProbeController,
	readinessProbeController probe.Controller) {
	log.Infof("Galley started with %s", sa)
	s, err := New(sa)
	if err != nil {
		log.Fatalf("Unable to initialize Galley Server: %v", err)
	}
	log.Infof("Istio Galley: %s\nStarting gRPC server on %v", version.Info, sa.APIAddress)
	s.Run()
	if livenessProbeController != nil {
		serverLivenessProbe := probe.NewProbe()
		serverLivenessProbe.SetAvailable(nil)
		serverLivenessProbe.RegisterProbe(livenessProbeController, "serverLiveness")
		defer serverLivenessProbe.SetAvailable(errors.New("stopped"))
	}
	if readinessProbeController != nil {
		serverReadinessProbe := probe.NewProbe()
		serverReadinessProbe.SetAvailable(nil)
		serverReadinessProbe.RegisterProbe(readinessProbeController, "serverReadiness")
		defer serverReadinessProbe.SetAvailable(errors.New("stopped"))
	}

	s.serveWG.Wait()
	_ = s.Close()
}
<commit_msg>Relax keepalive enforcement policy to avoid dropping connections under load (#15141)<commit_after>// Copyright 2018 Istio 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 server

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"

	"golang.org/x/time/rate"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"

	mcp "istio.io/api/mcp/v1alpha1"
	"istio.io/pkg/ctrlz"
	"istio.io/pkg/ctrlz/fw"
	"istio.io/pkg/log"
	"istio.io/pkg/probe"
	"istio.io/pkg/version"

	"istio.io/istio/galley/pkg/meshconfig"
	"istio.io/istio/galley/pkg/metadata"
	kubeMeta "istio.io/istio/galley/pkg/metadata/kube"
	"istio.io/istio/galley/pkg/runtime"
	"istio.io/istio/galley/pkg/runtime/groups"
	"istio.io/istio/galley/pkg/runtime/resource"
	"istio.io/istio/galley/pkg/source/fs"
	kubeSource "istio.io/istio/galley/pkg/source/kube"
	"istio.io/istio/galley/pkg/source/kube/builtin"
	"istio.io/istio/galley/pkg/source/kube/client"
	"istio.io/istio/galley/pkg/source/kube/dynamic/converter"
	"istio.io/istio/galley/pkg/source/kube/schema"
	"istio.io/istio/galley/pkg/source/kube/schema/check"
	configz "istio.io/istio/pkg/mcp/configz/server"
	"istio.io/istio/pkg/mcp/creds"
	"istio.io/istio/pkg/mcp/monitoring"
	mcprate "istio.io/istio/pkg/mcp/rate"
	"istio.io/istio/pkg/mcp/server"
	"istio.io/istio/pkg/mcp/snapshot"
	"istio.io/istio/pkg/mcp/source"
)

var scope = log.RegisterScope("server", "Galley server debugging", 0)

// Server is the main entry point into the Galley code.
type Server struct {
	serveWG       sync.WaitGroup
	grpcServer    *grpc.Server
	processor     *runtime.Processor
	mcpSource     *source.Server
	reporter      monitoring.Reporter
	listenerMutex sync.Mutex
	listener      net.Listener
	controlZ      *ctrlz.Server
	stopCh        chan struct{}
	callOut       *callout
}

type patchTable struct {
	newKubeFromConfigFile       func(string) (client.Interfaces, error)
	verifyResourceTypesPresence func(client.Interfaces, []schema.ResourceSpec) ([]schema.ResourceSpec, error)
	findSupportedResources      func(client.Interfaces, []schema.ResourceSpec) ([]schema.ResourceSpec, error)
	newSource                   func(client.Interfaces, time.Duration, *schema.Instance, *converter.Config) (runtime.Source, error)
	netListen                   func(network, address string) (net.Listener, error)
	newMeshConfigCache          func(path string) (meshconfig.Cache, error)
	mcpMetricReporter           func(string) monitoring.Reporter
	fsNew                       func(string, *schema.Instance, *converter.Config) (runtime.Source, error)
}

func defaultPatchTable() patchTable {
	return patchTable{
		newKubeFromConfigFile:       client.NewKubeFromConfigFile,
		verifyResourceTypesPresence: check.ResourceTypesPresence,
		findSupportedResources:      check.FindSupportedResourceSchemas,
		newSource:                   kubeSource.New,
		netListen:                   net.Listen,
		mcpMetricReporter:           func(prefix string) monitoring.Reporter { return monitoring.NewStatsContext(prefix) },
		newMeshConfigCache:          func(path string) (meshconfig.Cache, error) { return meshconfig.NewCacheFromFile(path) },
		fsNew:                       fs.New,
	}
}

// New returns a new instance of a Server.
func New(a *Args) (*Server, error) {
	return newServer(a, defaultPatchTable())
}

func newServer(a *Args, p patchTable) (*Server, error) {
	var err error
	s := &Server{}

	defer func() {
		// If returns with error, need to close the server.
		if err != nil {
			_ = s.Close()
		}
	}()

	mesh, err := p.newMeshConfigCache(a.MeshConfigFile)
	if err != nil {
		return nil, err
	}
	converterCfg := &converter.Config{
		Mesh:         mesh,
		DomainSuffix: a.DomainSuffix,
	}

	sourceSchema := getSourceSchema(a)

	var src runtime.Source
	if a.ConfigPath != "" {
		src, err = p.fsNew(a.ConfigPath, sourceSchema, converterCfg)
		if err != nil {
			return nil, err
		}
	} else {
		k, err := p.newKubeFromConfigFile(a.KubeConfig)
		if err != nil {
			return nil, err
		}
		var found []schema.ResourceSpec

		if !a.DisableResourceReadyCheck {
			found, err = p.verifyResourceTypesPresence(k, sourceSchema.All())
		} else {
			found, err = p.findSupportedResources(k, sourceSchema.All())
		}
		if err != nil {
			return nil, err
		}
		sourceSchema = schema.New(found...)
		src, err = p.newSource(k, a.ResyncPeriod, sourceSchema, converterCfg)
		if err != nil {
			return nil, err
		}
	}

	types := getMCPTypes(a)

	processorCfg := runtime.Config{
		DomainSuffix:             a.DomainSuffix,
		Mesh:                     mesh,
		Schema:                   types,
		SynthesizeServiceEntries: a.EnableServiceDiscovery,
	}
	distributor := snapshot.New(groups.IndexFunction)
	s.processor = runtime.NewProcessor(src, distributor, &processorCfg)

	var grpcOptions []grpc.ServerOption
	grpcOptions = append(grpcOptions,
		grpc.MaxConcurrentStreams(uint32(a.MaxConcurrentStreams)),
		grpc.MaxRecvMsgSize(int(a.MaxReceivedMessageSize)),
		grpc.InitialWindowSize(int32(a.InitialWindowSize)),
		grpc.InitialConnWindowSize(int32(a.InitialConnectionWindowSize)),
		grpc.KeepaliveParams(keepalive.ServerParameters{
			Timeout:               a.KeepAlive.Timeout,
			Time:                  a.KeepAlive.Time,
			MaxConnectionAge:      a.KeepAlive.MaxServerConnectionAge,
			MaxConnectionAgeGrace: a.KeepAlive.MaxServerConnectionAgeGrace,
		}),
		// Relax keepalive enforcement policy requirements to avoid dropping connections due to too many pings.
		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
			MinTime:             time.Minute,
			PermitWithoutStream: true,
		}),
	)

	s.stopCh = make(chan struct{})
	var checker source.AuthChecker = server.NewAllowAllChecker()
	if !a.Insecure {
		checker, err = watchAccessList(s.stopCh, a.AccessListFile)
		if err != nil {
			return nil, err
		}

		watcher, err := creds.PollFiles(s.stopCh, a.CredentialOptions)
		if err != nil {
			return nil, err
		}
		credentials := creds.CreateForServer(watcher)

		grpcOptions = append(grpcOptions, grpc.Creds(credentials))
	}
	grpc.EnableTracing = a.EnableGRPCTracing
	s.grpcServer = grpc.NewServer(grpcOptions...)

	s.reporter = p.mcpMetricReporter("galley/mcp/source")

	options := &source.Options{
		Watcher:            distributor,
		Reporter:           s.reporter,
		CollectionsOptions: source.CollectionOptionsFromSlice(types.Collections()),
		ConnRateLimiter:    mcprate.NewRateLimiter(time.Second, 100), // TODO(Nino-K): https://github.com/istio/istio/issues/12074
	}

	if a.SinkAddress != "" {
		s.callOut, err = newCallout(a.SinkAddress, a.SinkAuthMode, a.SinkMeta, options)
		if err != nil {
			s.callOut = nil
			scope.Fatalf("Callout could not be initialized: %v", err)
		}
	}

	serverOptions := &source.ServerOptions{
		AuthChecker: checker,
		RateLimiter: rate.NewLimiter(rate.Every(time.Second), 100), // TODO(Nino-K): https://github.com/istio/istio/issues/12074
	}

	s.mcpSource = source.NewServer(options, serverOptions)

	// get the network stuff setup
	network := "tcp"
	var address string
	idx := strings.Index(a.APIAddress, "://")
	if idx < 0 {
		address = a.APIAddress
	} else {
		network = a.APIAddress[:idx]
		address = a.APIAddress[idx+3:]
	}

	if s.listener, err = p.netListen(network, address); err != nil {
		return nil, fmt.Errorf("unable to listen: %v", err)
	}

	mcp.RegisterResourceSourceServer(s.grpcServer, s.mcpSource)

	s.controlZ, _ = ctrlz.Run(a.IntrospectionOptions, []fw.Topic{configz.CreateTopic(distributor)})

	return s, nil
}

func getSourceSchema(a *Args) *schema.Instance {
	b := schema.NewBuilder()
	for _, spec := range kubeMeta.Types.All() {

		if a.EnableServiceDiscovery {
			// TODO: IsBuiltIn is a proxy for types needed for service discovery
			if builtin.IsBuiltIn(spec.Kind) {
				b.Add(spec)
				continue
			}
		}

		if !isKindExcluded(a, spec.Kind) {
			b.Add(spec)
		}
	}
	return b.Build()
}

func isKindExcluded(a *Args, kind string) bool {
	for _, excludedKind := range a.ExcludedResourceKinds {
		if kind == excludedKind {
			return true
		}
	}
	return false
}

func getMCPTypes(a *Args) *resource.Schema {
	b := resource.NewSchemaBuilder()
	b.RegisterSchema(metadata.Types)
	b.Register(
		"istio/mesh/v1alpha1/MeshConfig",
		"type.googleapis.com/istio.mesh.v1alpha1.MeshConfig")

	for _, k := range a.ExcludedResourceKinds {
		spec := kubeMeta.Types.Get(k)
		if spec != nil {
			b.UnregisterInfo(spec.Target)
		}
	}

	return b.Build()
}

// Run enables Galley to start receiving gRPC requests on its main API port.
func (s *Server) Run() {
	s.serveWG.Add(1)
	go func() {
		defer s.serveWG.Done()
		err := s.processor.Start()
		if err != nil {
			scope.Errorf("Galley Server unexpectedly terminated: %v", err)
			return
		}

		l := s.getListener()
		if l != nil {
			// start serving
			err = s.grpcServer.Serve(l)
			if err != nil {
				scope.Errorf("Galley Server unexpectedly terminated: %v", err)
			}
		}
	}()
	if s.callOut != nil {
		s.serveWG.Add(1)
		go func() {
			defer s.serveWG.Done()
			s.callOut.Run()
		}()
	}
}

func (s *Server) getListener() net.Listener {
	s.listenerMutex.Lock()
	defer s.listenerMutex.Unlock()
	return s.listener
}

// Address returns the Address of the MCP service.
func (s *Server) Address() net.Addr {
	l := s.getListener()
	if l == nil {
		return nil
	}
	return l.Addr()
}

// Close cleans up resources used by the server.
func (s *Server) Close() error {
	if s.stopCh != nil {
		close(s.stopCh)
		s.stopCh = nil
	}

	if s.grpcServer != nil {
		s.grpcServer.GracefulStop()
	}

	if s.controlZ != nil {
		s.controlZ.Close()
	}

	if s.processor != nil {
		s.processor.Stop()
	}

	s.listenerMutex.Lock()
	if s.listener != nil {
		_ = s.listener.Close()
		s.listener = nil
	}
	s.listenerMutex.Unlock()

	if s.reporter != nil {
		_ = s.reporter.Close()
	}

	if s.callOut != nil {
		s.callOut.Close()
	}

	if s.grpcServer != nil || s.callOut != nil {
		s.serveWG.Wait()
	}

	// final attempt to purge buffered logs
	_ = log.Sync()

	return nil
}

//RunServer start Galley Server mode
func RunServer(sa *Args, livenessProbeController,
	readinessProbeController probe.Controller) {
	log.Infof("Galley started with %s", sa)
	s, err := New(sa)
	if err != nil {
		log.Fatalf("Unable to initialize Galley Server: %v", err)
	}
	log.Infof("Istio Galley: %s\nStarting gRPC server on %v", version.Info, sa.APIAddress)
	s.Run()
	if livenessProbeController != nil {
		serverLivenessProbe := probe.NewProbe()
		serverLivenessProbe.SetAvailable(nil)
		serverLivenessProbe.RegisterProbe(livenessProbeController, "serverLiveness")
		defer serverLivenessProbe.SetAvailable(errors.New("stopped"))
	}
	if readinessProbeController != nil {
		serverReadinessProbe := probe.NewProbe()
		serverReadinessProbe.SetAvailable(nil)
		serverReadinessProbe.RegisterProbe(readinessProbeController, "serverReadiness")
		defer serverReadinessProbe.SetAvailable(errors.New("stopped"))
	}

	s.serveWG.Wait()
	_ = s.Close()
}
<|endoftext|>


<commit_before>package publish

import (
	"fmt"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"

	"github.com/kidoman/embd"
	"github.com/siliconbeacon/iot/messages"
	"github.com/siliconbeacon/iot/mqtt"
	"github.com/siliconbeacon/iot/sensors/si70xx"

	MQTT "github.com/eclipse/paho.mqtt.golang"
)

func Weather(station string, i2cbus embd.I2CBus, mq MQTT.Client, shutdown chan bool) {

	sensor := si70xx.New(i2cbus)
	var (
		err      error
		serial   string
		model    string
		firmware string
	)
	if serial, err = sensor.SerialNumber(); err != nil {
		fmt.Println("Unable to initialize Si70xx sensor.")
		return
	}
	if model, err = sensor.ModelName(); err != nil {
		fmt.Println("Unable to initialize Si70xx sensor.")
		return
	}
	if firmware, err = sensor.FirmwareVersion(); err != nil {
		fmt.Println("Unable to initialize Si70xx sensor.")
		return
	}
	fmt.Println(model, "found. Serial:", serial, "running firmware version", firmware)

	// let's sample at 10Hz
	if err = sensor.Start(100 * time.Millisecond); err != nil {
		fmt.Println("Unable to commence sensor reads from Si70xx.")
		return
	}
	readings := sensor.Readings()
	var buffer [20]*si70xx.TemperatureAndHumidity
	sampleCount := 0
	for {
		select {
		case reading := <-readings:
			buffer[sampleCount] = reading
			sampleCount++
			if sampleCount == 20 {
				weatherBatch(station, mq, buffer[0:20])
				sampleCount = 0
			}
		case <-shutdown:
			sensor.Close()
			if sampleCount > 0 {
				weatherBatch(station, mq, buffer[0:sampleCount])
			}
			return
		}
	}
}

func weatherBatch(station string, mq MQTT.Client, readings []*si70xx.TemperatureAndHumidity) error {
	var msg []byte
	var err error
	if msg, err = serializeWeather(station, readings); err != nil {
		return err
	}
	topic := mqtt.CreateWeatherTopic(station)
	if token := mq.Publish(topic, 0, false, msg); token.Wait() && token.Error() != nil {
		return token.Error()
	}
	return nil
}

func serializeWeather(station string, readings []*si70xx.TemperatureAndHumidity) ([]byte, error) {
	baseTime := readings[0].Timestamp
	basePTime, _ := ptypes.TimestampProto(baseTime)
	msg := &messages.WeatherReadings{
		Device:   station,
		BaseTime: basePTime,
	}
	for _, reading := range readings {
		msg.Readings = append(msg.Readings, &messages.WeatherReadings_WeatherReading{
			RelativeTimeUs:             uint32(reading.Timestamp.Sub(baseTime) / time.Microsecond),
			TemperatureDegreesC:        reading.TemperatureDegreesCelsius,
			RelativeHumidityPercentage: reading.RelativeHumidityPercentage,
		})
	}
	fmt.Print("w")
	return proto.Marshal(msg)
}
<commit_msg>update to use latest protobuf messages<commit_after>package publish

import (
	"fmt"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes"

	"github.com/kidoman/embd"
	"github.com/siliconbeacon/iot/messages"
	"github.com/siliconbeacon/iot/mqtt"
	"github.com/siliconbeacon/iot/sensors/si70xx"

	MQTT "github.com/eclipse/paho.mqtt.golang"
)

func Weather(station string, i2cbus embd.I2CBus, mq MQTT.Client, shutdown chan bool) {

	sensor := si70xx.New(i2cbus)
	var (
		err      error
		serial   string
		model    string
		firmware string
	)
	if serial, err = sensor.SerialNumber(); err != nil {
		fmt.Println("Unable to initialize Si70xx sensor.")
		return
	}
	if model, err = sensor.ModelName(); err != nil {
		fmt.Println("Unable to initialize Si70xx sensor.")
		return
	}
	if firmware, err = sensor.FirmwareVersion(); err != nil {
		fmt.Println("Unable to initialize Si70xx sensor.")
		return
	}
	fmt.Println(model, "found. Serial:", serial, "running firmware version", firmware)

	// let's sample at 10Hz
	if err = sensor.Start(100 * time.Millisecond); err != nil {
		fmt.Println("Unable to commence sensor reads from Si70xx.")
		return
	}
	readings := sensor.Readings()
	var buffer [20]*si70xx.TemperatureAndHumidity
	sampleCount := 0
	for {
		select {
		case reading := <-readings:
			buffer[sampleCount] = reading
			sampleCount++
			if sampleCount == 20 {
				weatherBatch(station, mq, buffer[0:20])
				sampleCount = 0
			}
		case <-shutdown:
			sensor.Close()
			if sampleCount > 0 {
				weatherBatch(station, mq, buffer[0:sampleCount])
			}
			return
		}
	}
}

func weatherBatch(station string, mq MQTT.Client, readings []*si70xx.TemperatureAndHumidity) error {
	var msg []byte
	var err error
	if msg, err = serializeWeather(station, readings); err != nil {
		return err
	}
	topic := mqtt.CreateWeatherTopic(station)
	if token := mq.Publish(topic, 0, false, msg); token.Wait() && token.Error() != nil {
		return token.Error()
	}
	return nil
}

func serializeWeather(station string, readings []*si70xx.TemperatureAndHumidity) ([]byte, error) {
	baseTime := readings[0].Timestamp
	basePTime, _ := ptypes.TimestampProto(baseTime)
	msg := &messages.WeatherReadings{
		Device:   station,
		BaseTime: basePTime,
	}
	for _, reading := range readings {
		msg.Readings = append(msg.Readings, &messages.WeatherReading{
			RelativeTimeUs:             uint32(reading.Timestamp.Sub(baseTime) / time.Microsecond),
			TemperatureDegreesC:        reading.TemperatureDegreesCelsius,
			RelativeHumidityPercentage: reading.RelativeHumidityPercentage,
		})
	}
	fmt.Print("w")
	return proto.Marshal(msg)
}
<|endoftext|>


<commit_before>package sentinel

import (
	"github.com/mdevilliers/redishappy/types"
	"time"
)

type SentinelTopology struct{
	Sentinels map[string] *SentinelInfo
}

type SentinelInfo struct{
	SentinelLocation string
	LastUpdated time.Time
	KnownClusters []string
	State int
}

type TopologyRequest struct{
	ReplyChannel chan SentinelTopology
}

type SentinelEvent interface{
	GetSentinel() *types.Sentinel
}

type SentinelAdded struct{
	sentinel *types.Sentinel
}

type SentinelLost struct{
	sentinel *types.Sentinel
}

type SentinelPing struct{
    sentinel *types.Sentinel
	Clusters []string
}

// TODO : find a better way to implement base type
// functionality
func(s SentinelAdded) GetSentinel() *types.Sentinel {
	return s.sentinel
}

func(s SentinelLost) GetSentinel() *types.Sentinel {
	return s.sentinel
}

func(s SentinelPing) GetSentinel() *types.Sentinel {
	return s.sentinel
}

func(topology SentinelTopology) FindSentinelInfo(sentinel *types.Sentinel) (*SentinelInfo, bool) {
	info, ok := topology.Sentinels[sentinel.GetLocation()]
	return info, ok
}

<commit_msg>refactor for clarity<commit_after>package sentinel

import (
	"github.com/mdevilliers/redishappy/types"
	"time"
)

type SentinelTopology struct{
	Sentinels map[string] *SentinelInfo
}

type SentinelInfo struct{
	SentinelLocation string
	LastUpdated time.Time
	KnownClusters []string
	State int
}

type TopologyRequest struct{
	ReplyChannel chan SentinelTopology
}

type SentinelEvent interface{
	GetSentinel() *types.Sentinel
}

type SentinelAdded struct{
	sentinel *types.Sentinel
}

type SentinelLost struct{
	sentinel *types.Sentinel
}

type SentinelPing struct{
    sentinel *types.Sentinel
	Clusters []string
}

// TODO : find a better way to implement base type
// functionality
func(s SentinelAdded) GetSentinel() *types.Sentinel {
	return s.sentinel
}

func(s SentinelLost) GetSentinel() *types.Sentinel {
	return s.sentinel
}

func(s SentinelPing) GetSentinel() *types.Sentinel {
	return s.sentinel
}

func(topology SentinelTopology) FindSentinelInfo(sentinel *types.Sentinel) (*SentinelInfo, bool) {
	key := topology.createKey(sentinel)
	info, ok := topology.Sentinels[key]
	return info, ok
}
func(topology SentinelTopology) createKey(sentinel *types.Sentinel) (*SentinelInfo, bool) {
	return sentinel.GetLocation()
}
<|endoftext|>


<commit_before>// Copyright 2017-present Andrea Funtò. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package reflector

import (
	"reflect"

	"github.com/dihedron/go-log/log"
)

func StructFromPointer(obj interface{}) interface{} {
	// extract the struct from the pointer
	rv := reflect.ValueOf(obj)
	for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
		log.Debugf("%v -> %v", rv.Kind(), rv.Type())
		rv = rv.Elem()
	}
	log.Debugf("%v -> %v", rv.Kind(), rv.Type())
	obj = rv.Interface()
	return obj
}

func GetFields(obj interface{}) []reflect.Value {
	fields := make([]reflect.Value, 0)

	obj = StructFromPointer(obj)

	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)
	for i := 0; i < t.NumField(); i++ {
		value := v.Field(i)

		switch value.Kind() {
		case reflect.Struct:
			fields = append(fields, GetFields(value.Interface())...)
		default:
			fields = append(fields, value)
		}
	}

	return fields
}
<commit_msg>Removed reflect (not used anymore).<commit_after><|endoftext|>


<commit_before>package registry

import (
	"bytes"
	"compress/zlib"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
)

func encode(buf []byte) string {
	var b bytes.Buffer
	defer b.Reset()

	w := zlib.NewWriter(&b)
	if _, err := w.Write(buf); err != nil {
		return ""
	}
	w.Close()

	return hex.EncodeToString(b.Bytes())
}

func decode(d string) []byte {
	hr, err := hex.DecodeString(d)
	if err != nil {
		return nil
	}

	br := bytes.NewReader(hr)
	zr, err := zlib.NewReader(br)
	if err != nil {
		return nil
	}

	rbuf, err := ioutil.ReadAll(zr)
	if err != nil {
		return nil
	}

	return rbuf
}

func encodeEndpoints(en []*Endpoint) []string {
	var tags []string
	for _, e := range en {
		if b, err := json.Marshal(e); err == nil {
			// old encoding
			// TODO: remove in 09/2016
			tags = append(tags, "e="+string(b))
			// new encoding
			tags = append(tags, "e-"+encode(b))
		}
	}
	return tags
}

func decodeEndpoints(tags []string) []*Endpoint {
	var en []*Endpoint
	for _, tag := range tags {
		if len(tag) == 0 || tag[0] != 'e' {
			continue
		}

		var e *Endpoint
		var buf []byte

		// Old encoding was plain
		if tag[1] == '=' {
			buf = []byte(tag[2:])
		}

		// New encoding is hex
		if tag[1] == '-' {
			buf = decode(tag[2:])
		}

		if err := json.Unmarshal(buf, &e); err == nil {
			en = append(en, e)
		}
	}
	return en
}

func encodeMetadata(md map[string]string) []string {
	var tags []string
	for k, v := range md {
		if b, err := json.Marshal(map[string]string{
			k: v,
		}); err == nil {
			// old encoding
			// TODO: remove in 09/2016
			tags = append(tags, "t="+string(b))
			// new encoding
			tags = append(tags, "t-"+encode(b))
		}
	}
	return tags
}

func decodeMetadata(tags []string) map[string]string {
	md := make(map[string]string)
	for _, tag := range tags {
		if len(tag) == 0 || tag[0] != 't' {
			continue
		}

		var kv map[string]string
		var buf []byte

		// Old encoding was plain
		if tag[1] == '=' {
			buf = []byte(tag[2:])
		}

		// New encoding is hex
		if tag[1] == '-' {
			buf = decode(tag[2:])
		}

		// Now unmarshal
		if err := json.Unmarshal(buf, &kv); err == nil {
			for k, v := range kv {
				md[k] = v
			}
		}
	}
	return md
}

func encodeVersion(v string) []string {
	return []string{
		// old encoding,
		// TODO: remove in 09/2016
		"v=" + v,
		// new encoding,
		"v-" + encode([]byte(v)),
	}
}

func decodeVersion(tags []string) (string, bool) {
	for _, tag := range tags {
		if len(tag) < 2 || tag[0] != 'v' {
			continue
		}

		// Old encoding was plain
		if tag[1] == '=' {
			return tag[2:], true
		}

		// New encoding is hex
		if tag[1] == '-' {
			return string(decode(tag[2:])), true
		}
	}
	return "", false
}
<commit_msg>Set ver<commit_after>package registry

import (
	"bytes"
	"compress/zlib"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
)

func encode(buf []byte) string {
	var b bytes.Buffer
	defer b.Reset()

	w := zlib.NewWriter(&b)
	if _, err := w.Write(buf); err != nil {
		return ""
	}
	w.Close()

	return hex.EncodeToString(b.Bytes())
}

func decode(d string) []byte {
	hr, err := hex.DecodeString(d)
	if err != nil {
		return nil
	}

	br := bytes.NewReader(hr)
	zr, err := zlib.NewReader(br)
	if err != nil {
		return nil
	}

	rbuf, err := ioutil.ReadAll(zr)
	if err != nil {
		return nil
	}

	return rbuf
}

func encodeEndpoints(en []*Endpoint) []string {
	var tags []string
	for _, e := range en {
		if b, err := json.Marshal(e); err == nil {
			// old encoding
			// TODO: remove in 09/2016
			tags = append(tags, "e="+string(b))
			// new encoding
			tags = append(tags, "e-"+encode(b))
		}
	}
	return tags
}

func decodeEndpoints(tags []string) []*Endpoint {
	var en []*Endpoint

	// use the first format you find
	var ver byte

	for _, tag := range tags {
		if len(tag) == 0 || tag[0] != 'e' {
			continue
		}

		// check version
		if ver > 0 && tag[1] != ver {
			continue
		}

		var e *Endpoint
		var buf []byte

		// Old encoding was plain
		if tag[1] == '=' {
			buf = []byte(tag[2:])
		}

		// New encoding is hex
		if tag[1] == '-' {
			buf = decode(tag[2:])
		}

		if err := json.Unmarshal(buf, &e); err == nil {
			en = append(en, e)
		}

		// set version
		ver = tag[1]
	}
	return en
}

func encodeMetadata(md map[string]string) []string {
	var tags []string
	for k, v := range md {
		if b, err := json.Marshal(map[string]string{
			k: v,
		}); err == nil {
			// old encoding
			// TODO: remove in 09/2016
			tags = append(tags, "t="+string(b))
			// new encoding
			tags = append(tags, "t-"+encode(b))
		}
	}
	return tags
}

func decodeMetadata(tags []string) map[string]string {
	md := make(map[string]string)

	var ver byte

	for _, tag := range tags {
		if len(tag) == 0 || tag[0] != 't' {
			continue
		}

		// check version
		if ver > 0 && tag[1] != ver {
			continue
		}

		var kv map[string]string
		var buf []byte

		// Old encoding was plain
		if tag[1] == '=' {
			buf = []byte(tag[2:])
		}

		// New encoding is hex
		if tag[1] == '-' {
			buf = decode(tag[2:])
		}

		// Now unmarshal
		if err := json.Unmarshal(buf, &kv); err == nil {
			for k, v := range kv {
				md[k] = v
			}
		}

		// set version
		ver = tag[1]
	}
	return md
}

func encodeVersion(v string) []string {
	return []string{
		// old encoding,
		// TODO: remove in 09/2016
		"v=" + v,
		// new encoding,
		"v-" + encode([]byte(v)),
	}
}

func decodeVersion(tags []string) (string, bool) {
	for _, tag := range tags {
		if len(tag) < 2 || tag[0] != 'v' {
			continue
		}

		// Old encoding was plain
		if tag[1] == '=' {
			return tag[2:], true
		}

		// New encoding is hex
		if tag[1] == '-' {
			return string(decode(tag[2:])), true
		}
	}
	return "", false
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"sync"

	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/router/types"
)

func NewTCPListener(ip string, startPort, endPort int, ds DataStore, dc DiscoverdClient) *TCPListener {
	l := &TCPListener{
		IP:         ip,
		ds:         ds,
		wm:         NewWatchManager(),
		discoverd:  dc,
		services:   make(map[int]*tcpService),
		serviceIDs: make(map[string]*tcpService),
		listeners:  make(map[int]net.Listener),
		startPort:  startPort,
		endPort:    endPort,
	}
	l.Watcher = l.wm
	l.DataStoreReader = l.ds
	return l
}

type TCPListener struct {
	Watcher
	DataStoreReader

	IP string

	discoverd DiscoverdClient
	ds        DataStore
	wm        *WatchManager

	startPort int
	endPort   int
	listeners map[int]net.Listener

	mtx        sync.RWMutex
	services   map[int]*tcpService
	serviceIDs map[string]*tcpService
	closed     bool
}

func (l *TCPListener) AddRoute(route *router.Route) error {
	r := route.TCPRoute()
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	if l.closed {
		return ErrClosed
	}
	if r.Port == 0 {
		return l.addWithAllocatedPort(route)
	}
	route.ID = md5sum(strconv.Itoa(r.Port))
	return l.ds.Add(route)
}

func (l *TCPListener) SetRoute(route *router.Route) error {
	r := route.TCPRoute()
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	if l.closed {
		return ErrClosed
	}
	if r.Port == 0 {
		return errors.New("router: a port number needs to be specified")
	}
	route.ID = md5sum(strconv.Itoa(r.Port))
	return l.ds.Set(route)
}

var ErrNoPorts = errors.New("router: no ports available")

func (l *TCPListener) addWithAllocatedPort(route *router.Route) error {
	r := route.TCPRoute()
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	for r.Port = range l.listeners {
		r.Route.ID = md5sum(strconv.Itoa(r.Port))
		tempRoute := r.ToRoute()
		if err := l.ds.Add(tempRoute); err == nil {
			*route = *tempRoute
			return nil
		}
	}
	return ErrNoPorts
}

func (l *TCPListener) RemoveRoute(id string) error {
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	if l.closed {
		return ErrClosed
	}
	return l.ds.Remove(id)
}

func (l *TCPListener) Start() error {
	started := make(chan error)

	if l.startPort != 0 && l.endPort != 0 {
		for i := l.startPort; i <= l.endPort; i++ {
			listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", l.IP, i))
			if err != nil {
				l.Close()
				return err
			}
			l.listeners[i] = listener
		}
	}

	go l.ds.Sync(&tcpSyncHandler{l: l}, started)
	return <-started
}

func (l *TCPListener) Close() error {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.ds.StopSync()
	for _, s := range l.services {
		s.Close()
	}
	for _, listener := range l.listeners {
		listener.Close()
	}
	l.closed = true
	return nil
}

type tcpSyncHandler struct {
	l *TCPListener
}

func (h *tcpSyncHandler) Set(data *router.Route) error {
	r := data.TCPRoute()

	h.l.mtx.Lock()
	defer h.l.mtx.Unlock()
	if h.l.closed {
		return nil
	}
	if _, ok := h.l.services[r.Port]; ok {
		return ErrExists
	}

	s := &tcpService{
		addr:   h.l.IP + ":" + strconv.Itoa(r.Port),
		port:   r.Port,
		parent: h.l,
	}
	var err error
	s.ss, err = h.l.discoverd.NewServiceSet(r.Service)
	if err != nil {
		return err
	}

	if listener, ok := h.l.listeners[r.Port]; ok {
		s.l = listener
		delete(h.l.listeners, r.Port)
	}

	started := make(chan error)
	go s.Serve(started)
	if err := <-started; err != nil {
		s.ss.Close()
		if s.l != nil {
			h.l.listeners[r.Port] = s.l
		}
		return err
	}
	h.l.services[r.Port] = s
	h.l.serviceIDs[data.ID] = s
	go h.l.wm.Send(&router.Event{Event: "set", ID: data.ID})
	return nil
}

func (h *tcpSyncHandler) Remove(id string) error {
	h.l.mtx.Lock()
	defer h.l.mtx.Unlock()
	if h.l.closed {
		return nil
	}

	service, ok := h.l.serviceIDs[id]
	if !ok {
		return ErrNotFound
	}
	service.Close()
	delete(h.l.services, service.port)
	delete(h.l.serviceIDs, id)
	go h.l.wm.Send(&router.Event{Event: "remove", ID: id})
	return nil
}

type tcpService struct {
	parent *TCPListener
	addr   string
	port   int
	l      net.Listener
	ss     discoverd.ServiceSet
}

func (s *tcpService) Close() {
	if s.port >= s.parent.startPort && s.port <= s.parent.endPort {
		// make a copy of the fd and create a new listener with it
		fd, err := s.l.(*net.TCPListener).File()
		if err != nil {
			log.Println("Error getting listener fd", s.l)
			return
		}
		s.parent.listeners[s.port], err = net.FileListener(fd)
		if err != nil {
			log.Println("Error copying listener", s.l)
			return
		}
		fd.Close()
	}
	s.l.Close()
	s.ss.Close()
}

func (s *tcpService) Serve(started chan<- error) {
	var err error
	// TODO: close the listener while there are no backends available
	if s.l == nil {
		s.l, err = net.Listen("tcp", s.addr)
	}
	started <- err
	if err != nil {
		return
	}
	for {
		conn, err := s.l.Accept()
		if err != nil {
			break
		}
		go s.handle(conn)
	}
}

func (s *tcpService) getBackend() (conn net.Conn) {
	var err error
	for _, addr := range shuffle(s.ss.Addrs()) {
		// TODO: set deadlines
		conn, err = net.Dial("tcp", addr)
		if err != nil {
			log.Println("Error connecting to TCP backend:", err)
			// TODO: limit number of backends tried
			// TODO: temporarily quarantine failing backends
			continue
		}
		return
	}
	if err == nil {
		log.Println("No TCP backends found")
	} else {
		log.Println("Unable to find live backend, last error:", err)
	}
	return
}

func (s *tcpService) handle(conn net.Conn) {
	defer conn.Close()
	backend := s.getBackend()
	if backend == nil {
		return
	}
	defer backend.Close()

	// TODO: PROXY protocol

	done := make(chan struct{})
	go func() {
		io.Copy(backend, conn)
		backend.(*net.TCPConn).CloseWrite()
		close(done)
	}()
	io.Copy(conn, backend)
	conn.(*net.TCPConn).CloseWrite()
	<-done
	return
}
<commit_msg>router: Bring TCP implementation closer to HTTP.<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"sync"

	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/router/types"
)

func NewTCPListener(ip string, startPort, endPort int, ds DataStore, dc DiscoverdClient) *TCPListener {
	l := &TCPListener{
		IP:        ip,
		ds:        ds,
		wm:        NewWatchManager(),
		discoverd: dc,
		services:  make(map[int]*tcpService),
		routes:    make(map[string]*tcpRoute),
		listeners: make(map[int]net.Listener),
		startPort: startPort,
		endPort:   endPort,
	}
	l.Watcher = l.wm
	l.DataStoreReader = l.ds
	return l
}

type TCPListener struct {
	Watcher
	DataStoreReader

	IP string

	discoverd DiscoverdClient
	ds        DataStore
	wm        *WatchManager

	startPort int
	endPort   int
	listeners map[int]net.Listener

	mtx      sync.RWMutex
	services map[int]*tcpService
	routes   map[string]*tcpRoute
	closed   bool
}

func (l *TCPListener) AddRoute(route *router.Route) error {
	r := route.TCPRoute()
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	if l.closed {
		return ErrClosed
	}
	if r.Port == 0 {
		return l.addWithAllocatedPort(route)
	}
	route.ID = md5sum(strconv.Itoa(r.Port))
	return l.ds.Add(route)
}

func (l *TCPListener) SetRoute(route *router.Route) error {
	r := route.TCPRoute()
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	if l.closed {
		return ErrClosed
	}
	if r.Port == 0 {
		return errors.New("router: a port number needs to be specified")
	}
	route.ID = md5sum(strconv.Itoa(r.Port))
	return l.ds.Set(route)
}

var ErrNoPorts = errors.New("router: no ports available")

func (l *TCPListener) addWithAllocatedPort(route *router.Route) error {
	r := route.TCPRoute()
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	for r.Port = range l.listeners {
		r.Route.ID = md5sum(strconv.Itoa(r.Port))
		tempRoute := r.ToRoute()
		if err := l.ds.Add(tempRoute); err == nil {
			*route = *tempRoute
			return nil
		}
	}
	return ErrNoPorts
}

func (l *TCPListener) RemoveRoute(id string) error {
	l.mtx.RLock()
	defer l.mtx.RUnlock()
	if l.closed {
		return ErrClosed
	}
	return l.ds.Remove(id)
}

func (l *TCPListener) Start() error {
	started := make(chan error)

	if l.startPort != 0 && l.endPort != 0 {
		for i := l.startPort; i <= l.endPort; i++ {
			listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", l.IP, i))
			if err != nil {
				l.Close()
				return err
			}
			l.listeners[i] = listener
		}
	}

	go l.ds.Sync(&tcpSyncHandler{l: l}, started)
	return <-started
}

func (l *TCPListener) Close() error {
	l.mtx.Lock()
	defer l.mtx.Unlock()
	l.ds.StopSync()
	for _, s := range l.services {
		s.Close()
	}
	for _, listener := range l.listeners {
		listener.Close()
	}
	l.closed = true
	return nil
}

type tcpSyncHandler struct {
	l *TCPListener
}

func (h *tcpSyncHandler) Set(data *router.Route) error {
	route := data.TCPRoute()
	r := &tcpRoute{TCPRoute: route}

	h.l.mtx.Lock()
	defer h.l.mtx.Unlock()
	if h.l.closed {
		return nil
	}

	service := h.l.services[r.Port]
	if service != nil && service.port != r.Port {
		service.refs--
		if service.refs <= 0 {
			service.ss.Close()
			delete(h.l.services, service.port)
		}
		service = nil
	}
	if service == nil {
		ss, err := h.l.discoverd.NewServiceSet(r.Service)
		if err != nil {
			return err
		}
		service = &tcpService{
			addr:   h.l.IP + ":" + strconv.Itoa(r.Port),
			port:   r.Port,
			parent: h.l,
			ss:     ss,
		}
		if listener, ok := h.l.listeners[r.Port]; ok {
			service.l = listener
			delete(h.l.listeners, r.Port)
		}
		started := make(chan error)
		go service.Serve(started)
		if err := <-started; err != nil {
			service.ss.Close()
			if service.l != nil {
				h.l.listeners[r.Port] = service.l
			}
			return err
		}
		h.l.services[r.Port] = service
	}
	service.refs++
	r.service = service
	h.l.routes[data.ID] = r

	go h.l.wm.Send(&router.Event{Event: "set", ID: data.ID})
	return nil
}

func (h *tcpSyncHandler) Remove(id string) error {
	h.l.mtx.Lock()
	defer h.l.mtx.Unlock()
	if h.l.closed {
		return nil
	}
	r, ok := h.l.routes[id]
	if !ok {
		return ErrNotFound
	}

	r.service.refs--
	if r.service.refs <= 0 {
		r.service.Close()
		delete(h.l.services, r.service.port)
	}

	delete(h.l.routes, id)
	go h.l.wm.Send(&router.Event{Event: "remove", ID: id})
	return nil
}

type tcpRoute struct {
	*router.TCPRoute
	service *tcpService
}

type tcpService struct {
	parent *TCPListener
	addr   string
	port   int
	l      net.Listener
	ss     discoverd.ServiceSet
	refs   int
}

func (s *tcpService) Close() {
	if s.port >= s.parent.startPort && s.port <= s.parent.endPort {
		// make a copy of the fd and create a new listener with it
		fd, err := s.l.(*net.TCPListener).File()
		if err != nil {
			log.Println("Error getting listener fd", s.l)
			return
		}
		s.parent.listeners[s.port], err = net.FileListener(fd)
		if err != nil {
			log.Println("Error copying listener", s.l)
			return
		}
		fd.Close()
	}
	s.l.Close()
	s.ss.Close()
}

func (s *tcpService) Serve(started chan<- error) {
	var err error
	// TODO: close the listener while there are no backends available
	if s.l == nil {
		s.l, err = net.Listen("tcp", s.addr)
	}
	started <- err
	if err != nil {
		return
	}
	for {
		conn, err := s.l.Accept()
		if err != nil {
			break
		}
		go s.handle(conn)
	}
}

func (s *tcpService) getBackend() (conn net.Conn) {
	var err error
	for _, addr := range shuffle(s.ss.Addrs()) {
		// TODO: set deadlines
		conn, err = net.Dial("tcp", addr)
		if err != nil {
			log.Println("Error connecting to TCP backend:", err)
			// TODO: limit number of backends tried
			// TODO: temporarily quarantine failing backends
			continue
		}
		return
	}
	if err == nil {
		log.Println("No TCP backends found")
	} else {
		log.Println("Unable to find live backend, last error:", err)
	}
	return
}

func (s *tcpService) handle(conn net.Conn) {
	defer conn.Close()
	backend := s.getBackend()
	if backend == nil {
		return
	}
	defer backend.Close()

	// TODO: PROXY protocol

	done := make(chan struct{})
	go func() {
		io.Copy(backend, conn)
		backend.(*net.TCPConn).CloseWrite()
		close(done)
	}()
	io.Copy(conn, backend)
	conn.(*net.TCPConn).CloseWrite()
	<-done
	return
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 siv

import (
	"fmt"
)

// NotAuthenticError is returned by Decrypt if the input is otherwise
// well-formed but the ciphertext doesn't check out as authentic. This could be
// due to an incorrect key, corrupted ciphertext, or incorrect/corrupted
// associated data.
type NotAuthenticError string

func (e NotAuthenticError) Error() string {
	return string(e)
}

// Given ciphertext previously generated by Encrypt and the key and associated
// data that were used when generating the ciphertext, return the original
// plaintext given to Encrypt. If the input is well-formed but the key is
// incorrect, return an instance of WrongKeyError.
func Decrypt(key, ciphertext []byte, associated [][]byte) ([]byte, error) {
	return nil, fmt.Errorf("TODO")
}
<commit_msg>Added some error checking.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 siv

import (
	"crypto/aes"
	"fmt"
)

// NotAuthenticError is returned by Decrypt if the input is otherwise
// well-formed but the ciphertext doesn't check out as authentic. This could be
// due to an incorrect key, corrupted ciphertext, or incorrect/corrupted
// associated data.
type NotAuthenticError string

func (e NotAuthenticError) Error() string {
	return string(e)
}

// Given ciphertext previously generated by Encrypt and the key and associated
// data that were used when generating the ciphertext, return the original
// plaintext given to Encrypt. If the input is well-formed but the key is
// incorrect, return an instance of WrongKeyError.
func Decrypt(key, ciphertext []byte, associated [][]byte) ([]byte, error) {
	keyLen := len(key)
	associatedLen := len(associated)

	// The first 16 bytes of the ciphertext are the SIV.
	if len(ciphertext) < aes.BlockSize {
		return nil, fmt.Errorf("Invalid ciphertext; length must be at least 16.")
	}

	v := ciphertext[0:aes.BlockSize]
	c := ciphertext[aes.BlockSize:]

	// Make sure the key length is legal.
	switch keyLen {
	case 32, 48, 64:
	default:
		return nil, fmt.Errorf("SIV requires a 32-, 48-, or 64-byte key.")
	}

	// Derive subkeys.
	k1 := key[:keyLen/2]
	k2 := key[keyLen/2:]

	// Make sure the number of associated data is legal, per RFC 5297 section 7.
	if associatedLen > 126 {
		return nil, fmt.Errorf("len(associated) may be no more than 126.")
	}

	return nil, fmt.Errorf("TODO")
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 v2alpha1

import (
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/schema"
	versionedwatch "k8s.io/kubernetes/pkg/watch/versioned"
)

// GroupName is the group name use in this package
const GroupName = "batch"

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v2alpha1"}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
	return SchemeGroupVersion.WithResource(resource).GroupResource()
}

var (
	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
	AddToScheme   = SchemeBuilder.AddToScheme
)

// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
	scheme.AddKnownTypes(SchemeGroupVersion,
		&Job{},
		&JobList{},
		&JobTemplate{},
		&CronJob{},
		&CronJobList{},
		&v1.ListOptions{},
		&v1.DeleteOptions{},
	)
	scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ScheduledJob"), &CronJob{})
	scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ScheduledJobList"), &CronJobList{})
	versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion)
	return nil
}
<commit_msg>Register GetOptions in batch/v2alpha1 group<commit_after>/*
Copyright 2016 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 v2alpha1

import (
	"k8s.io/kubernetes/pkg/api/v1"
	metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/schema"
	versionedwatch "k8s.io/kubernetes/pkg/watch/versioned"
)

// GroupName is the group name use in this package
const GroupName = "batch"

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v2alpha1"}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
	return SchemeGroupVersion.WithResource(resource).GroupResource()
}

var (
	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
	AddToScheme   = SchemeBuilder.AddToScheme
)

// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
	scheme.AddKnownTypes(SchemeGroupVersion,
		&Job{},
		&JobList{},
		&JobTemplate{},
		&CronJob{},
		&CronJobList{},
		&v1.ListOptions{},
		&v1.DeleteOptions{},
		&metav1.ExportOptions{},
		&metav1.GetOptions{},
	)
	scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ScheduledJob"), &CronJob{})
	scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ScheduledJobList"), &CronJobList{})
	versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion)
	return nil
}
<|endoftext|>


<commit_before>package opc

import "github.com/junhwong/mano/otpl/common"

type opLoadVariable struct {
	opBase
	name string
}

func (op *opLoadVariable) Load() (err error) {
	op.name, err = op.loader.ReadString()

	return
}

func (op *opLoadVariable) Exec(ctx common.Context) (ptr common.Ptr, err error) {
	ptr = op.ptr + 1
	val, ok := ctx.Var(op.name)
	err = handErr(!ok, ctx, "unset variable:%s", op.name)
	if err == nil {
		ctx.Push(val)
	}
	return
}

func init() {
	common.RegisterOpcode(0x05, func(loader common.Loader, ptr common.Ptr, line common.LineNo, flag byte) common.Opcode {

		return &opLoadVariable{
			opBase: opBase{
				loader:     loader,
				ptr:        ptr,
				lineNumber: line,
				flag:       flag,
			},
		}
	})
}
<commit_msg>修复变量未设置时获取的BUG<commit_after>package opc

import "github.com/junhwong/mano/otpl/common"

type opLoadVariable struct {
	opBase
	name string
}

func (op *opLoadVariable) Load() (err error) {
	op.name, err = op.loader.ReadString()

	return
}

func (op *opLoadVariable) Exec(ctx common.Context) (ptr common.Ptr, err error) {
	ptr = op.ptr + 1
	val, _ := ctx.Var(op.name)
	//TODO: 未设置变量是否输出日志？
	// err = handErr(!ok, ctx, "unset variable:%s", op.name)
	// if err == nil {
	// 	ctx.Push(val)
	// }
	ctx.Push(val)
	return
}

func init() {
	common.RegisterOpcode(0x05, func(loader common.Loader, ptr common.Ptr, line common.LineNo, flag byte) common.Opcode {

		return &opLoadVariable{
			opBase: opBase{
				loader:     loader,
				ptr:        ptr,
				lineNumber: line,
				flag:       flag,
			},
		}
	})
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package util

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/pagodabox/nanobox-cli/config"
	// "github.com/pagodabox/nanobox-golang-stylish"
)

// GetVMUUID tries to return the VMs uuid found in it's corresponding .vangrant
// folder. If a uuid is not found than the VM has not yet been created. Don't
// really care about the error here since the value will be "" if there is no
// file to read
func GetVMUUID() string {
	b, _ := ioutil.ReadFile(fmt.Sprintf("%v/.vagrant/machines/%v/%v/index_uuid", config.AppDir, config.Nanofile.Name, config.Nanofile.Provider))
	return string(b)
}

// GetVMStatus returns the current status of the VM; this command needs to be run
// in a way independant of a Vagrantfile to ensure that the status will always
// be available
func GetVMStatus() string {

	var status string

	//
	uuid := GetVMUUID()

	if uuid == "" {
		return uuid
	}

	// run the command with the uuid; this allows the command to be run from any
	// context (not dependent on a Vagrantfile)
	cmd := exec.Command("vagrant", "status", uuid)

	//
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	//
	scanner := bufio.NewScanner(stdout)
	go func() {
		for scanner.Scan() {

			// if the scan line starts with the name of the VM thats the line thats going
			// to have the status in it
			if strings.HasPrefix(scanner.Text(), config.Nanofile.Name) {

				// pull the status out of the line; it's easiest to just pull the exact
				// states out of the string since there aren't that many
				status = regexp.MustCompile(`\s(running|saved|poweroff|not created)\s`).FindStringSubmatch(scanner.Text())[1]
			}
		}
	}()

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output
	if err := cmd.Start(); err != nil {
		panic(err)
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		panic(err)
	}

	return status
}

// RunVagrantCommand provides a wrapper around a standard cmd.Run() in which
// all standard in/outputs are connected to the command, and the directory is
// changed to the corresponding app directory. This allows nanobox to run Vagrant
// commands w/o contaminating a users codebase.
func RunVagrantCommand(cmd *exec.Cmd) error {

	// run the command from ~/.nanobox/apps/<config.App>. if the directory doesn't
	// exist, simply return; running the command from the directory that contains
	// the Vagratfile ensure that the command can atleast run (especially in cases
	// like 'create' where a VM hadn't been created yet, and a UUID isn't available)
	if err := os.Chdir(config.AppDir); err != nil {
		return err
	}

	// create a pipe that we can pipe the cmd standard output's too. The reason this
	// is done rather than just piping directly to os standard outputs and .Run()ing
	// the command (vs .Start()ing) is because the output needs to be modified
	// according to http://nanodocs.gopagoda.io/engines/style-guide
	//
	// NOTE: the reason it's done this way vs using the cmd.*Pipe's is so that all
	// the command output can be read from a single pipe, rather than having to create
	// a new pipe/scanner for each type of output
	pr, pw := io.Pipe()
	defer pr.Close()
	defer pw.Close()

	// connect standard output
	cmd.Stdout = pw
	cmd.Stderr = pw

	//
	output := make(chan string)

	//
	go func() {

		tick := time.Second

		for {
			select {

			//
			case msg, ok := <-output:

				//
				if !ok {
					fmt.Println("")
					return
				}

				//
				fmt.Printf("\n   - %s", msg)
				tick = time.Second

			//
			case <-time.After(tick):
				fmt.Print(".")

				// increase the wait time by half of the total previous time
				tick += tick / 2
			}
		}
	}()

	// scan the command output modifying it according to
	// http://nanodocs.gopagoda.io/engines/style-guide
	scanner := bufio.NewScanner(pr)
	go func() {
		for scanner.Scan() {

			// intercept and modify only 'important' lines of vagrant output' so as to
			// not flood the output
			switch scanner.Text() {
			case fmt.Sprintf("==> %v: Importing base box 'nanobox/boot2docker'...", config.Nanofile.Name):
				output <- "Importing nanobox base image"
			case fmt.Sprintf("==> %v: Booting VM...", config.Nanofile.Name):
				output <- "Booting virtual machine"
			case fmt.Sprintf("==> %v: Configuring and enabling network interfaces...", config.Nanofile.Name):
				output <- "Configuring virtual network"
			case fmt.Sprintf("==> %v: Mounting shared folders...", config.Nanofile.Name):
				output <- fmt.Sprintf("Mounting source code (%s)", config.CWDir)
			case fmt.Sprintf("==> %v: Waiting for nanobox server...", config.Nanofile.Name):
				output <- "Starting nanobox server"
			case fmt.Sprintf("==> %v: Attempting graceful shutdown of VM...", config.Nanofile.Name):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Destroying VM and associated drives...", config.Nanofile.Name):
				output <- "Destroying virtual machine"
			case fmt.Sprintf("==> %v: Forcing shutdown of VM...", config.Nanofile.Name):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Saving VM state and suspending execution...", config.Nanofile.Name):
				output <- "Suspending virtual machine"
			case fmt.Sprintf("==> %v: Resuming suspended VM...", config.Nanofile.Name):
				output <- "Resuming virtual machine"
			}
		}

		// close the channel
		close(output)
	}()

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	// switch back to project dir
	if err := os.Chdir(config.CWDir); err != nil {
		return err
	}

	return nil
}
<commit_msg>cleaning up vagrant command output<commit_after>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package util

import (
	"bufio"
	"fmt"
	// "io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/pagodabox/nanobox-cli/config"
	// "github.com/pagodabox/nanobox-golang-stylish"
)

// GetVMUUID tries to return the VMs uuid found in it's corresponding .vangrant
// folder. If a uuid is not found than the VM has not yet been created. Don't
// really care about the error here since the value will be "" if there is no
// file to read
func GetVMUUID() string {
	b, _ := ioutil.ReadFile(fmt.Sprintf("%v/.vagrant/machines/%v/%v/index_uuid", config.AppDir, config.Nanofile.Name, config.Nanofile.Provider))
	return string(b)
}

// GetVMStatus returns the current status of the VM; this command needs to be run
// in a way independant of a Vagrantfile to ensure that the status will always
// be available
func GetVMStatus() string {

	var status string

	//
	uuid := GetVMUUID()

	if uuid == "" {
		return uuid
	}

	// run the command with the uuid; this allows the command to be run from any
	// context (not dependent on a Vagrantfile)
	cmd := exec.Command("vagrant", "status", uuid)

	//
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	//
	scanner := bufio.NewScanner(stdout)
	go func() {
		for scanner.Scan() {

			// if the scan line starts with the name of the VM thats the line thats going
			// to have the status in it
			if strings.HasPrefix(scanner.Text(), config.Nanofile.Name) {

				// pull the status out of the line; it's easiest to just pull the exact
				// states out of the string since there aren't that many
				status = regexp.MustCompile(`\s(running|saved|poweroff|not created)\s`).FindStringSubmatch(scanner.Text())[1]
			}
		}
	}()

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output
	if err := cmd.Start(); err != nil {
		panic(err)
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		panic(err)
	}

	return status
}

// RunVagrantCommand provides a wrapper around a standard cmd.Run() in which
// all standard in/outputs are connected to the command, and the directory is
// changed to the corresponding app directory. This allows nanobox to run Vagrant
// commands w/o contaminating a users codebase.
func RunVagrantCommand(cmd *exec.Cmd) error {

	// run the command from ~/.nanobox/apps/<config.App>. if the directory doesn't
	// exist, simply return; running the command from the directory that contains
	// the Vagratfile ensure that the command can atleast run (especially in cases
	// like 'create' where a VM hadn't been created yet, and a UUID isn't available)
	if err := os.Chdir(config.AppDir); err != nil {
		return err
	}

	// create a stdout pipe that will allow for scanning the output line-by-line;
	// if needed a stderr pipe could also be created at some point
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	// start a goroutine that will act as an 'outputer' allowing us to add 'dots'
	// to the end of each line (as these lines are a reduced version of the actual
	// output there will be some delay between output)
	output := make(chan string)
	go func() {

		tick := time.Second

		// block until any one message outputs
		msg, ok := <-output

		// print initial message to 'get the ball rolling' on our 'outputer'
		fmt.Printf("   - %s", msg)

		// begin a loop to read off the channel until it's closed
		for {
			select {

			// print any messages and reset ticker
			case msg, ok = <-output:

				// once the channel closes print the final newline and close the goroutine
				if !ok {
					fmt.Println("")
					return
				}

				fmt.Printf("\n   - %s", msg)

				tick = time.Second

			// after every tick print a '.' until we get another message one the channel
			// (at which point ticker is reset and it starts all over again)
			case <-time.After(tick):
				fmt.Print(".")

				// increase the wait time by half of the total previous time
				tick += tick / 2
			}
		}
	}()

	// scan the command output intercepting only 'important' lines of vagrant output'
	// and tailoring their message so as to not flood the output.
	// styled according to: http://nanodocs.gopagoda.io/engines/style-guide
	scanner := bufio.NewScanner(stdout)
	go func() {
		for scanner.Scan() {

			//
			switch scanner.Text() {
			case fmt.Sprintf("==> %v: Importing base box 'nanobox/boot2docker'...", config.Nanofile.Name):
				output <- "Importing nanobox base image"
			case fmt.Sprintf("==> %v: Booting VM...", config.Nanofile.Name):
				output <- "Booting virtual machine"
			case fmt.Sprintf("==> %v: Configuring and enabling network interfaces...", config.Nanofile.Name):
				output <- "Configuring virtual network"
			case fmt.Sprintf("==> %v: Mounting shared folders...", config.Nanofile.Name):
				output <- fmt.Sprintf("Mounting source code (%s)", config.CWDir)
			case fmt.Sprintf("==> %v: Waiting for nanobox server...", config.Nanofile.Name):
				output <- "Starting nanobox server"
			case fmt.Sprintf("==> %v: Attempting graceful shutdown of VM...", config.Nanofile.Name):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Destroying VM and associated drives...", config.Nanofile.Name):
				output <- "Destroying virtual machine"
			case fmt.Sprintf("==> %v: Forcing shutdown of VM...", config.Nanofile.Name):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Saving VM state and suspending execution...", config.Nanofile.Name):
				output <- "Suspending virtual machine"
			case fmt.Sprintf("==> %v: Resuming suspended VM...", config.Nanofile.Name):
				output <- "Resuming virtual machine"
			}
		}

		// close the output channel once all lines of command output have been read
		close(output)
	}()

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	// switch back to project dir
	if err := os.Chdir(config.CWDir); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "fmt"
    "log"
    "strconv"
    "net/http"
    "database/sql"
    "encoding/json"
    "github.com/gorilla/mux"
    _ "github.com/mattn/go-sqlite3"
)

type Town struct {
    Id        uint32  `json:"id"`
    Name      string  `json:"name"`
    NameTr    string  `json:"name_tr"`
    Latitude  float32 `json:"latitude"`
    Longitude float32 `json:"longitude"`
    Zoom      uint32  `json:"zoom"`
}

type CashPoint struct {
    Id             uint32  `json:"id"`
    Type           string  `json:"type"`
    BankId         uint32  `json:"bank_id"`
    TownId         uint32  `json:"town_id"`
    Longitude      float32 `json:"longitude"`
    Latitude       float32 `json:"latitude"`
    Address        string  `json:"address"`
    AddressComment string  `json:"address_comment"`
    MetroName      string  `json:"metro_name"`
    FreeAccess     bool    `json:"free_access"`
    MainOffice     bool    `json:"main_office"`
    WithoutWeekend bool    `json:"without_weekend"`
    RoundTheClock  bool    `json:"round_the_clock"`
    WorksAsShop    bool    `json:"works_as_shop"`
    Schedule       string  `json:"schedule"`
    Tel            string  `json:"tel"`
    Additional     string  `json:"additional"`
    Rub            bool    `json:"rub"`
    Usd            bool    `json:"usd"`
    Eur            bool    `json:"eur"`
    CashIn         bool    `json:"cash_in"`
}

type CashPointIdsInTown struct {
    TownId        uint32   `json:"town_id"`
    BankId        uint32   `json:"bank_id"`
    CashPointIds  []uint32 `json:"cash_points"`
}

var towns_db *sql.DB
var cp_db *sql.DB

func handlerTown(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    townId := params["id"]

    stmt, err := towns_db.Prepare("SELECT id, name, name_tr, latitude, longitude, zoom FROM towns WHERE id = ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    town := new(Town)
    err = stmt.QueryRow(townId).Scan(&town.Id, &town.Name, &town.NameTr, &town.Latitude, &town.Longitude, &town.Zoom)
    if err != nil {
        if err == sql.ErrNoRows {
            fmt.Fprintf(w, "{ id: null }")
            return
        } else {
            log.Fatal(err)
        }
    }

    jsonStr, _ := json.Marshal(town)
    fmt.Fprintf(w, string(jsonStr))
}

func handlerCashpoint(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    cashPointId := params["id"]

    stmt, err := cp_db.Prepare("SELECT id, type, bank_id, town_id, longitude, latitude, address, address_comment, metro_name, free_access, main_office, without_weekend, round_the_clock, works_as_shop, schedule_general, tel, additional, rub, usd, eur, cash_in FROM cashpoints WHERE id = ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    cp := new(CashPoint)
    // Todo: parsing schedule
    err = stmt.QueryRow(cashPointId).Scan(&cp.Id, &cp.Type, &cp.BankId,
                                          &cp.TownId, &cp.Longitude, &cp.Latitude,
                                          &cp.Address, &cp.AddressComment,
                                          &cp.MetroName, &cp.FreeAccess,
                                          &cp.MainOffice, &cp.WithoutWeekend,
                                          &cp.RoundTheClock, &cp.WorksAsShop,
                                          &cp.Schedule, &cp.Tel, &cp.Additional,
                                          &cp.Rub, &cp.Usd, &cp.Eur, &cp.CashIn)
    if err != nil {
        if err == sql.ErrNoRows {
            fmt.Fprintf(w, "{ id: null }")
            return
        } else {
            log.Fatal(err)
        }
    }

    jsonStr, _ := json.Marshal(cp)
    fmt.Fprintf(w, string(jsonStr))
}

func handlerCashpointsByTownAndBank(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    townId, _ := strconv.ParseUint(params["town_id"], 10, 32)
    bankId, _ := strconv.ParseUint(params["bank_id"], 10, 32)

    stmt, err := cp_db.Prepare("SELECT id FROM cashpoints WHERE town_id = ? AND bank_id = ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    rows, err := stmt.Query(params["town_id"], params["bank_id"])
    if err != nil {
        log.Fatal(err)
    }

    ids := CashPointIdsInTown{ TownId: uint32(townId), BankId: uint32(bankId) }

    for rows.Next() {
        var id uint32
        if err := rows.Scan(&id); err != nil {
            log.Fatal(err)
        }
        ids.CashPointIds = append(ids.CashPointIds, id)
    }

    jsonStr, _ := json.Marshal(ids)
    fmt.Fprintf(w, string(jsonStr))
}

func main() {
    args := os.Args[1:]

    if _, err := os.Stat(args[0]); os.IsNotExist(err) {
        log.Fatal("no such file or directory: %s\n", args[0])
        os.Exit(1)
    }

    if _, err := os.Stat(args[1]); os.IsNotExist(err) {
        log.Fatal("no such file or directory: %s\n", args[1])
        os.Exit(2)
    }

    var err error
    towns_db, err = sql.Open("sqlite3", args[0])
    if err != nil {
        log.Fatal(err)
        os.Exit(3)
    }
    defer towns_db.Close()

    cp_db, err = sql.Open("sqlite3", args[1])
    if err != nil {
        log.Fatal(err)
        os.Exit(4)
    }
    defer cp_db.Close()

    router := mux.NewRouter()
    router.HandleFunc("/town/{id:[0-9]+}", handlerTown)
    router.HandleFunc("/cashpoint/{id:[0-9]+}", handlerCashpoint)
    router.HandleFunc("/town/{town_id:[0-9]+}/bank/{bank_id:[0-9]+}/cashpoints", handlerCashpointsByTownAndBank)

    http.Handle("/", router)
    http.ListenAndServe(":8080", nil)
}<commit_msg>[C|Server] Added checking of cmdline args count<commit_after>package main

import (
    "os"
    "fmt"
    "log"
    "strconv"
    "net/http"
    "database/sql"
    "encoding/json"
    "github.com/gorilla/mux"
    _ "github.com/mattn/go-sqlite3"
)

type Town struct {
    Id        uint32  `json:"id"`
    Name      string  `json:"name"`
    NameTr    string  `json:"name_tr"`
    Latitude  float32 `json:"latitude"`
    Longitude float32 `json:"longitude"`
    Zoom      uint32  `json:"zoom"`
}

type CashPoint struct {
    Id             uint32  `json:"id"`
    Type           string  `json:"type"`
    BankId         uint32  `json:"bank_id"`
    TownId         uint32  `json:"town_id"`
    Longitude      float32 `json:"longitude"`
    Latitude       float32 `json:"latitude"`
    Address        string  `json:"address"`
    AddressComment string  `json:"address_comment"`
    MetroName      string  `json:"metro_name"`
    FreeAccess     bool    `json:"free_access"`
    MainOffice     bool    `json:"main_office"`
    WithoutWeekend bool    `json:"without_weekend"`
    RoundTheClock  bool    `json:"round_the_clock"`
    WorksAsShop    bool    `json:"works_as_shop"`
    Schedule       string  `json:"schedule"`
    Tel            string  `json:"tel"`
    Additional     string  `json:"additional"`
    Rub            bool    `json:"rub"`
    Usd            bool    `json:"usd"`
    Eur            bool    `json:"eur"`
    CashIn         bool    `json:"cash_in"`
}

type CashPointIdsInTown struct {
    TownId        uint32   `json:"town_id"`
    BankId        uint32   `json:"bank_id"`
    CashPointIds  []uint32 `json:"cash_points"`
}

var towns_db *sql.DB
var cp_db *sql.DB

func handlerTown(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    townId := params["id"]

    stmt, err := towns_db.Prepare("SELECT id, name, name_tr, latitude, longitude, zoom FROM towns WHERE id = ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    town := new(Town)
    err = stmt.QueryRow(townId).Scan(&town.Id, &town.Name, &town.NameTr, &town.Latitude, &town.Longitude, &town.Zoom)
    if err != nil {
        if err == sql.ErrNoRows {
            fmt.Fprintf(w, "{ id: null }")
            return
        } else {
            log.Fatal(err)
        }
    }

    jsonStr, _ := json.Marshal(town)
    fmt.Fprintf(w, string(jsonStr))
}

func handlerCashpoint(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    cashPointId := params["id"]

    stmt, err := cp_db.Prepare("SELECT id, type, bank_id, town_id, longitude, latitude, address, address_comment, metro_name, free_access, main_office, without_weekend, round_the_clock, works_as_shop, schedule_general, tel, additional, rub, usd, eur, cash_in FROM cashpoints WHERE id = ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    cp := new(CashPoint)
    // Todo: parsing schedule
    err = stmt.QueryRow(cashPointId).Scan(&cp.Id, &cp.Type, &cp.BankId,
                                          &cp.TownId, &cp.Longitude, &cp.Latitude,
                                          &cp.Address, &cp.AddressComment,
                                          &cp.MetroName, &cp.FreeAccess,
                                          &cp.MainOffice, &cp.WithoutWeekend,
                                          &cp.RoundTheClock, &cp.WorksAsShop,
                                          &cp.Schedule, &cp.Tel, &cp.Additional,
                                          &cp.Rub, &cp.Usd, &cp.Eur, &cp.CashIn)
    if err != nil {
        if err == sql.ErrNoRows {
            fmt.Fprintf(w, "{ id: null }")
            return
        } else {
            log.Fatal(err)
        }
    }

    jsonStr, _ := json.Marshal(cp)
    fmt.Fprintf(w, string(jsonStr))
}

func handlerCashpointsByTownAndBank(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    townId, _ := strconv.ParseUint(params["town_id"], 10, 32)
    bankId, _ := strconv.ParseUint(params["bank_id"], 10, 32)

    stmt, err := cp_db.Prepare("SELECT id FROM cashpoints WHERE town_id = ? AND bank_id = ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    rows, err := stmt.Query(params["town_id"], params["bank_id"])
    if err != nil {
        log.Fatal(err)
    }

    ids := CashPointIdsInTown{ TownId: uint32(townId), BankId: uint32(bankId) }

    for rows.Next() {
        var id uint32
        if err := rows.Scan(&id); err != nil {
            log.Fatal(err)
        }
        ids.CashPointIds = append(ids.CashPointIds, id)
    }

    jsonStr, _ := json.Marshal(ids)
    fmt.Fprintf(w, string(jsonStr))
}

func main() {
    args := os.Args[1:]

    if len(args) == 0 {
        log.Fatal("towns database path is not specified")
    }

    if len(args) == 1 {
        log.Fatal("cashpoints database path is not specified")
    }

    if _, err := os.Stat(args[0]); os.IsNotExist(err) {
        log.Fatal("no such file or directory: %s\n", args[0])
    }

    if _, err := os.Stat(args[1]); os.IsNotExist(err) {
        log.Fatal("no such file or directory: %s\n", args[1])
    }

    var err error
    towns_db, err = sql.Open("sqlite3", args[0])
    if err != nil {
        log.Fatal(err)
        os.Exit(3)
    }
    defer towns_db.Close()

    cp_db, err = sql.Open("sqlite3", args[1])
    if err != nil {
        log.Fatal(err)
        os.Exit(4)
    }
    defer cp_db.Close()

    router := mux.NewRouter()
    router.HandleFunc("/town/{id:[0-9]+}", handlerTown)
    router.HandleFunc("/cashpoint/{id:[0-9]+}", handlerCashpoint)
    router.HandleFunc("/town/{town_id:[0-9]+}/bank/{bank_id:[0-9]+}/cashpoints", handlerCashpointsByTownAndBank)

    http.Handle("/", router)
    http.ListenAndServe(":8080", nil)
}

<|endoftext|>