Esempio n. 1
1
func TestStatic(t *testing.T) {
	var (
		instances = []string{"foo", "bar", "baz"}
		endpoints = map[string]endpoint.Endpoint{
			"foo": func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
			"bar": func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
			"baz": func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil },
		}
		factory = func(instance string) (endpoint.Endpoint, error) {
			if e, ok := endpoints[instance]; ok {
				return e, nil
			}
			return nil, fmt.Errorf("%s: not found", instance)
		}
	)
	p := static.NewPublisher(instances, factory, log.NewNopLogger())
	have, err := p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}
	want := []endpoint.Endpoint{endpoints["foo"], endpoints["bar"], endpoints["baz"]}
	if fmt.Sprint(want) != fmt.Sprint(have) {
		t.Fatalf("want %v, have %v", want, have)
	}
}
Esempio n. 2
0
func testEmitter() (*Emitter, *syncbuf) {
	buf := &syncbuf{buf: &bytes.Buffer{}}
	e := &Emitter{
		prefix:  "prefix.",
		mgr:     conn.NewManager(mockDialer(buf), "", "", time.After, log.NewNopLogger()),
		logger:  log.NewNopLogger(),
		keyVals: make(chan keyVal),
		quitc:   make(chan chan struct{}),
	}
	go e.loop(time.Millisecond * 20)
	return e, buf
}
Esempio n. 3
0
func Example() {
	// Let's say this is a service that means to register itself.
	// First, we will set up some context.
	var (
		etcdServer = "http://10.0.0.1:2379" // don't forget schema and port!
		prefix     = "/services/foosvc/"    // known at compile time
		instance   = "1.2.3.4:8080"         // taken from runtime or platform, somehow
		key        = prefix + instance      // should be globally unique
		value      = "http://" + instance   // based on our transport
		ctx        = context.Background()
	)

	// Build the client.
	client, err := NewClient(ctx, []string{etcdServer}, ClientOptions{})
	if err != nil {
		panic(err)
	}

	// Build the registrar.
	registrar := NewRegistrar(client, Service{
		Key:   key,
		Value: value,
	}, log.NewNopLogger())

	// Register our instance.
	registrar.Register()

	// At the end of our service lifecycle, for example at the end of func main,
	// we should make sure to deregister ourselves. This is important! Don't
	// accidentally skip this step by invoking a log.Fatal or os.Exit in the
	// interim, which bypasses the defer stack.
	defer registrar.Deregister()

	// It's likely that we'll also want to connect to other services and call
	// their methods. We can build a subscriber to listen for changes from etcd
	// and build endpoints, wrap it with a load-balancer to pick a single
	// endpoint, and finally wrap it with a retry strategy to get something that
	// can be used as an endpoint directly.
	barPrefix := "/services/barsvc"
	subscriber, err := NewSubscriber(client, barPrefix, barFactory, log.NewNopLogger())
	if err != nil {
		panic(err)
	}
	balancer := lb.NewRoundRobin(subscriber)
	retry := lb.Retry(3, 3*time.Second, balancer)

	// And now retry can be used like any other endpoint.
	req := struct{}{}
	if _, err = retry(ctx, req); err != nil {
		panic(err)
	}
}
Esempio n. 4
0
func TestIssue292(t *testing.T) {
	// The util/conn.Manager won't attempt to reconnect to the provided endpoint
	// if the endpoint is initially unavailable (e.g. dial tcp :8080:
	// getsockopt: connection refused). If the endpoint is up when
	// conn.NewManager is called and then goes down/up, it reconnects just fine.

	var (
		tickc    = make(chan time.Time)
		after    = func(time.Duration) <-chan time.Time { return tickc }
		dialconn = net.Conn(nil)
		dialerr  = errors.New("fail")
		dialer   = func(string, string) (net.Conn, error) { return dialconn, dialerr }
		mgr      = NewManager(dialer, "netw", "addr", after, log.NewNopLogger())
	)

	if conn := mgr.Take(); conn != nil {
		t.Fatal("first Take should have yielded nil conn, but didn't")
	}

	dialconn, dialerr = &mockConn{}, nil
	select {
	case tickc <- time.Now():
	case <-time.After(time.Second):
		t.Fatal("manager isn't listening for a tick, despite a failed dial")
	}

	if !within(time.Second, func() bool {
		return mgr.Take() != nil
	}) {
		t.Fatal("second Take should have yielded good conn, but didn't")
	}
}
Esempio n. 5
0
func TestBadFactory(t *testing.T) {
	logger := log.NewNopLogger()

	factory := func(string) (endpoint.Endpoint, io.Closer, error) {
		return nil, nil, errors.New("kaboom")
	}

	client := &fakeClient{
		responses: map[string]*stdetcd.Response{"/foo": fakeResponse},
	}

	p, err := kitetcd.NewPublisher(client, "/foo", factory, logger)
	if err != nil {
		t.Fatalf("failed to create new publisher: %v", err)
	}
	defer p.Stop()

	endpoints, err := p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}

	if want, have := 0, len(endpoints); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}
Esempio n. 6
0
// NewScribeCollector returns a new Scribe-backed Collector. addr should be a
// TCP endpoint of the form "host:port". timeout is passed to the Thrift dial
// function NewTSocketFromAddrTimeout. batchSize and batchInterval control the
// maximum size and interval of a batch of spans; as soon as either limit is
// reached, the batch is sent. The logger is used to log errors, such as batch
// send failures; users should provide an appropriate context, if desired.
func NewScribeCollector(addr string, timeout time.Duration, options ...ScribeOption) (Collector, error) {
	factory := scribeClientFactory(addr, timeout)
	client, err := factory()
	if err != nil {
		return nil, err
	}
	c := &ScribeCollector{
		client:        client,
		factory:       factory,
		spanc:         make(chan *Span),
		sendc:         make(chan struct{}),
		batch:         []*scribe.LogEntry{},
		batchInterval: defaultBatchInterval * time.Second,
		batchSize:     100,
		shouldSample:  SampleRate(1.0, rand.Int63()),
		logger:        log.NewNopLogger(),
		category:      defaultScribeCategory,
		quit:          make(chan struct{}),
	}
	for _, option := range options {
		option(c)
	}
	c.nextSend = time.Now().Add(c.batchInterval)
	go c.loop()
	return c, nil
}
Esempio n. 7
0
func TestQuery(t *testing.T) {
	g := New(dbPath, log.NewNopLogger())
	ts, err := g.Query("", 0, 100)
	if err != nil {
		t.Fatalf("g.Query(%q, %d, %d): got error: %s", "", 0, 100, err)
	}
	if want, got := 24, len(ts); want != got {
		t.Errorf("g.Query(%q, %d, %d): want %d threads got %d", "", 0, 100, want, got)
	}

	ts, err = g.Query("", 0, 20)
	if err != nil {
		t.Fatalf("g.Query(%q, %d, %d): got error: %s", "", 0, 20, err)
	}
	if want, got := 20, len(ts); want != got {
		t.Errorf("g.Query(%q, %d, %d): want %d threads got %d", "", 0, 20, want, got)
	}

	ts, err = g.Query("", 20, 100)
	if err != nil {
		t.Fatalf("g.Query(%q, %d, %d): got error: %s", "", 20, 100, err)
	}
	if want, got := 4, len(ts); want != got {
		t.Errorf("g.Query(%q, %d, %d): want %d threads got %d", "", 20, 100, want, got)
	}
}
Esempio n. 8
0
func TestRefreshNoChange(t *testing.T) {
	var (
		addr      = &net.SRV{Target: "my-target", Port: 5678}
		addrs     = []*net.SRV{addr}
		name      = "my-name"
		ticker    = time.NewTicker(time.Second)
		lookups   = uint32(0)
		lookupSRV = func(string, string, string) (string, []*net.SRV, error) {
			atomic.AddUint32(&lookups, 1)
			return "", addrs, nil
		}
		e       = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
		factory = func(string) (endpoint.Endpoint, io.Closer, error) { return e, nil, nil }
		logger  = log.NewNopLogger()
	)

	ticker.Stop()
	tickc := make(chan time.Time)
	ticker.C = tickc

	p := NewPublisherDetailed(name, ticker, lookupSRV, factory, logger)
	defer p.Stop()

	if want, have := uint32(1), atomic.LoadUint32(&lookups); want != have {
		t.Errorf("want %d, have %d", want, have)
	}

	tickc <- time.Now()

	if want, have := uint32(2), atomic.LoadUint32(&lookups); want != have {
		t.Errorf("want %d, have %d", want, have)
	}
}
Esempio n. 9
0
func TestBadFactory(t *testing.T) {
	var (
		name      = "some-name"
		ticker    = time.NewTicker(time.Second)
		addr      = &net.SRV{Target: "foo", Port: 1234}
		addrs     = []*net.SRV{addr}
		lookupSRV = func(a, b, c string) (string, []*net.SRV, error) { return "", addrs, nil }
		creates   = uint32(0)
		factory   = func(s string) (endpoint.Endpoint, io.Closer, error) {
			atomic.AddUint32(&creates, 1)
			return nil, nil, errors.New("kaboom")
		}
		logger = log.NewNopLogger()
	)

	p := NewPublisherDetailed(name, ticker, lookupSRV, factory, logger)
	defer p.Stop()

	endpoints, err := p.Endpoints()
	if err != nil {
		t.Error(err)
	}
	if want, have := 0, len(endpoints); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
	if want, have := uint32(1), atomic.LoadUint32(&creates); want != have {
		t.Errorf("want %d, have %d", want, have)
	}
}
Esempio n. 10
0
func TestBadLookup(t *testing.T) {
	var (
		name      = "some-name"
		ticker    = time.NewTicker(time.Second)
		lookups   = uint32(0)
		lookupSRV = func(string, string, string) (string, []*net.SRV, error) {
			atomic.AddUint32(&lookups, 1)
			return "", nil, errors.New("kaboom")
		}
		e       = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
		factory = func(string) (endpoint.Endpoint, io.Closer, error) { return e, nil, nil }
		logger  = log.NewNopLogger()
	)

	p := NewPublisherDetailed(name, ticker, lookupSRV, factory, logger)
	defer p.Stop()

	endpoints, err := p.Endpoints()
	if err != nil {
		t.Error(err)
	}
	if want, have := 0, len(endpoints); want != have {
		t.Errorf("want %d, have %d", want, have)
	}
	if want, have := uint32(1), atomic.LoadUint32(&lookups); want != have {
		t.Errorf("want %d, have %d", want, have)
	}
}
Esempio n. 11
0
func TestGraphite(t *testing.T) {
	p, err := NewGraphiteProvider("network", "address", "prefix", time.Second, log.NewNopLogger())
	if err != nil {
		t.Fatal(err)
	}
	testProvider(t, "Graphite", p)
}
Esempio n. 12
0
func TestPublisher(t *testing.T) {
	var (
		target = "my-target"
		port   = uint16(1234)
		addr   = &net.SRV{Target: target, Port: port}
		addrs  = []*net.SRV{addr}
		name   = "my-name"
		ttl    = time.Second
		logger = log.NewNopLogger()
		e      = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
	)

	oldLookup := lookupSRV
	defer func() { lookupSRV = oldLookup }()
	lookupSRV = mockLookupSRV(addrs, nil, nil)

	factory := func(instance string) (endpoint.Endpoint, error) {
		if want, have := addr2instance(addr), instance; want != have {
			t.Errorf("want %q, have %q", want, have)
		}
		return e, nil
	}

	p, err := NewPublisher(name, ttl, factory, logger)
	if err != nil {
		t.Fatal(err)
	}
	defer p.Stop()

	if _, err := p.Endpoints(); err != nil {
		t.Fatal(err)
	}
}
Esempio n. 13
0
func TestBadFactory(t *testing.T) {
	var (
		logger = log.NewNopLogger()
		e      = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
	)

	factory := func(instance string) (endpoint.Endpoint, error) {
		return e, errors.New("_")
	}

	machines := []string{"http://127.0.0.1:2379"}
	client := etcd.NewClient(machines)
	p, err := NewPublisher(client, "/stream", factory, logger)
	if err != nil {
		t.Fatalf("failed to create new publisher: %v", err)
	}
	defer p.Stop()

	endpoints, err := p.Endpoints()
	if err != nil {
		t.Fatal(err)
	}
	if want, have := 0, len(endpoints); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}
Esempio n. 14
0
func TestBadFactory(t *testing.T) {
	var (
		addr    = &net.SRV{Target: "foo", Port: 1234}
		addrs   = []*net.SRV{addr}
		name    = "some-name"
		ttl     = time.Second
		factory = func(string) (endpoint.Endpoint, io.Closer, error) { return nil, nil, errors.New("kaboom") }
		logger  = log.NewNopLogger()
	)

	oldLookup := lookupSRV
	defer func() { lookupSRV = oldLookup }()
	lookupSRV = mockLookupSRV(addrs, nil, nil)

	p := NewPublisher(name, ttl, factory, logger)
	defer p.Stop()

	endpoints, err := p.Endpoints()
	if err != nil {
		t.Error(err)
	}
	if want, have := 0, len(endpoints); want != have {
		t.Errorf("want %q, have %q", want, have)
	}
}
Esempio n. 15
0
func TestPublisherStoppped(t *testing.T) {
	var (
		logger = log.NewNopLogger()
		e      = func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil }
	)

	factory := func(instance string) (endpoint.Endpoint, error) {
		return e, errors.New("_")
	}

	client := &fakeClient{
		responses: map[string]*stdetcd.Response{"/foo": fakeResponse},
	}

	p, err := kitetcd.NewPublisher(client, "/foo", factory, logger)
	if err != nil {
		t.Fatalf("failed to create new publisher: %v", err)
	}

	p.Stop()

	_, have := p.Endpoints()
	if want := loadbalancer.ErrPublisherStopped; want != have {
		t.Fatalf("want %v, have %v", want, have)
	}
}
Esempio n. 16
0
func TestRefreshNoChange(t *testing.T) {
	var (
		tick    = make(chan time.Time)
		target  = "my-target"
		port    = uint16(5678)
		addr    = &net.SRV{Target: target, Port: port}
		addrs   = []*net.SRV{addr}
		name    = "my-name"
		ttl     = time.Second
		factory = func(string) (endpoint.Endpoint, io.Closer, error) { return nil, nil, errors.New("kaboom") }
		logger  = log.NewNopLogger()
	)

	oldTicker := newTicker
	defer func() { newTicker = oldTicker }()
	newTicker = func(time.Duration) *time.Ticker { return &time.Ticker{C: tick} }

	var resolves uint64
	oldLookup := lookupSRV
	defer func() { lookupSRV = oldLookup }()
	lookupSRV = mockLookupSRV(addrs, nil, &resolves)

	p := NewPublisher(name, ttl, factory, logger)
	defer p.Stop()

	tick <- time.Now()
	if want, have := uint64(2), resolves; want != have {
		t.Errorf("want %d, have %d", want, have)
	}
}
Esempio n. 17
0
func TestDogStatsd(t *testing.T) {
	p, err := NewDogStatsdProvider("network", "address", "prefix", time.Second, log.NewNopLogger())
	if err != nil {
		t.Fatal(err)
	}
	testProvider(t, "DogStatsd", p)
}
Esempio n. 18
0
func testServer(t *testing.T) (cancel, step func(), resp <-chan *http.Response) {
	var (
		ctx, cancelfn = context.WithCancel(context.Background())
		stepch        = make(chan bool)
		endpoint      = func(context.Context, interface{}) (interface{}, error) { <-stepch; return struct{}{}, nil }
		response      = make(chan *http.Response)
		handler       = httptransport.Server{
			Context:            ctx,
			Endpoint:           endpoint,
			DecodeRequestFunc:  func(*http.Request) (interface{}, error) { return struct{}{}, nil },
			EncodeResponseFunc: func(http.ResponseWriter, interface{}) error { return nil },
			Before:             []httptransport.RequestFunc{func(ctx context.Context, r *http.Request) context.Context { return ctx }},
			After:              []httptransport.ResponseFunc{func(ctx context.Context, w http.ResponseWriter) { return }},
			Logger:             log.NewNopLogger(),
		}
	)
	go func() {
		server := httptest.NewServer(handler)
		defer server.Close()
		resp, err := http.Get(server.URL)
		if err != nil {
			t.Error(err)
			return
		}
		response <- resp
	}()
	return cancelfn, func() { stepch <- true }, response
}
Esempio n. 19
0
func TestHistogram(t *testing.T) {
	// The histogram test is actually like 4 gauge tests.
	prefix, name := "statsd.", "histogram_test"
	label, value := "abc", "def" // ignored for Graphite
	re50 := regexp.MustCompile(prefix + name + `.p50 ([0-9\.]+) [0-9]+`)
	re90 := regexp.MustCompile(prefix + name + `.p90 ([0-9\.]+) [0-9]+`)
	re95 := regexp.MustCompile(prefix + name + `.p95 ([0-9\.]+) [0-9]+`)
	re99 := regexp.MustCompile(prefix + name + `.p99 ([0-9\.]+) [0-9]+`)
	g := New(prefix, log.NewNopLogger())
	histogram := g.NewHistogram(name, 50).With(label, value)
	quantiles := func() (float64, float64, float64, float64) {
		var buf bytes.Buffer
		g.WriteTo(&buf)
		match50 := re50.FindStringSubmatch(buf.String())
		p50, _ := strconv.ParseFloat(match50[1], 64)
		match90 := re90.FindStringSubmatch(buf.String())
		p90, _ := strconv.ParseFloat(match90[1], 64)
		match95 := re95.FindStringSubmatch(buf.String())
		p95, _ := strconv.ParseFloat(match95[1], 64)
		match99 := re99.FindStringSubmatch(buf.String())
		p99, _ := strconv.ParseFloat(match99[1], 64)
		return p50, p90, p95, p99
	}
	if err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {
		t.Fatal(err)
	}
}
Esempio n. 20
0
func TestPublisherAddressOverride(t *testing.T) {
	var (
		ctx    = context.Background()
		logger = log.NewNopLogger()
		client = newTestClient(consulState)
	)

	p, err := NewPublisher(client, testFactory, logger, "search", "db")
	if err != nil {
		t.Fatalf("publisher setup failed: %s", err)
	}
	defer p.Stop()

	eps, err := p.Endpoints()
	if err != nil {
		t.Fatalf("endpoints failed: %s", err)
	}

	if have, want := len(eps), 1; have != want {
		t.Fatalf("have %v, want %v", have, want)
	}

	ins, err := eps[0](ctx, struct{}{})
	if err != nil {
		t.Fatal(err)
	}

	if have, want := ins.(string), "10.0.0.10:9000"; have != want {
		t.Errorf("have %#v, want %#v", have, want)
	}
}
Esempio n. 21
0
func loadConfiguration(l log.Logger, cfgFile string) (Config, error) {
	if l == nil {
		l = log.NewNopLogger()
	}

	var err error
	var buf []byte

	if _, err := os.Stat(cfgFile); err == nil {
		l.Log("level", "debug", "msg", "Loading config", "source", cfgFile)
		buf, err = ioutil.ReadFile(cfgFile)
		if err != nil {
			l.Log("level", "error", "msg", "Could not load config", "source", cfgFile)
			buf = []byte(defaultConfig)
		}
	} else {
		l.Log("level", "error", "msg", "Loading default config due to error", "err", err)
		buf = []byte(defaultConfig)
	}

	var cfg Config
	err = yaml.Unmarshal(buf, &cfg)
	if err != nil {
		return Config{}, err
	}

	return cfg, nil
}
Esempio n. 22
0
// NewProvider will use the values in the Metrics config object
// to generate a new go-kit/metrics/provider.Provider implementation.
// If no type is given, a no-op implementation will be used.
func (cfg Config) NewProvider() (provider.Provider, error) {
	if cfg.Logger == nil {
		cfg.Logger = log.NewNopLogger()
	}
	if cfg.Path == "" {
		cfg.Path = "/debug/vars"
	}
	if cfg.Interval == 0 {
		cfg.Interval = time.Second * 30
	}
	switch cfg.Type {
	case Statsd:
		return provider.NewStatsdProvider(cfg.Network, cfg.Addr,
			cfg.Prefix, cfg.Interval, cfg.Logger)
	case DogStatsd:
		return provider.NewDogStatsdProvider(cfg.Network, cfg.Addr,
			cfg.Prefix, cfg.Interval, cfg.Logger)
	case Graphite:
		return provider.NewGraphiteProvider(cfg.Network, cfg.Addr,
			cfg.Prefix, cfg.Interval, cfg.Logger)
	case Prometheus:
		return provider.NewPrometheusProvider(cfg.Namespace, cfg.Subsystem), nil
	case Expvar:
		return provider.NewExpvarProvider(cfg.Prefix), nil
	default:
		return provider.NewDiscardProvider(), nil
	}
}
Esempio n. 23
0
func BenchmarkDiscard(b *testing.B) {
	logger := log.NewNopLogger()
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		logger.Log("k", "v")
	}
}
Esempio n. 24
0
func TestNopLogger(t *testing.T) {
	logger := log.NewNopLogger()
	if err := logger.Log("abc", 123); err != nil {
		t.Error(err)
	}
	if err := log.NewContext(logger).With("def", "ghi").Log(); err != nil {
		t.Error(err)
	}
}
Esempio n. 25
0
func BenchmarkValueBindingTimestamp(b *testing.B) {
	logger := log.NewNopLogger()
	lc := log.NewContext(logger).With("ts", log.DefaultTimestamp)
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		lc.Log("k", "v")
	}
}
Esempio n. 26
0
func BenchmarkOneWith(b *testing.B) {
	logger := log.NewNopLogger()
	lc := log.NewContext(logger).With("k", "v")
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		lc.Log("k", "v")
	}
}
Esempio n. 27
0
func TestBadFactory(t *testing.T) {
	cache := New(func(string) (endpoint.Endpoint, io.Closer, error) {
		return nil, nil, errors.New("bad factory")
	}, log.NewNopLogger())

	cache.Update([]string{"foo:1234", "bar:5678"})
	if want, have := 0, len(cache.Endpoints()); want != have {
		t.Errorf("want %d, have %d", want, have)
	}
}
Esempio n. 28
0
func TestCounter(t *testing.T) {
	prefix, name := "abc.", "def"
	label, value := "label", "value" // ignored
	regex := `^` + prefix + name + `:([0-9\.]+)\|c$`
	s := New(prefix, log.NewNopLogger())
	counter := s.NewCounter(name, 1.0).With(label, value)
	valuef := teststat.SumLines(s, regex)
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal(err)
	}
}
Esempio n. 29
0
func TestTimingSampled(t *testing.T) {
	prefix, name := "statsd.", "sampled_timing_test"
	label, value := "foo", "bar" // ignored
	regex := `^` + prefix + name + `:([0-9\.]+)\|ms\|@0\.01[0]*$`
	s := New(prefix, log.NewNopLogger())
	timing := s.NewTiming(name, 0.01).With(label, value)
	quantiles := teststat.Quantiles(s, regex, 50)
	if err := teststat.TestHistogram(timing, quantiles, 0.02); err != nil {
		t.Fatal(err)
	}
}
Esempio n. 30
0
func TestTiming(t *testing.T) {
	prefix, name := "statsd.", "timing_test"
	label, value := "abc", "def" // ignored
	regex := `^` + prefix + name + `:([0-9\.]+)\|ms$`
	s := New(prefix, log.NewNopLogger())
	timing := s.NewTiming(name, 1.0).With(label, value)
	quantiles := teststat.Quantiles(s, regex, 50) // no |@0.X
	if err := teststat.TestHistogram(timing, quantiles, 0.01); err != nil {
		t.Fatal(err)
	}
}