mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
Fix vendoring
Signed-off-by: Drasko Draskovic <drasko.draskovic@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
BUILD_DIR = build
|
||||
SERVICES = agent cli
|
||||
SERVICES = manager agent cli
|
||||
CGO_ENABLED ?= 0
|
||||
GOARCH ?= amd64
|
||||
VERSION ?= $(shell git describe --abbrev=0 --tags)
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
module github.com/ultravioletrs/manager
|
||||
module github.com/ultravioletrs/cocos-ai
|
||||
|
||||
go 1.20
|
||||
go 1.21.3
|
||||
|
||||
require (
|
||||
github.com/caarlos0/env/v7 v7.1.0
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20221205150000-2939327a8519
|
||||
github.com/go-kit/kit v0.12.0
|
||||
github.com/go-kit/kit v0.13.0
|
||||
github.com/go-zoo/bone v1.3.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/mainflux/mainflux v0.0.0-20230726142711-2b78902e0170
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/ultravioletrs/agent v0.0.0-20230905145147-a3c466449737
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel v1.16.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.16.0
|
||||
go.opentelemetry.io/otel/sdk v1.16.0
|
||||
go.opentelemetry.io/otel/trace v1.16.0
|
||||
golang.org/x/sync v0.3.0
|
||||
google.golang.org/grpc v1.57.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/mainflux/mainflux v0.12.0
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/ultravioletrs/agent v0.0.0-20230921133856-4f0580560012
|
||||
github.com/ultravioletrs/manager v0.0.0-20231009152116-740a63128799
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.20.0
|
||||
go.opentelemetry.io/otel v1.19.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel/sdk v1.19.0
|
||||
go.opentelemetry.io/otel/trace v1.19.0
|
||||
golang.org/x/sync v0.4.0
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
)
|
||||
|
||||
@@ -32,15 +36,15 @@ require (
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.11.1 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
)
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
# package log
|
||||
|
||||
**Deprecation notice:** The core Go kit log packages (log, log/level, log/term, and
|
||||
log/syslog) have been moved to their own repository at github.com/go-kit/log.
|
||||
The corresponding packages in this directory remain for backwards compatibility.
|
||||
Their types alias the types and their functions call the functions provided by
|
||||
the new repository. Using either import path should be equivalent. Prefer the
|
||||
new import path when practical.
|
||||
|
||||
______
|
||||
|
||||
`package log` provides a minimal interface for structured logging in services.
|
||||
It may be wrapped to encode conventions, enforce type-safety, provide leveled
|
||||
logging, and so on. It can be used for both typical application log events,
|
||||
and log-structured data streams.
|
||||
|
||||
## Structured logging
|
||||
|
||||
Structured logging is, basically, conceding to the reality that logs are
|
||||
_data_, and warrant some level of schematic rigor. Using a stricter,
|
||||
key/value-oriented message format for our logs, containing contextual and
|
||||
semantic information, makes it much easier to get insight into the
|
||||
operational activity of the systems we build. Consequently, `package log` is
|
||||
of the strong belief that "[the benefits of structured logging outweigh the
|
||||
minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)".
|
||||
|
||||
Migrating from unstructured to structured logging is probably a lot easier
|
||||
than you'd expect.
|
||||
|
||||
```go
|
||||
// Unstructured
|
||||
log.Printf("HTTP server listening on %s", addr)
|
||||
|
||||
// Structured
|
||||
logger.Log("transport", "HTTP", "addr", addr, "msg", "listening")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Typical application logging
|
||||
|
||||
```go
|
||||
w := log.NewSyncWriter(os.Stderr)
|
||||
logger := log.NewLogfmtLogger(w)
|
||||
logger.Log("question", "what is the meaning of life?", "answer", 42)
|
||||
|
||||
// Output:
|
||||
// question="what is the meaning of life?" answer=42
|
||||
```
|
||||
|
||||
### Contextual Loggers
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
|
||||
logger = log.With(logger, "instance_id", 123)
|
||||
|
||||
logger.Log("msg", "starting")
|
||||
NewWorker(log.With(logger, "component", "worker")).Run()
|
||||
NewSlacker(log.With(logger, "component", "slacker")).Run()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// instance_id=123 msg=starting
|
||||
// instance_id=123 component=worker msg=running
|
||||
// instance_id=123 component=slacker msg=running
|
||||
```
|
||||
|
||||
### Interact with stdlib logger
|
||||
|
||||
Redirect stdlib logger to Go kit logger.
|
||||
|
||||
```go
|
||||
import (
|
||||
"os"
|
||||
stdlog "log"
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout))
|
||||
stdlog.SetOutput(kitlog.NewStdlibAdapter(logger))
|
||||
stdlog.Print("I sure like pie")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"}
|
||||
```
|
||||
|
||||
Or, if, for legacy reasons, you need to pipe all of your logging through the
|
||||
stdlib log package, you can redirect Go kit logger to the stdlib logger.
|
||||
|
||||
```go
|
||||
logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{})
|
||||
logger.Log("legacy", true, "msg", "at least it's something")
|
||||
|
||||
// Output:
|
||||
// 2016/01/01 12:34:56 legacy=true msg="at least it's something"
|
||||
```
|
||||
|
||||
### Timestamps and callers
|
||||
|
||||
```go
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
|
||||
logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
|
||||
|
||||
logger.Log("msg", "hello")
|
||||
|
||||
// Output:
|
||||
// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello
|
||||
```
|
||||
|
||||
## Levels
|
||||
|
||||
Log levels are supported via the [level package](https://godoc.org/github.com/go-kit/kit/log/level).
|
||||
|
||||
## Supported output formats
|
||||
|
||||
- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write))
|
||||
- JSON
|
||||
|
||||
## Enhancements
|
||||
|
||||
`package log` is centered on the one-method Logger interface.
|
||||
|
||||
```go
|
||||
type Logger interface {
|
||||
Log(keyvals ...interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
This interface, and its supporting code like is the product of much iteration
|
||||
and evaluation. For more details on the evolution of the Logger interface,
|
||||
see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1),
|
||||
a talk by [Chris Hines](https://github.com/ChrisHines).
|
||||
Also, please see
|
||||
[#63](https://github.com/go-kit/kit/issues/63),
|
||||
[#76](https://github.com/go-kit/kit/pull/76),
|
||||
[#131](https://github.com/go-kit/kit/issues/131),
|
||||
[#157](https://github.com/go-kit/kit/pull/157),
|
||||
[#164](https://github.com/go-kit/kit/issues/164), and
|
||||
[#252](https://github.com/go-kit/kit/pull/252)
|
||||
to review historical conversations about package log and the Logger interface.
|
||||
|
||||
Value-add packages and suggestions,
|
||||
like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level),
|
||||
are of course welcome. Good proposals should
|
||||
|
||||
- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With),
|
||||
- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and
|
||||
- Be friendly to packages that accept only an unadorned log.Logger.
|
||||
|
||||
## Benchmarks & comparisons
|
||||
|
||||
There are a few Go logging benchmarks and comparisons that include Go kit's package log.
|
||||
|
||||
- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log
|
||||
- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// Package log provides a structured logger.
|
||||
//
|
||||
// Deprecated: Use github.com/go-kit/log instead.
|
||||
//
|
||||
// Structured logging produces logs easily consumed later by humans or
|
||||
// machines. Humans might be interested in debugging errors, or tracing
|
||||
// specific requests. Machines might be interested in counting interesting
|
||||
// events, or aggregating information for off-line processing. In both cases,
|
||||
// it is important that the log messages are structured and actionable.
|
||||
// Package log is designed to encourage both of these best practices.
|
||||
//
|
||||
// Basic Usage
|
||||
//
|
||||
// The fundamental interface is Logger. Loggers create log events from
|
||||
// key/value data. The Logger interface has a single method, Log, which
|
||||
// accepts a sequence of alternating key/value pairs, which this package names
|
||||
// keyvals.
|
||||
//
|
||||
// type Logger interface {
|
||||
// Log(keyvals ...interface{}) error
|
||||
// }
|
||||
//
|
||||
// Here is an example of a function using a Logger to create log events.
|
||||
//
|
||||
// func RunTask(task Task, logger log.Logger) string {
|
||||
// logger.Log("taskID", task.ID, "event", "starting task")
|
||||
// ...
|
||||
// logger.Log("taskID", task.ID, "event", "task complete")
|
||||
// }
|
||||
//
|
||||
// The keys in the above example are "taskID" and "event". The values are
|
||||
// task.ID, "starting task", and "task complete". Every key is followed
|
||||
// immediately by its value.
|
||||
//
|
||||
// Keys are usually plain strings. Values may be any type that has a sensible
|
||||
// encoding in the chosen log format. With structured logging it is a good
|
||||
// idea to log simple values without formatting them. This practice allows
|
||||
// the chosen logger to encode values in the most appropriate way.
|
||||
//
|
||||
// Contextual Loggers
|
||||
//
|
||||
// A contextual logger stores keyvals that it includes in all log events.
|
||||
// Building appropriate contextual loggers reduces repetition and aids
|
||||
// consistency in the resulting log output. With, WithPrefix, and WithSuffix
|
||||
// add context to a logger. We can use With to improve the RunTask example.
|
||||
//
|
||||
// func RunTask(task Task, logger log.Logger) string {
|
||||
// logger = log.With(logger, "taskID", task.ID)
|
||||
// logger.Log("event", "starting task")
|
||||
// ...
|
||||
// taskHelper(task.Cmd, logger)
|
||||
// ...
|
||||
// logger.Log("event", "task complete")
|
||||
// }
|
||||
//
|
||||
// The improved version emits the same log events as the original for the
|
||||
// first and last calls to Log. Passing the contextual logger to taskHelper
|
||||
// enables each log event created by taskHelper to include the task.ID even
|
||||
// though taskHelper does not have access to that value. Using contextual
|
||||
// loggers this way simplifies producing log output that enables tracing the
|
||||
// life cycle of individual tasks. (See the Contextual example for the full
|
||||
// code of the above snippet.)
|
||||
//
|
||||
// Dynamic Contextual Values
|
||||
//
|
||||
// A Valuer function stored in a contextual logger generates a new value each
|
||||
// time an event is logged. The Valuer example demonstrates how this feature
|
||||
// works.
|
||||
//
|
||||
// Valuers provide the basis for consistently logging timestamps and source
|
||||
// code location. The log package defines several valuers for that purpose.
|
||||
// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and
|
||||
// DefaultCaller. A common logger initialization sequence that ensures all log
|
||||
// entries contain a timestamp and source location looks like this:
|
||||
//
|
||||
// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
|
||||
// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
|
||||
//
|
||||
// Concurrent Safety
|
||||
//
|
||||
// Applications with multiple goroutines want each log event written to the
|
||||
// same logger to remain separate from other log events. Package log provides
|
||||
// two simple solutions for concurrent safe logging.
|
||||
//
|
||||
// NewSyncWriter wraps an io.Writer and serializes each call to its Write
|
||||
// method. Using a SyncWriter has the benefit that the smallest practical
|
||||
// portion of the logging logic is performed within a mutex, but it requires
|
||||
// the formatting Logger to make only one call to Write per log event.
|
||||
//
|
||||
// NewSyncLogger wraps any Logger and serializes each call to its Log method.
|
||||
// Using a SyncLogger has the benefit that it guarantees each log event is
|
||||
// handled atomically within the wrapped logger, but it typically serializes
|
||||
// both the formatting and output logic. Use a SyncLogger if the formatting
|
||||
// logger may perform multiple writes per log event.
|
||||
//
|
||||
// Error Handling
|
||||
//
|
||||
// This package relies on the practice of wrapping or decorating loggers with
|
||||
// other loggers to provide composable pieces of functionality. It also means
|
||||
// that Logger.Log must return an error because some
|
||||
// implementations—especially those that output log data to an io.Writer—may
|
||||
// encounter errors that cannot be handled locally. This in turn means that
|
||||
// Loggers that wrap other loggers should return errors from the wrapped
|
||||
// logger up the stack.
|
||||
//
|
||||
// Fortunately, the decorator pattern also provides a way to avoid the
|
||||
// necessity to check for errors every time an application calls Logger.Log.
|
||||
// An application required to panic whenever its Logger encounters
|
||||
// an error could initialize its logger as follows.
|
||||
//
|
||||
// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
|
||||
// logger := log.LoggerFunc(func(keyvals ...interface{}) error {
|
||||
// if err := fmtlogger.Log(keyvals...); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// return nil
|
||||
// })
|
||||
package log
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a
|
||||
// single JSON object. Each log event produces no more than one call to
|
||||
// w.Write. The passed Writer must be safe for concurrent use by multiple
|
||||
// goroutines if the returned Logger will be used concurrently.
|
||||
func NewJSONLogger(w io.Writer) Logger {
|
||||
return log.NewJSONLogger(w)
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Logger is the fundamental interface for all log operations. Log creates a
|
||||
// log event from keyvals, a variadic sequence of alternating keys and values.
|
||||
// Implementations must be safe for concurrent use by multiple goroutines. In
|
||||
// particular, any implementation of Logger that appends to keyvals or
|
||||
// modifies or retains any of its elements must make a copy first.
|
||||
type Logger = log.Logger
|
||||
|
||||
// ErrMissingValue is appended to keyvals slices with odd length to substitute
|
||||
// the missing value.
|
||||
var ErrMissingValue = log.ErrMissingValue
|
||||
|
||||
// With returns a new contextual logger with keyvals prepended to those passed
|
||||
// to calls to Log. If logger is also a contextual logger created by With,
|
||||
// WithPrefix, or WithSuffix, keyvals is appended to the existing context.
|
||||
//
|
||||
// The returned Logger replaces all value elements (odd indexes) containing a
|
||||
// Valuer with their generated value for each call to its Log method.
|
||||
func With(logger Logger, keyvals ...interface{}) Logger {
|
||||
return log.With(logger, keyvals...)
|
||||
}
|
||||
|
||||
// WithPrefix returns a new contextual logger with keyvals prepended to those
|
||||
// passed to calls to Log. If logger is also a contextual logger created by
|
||||
// With, WithPrefix, or WithSuffix, keyvals is prepended to the existing context.
|
||||
//
|
||||
// The returned Logger replaces all value elements (odd indexes) containing a
|
||||
// Valuer with their generated value for each call to its Log method.
|
||||
func WithPrefix(logger Logger, keyvals ...interface{}) Logger {
|
||||
return log.WithPrefix(logger, keyvals...)
|
||||
}
|
||||
|
||||
// WithSuffix returns a new contextual logger with keyvals appended to those
|
||||
// passed to calls to Log. If logger is also a contextual logger created by
|
||||
// With, WithPrefix, or WithSuffix, keyvals is appended to the existing context.
|
||||
//
|
||||
// The returned Logger replaces all value elements (odd indexes) containing a
|
||||
// Valuer with their generated value for each call to its Log method.
|
||||
func WithSuffix(logger Logger, keyvals ...interface{}) Logger {
|
||||
return log.WithSuffix(logger, keyvals...)
|
||||
}
|
||||
|
||||
// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If
|
||||
// f is a function with the appropriate signature, LoggerFunc(f) is a Logger
|
||||
// object that calls f.
|
||||
type LoggerFunc = log.LoggerFunc
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in
|
||||
// logfmt format. Each log event produces no more than one call to w.Write.
|
||||
// The passed Writer must be safe for concurrent use by multiple goroutines if
|
||||
// the returned Logger will be used concurrently.
|
||||
func NewLogfmtLogger(w io.Writer) Logger {
|
||||
return log.NewLogfmtLogger(w)
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package log
|
||||
|
||||
import "github.com/go-kit/log"
|
||||
|
||||
// NewNopLogger returns a logger that doesn't do anything.
|
||||
func NewNopLogger() Logger {
|
||||
return log.NewNopLogger()
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's
|
||||
// designed to be passed to a Go kit logger as the writer, for cases where
|
||||
// it's necessary to redirect all Go kit log output to the stdlib logger.
|
||||
//
|
||||
// If you have any choice in the matter, you shouldn't use this. Prefer to
|
||||
// redirect the stdlib log to the Go kit logger via NewStdlibAdapter.
|
||||
type StdlibWriter = log.StdlibWriter
|
||||
|
||||
// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib
|
||||
// logger's SetOutput. It will extract date/timestamps, filenames, and
|
||||
// messages, and place them under relevant keys.
|
||||
type StdlibAdapter = log.StdlibAdapter
|
||||
|
||||
// StdlibAdapterOption sets a parameter for the StdlibAdapter.
|
||||
type StdlibAdapterOption = log.StdlibAdapterOption
|
||||
|
||||
// TimestampKey sets the key for the timestamp field. By default, it's "ts".
|
||||
func TimestampKey(key string) StdlibAdapterOption {
|
||||
return log.TimestampKey(key)
|
||||
}
|
||||
|
||||
// FileKey sets the key for the file and line field. By default, it's "caller".
|
||||
func FileKey(key string) StdlibAdapterOption {
|
||||
return log.FileKey(key)
|
||||
}
|
||||
|
||||
// MessageKey sets the key for the actual log message. By default, it's "msg".
|
||||
func MessageKey(key string) StdlibAdapterOption {
|
||||
return log.MessageKey(key)
|
||||
}
|
||||
|
||||
// Prefix configures the adapter to parse a prefix from stdlib log events. If
|
||||
// you provide a non-empty prefix to the stdlib logger, then your should provide
|
||||
// that same prefix to the adapter via this option.
|
||||
//
|
||||
// By default, the prefix isn't included in the msg key. Set joinPrefixToMsg to
|
||||
// true if you want to include the parsed prefix in the msg.
|
||||
func Prefix(prefix string, joinPrefixToMsg bool) StdlibAdapterOption {
|
||||
return log.Prefix(prefix, joinPrefixToMsg)
|
||||
}
|
||||
|
||||
// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed
|
||||
// logger. It's designed to be passed to log.SetOutput.
|
||||
func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer {
|
||||
return log.NewStdlibAdapter(logger, options...)
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// SwapLogger wraps another logger that may be safely replaced while other
|
||||
// goroutines use the SwapLogger concurrently. The zero value for a SwapLogger
|
||||
// will discard all log events without error.
|
||||
//
|
||||
// SwapLogger serves well as a package global logger that can be changed by
|
||||
// importers.
|
||||
type SwapLogger = log.SwapLogger
|
||||
|
||||
// NewSyncWriter returns a new writer that is safe for concurrent use by
|
||||
// multiple goroutines. Writes to the returned writer are passed on to w. If
|
||||
// another write is already in progress, the calling goroutine blocks until
|
||||
// the writer is available.
|
||||
//
|
||||
// If w implements the following interface, so does the returned writer.
|
||||
//
|
||||
// interface {
|
||||
// Fd() uintptr
|
||||
// }
|
||||
func NewSyncWriter(w io.Writer) io.Writer {
|
||||
return log.NewSyncWriter(w)
|
||||
}
|
||||
|
||||
// NewSyncLogger returns a logger that synchronizes concurrent use of the
|
||||
// wrapped logger. When multiple goroutines use the SyncLogger concurrently
|
||||
// only one goroutine will be allowed to log to the wrapped logger at a time.
|
||||
// The other goroutines will block until the logger is available.
|
||||
func NewSyncLogger(logger Logger) Logger {
|
||||
return log.NewSyncLogger(logger)
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// A Valuer generates a log value. When passed to With, WithPrefix, or
|
||||
// WithSuffix in a value element (odd indexes), it represents a dynamic
|
||||
// value which is re-evaluated with each log event.
|
||||
type Valuer = log.Valuer
|
||||
|
||||
// Timestamp returns a timestamp Valuer. It invokes the t function to get the
|
||||
// time; unless you are doing something tricky, pass time.Now.
|
||||
//
|
||||
// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which
|
||||
// are TimestampFormats that use the RFC3339Nano format.
|
||||
func Timestamp(t func() time.Time) Valuer {
|
||||
return log.Timestamp(t)
|
||||
}
|
||||
|
||||
// TimestampFormat returns a timestamp Valuer with a custom time format. It
|
||||
// invokes the t function to get the time to format; unless you are doing
|
||||
// something tricky, pass time.Now. The layout string is passed to
|
||||
// Time.Format.
|
||||
//
|
||||
// Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which
|
||||
// are TimestampFormats that use the RFC3339Nano format.
|
||||
func TimestampFormat(t func() time.Time, layout string) Valuer {
|
||||
return log.TimestampFormat(t, layout)
|
||||
}
|
||||
|
||||
// Caller returns a Valuer that returns a file and line from a specified depth
|
||||
// in the callstack. Users will probably want to use DefaultCaller.
|
||||
func Caller(depth int) Valuer {
|
||||
return log.Caller(depth)
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultTimestamp is a Valuer that returns the current wallclock time,
|
||||
// respecting time zones, when bound.
|
||||
DefaultTimestamp = log.DefaultTimestamp
|
||||
|
||||
// DefaultTimestampUTC is a Valuer that returns the current time in UTC
|
||||
// when bound.
|
||||
DefaultTimestampUTC = log.DefaultTimestampUTC
|
||||
|
||||
// DefaultCaller is a Valuer that returns the file and line where the Log
|
||||
// method was invoked. It can only be used with log.With.
|
||||
DefaultCaller = log.DefaultCaller
|
||||
)
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type interceptingWriter struct {
|
||||
http.ResponseWriter
|
||||
code int
|
||||
written int64
|
||||
}
|
||||
|
||||
// WriteHeader may not be explicitly called, so care must be taken to
|
||||
// initialize w.code to its default value of http.StatusOK.
|
||||
func (w *interceptingWriter) WriteHeader(code int) {
|
||||
w.code = code
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *interceptingWriter) Write(p []byte) (int, error) {
|
||||
n, err := w.ResponseWriter.Write(p)
|
||||
w.written += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// reimplementInterfaces returns a wrapped version of the embedded ResponseWriter
|
||||
// and selectively implements the same combination of additional interfaces as
|
||||
// the wrapped one. The interfaces it may implement are: http.Hijacker,
|
||||
// http.CloseNotifier, http.Pusher, http.Flusher and io.ReaderFrom. The standard
|
||||
// library is known to assert the existence of these interfaces and behaves
|
||||
// differently. This implementation is derived from
|
||||
// https://github.com/felixge/httpsnoop.
|
||||
func (w *interceptingWriter) reimplementInterfaces() http.ResponseWriter {
|
||||
var (
|
||||
hj, i0 = w.ResponseWriter.(http.Hijacker)
|
||||
cn, i1 = w.ResponseWriter.(http.CloseNotifier)
|
||||
pu, i2 = w.ResponseWriter.(http.Pusher)
|
||||
fl, i3 = w.ResponseWriter.(http.Flusher)
|
||||
rf, i4 = w.ResponseWriter.(io.ReaderFrom)
|
||||
)
|
||||
|
||||
switch {
|
||||
case !i0 && !i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
}{w}
|
||||
case !i0 && !i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
io.ReaderFrom
|
||||
}{w, rf}
|
||||
case !i0 && !i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
}{w, fl}
|
||||
case !i0 && !i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, fl, rf}
|
||||
case !i0 && !i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
}{w, pu}
|
||||
case !i0 && !i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{w, pu, rf}
|
||||
case !i0 && !i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{w, pu, fl}
|
||||
case !i0 && !i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, pu, fl, rf}
|
||||
case !i0 && i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
}{w, cn}
|
||||
case !i0 && i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
io.ReaderFrom
|
||||
}{w, cn, rf}
|
||||
case !i0 && i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
}{w, cn, fl}
|
||||
case !i0 && i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, cn, fl, rf}
|
||||
case !i0 && i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
}{w, cn, pu}
|
||||
case !i0 && i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{w, cn, pu, rf}
|
||||
case !i0 && i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{w, cn, pu, fl}
|
||||
case !i0 && i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, cn, pu, fl, rf}
|
||||
case i0 && !i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
}{w, hj}
|
||||
case i0 && !i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
io.ReaderFrom
|
||||
}{w, hj, rf}
|
||||
case i0 && !i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Flusher
|
||||
}{w, hj, fl}
|
||||
case i0 && !i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, hj, fl, rf}
|
||||
case i0 && !i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
}{w, hj, pu}
|
||||
case i0 && !i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{w, hj, pu, rf}
|
||||
case i0 && !i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{w, hj, pu, fl}
|
||||
case i0 && !i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, hj, pu, fl, rf}
|
||||
case i0 && i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
}{w, hj, cn}
|
||||
case i0 && i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
io.ReaderFrom
|
||||
}{w, hj, cn, rf}
|
||||
case i0 && i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
}{w, hj, cn, fl}
|
||||
case i0 && i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, hj, cn, fl, rf}
|
||||
case i0 && i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
}{w, hj, cn, pu}
|
||||
case i0 && i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{w, hj, cn, pu, rf}
|
||||
case i0 && i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{w, hj, cn, pu, fl}
|
||||
case i0 && i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{w, hj, cn, pu, fl, rf}
|
||||
default:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
}{w}
|
||||
}
|
||||
}
|
||||
+1
-20
@@ -104,7 +104,7 @@ func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
f(ctx, iw.code, r)
|
||||
}
|
||||
}()
|
||||
w = iw
|
||||
w = iw.reimplementInterfaces()
|
||||
}
|
||||
|
||||
for _, f := range s.before {
|
||||
@@ -223,22 +223,3 @@ type StatusCoder interface {
|
||||
type Headerer interface {
|
||||
Headers() http.Header
|
||||
}
|
||||
|
||||
type interceptingWriter struct {
|
||||
http.ResponseWriter
|
||||
code int
|
||||
written int64
|
||||
}
|
||||
|
||||
// WriteHeader may not be explicitly called, so care must be taken to
|
||||
// initialize w.code to its default value of http.StatusOK.
|
||||
func (w *interceptingWriter) WriteHeader(code int) {
|
||||
w.code = code
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *interceptingWriter) Write(p []byte) (int, error) {
|
||||
n, err := w.ResponseWriter.Write(p)
|
||||
w.written += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/empty/empty.proto
|
||||
|
||||
package empty
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/empty.proto.
|
||||
|
||||
type Empty = emptypb.Empty
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_empty_empty_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc = []byte{
|
||||
0x0a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||
0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x65, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_empty_empty_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_empty_empty_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_empty_empty_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_empty_empty_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_empty_empty_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_empty_empty_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_empty_empty_proto_depIdxs = nil
|
||||
}
|
||||
-5
@@ -6,8 +6,3 @@
|
||||
|
||||
build
|
||||
|
||||
# tools
|
||||
tools/e2e/e2e
|
||||
tools/mqtt-bench/mqtt-bench
|
||||
tools/provision/provision
|
||||
tools/provision/mfconn.toml
|
||||
|
||||
-92
@@ -11,98 +11,6 @@ Otherwise, whole log in a similar format can be observed via:
|
||||
git log --pretty=oneline --abbrev-commit
|
||||
```
|
||||
|
||||
## 0.13.0 - 15. APR 2022.
|
||||
### Features and Bugfixes
|
||||
- NOISSUE - Update changelog for release 0.13.0
|
||||
- Update VerneMQ release (#1593)
|
||||
- NOISSUE - Update changelog and readme for release 0.13.0
|
||||
- MF-1582 - Fix lora-adapter MQTT client (#1583)
|
||||
- NOISSUE - Fix CoAP adapter (#1572)
|
||||
- NOISSUE - Unify MF_INFLUX_READER_DB_HOST and MF_INFLUX_WRITER_DB_HOST envars (#1585)
|
||||
- MF-1580 - Influxdb Writer changes format of update-time to string (#1581)
|
||||
- MF-1575 Add 'Name' field to ListMembers response in things svc (#1576)
|
||||
- MF-1565 - Document Bearer, Thing and Basic Authorization header (#1566)
|
||||
- MF-1567 - Use Bearer, Thing or Basic scheme in Authorization header (#1568)
|
||||
- MF-1348 - Add transport errors logging (#1544)
|
||||
- NOISSUE - Add nats wrapper for COAP (#1569)
|
||||
- MF-1469 - Indicate proper authentication scheme in Authorization header (#1523)
|
||||
- MF-1240 - Return to service transport layer only service errors (#1559)
|
||||
- Update dependencies (#1564)
|
||||
- NOISSUE - Separate Keto hosts for read and write (#1563)
|
||||
- MF-1551 - Fix Cobra usage commands and clean unnecessary struct types (#1558)
|
||||
- MF-1257 - Access messages from readers endpoint with user access token (#1470)
|
||||
- NOISSUE - Refactor MQTT subscriber (#1561)
|
||||
- MF-1059 - Add TLS support for email (#1560)
|
||||
- MF-1261 - Use StatusUnauthorized for authn and StatusForbidden for authz (#1538)
|
||||
- NOISSUE - Fix auth members list response (#1555)
|
||||
- MF-1263 - Move repeating errors to the separate package (#1540)
|
||||
- NOISSUE - Add API keys functions to CLI (#1537)
|
||||
- Fix SDK for group members (#1553)
|
||||
- NOISSUE - Fix Swagger UI (#1552)
|
||||
- MF-1008 - Make token duration configurable (#1550)
|
||||
- MF-1308 - Use IETF Health Check standard (#1541)
|
||||
- Fix user listing access control (#1546)
|
||||
- Update dependencies (#1545)
|
||||
- MF-1478 - TimescaleDB writer and reader add-on (#1542)
|
||||
- MF-1149 - Add AsyncAPI MQTT API doc (#1539)
|
||||
- MF-1535 - Add API keys functions to SDK (#1536)
|
||||
- NOISSUE - Add view and list serials endpoints in certs service (#1483)
|
||||
- MF-1516 - Fix API key issuing (#1530)
|
||||
- NOISSUE - Add disconnect endpoint in nginx conf (#1528)
|
||||
- NOISSUE - Add timestamp transformation rules for specifc JSON fields (#1514)
|
||||
- MF-1425 - Support external UUIDs for Things and Channels (#1518)
|
||||
- MF-1521 - Fix email headers (#1522)
|
||||
- Fix SenML lib dependency version (#1519)
|
||||
- Bump vernemq to 1.12.3 (#1520)
|
||||
- NOISSUE - Remove auth URL from SDK (#1511)
|
||||
- NOISSUE - Apply policies to Channels (#1505)
|
||||
- remove dead code (#1503)
|
||||
- NOISSUE - Fix listing (#1502)
|
||||
- NOISSUE - Listing Policies (#1498)
|
||||
- Fix standalone mode (#1497)
|
||||
- MF-1489 - Add API for deleting policies (#1491)
|
||||
- NOISSUE - Update group sharing policies (#1494)
|
||||
- NOISSUE - Refactor InfluxDB Reader: explicit check event + add safe conversion (#1460)
|
||||
- NOISSUE - Update users create command for CLI (#1495)
|
||||
- NOISSUE - Update self register environment variable name (#1493)
|
||||
- Bring back the job add
|
||||
- NOISSUE - Fix assigning invalid group policy (#1487)
|
||||
- MF-1443 - Add policies (#1482)
|
||||
- NOISSUE - Fix retrieving all users (#1477)
|
||||
- MF-1468 - Fix ThingsURL in Certs Service (#1474)
|
||||
- NOISSUE - Refactor single-user mode (#1471)
|
||||
- Fix UpdateChannelHandler for Redis producer (#1473)
|
||||
- NOISSUE - Add SMPP notifier (#1464)
|
||||
- NOISSUE - Update dependencies (#1453)
|
||||
- NOISSUE - Fix security warnings for dependencies (#1452)
|
||||
- Bump docker-compose version in prereq (#1449)
|
||||
- NOISSUE - Fix bootstraping (#1448)
|
||||
- MF 1413 - Use per-service URL in SDK (#1444)
|
||||
- MF-1439 - Add support for Basic Authentication in HTTP Adapter (#1441)
|
||||
- MF-1421 - Make flattening of JSON transformer only available on InfluxDB (#1432)
|
||||
- NOISSUE - Update the /disconnect endpoint HTTP method as PUT (#1438)
|
||||
- MF-1389 - Add /disconnect endpoint in Things service (#1433)
|
||||
- NOISSUE - Fix httputil implementation in users service (#1434)
|
||||
- Fix fetching user members of an empty group (#1436)
|
||||
- Change to user friendly docs urls (#1430)
|
||||
- NOISSUE - Use github action for showing OpenAPI spec with Swagger UI (#1427)
|
||||
- Fix JSON Transformer empty format handling (#1429)
|
||||
- Update README
|
||||
- NOISSUE - Update docker-compose images to latest release (#1419)
|
||||
- MF-1378 - Update dependencies (#1379)
|
||||
|
||||
## 0.12.1 - 05. MAY 2021.
|
||||
### Features and Bugfixes
|
||||
- NOISSUE - Refactor SDK memberships and fix openapi for memberships.
|
||||
- NOISSUE - Fix incorrect influxdb credentials
|
||||
- MF-1408 - Fix error handling for Thing update SQL(#1408)
|
||||
- MF-1288 - Add tests for JSON messages in message writers and readers
|
||||
- NOISSUE - Fix Postgres Reader order
|
||||
- NOISSUE - Fix nginx configuration for groups
|
||||
- NOISSUE - Add tests and connection route-map to lora-adapter
|
||||
- MF-1403 - Change vernemq building source revision
|
||||
- NOISSUE - Rm content-type check from list endpoint
|
||||
|
||||
## 0.12.0 - 29. MAR 2021.
|
||||
### Features and Bugfixes
|
||||
- MF-1394 - SDK groups (#1396)
|
||||
|
||||
+16
-26
@@ -3,30 +3,16 @@
|
||||
|
||||
MF_DOCKER_IMAGE_NAME_PREFIX ?= mainflux
|
||||
BUILD_DIR = build
|
||||
SERVICES = users things http coap ws lora influxdb-writer influxdb-reader mongodb-writer \
|
||||
mongodb-reader cassandra-writer cassandra-reader postgres-writer postgres-reader timescale-writer timescale-reader cli \
|
||||
bootstrap opcua twins mqtt provision certs smtp-notifier smpp-notifier
|
||||
SERVICES = users things http coap lora influxdb-writer influxdb-reader mongodb-writer \
|
||||
mongodb-reader cassandra-writer cassandra-reader postgres-writer postgres-reader cli \
|
||||
bootstrap opcua auth twins mqtt provision certs smtp-notifier
|
||||
DOCKERS = $(addprefix docker_,$(SERVICES))
|
||||
DOCKERS_DEV = $(addprefix docker_dev_,$(SERVICES))
|
||||
CGO_ENABLED ?= 0
|
||||
GOARCH ?= amd64
|
||||
VERSION ?= $(shell git describe --abbrev=0 --tags)
|
||||
COMMIT ?= $(shell git rev-parse HEAD)
|
||||
TIME ?= $(shell date +%F_%T)
|
||||
|
||||
ifneq ($(MF_BROKER_TYPE),)
|
||||
MF_BROKER_TYPE := $(MF_BROKER_TYPE)
|
||||
else
|
||||
MF_BROKER_TYPE=nats
|
||||
endif
|
||||
|
||||
define compile_service
|
||||
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) \
|
||||
go build -mod=vendor -tags $(MF_BROKER_TYPE) -ldflags "-s -w \
|
||||
-X 'github.com/mainflux/mainflux.BuildTime=$(TIME)' \
|
||||
-X 'github.com/mainflux/mainflux.Version=$(VERSION)' \
|
||||
-X 'github.com/mainflux/mainflux.Commit=$(COMMIT)'" \
|
||||
-o ${BUILD_DIR}/mainflux-$(1) cmd/$(1)/main.go
|
||||
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -mod=vendor -ldflags "-s -w" -o ${BUILD_DIR}/mainflux-$(1) cmd/$(1)/main.go
|
||||
endef
|
||||
|
||||
define make_docker
|
||||
@@ -37,9 +23,6 @@ define make_docker
|
||||
--build-arg SVC=$(svc) \
|
||||
--build-arg GOARCH=$(GOARCH) \
|
||||
--build-arg GOARM=$(GOARM) \
|
||||
--build-arg VERSION=$(VERSION) \
|
||||
--build-arg COMMIT=$(COMMIT) \
|
||||
--build-arg TIME=$(TIME) \
|
||||
--tag=$(MF_DOCKER_IMAGE_NAME_PREFIX)/$(svc) \
|
||||
-f docker/Dockerfile .
|
||||
endef
|
||||
@@ -77,9 +60,8 @@ test:
|
||||
go test -mod=vendor -v -race -count 1 -tags test $(shell go list ./... | grep -v 'vendor\|cmd')
|
||||
|
||||
proto:
|
||||
protoc -I. --go_out=. --go_opt=paths=source_relative pkg/messaging/*.proto
|
||||
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative users/policies/*.proto
|
||||
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative things/policies/*.proto
|
||||
protoc --gofast_out=plugins=grpc:. *.proto
|
||||
protoc --gofast_out=plugins=grpc:. pkg/messaging/*.proto
|
||||
|
||||
$(SERVICES):
|
||||
$(call compile_service,$(@))
|
||||
@@ -118,6 +100,14 @@ rundev:
|
||||
cd scripts && ./run.sh
|
||||
|
||||
run:
|
||||
sed -i "s,file: brokers/.*.yml,file: brokers/${MF_BROKER_TYPE}.yml," docker/docker-compose.yml
|
||||
sed -i "s,MF_BROKER_URL=.*,MF_BROKER_URL=$$\{MF_$(shell echo ${MF_BROKER_TYPE} | tr 'a-z' 'A-Z')_URL\}," docker/.env
|
||||
docker-compose -f docker/docker-compose.yml up
|
||||
|
||||
runlora:
|
||||
docker-compose \
|
||||
-f docker/docker-compose.yml \
|
||||
-f docker/addons/influxdb-writer/docker-compose.yml \
|
||||
-f docker/addons/lora-adapter/docker-compose.yml up \
|
||||
|
||||
# Run all Mainflux core services except distributed tracing system - Jaeger. Recommended on gateways:
|
||||
rungw:
|
||||
MF_JAEGER_URL= docker-compose -f docker/docker-compose.yml up --scale jaeger=0
|
||||
|
||||
+18
-34
@@ -1,6 +1,6 @@
|
||||
# Mainflux
|
||||
|
||||
[](https://mainflux.semaphoreci.com/projects/mainflux)
|
||||
[![build][ci-badge]][ci-url]
|
||||
[![go report card][grc-badge]][grc-url]
|
||||
[![coverage][cov-badge]][cov-url]
|
||||
[![license][license]](LICENSE)
|
||||
@@ -16,6 +16,10 @@ for building complex IoT solutions.
|
||||
|
||||
For more details, check out the [official documentation][docs].
|
||||
|
||||
Mainflux is member of the [Linux Foundation][lf] and an active contributor
|
||||
to the [EdgeX Foundry][edgex] project. It has been made with :heart: by [Mainflux Labs][company],
|
||||
which maintains the project and offers professional services around it.
|
||||
|
||||
## Features
|
||||
|
||||
- Multi-protocol connectivity and bridging (HTTP, MQTT, WebSocket and CoAP)
|
||||
@@ -23,7 +27,7 @@ For more details, check out the [official documentation][docs].
|
||||
- Mutual TLS Authentication (mTLS) using X.509 Certificates
|
||||
- Fine-grained access control (policies, ABAC/RBAC)
|
||||
- Message persistence (Cassandra, InfluxDB, MongoDB and PostgresSQL)
|
||||
- Platform logging and instrumentation support (Prometheus and OpenTelemetry)
|
||||
- Platform logging and instrumentation support (Grafana, Prometheus and OpenTracing)
|
||||
- Event sourcing
|
||||
- Container-based deployment using [Docker][docker] and [Kubernetes][kubernetes]
|
||||
- [LoRaWAN][lora] network integration
|
||||
@@ -39,11 +43,11 @@ For more details, check out the [official documentation][docs].
|
||||
The following are needed to run Mainflux:
|
||||
|
||||
- [Docker](https://docs.docker.com/install/) (version 20.10)
|
||||
- [Docker compose](https://docs.docker.com/compose/install/) (version 1.29)
|
||||
- [Docker compose](https://docs.docker.com/compose/install/) (version 1.28)
|
||||
|
||||
Developing Mainflux will also require:
|
||||
|
||||
- [Go](https://golang.org/doc/install) (version 1.19.2)
|
||||
- [Go](https://golang.org/doc/install) (version 1.13.3)
|
||||
- [Protobuf](https://github.com/protocolbuffers/protobuf#protocol-compiler-installation) (version 3.6.1)
|
||||
|
||||
## Install
|
||||
@@ -65,7 +69,7 @@ If you want to run services from specific release checkout code from github and
|
||||
|
||||
```bash
|
||||
git checkout tags/<release_number> -b <release_number>
|
||||
# e.g. `git checkout tags/0.13.0 -b 0.13.0`
|
||||
# e.g. `git checkout tags/0.12.0 -b 0.12.0`
|
||||
```
|
||||
|
||||
Check that `.env` file contains:
|
||||
@@ -74,7 +78,7 @@ Check that `.env` file contains:
|
||||
MF_RELEASE_TAG=<release_number>
|
||||
```
|
||||
|
||||
>`docker-compose` should be used for development and testing deployments. For production we suggest using [Kubernetes](https://docs.mainflux.io/kubernetes).
|
||||
>`docker-compose` should be used for development and testing deployments. For production we suggest using [Kubernetes](https://mainflux.readthedocs.io/en/latest/kubernetes/).
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -87,14 +91,16 @@ make cli
|
||||
./build/mainflux-cli version
|
||||
```
|
||||
|
||||
Additional details on using the CLI can be found in the [CLI documentation](https://docs.mainflux.io/cli).
|
||||
Additional details on using the CLI can be found in the [CLI documentation](https://mainflux.readthedocs.io/en/latest/cli/).
|
||||
|
||||
## Documentation
|
||||
|
||||
Official documentation is hosted at [Mainflux official docs page][docs]. Documentation is auto-generated, checkout the instructions on [official docs repository](https://github.com/mainflux/docs):
|
||||
Official documentation is hosted at [Mainflux Read The Docs page][docs]. Documentation is auto-generated, checkout the instructions on [official docs repository](https://github.com/mainflux/docs):
|
||||
|
||||
If you spot an error or a need for corrections, please let us know - or even better: send us a PR.
|
||||
|
||||
Additional practical information, news and tutorials can be found on the [Mainflux blog][blog].
|
||||
|
||||
## Authors
|
||||
|
||||
Main architect and BDFL of Mainflux project is [@drasko][drasko].
|
||||
@@ -113,12 +119,6 @@ The Mainflux team would like to give special thanks to [@mijicd][dejan] for his
|
||||
on designing and implementing a highly improved and optimized version of the platform,
|
||||
and [@malidukica][dusanm] for his effort on implementing the initial user interface.
|
||||
|
||||
## Professional Support
|
||||
|
||||
There are many companies offering professional support for the Mainflux system.
|
||||
|
||||
If you need this kind of support, best is to reach out to [@drasko][drasko] directly, and he will point you out to the best-matching support team.
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for your interest in Mainflux and the desire to contribute!
|
||||
@@ -129,9 +129,10 @@ Thank you for your interest in Mainflux and the desire to contribute!
|
||||
|
||||
### We're Hiring
|
||||
|
||||
You like Mainflux and you would like to make it your day job? We're always looking for talented engineers interested in open-source, IoT and distributed systems. If you recognize yourself, reach out to [@drasko][drasko] - he will contact you back.
|
||||
If you are interested in working professionally on Mainflux,
|
||||
please head to company's [careers page][careers] or shoot us an e-mail at <careers@mainflux.com>.
|
||||
|
||||
>The best way to grab our attention is, of course, by sending PRs :sunglasses:.
|
||||
>The best way to grab our attention is by sending PRs :sunglasses:.
|
||||
|
||||
## Community
|
||||
|
||||
@@ -145,26 +146,10 @@ You like Mainflux and you would like to make it your day job? We're always looki
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fmainflux%2Fmainflux?ref=badge_large)
|
||||
|
||||
## Data Collection for Mainflux
|
||||
Mainflux is committed to continuously improving its services and ensuring a seamless experience for its users. To achieve this, we collect certain data from your deployments. Rest assured, this data is collected solely for the purpose of enhancing Mainflux and is not used with any malicious intent. The deployment summary can be found on our [website][callhome].
|
||||
|
||||
The collected data includes:
|
||||
- **IP Address** - Used for approximate location information on deployments.
|
||||
- **Services Used** - To understand which features are popular and prioritize future developments.
|
||||
- **Last Seen Time** - To ensure the stability and availability of Mainflux.
|
||||
- **Mainflux Version** - To track the software version and deliver relevant updates.
|
||||
|
||||
We take your privacy and data security seriously. All data collected is handled in accordance with our stringent privacy policies and industry best practices.
|
||||
|
||||
Data collection is on by default and can be disabled by setting the env variable:
|
||||
`MF_SEND_TELEMETRY=false`
|
||||
|
||||
By utilizing Mainflux, you actively contribute to its improvement. Together, we can build a more robust and efficient IoT platform. Thank you for your trust in Mainflux!
|
||||
|
||||
[banner]: https://github.com/mainflux/docs/blob/master/docs/img/gopherBanner.jpg
|
||||
[ci-badge]: https://semaphoreci.com/api/v1/mainflux/mainflux/branches/master/badge.svg
|
||||
[ci-url]: https://semaphoreci.com/mainflux/mainflux
|
||||
[docs]: https://docs.mainflux.io
|
||||
[docs]: http://mainflux.readthedocs.io
|
||||
[docker]: https://www.docker.com
|
||||
[forum]: https://groups.google.com/forum/#!forum/mainflux
|
||||
[gitter]: https://gitter.im/mainflux/mainflux?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
@@ -200,4 +185,3 @@ By utilizing Mainflux, you actively contribute to its improvement. Together, we
|
||||
[kole]: https://github.com/chombium
|
||||
[dusanm]: https://github.com/malidukica
|
||||
[mirko]: https://github.com/mteodor
|
||||
[callhome]: https://deployments.mainflux.io
|
||||
|
||||
+3794
File diff suppressed because it is too large
Load Diff
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package mainflux;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
service ThingsService {
|
||||
rpc CanAccessByKey(AccessByKeyReq) returns (ThingID) {}
|
||||
rpc IsChannelOwner(ChannelOwnerReq) returns (google.protobuf.Empty) {}
|
||||
rpc CanAccessByID(AccessByIDReq) returns (google.protobuf.Empty) {}
|
||||
rpc Identify(Token) returns (ThingID) {}
|
||||
}
|
||||
|
||||
service AuthService {
|
||||
rpc Issue(IssueReq) returns (Token) {}
|
||||
rpc Identify(Token) returns (UserIdentity) {}
|
||||
rpc Authorize(AuthorizeReq) returns (AuthorizeRes) {}
|
||||
rpc Assign(Assignment) returns(google.protobuf.Empty) {}
|
||||
rpc Members(MembersReq) returns (MembersRes) {}
|
||||
}
|
||||
|
||||
message AccessByKeyReq {
|
||||
string token = 1;
|
||||
string chanID = 2;
|
||||
}
|
||||
|
||||
message ChannelOwnerReq {
|
||||
string owner = 1;
|
||||
string chanID = 2;
|
||||
}
|
||||
|
||||
message ThingID {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message ChannelID {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message AccessByIDReq {
|
||||
string thingID = 1;
|
||||
string chanID = 2;
|
||||
}
|
||||
|
||||
// If a token is not carrying any information itself, the type
|
||||
// field can be used to determine how to validate the token.
|
||||
// Also, different tokens can be encoded in different ways.
|
||||
message Token {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message UserIdentity {
|
||||
string id = 1;
|
||||
string email = 2;
|
||||
}
|
||||
|
||||
message IssueReq {
|
||||
string id = 1;
|
||||
string email = 2;
|
||||
uint32 type = 3;
|
||||
}
|
||||
|
||||
message AuthorizeReq {
|
||||
string sub = 1;
|
||||
string obj = 2;
|
||||
string act = 3;
|
||||
}
|
||||
|
||||
message AuthorizeRes {
|
||||
bool authorized = 1;
|
||||
}
|
||||
|
||||
message Assignment {
|
||||
string token = 1;
|
||||
string groupID = 2;
|
||||
string memberID = 3;
|
||||
}
|
||||
|
||||
message MembersReq {
|
||||
string token = 1;
|
||||
string groupID = 2;
|
||||
uint64 offset = 3;
|
||||
uint64 limit = 4;
|
||||
string type = 5;
|
||||
}
|
||||
|
||||
message MembersRes {
|
||||
uint64 total = 1;
|
||||
uint64 offset = 2;
|
||||
uint64 limit = 3;
|
||||
string type = 4;
|
||||
repeated string members = 5;
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mainflux
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "Content-Type"
|
||||
contentTypeJSON = "application/health+json"
|
||||
svcStatus = "pass"
|
||||
description = " service"
|
||||
)
|
||||
|
||||
var (
|
||||
// Version represents the last service git tag in git history.
|
||||
// It's meant to be set using go build ldflags:
|
||||
// -ldflags "-X 'github.com/mainflux/mainflux.Version=0.0.0'".
|
||||
Version = "0.0.0"
|
||||
// Commit represents the service git commit hash.
|
||||
// It's meant to be set using go build ldflags:
|
||||
// -ldflags "-X 'github.com/mainflux/mainflux.Commit=ffffffff'".
|
||||
Commit = "ffffffff"
|
||||
// BuildTime represetns the service build time.
|
||||
// It's meant to be set using go build ldflags:
|
||||
// -ldflags "-X 'github.com/mainflux/mainflux.BuildTime=1970-01-01_00:00:00'".
|
||||
BuildTime = "1970-01-01_00:00:00"
|
||||
)
|
||||
|
||||
// HealthInfo contains version endpoint response.
|
||||
type HealthInfo struct {
|
||||
// Status contains service status.
|
||||
Status string `json:"status"`
|
||||
|
||||
// Version contains current service version.
|
||||
Version string `json:"version"`
|
||||
|
||||
// Commit represents the git hash commit.
|
||||
Commit string `json:"commit"`
|
||||
|
||||
// Description contains service description.
|
||||
Description string `json:"description"`
|
||||
|
||||
// BuildTime contains service build time.
|
||||
BuildTime string `json:"build_time"`
|
||||
|
||||
// InstanceID contains the ID of the current service instance
|
||||
InstanceID string `json:"instance_id"`
|
||||
}
|
||||
|
||||
// Health exposes an HTTP handler for retrieving service health.
|
||||
func Health(service, instanceID string) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add(contentType, contentTypeJSON)
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
res := HealthInfo{
|
||||
Status: svcStatus,
|
||||
Version: Version,
|
||||
Commit: Commit,
|
||||
Description: service + description,
|
||||
BuildTime: BuildTime,
|
||||
InstanceID: instanceID,
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(res); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
-1
@@ -19,7 +19,6 @@ const (
|
||||
Debug
|
||||
)
|
||||
|
||||
// ErrInvalidLogLevel indicates an unrecognized log level.
|
||||
var ErrInvalidLogLevel = errors.New("unrecognized log level")
|
||||
|
||||
// Level represents severity level while logging.
|
||||
|
||||
+5
-14
@@ -5,11 +5,9 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-kit/kit/log"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
// Logger specifies logging API.
|
||||
@@ -22,8 +20,6 @@ type Logger interface {
|
||||
Warn(string)
|
||||
// Error logs any object in JSON format on error level.
|
||||
Error(string)
|
||||
// Fatal logs any object in JSON format on any level and calls os.Exit(1).
|
||||
Fatal(string)
|
||||
}
|
||||
|
||||
var _ Logger = (*logger)(nil)
|
||||
@@ -47,29 +43,24 @@ func New(out io.Writer, levelText string) (Logger, error) {
|
||||
|
||||
func (l logger) Debug(msg string) {
|
||||
if Debug.isAllowed(l.level) {
|
||||
_ = l.kitLogger.Log("level", Debug.String(), "message", msg)
|
||||
l.kitLogger.Log("level", Debug.String(), "message", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) Info(msg string) {
|
||||
if Info.isAllowed(l.level) {
|
||||
_ = l.kitLogger.Log("level", Info.String(), "message", msg)
|
||||
l.kitLogger.Log("level", Info.String(), "message", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) Warn(msg string) {
|
||||
if Warn.isAllowed(l.level) {
|
||||
_ = l.kitLogger.Log("level", Warn.String(), "message", msg)
|
||||
l.kitLogger.Log("level", Warn.String(), "message", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) Error(msg string) {
|
||||
if Error.isAllowed(l.level) {
|
||||
_ = l.kitLogger.Log("level", Error.String(), "message", msg)
|
||||
l.kitLogger.Log("level", Error.String(), "message", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) Fatal(msg string) {
|
||||
_ = l.kitLogger.Log("fatal", msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package logger
|
||||
|
||||
var _ Logger = (*loggerMock)(nil)
|
||||
|
||||
type loggerMock struct{}
|
||||
|
||||
// NewMock returns wrapped go kit logger mock.
|
||||
func NewMock() Logger {
|
||||
return &loggerMock{}
|
||||
}
|
||||
|
||||
func (l loggerMock) Debug(msg string) {
|
||||
}
|
||||
|
||||
func (l loggerMock) Info(msg string) {
|
||||
}
|
||||
|
||||
func (l loggerMock) Warn(msg string) {
|
||||
}
|
||||
|
||||
func (l loggerMock) Error(msg string) {
|
||||
}
|
||||
|
||||
func (l loggerMock) Fatal(msg string) {
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package errors contains Mainflux errors definitions.
|
||||
package errors
|
||||
+5
-23
@@ -3,16 +3,9 @@
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Error specifies an API that must be fullfiled by error type.
|
||||
// Error specifies an API that must be fullfiled by error type
|
||||
type Error interface {
|
||||
|
||||
// Error implements the error interface.
|
||||
Error() string
|
||||
|
||||
@@ -25,7 +18,7 @@ type Error interface {
|
||||
|
||||
var _ Error = (*customError)(nil)
|
||||
|
||||
// customError represents a Mainflux error.
|
||||
// customError struct represents a Mainflux error
|
||||
type customError struct {
|
||||
msg string
|
||||
err Error
|
||||
@@ -49,7 +42,7 @@ func (ce *customError) Err() Error {
|
||||
return ce.err
|
||||
}
|
||||
|
||||
// Contains inspects if e2 error is contained in any layer of e1 error.
|
||||
// Contains inspects if e2 error is contained in any layer of e1 error
|
||||
func Contains(e1 error, e2 error) bool {
|
||||
if e1 == nil || e2 == nil {
|
||||
return e2 == e1
|
||||
@@ -64,7 +57,7 @@ func Contains(e1 error, e2 error) bool {
|
||||
return e1.Error() == e2.Error()
|
||||
}
|
||||
|
||||
// Wrap returns an Error that wrap err with wrapper.
|
||||
// Wrap returns an Error that wrap err with wrapper
|
||||
func Wrap(wrapper error, err error) error {
|
||||
if wrapper == nil || err == nil {
|
||||
return wrapper
|
||||
@@ -101,14 +94,3 @@ func New(text string) Error {
|
||||
err: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func SignalHandler(ctx context.Context) error {
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT)
|
||||
select {
|
||||
case sig := <-c:
|
||||
return fmt.Errorf("%s", sig)
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const err = "error"
|
||||
|
||||
var (
|
||||
// ErrJSONErrKey indicates response body did not contain erorr message.
|
||||
errJSONKey = New("response body expected error message json key not found")
|
||||
|
||||
// ErrUnknown indicates that an unknown error was found in the response body.
|
||||
errUnknown = New("unknown error")
|
||||
)
|
||||
|
||||
// SDKError is an error type for Mainflux SDK.
|
||||
type SDKError interface {
|
||||
Error
|
||||
StatusCode() int
|
||||
}
|
||||
|
||||
var _ SDKError = (*sdkError)(nil)
|
||||
|
||||
type sdkError struct {
|
||||
*customError
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (ce *sdkError) Error() string {
|
||||
if ce == nil {
|
||||
return ""
|
||||
}
|
||||
if ce.customError == nil {
|
||||
return http.StatusText(ce.statusCode)
|
||||
}
|
||||
return fmt.Sprintf("Status: %s: %s", http.StatusText(ce.statusCode), ce.customError.Error())
|
||||
}
|
||||
|
||||
func (ce *sdkError) StatusCode() int {
|
||||
return ce.statusCode
|
||||
}
|
||||
|
||||
// NewSDKError returns an SDK Error that formats as the given text.
|
||||
func NewSDKError(err error) SDKError {
|
||||
return &sdkError{
|
||||
customError: &customError{
|
||||
msg: err.Error(),
|
||||
err: nil,
|
||||
},
|
||||
statusCode: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSDKErrorWithStatus returns an SDK Error setting the status code.
|
||||
func NewSDKErrorWithStatus(err error, statusCode int) SDKError {
|
||||
return &sdkError{
|
||||
statusCode: statusCode,
|
||||
customError: &customError{
|
||||
msg: err.Error(),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CheckError will check the HTTP response status code and matches it with the given status codes.
|
||||
// Since multiple status codes can be valid, we can pass multiple status codes to the function.
|
||||
// The function then checks for errors in the HTTP response.
|
||||
func CheckError(resp *http.Response, expectedStatusCodes ...int) SDKError {
|
||||
for _, expectedStatusCode := range expectedStatusCodes {
|
||||
if resp.StatusCode == expectedStatusCode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var content map[string]interface{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&content); err != nil {
|
||||
return NewSDKErrorWithStatus(err, resp.StatusCode)
|
||||
}
|
||||
|
||||
if msg, ok := content[err]; ok {
|
||||
if v, ok := msg.(string); ok {
|
||||
return NewSDKErrorWithStatus(errors.New(v), resp.StatusCode)
|
||||
}
|
||||
return NewSDKErrorWithStatus(errUnknown, resp.StatusCode)
|
||||
}
|
||||
|
||||
return NewSDKErrorWithStatus(errJSONKey, resp.StatusCode)
|
||||
}
|
||||
+4
-34
@@ -4,45 +4,15 @@
|
||||
package errors
|
||||
|
||||
var (
|
||||
// ErrAuthentication indicates failure occurred while authenticating the entity.
|
||||
ErrAuthentication = New("failed to perform authentication over the entity")
|
||||
|
||||
// ErrAuthorization indicates failure occurred while authorizing the entity.
|
||||
ErrAuthorization = New("failed to perform authorization over the entity")
|
||||
|
||||
// ErrUnsupportedContentType indicates unacceptable or lack of Content-Type.
|
||||
// ErrUnsupportedContentType indicates unacceptable or lack of Content-Type
|
||||
ErrUnsupportedContentType = New("unsupported content type")
|
||||
|
||||
// ErrInvalidQueryParams indicates invalid query parameters.
|
||||
// ErrInvalidQueryParams indicates invalid query parameters
|
||||
ErrInvalidQueryParams = New("invalid query parameters")
|
||||
|
||||
// ErrNotFoundParam indicates that the parameter was not found in the query.
|
||||
// ErrNotFoundParam indicates that the parameter was not found in the query
|
||||
ErrNotFoundParam = New("parameter not found in the query")
|
||||
|
||||
// ErrMalformedEntity indicates a malformed entity specification.
|
||||
// ErrMalformedEntity indicates a malformed entity specification
|
||||
ErrMalformedEntity = New("malformed entity specification")
|
||||
|
||||
// ErrNotFound indicates a non-existent entity request.
|
||||
ErrNotFound = New("entity not found")
|
||||
|
||||
// ErrConflict indicates that entity already exists.
|
||||
ErrConflict = New("entity already exists")
|
||||
|
||||
// ErrCreateEntity indicates error in creating entity or entities.
|
||||
ErrCreateEntity = New("failed to create entity in the db")
|
||||
|
||||
// ErrViewEntity indicates error in viewing entity or entities.
|
||||
ErrViewEntity = New("view entity failed")
|
||||
|
||||
// ErrUpdateEntity indicates error in updating entity or entities.
|
||||
ErrUpdateEntity = New("update entity failed")
|
||||
|
||||
// ErrRemoveEntity indicates error in removing entity.
|
||||
ErrRemoveEntity = New("failed to remove entity")
|
||||
|
||||
// ErrScanMetadata indicates problem with metadata in db.
|
||||
ErrScanMetadata = New("failed to scan metadata in db")
|
||||
|
||||
// ErrWrongSecret indicates a wrong secret was provided.
|
||||
ErrWrongSecret = New("wrong secret")
|
||||
)
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package uuid contains UUID generator.
|
||||
package uuid
|
||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/mainflux/mainflux"
|
||||
)
|
||||
|
||||
// Prefix represents the prefix used to generate UUID mocks.
|
||||
// Prefix represents the prefix used to generate UUID mocks
|
||||
const Prefix = "123e4567-e89b-12d3-a456-"
|
||||
|
||||
var _ mainflux.IDProvider = (*uuidProviderMock)(nil)
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ import (
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
// ErrGeneratingID indicates error in generating UUID.
|
||||
var ErrGeneratingID = errors.New("failed to generate uuid")
|
||||
// ErrGeneratingID indicates error in generating UUID
|
||||
var ErrGeneratingID = errors.New("generating id failed")
|
||||
|
||||
var _ mainflux.IDProvider = (*uuidProvider)(nil)
|
||||
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mainflux
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const version string = "0.12.0"
|
||||
|
||||
// VersionInfo contains version endpoint response.
|
||||
type VersionInfo struct {
|
||||
// Service contains service name.
|
||||
Service string `json:"service"`
|
||||
|
||||
// Version contains service current version value.
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// Version exposes an HTTP handler for retrieving service version.
|
||||
func Version(service string) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
|
||||
res := VersionInfo{service, version}
|
||||
|
||||
data, _ := json.Marshal(res)
|
||||
|
||||
rw.Write(data)
|
||||
})
|
||||
}
|
||||
+18
-8
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Counter is a Metric that represents a single numerical value that only ever
|
||||
@@ -66,7 +67,7 @@ type CounterVecOpts struct {
|
||||
CounterOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -90,8 +91,12 @@ func NewCounter(opts CounterOpts) Counter {
|
||||
nil,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -106,10 +111,12 @@ type counter struct {
|
||||
selfCollector
|
||||
desc *Desc
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
labelPairs []*dto.LabelPair
|
||||
exemplar atomic.Value // Containing nil or a *dto.Exemplar.
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func (c *counter) Desc() *Desc {
|
||||
@@ -159,8 +166,7 @@ func (c *counter) Write(out *dto.Metric) error {
|
||||
exemplar = e.(*dto.Exemplar)
|
||||
}
|
||||
val := c.get()
|
||||
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out, c.createdTs)
|
||||
}
|
||||
|
||||
func (c *counter) updateExemplar(v float64, l Labels) {
|
||||
@@ -200,13 +206,17 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
|
||||
opts.VariableLabels,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
return &CounterVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}),
|
||||
}
|
||||
|
||||
+18
-10
@@ -52,7 +52,7 @@ type Desc struct {
|
||||
constLabelPairs []*dto.LabelPair
|
||||
// variableLabels contains names of labels and normalization function for
|
||||
// which the metric maintains variable values.
|
||||
variableLabels ConstrainedLabels
|
||||
variableLabels *compiledLabels
|
||||
// id is a hash of the values of the ConstLabels and fqName. This
|
||||
// must be unique among all registered descriptors and can therefore be
|
||||
// used as an identifier of the descriptor.
|
||||
@@ -93,7 +93,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||
d := &Desc{
|
||||
fqName: fqName,
|
||||
help: help,
|
||||
variableLabels: variableLabels.constrainedLabels(),
|
||||
variableLabels: variableLabels.compile(),
|
||||
}
|
||||
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||
@@ -103,7 +103,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||
// their sorted label names) plus the fqName (at position 0).
|
||||
labelValues := make([]string, 1, len(constLabels)+1)
|
||||
labelValues[0] = fqName
|
||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels))
|
||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels.names))
|
||||
labelNameSet := map[string]struct{}{}
|
||||
// First add only the const label names and sort them...
|
||||
for labelName := range constLabels {
|
||||
@@ -128,13 +128,13 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||
// Now add the variable label names, but prefix them with something that
|
||||
// cannot be in a regular label name. That prevents matching the label
|
||||
// dimension with a different mix between preset and variable labels.
|
||||
for _, label := range d.variableLabels {
|
||||
if !checkLabelName(label.Name) {
|
||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label.Name, fqName)
|
||||
for _, label := range d.variableLabels.names {
|
||||
if !checkLabelName(label) {
|
||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label, fqName)
|
||||
return d
|
||||
}
|
||||
labelNames = append(labelNames, "$"+label.Name)
|
||||
labelNameSet[label.Name] = struct{}{}
|
||||
labelNames = append(labelNames, "$"+label)
|
||||
labelNameSet[label] = struct{}{}
|
||||
}
|
||||
if len(labelNames) != len(labelNameSet) {
|
||||
d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName)
|
||||
@@ -189,11 +189,19 @@ func (d *Desc) String() string {
|
||||
fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
|
||||
)
|
||||
}
|
||||
vlStrings := make([]string, 0, len(d.variableLabels.names))
|
||||
for _, vl := range d.variableLabels.names {
|
||||
if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil {
|
||||
vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl))
|
||||
} else {
|
||||
vlStrings = append(vlStrings, vl)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
|
||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}",
|
||||
d.fqName,
|
||||
d.help,
|
||||
strings.Join(lpStrings, ","),
|
||||
d.variableLabels,
|
||||
strings.Join(vlStrings, ","),
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) {
|
||||
continue
|
||||
}
|
||||
var v interface{}
|
||||
labels := make([]string, len(desc.variableLabels))
|
||||
labels := make([]string, len(desc.variableLabels.names))
|
||||
if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
|
||||
ch <- NewInvalidMetric(desc, err)
|
||||
continue
|
||||
|
||||
+4
-4
@@ -62,7 +62,7 @@ type GaugeVecOpts struct {
|
||||
GaugeOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func (g *gauge) Sub(val float64) {
|
||||
|
||||
func (g *gauge) Write(out *dto.Metric) error {
|
||||
val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out)
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
||||
@@ -166,8 +166,8 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec {
|
||||
)
|
||||
return &GaugeVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||
}
|
||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
||||
result.init(result) // Init self-collection.
|
||||
|
||||
+75
-43
@@ -25,6 +25,7 @@ import (
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// nativeHistogramBounds for the frac of observed values. Only relevant for
|
||||
@@ -391,7 +392,7 @@ type HistogramOpts struct {
|
||||
// zero, it is replaced by default buckets. The default buckets are
|
||||
// DefBuckets if no buckets for a native histogram (see below) are used,
|
||||
// otherwise the default is no buckets. (In other words, if you want to
|
||||
// use both reguler buckets and buckets for a native histogram, you have
|
||||
// use both regular buckets and buckets for a native histogram, you have
|
||||
// to define the regular buckets here explicitly.)
|
||||
Buckets []float64
|
||||
|
||||
@@ -413,8 +414,8 @@ type HistogramOpts struct {
|
||||
// and 2, same as between 2 and 4, and 4 and 8, etc.).
|
||||
//
|
||||
// Details about the actually used factor: The factor is calculated as
|
||||
// 2^(2^n), where n is an integer number between (and including) -8 and
|
||||
// 4. n is chosen so that the resulting factor is the largest that is
|
||||
// 2^(2^-n), where n is an integer number between (and including) -4 and
|
||||
// 8. n is chosen so that the resulting factor is the largest that is
|
||||
// still smaller or equal to NativeHistogramBucketFactor. Note that the
|
||||
// smallest possible factor is therefore approx. 1.00271 (i.e. 2^(2^-8)
|
||||
// ). If NativeHistogramBucketFactor is greater than 1 but smaller than
|
||||
@@ -428,12 +429,12 @@ type HistogramOpts struct {
|
||||
// a major version bump.
|
||||
NativeHistogramBucketFactor float64
|
||||
// All observations with an absolute value of less or equal
|
||||
// NativeHistogramZeroThreshold are accumulated into a “zero”
|
||||
// bucket. For best results, this should be close to a bucket
|
||||
// boundary. This is usually the case if picking a power of two. If
|
||||
// NativeHistogramZeroThreshold are accumulated into a “zero” bucket.
|
||||
// For best results, this should be close to a bucket boundary. This is
|
||||
// usually the case if picking a power of two. If
|
||||
// NativeHistogramZeroThreshold is left at zero,
|
||||
// DefNativeHistogramZeroThreshold is used as the threshold. To configure
|
||||
// a zero bucket with an actual threshold of zero (i.e. only
|
||||
// DefNativeHistogramZeroThreshold is used as the threshold. To
|
||||
// configure a zero bucket with an actual threshold of zero (i.e. only
|
||||
// observations of precisely zero will go into the zero bucket), set
|
||||
// NativeHistogramZeroThreshold to the NativeHistogramZeroThresholdZero
|
||||
// constant (or any negative float value).
|
||||
@@ -446,26 +447,34 @@ type HistogramOpts struct {
|
||||
// Histogram are sufficiently wide-spread. In particular, this could be
|
||||
// used as a DoS attack vector. Where the observed values depend on
|
||||
// external inputs, it is highly recommended to set a
|
||||
// NativeHistogramMaxBucketNumber.) Once the set
|
||||
// NativeHistogramMaxBucketNumber.) Once the set
|
||||
// NativeHistogramMaxBucketNumber is exceeded, the following strategy is
|
||||
// enacted: First, if the last reset (or the creation) of the histogram
|
||||
// is at least NativeHistogramMinResetDuration ago, then the whole
|
||||
// histogram is reset to its initial state (including regular
|
||||
// buckets). If less time has passed, or if
|
||||
// NativeHistogramMinResetDuration is zero, no reset is
|
||||
// performed. Instead, the zero threshold is increased sufficiently to
|
||||
// reduce the number of buckets to or below
|
||||
// NativeHistogramMaxBucketNumber, but not to more than
|
||||
// NativeHistogramMaxZeroThreshold. Thus, if
|
||||
// NativeHistogramMaxZeroThreshold is already at or below the current
|
||||
// zero threshold, nothing happens at this step. After that, if the
|
||||
// number of buckets still exceeds NativeHistogramMaxBucketNumber, the
|
||||
// resolution of the histogram is reduced by doubling the width of the
|
||||
// sparse buckets (up to a growth factor between one bucket to the next
|
||||
// of 2^(2^4) = 65536, see above).
|
||||
// enacted:
|
||||
// - First, if the last reset (or the creation) of the histogram is at
|
||||
// least NativeHistogramMinResetDuration ago, then the whole
|
||||
// histogram is reset to its initial state (including regular
|
||||
// buckets).
|
||||
// - If less time has passed, or if NativeHistogramMinResetDuration is
|
||||
// zero, no reset is performed. Instead, the zero threshold is
|
||||
// increased sufficiently to reduce the number of buckets to or below
|
||||
// NativeHistogramMaxBucketNumber, but not to more than
|
||||
// NativeHistogramMaxZeroThreshold. Thus, if
|
||||
// NativeHistogramMaxZeroThreshold is already at or below the current
|
||||
// zero threshold, nothing happens at this step.
|
||||
// - After that, if the number of buckets still exceeds
|
||||
// NativeHistogramMaxBucketNumber, the resolution of the histogram is
|
||||
// reduced by doubling the width of the sparse buckets (up to a
|
||||
// growth factor between one bucket to the next of 2^(2^4) = 65536,
|
||||
// see above).
|
||||
// - Any increased zero threshold or reduced resolution is reset back
|
||||
// to their original values once NativeHistogramMinResetDuration has
|
||||
// passed (since the last reset or the creation of the histogram).
|
||||
NativeHistogramMaxBucketNumber uint32
|
||||
NativeHistogramMinResetDuration time.Duration
|
||||
NativeHistogramMaxZeroThreshold float64
|
||||
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// HistogramVecOpts bundles the options to create a HistogramVec metric.
|
||||
@@ -475,7 +484,7 @@ type HistogramVecOpts struct {
|
||||
HistogramOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -499,12 +508,12 @@ func NewHistogram(opts HistogramOpts) Histogram {
|
||||
}
|
||||
|
||||
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
||||
if len(desc.variableLabels) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
|
||||
if len(desc.variableLabels.names) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
|
||||
}
|
||||
|
||||
for _, n := range desc.variableLabels {
|
||||
if n.Name == bucketLabel {
|
||||
for _, n := range desc.variableLabels.names {
|
||||
if n == bucketLabel {
|
||||
panic(errBucketLabelNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -514,6 +523,10 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||
}
|
||||
}
|
||||
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
|
||||
h := &histogram{
|
||||
desc: desc,
|
||||
upperBounds: opts.Buckets,
|
||||
@@ -521,8 +534,8 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||
nativeHistogramMaxBuckets: opts.NativeHistogramMaxBucketNumber,
|
||||
nativeHistogramMaxZeroThreshold: opts.NativeHistogramMaxZeroThreshold,
|
||||
nativeHistogramMinResetDuration: opts.NativeHistogramMinResetDuration,
|
||||
lastResetTime: time.Now(),
|
||||
now: time.Now,
|
||||
lastResetTime: opts.now(),
|
||||
now: opts.now,
|
||||
}
|
||||
if len(h.upperBounds) == 0 && opts.NativeHistogramBucketFactor <= 1 {
|
||||
h.upperBounds = DefBuckets
|
||||
@@ -701,9 +714,11 @@ type histogram struct {
|
||||
nativeHistogramMaxZeroThreshold float64
|
||||
nativeHistogramMaxBuckets uint32
|
||||
nativeHistogramMinResetDuration time.Duration
|
||||
lastResetTime time.Time // Protected by mtx.
|
||||
// lastResetTime is protected by mtx. It is also used as created timestamp.
|
||||
lastResetTime time.Time
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func (h *histogram) Desc() *Desc {
|
||||
@@ -742,9 +757,10 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||
waitForCooldown(count, coldCounts)
|
||||
|
||||
his := &dto.Histogram{
|
||||
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
CreatedTimestamp: timestamppb.New(h.lastResetTime),
|
||||
}
|
||||
out.Histogram = his
|
||||
out.Label = h.labelPairs
|
||||
@@ -782,6 +798,16 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||
his.ZeroCount = proto.Uint64(zeroBucket)
|
||||
his.NegativeSpan, his.NegativeDelta = makeBuckets(&coldCounts.nativeHistogramBucketsNegative)
|
||||
his.PositiveSpan, his.PositiveDelta = makeBuckets(&coldCounts.nativeHistogramBucketsPositive)
|
||||
|
||||
// Add a no-op span to a histogram without observations and with
|
||||
// a zero threshold of zero. Otherwise, a native histogram would
|
||||
// look like a classic histogram to scrapers.
|
||||
if *his.ZeroThreshold == 0 && *his.ZeroCount == 0 && len(his.PositiveSpan) == 0 && len(his.NegativeSpan) == 0 {
|
||||
his.PositiveSpan = []*dto.BucketSpan{{
|
||||
Offset: proto.Int32(0),
|
||||
Length: proto.Uint32(0),
|
||||
}}
|
||||
}
|
||||
}
|
||||
addAndResetCounts(hotCounts, coldCounts)
|
||||
return nil
|
||||
@@ -854,20 +880,23 @@ func (h *histogram) limitBuckets(counts *histogramCounts, value float64, bucket
|
||||
h.doubleBucketWidth(hotCounts, coldCounts)
|
||||
}
|
||||
|
||||
// maybeReset resests the whole histogram if at least h.nativeHistogramMinResetDuration
|
||||
// maybeReset resets the whole histogram if at least h.nativeHistogramMinResetDuration
|
||||
// has been passed. It returns true if the histogram has been reset. The caller
|
||||
// must have locked h.mtx.
|
||||
func (h *histogram) maybeReset(hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int) bool {
|
||||
func (h *histogram) maybeReset(
|
||||
hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int,
|
||||
) bool {
|
||||
// We are using the possibly mocked h.now() rather than
|
||||
// time.Since(h.lastResetTime) to enable testing.
|
||||
if h.nativeHistogramMinResetDuration == 0 || h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
|
||||
if h.nativeHistogramMinResetDuration == 0 ||
|
||||
h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
|
||||
return false
|
||||
}
|
||||
// Completely reset coldCounts.
|
||||
h.resetCounts(cold)
|
||||
// Repeat the latest observation to not lose it completely.
|
||||
cold.observe(value, bucket, true)
|
||||
// Make coldCounts the new hot counts while ressetting countAndHotIdx.
|
||||
// Make coldCounts the new hot counts while resetting countAndHotIdx.
|
||||
n := atomic.SwapUint64(&h.countAndHotIdx, (coldIdx<<63)+1)
|
||||
count := n & ((1 << 63) - 1)
|
||||
waitForCooldown(count, hot)
|
||||
@@ -1176,6 +1205,7 @@ type constHistogram struct {
|
||||
sum float64
|
||||
buckets map[float64]uint64
|
||||
labelPairs []*dto.LabelPair
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (h *constHistogram) Desc() *Desc {
|
||||
@@ -1183,7 +1213,9 @@ func (h *constHistogram) Desc() *Desc {
|
||||
}
|
||||
|
||||
func (h *constHistogram) Write(out *dto.Metric) error {
|
||||
his := &dto.Histogram{}
|
||||
his := &dto.Histogram{
|
||||
CreatedTimestamp: h.createdTs,
|
||||
}
|
||||
|
||||
buckets := make([]*dto.Bucket, 0, len(h.buckets))
|
||||
|
||||
@@ -1230,7 +1262,7 @@ func NewConstHistogram(
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &constHistogram{
|
||||
@@ -1324,7 +1356,7 @@ func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) {
|
||||
// Multiple spans with only small gaps in between are probably
|
||||
// encoded more efficiently as one larger span with a few empty
|
||||
// buckets. Needs some research to find the sweet spot. For now,
|
||||
// we assume that gaps of one ore two buckets should not create
|
||||
// we assume that gaps of one or two buckets should not create
|
||||
// a new span.
|
||||
iDelta := int32(i - nextI)
|
||||
if n == 0 || iDelta > 2 {
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
// It provides tools to compare sequences of strings and generate textual diffs.
|
||||
//
|
||||
// Maintaining `GetUnifiedDiffString` here because original repository
|
||||
// (https://github.com/pmezard/go-difflib) is no loger maintained.
|
||||
// (https://github.com/pmezard/go-difflib) is no longer maintained.
|
||||
package internal
|
||||
|
||||
import (
|
||||
|
||||
+42
-16
@@ -32,19 +32,15 @@ import (
|
||||
// create a Desc.
|
||||
type Labels map[string]string
|
||||
|
||||
// LabelConstraint normalizes label values.
|
||||
type LabelConstraint func(string) string
|
||||
|
||||
// ConstrainedLabels represents a label name and its constrain function
|
||||
// to normalize label values. This type is commonly used when constructing
|
||||
// metric vector Collectors.
|
||||
type ConstrainedLabel struct {
|
||||
Name string
|
||||
Constraint func(string) string
|
||||
}
|
||||
|
||||
func (cl ConstrainedLabel) Constrain(v string) string {
|
||||
if cl.Constraint == nil {
|
||||
return v
|
||||
}
|
||||
return cl.Constraint(v)
|
||||
Constraint LabelConstraint
|
||||
}
|
||||
|
||||
// ConstrainableLabels is an interface that allows creating of labels that can
|
||||
@@ -58,7 +54,7 @@ func (cl ConstrainedLabel) Constrain(v string) string {
|
||||
// },
|
||||
// })
|
||||
type ConstrainableLabels interface {
|
||||
constrainedLabels() ConstrainedLabels
|
||||
compile() *compiledLabels
|
||||
labelNames() []string
|
||||
}
|
||||
|
||||
@@ -67,8 +63,20 @@ type ConstrainableLabels interface {
|
||||
// metric vector Collectors.
|
||||
type ConstrainedLabels []ConstrainedLabel
|
||||
|
||||
func (cls ConstrainedLabels) constrainedLabels() ConstrainedLabels {
|
||||
return cls
|
||||
func (cls ConstrainedLabels) compile() *compiledLabels {
|
||||
compiled := &compiledLabels{
|
||||
names: make([]string, len(cls)),
|
||||
labelConstraints: map[string]LabelConstraint{},
|
||||
}
|
||||
|
||||
for i, label := range cls {
|
||||
compiled.names[i] = label.Name
|
||||
if label.Constraint != nil {
|
||||
compiled.labelConstraints[label.Name] = label.Constraint
|
||||
}
|
||||
}
|
||||
|
||||
return compiled
|
||||
}
|
||||
|
||||
func (cls ConstrainedLabels) labelNames() []string {
|
||||
@@ -92,18 +100,36 @@ func (cls ConstrainedLabels) labelNames() []string {
|
||||
// }
|
||||
type UnconstrainedLabels []string
|
||||
|
||||
func (uls UnconstrainedLabels) constrainedLabels() ConstrainedLabels {
|
||||
constrainedLabels := make([]ConstrainedLabel, len(uls))
|
||||
for i, l := range uls {
|
||||
constrainedLabels[i] = ConstrainedLabel{Name: l}
|
||||
func (uls UnconstrainedLabels) compile() *compiledLabels {
|
||||
return &compiledLabels{
|
||||
names: uls,
|
||||
}
|
||||
return constrainedLabels
|
||||
}
|
||||
|
||||
func (uls UnconstrainedLabels) labelNames() []string {
|
||||
return uls
|
||||
}
|
||||
|
||||
type compiledLabels struct {
|
||||
names []string
|
||||
labelConstraints map[string]LabelConstraint
|
||||
}
|
||||
|
||||
func (cls *compiledLabels) compile() *compiledLabels {
|
||||
return cls
|
||||
}
|
||||
|
||||
func (cls *compiledLabels) labelNames() []string {
|
||||
return cls.names
|
||||
}
|
||||
|
||||
func (cls *compiledLabels) constrain(labelName, value string) string {
|
||||
if fn, ok := cls.labelConstraints[labelName]; ok && fn != nil {
|
||||
return fn(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||
// label names.
|
||||
const reservedLabelPrefix = "__"
|
||||
|
||||
+3
@@ -92,6 +92,9 @@ type Opts struct {
|
||||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// BuildFQName joins the given three name components by "_". Empty name
|
||||
|
||||
Generated
Vendored
+4
-7
@@ -389,16 +389,13 @@ func isLabelCurried(c prometheus.Collector, label string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// emptyLabels is a one-time allocation for non-partitioned metrics to avoid
|
||||
// unnecessary allocations on each request.
|
||||
var emptyLabels = prometheus.Labels{}
|
||||
|
||||
func labels(code, method bool, reqMethod string, status int, extraMethods ...string) prometheus.Labels {
|
||||
if !(code || method) {
|
||||
return emptyLabels
|
||||
}
|
||||
labels := prometheus.Labels{}
|
||||
|
||||
if !(code || method) {
|
||||
return labels
|
||||
}
|
||||
|
||||
if code {
|
||||
labels["code"] = sanitizeCode(status)
|
||||
}
|
||||
|
||||
+3
-3
@@ -548,7 +548,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||
goroutineBudget--
|
||||
runtime.Gosched()
|
||||
}
|
||||
// Once both checkedMetricChan and uncheckdMetricChan are closed
|
||||
// Once both checkedMetricChan and uncheckedMetricChan are closed
|
||||
// and drained, the contraption above will nil out cmc and umc,
|
||||
// and then we can leave the collect loop here.
|
||||
if cmc == nil && umc == nil {
|
||||
@@ -963,9 +963,9 @@ func checkDescConsistency(
|
||||
// Is the desc consistent with the content of the metric?
|
||||
lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
|
||||
copy(lpsFromDesc, desc.constLabelPairs)
|
||||
for _, l := range desc.variableLabels {
|
||||
for _, l := range desc.variableLabels.names {
|
||||
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
||||
Name: proto.String(l.Name),
|
||||
Name: proto.String(l),
|
||||
})
|
||||
}
|
||||
if len(lpsFromDesc) != len(dtoMetric.Label) {
|
||||
|
||||
+30
-11
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/beorn7/perks/quantile"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// quantileLabel is used for the label that defines the quantile in a
|
||||
@@ -145,6 +146,9 @@ type SummaryOpts struct {
|
||||
// is the internal buffer size of the underlying package
|
||||
// "github.com/bmizerany/perks/quantile").
|
||||
BufCap uint32
|
||||
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// SummaryVecOpts bundles the options to create a SummaryVec metric.
|
||||
@@ -154,7 +158,7 @@ type SummaryVecOpts struct {
|
||||
SummaryOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -188,12 +192,12 @@ func NewSummary(opts SummaryOpts) Summary {
|
||||
}
|
||||
|
||||
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
if len(desc.variableLabels) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
|
||||
if len(desc.variableLabels.names) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
|
||||
}
|
||||
|
||||
for _, n := range desc.variableLabels {
|
||||
if n.Name == quantileLabel {
|
||||
for _, n := range desc.variableLabels.names {
|
||||
if n == quantileLabel {
|
||||
panic(errQuantileLabelNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -222,6 +226,9 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
opts.BufCap = DefBufCap
|
||||
}
|
||||
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
if len(opts.Objectives) == 0 {
|
||||
// Use the lock-free implementation of a Summary without objectives.
|
||||
s := &noObjectivesSummary{
|
||||
@@ -230,6 +237,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
counts: [2]*summaryCounts{{}, {}},
|
||||
}
|
||||
s.init(s) // Init self-collection.
|
||||
s.createdTs = timestamppb.New(opts.now())
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -245,7 +253,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
coldBuf: make([]float64, 0, opts.BufCap),
|
||||
streamDuration: opts.MaxAge / time.Duration(opts.AgeBuckets),
|
||||
}
|
||||
s.headStreamExpTime = time.Now().Add(s.streamDuration)
|
||||
s.headStreamExpTime = opts.now().Add(s.streamDuration)
|
||||
s.hotBufExpTime = s.headStreamExpTime
|
||||
|
||||
for i := uint32(0); i < opts.AgeBuckets; i++ {
|
||||
@@ -259,6 +267,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
sort.Float64s(s.sortedObjectives)
|
||||
|
||||
s.init(s) // Init self-collection.
|
||||
s.createdTs = timestamppb.New(opts.now())
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -286,6 +295,8 @@ type summary struct {
|
||||
headStream *quantile.Stream
|
||||
headStreamIdx int
|
||||
headStreamExpTime, hotBufExpTime time.Time
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *summary) Desc() *Desc {
|
||||
@@ -307,7 +318,9 @@ func (s *summary) Observe(v float64) {
|
||||
}
|
||||
|
||||
func (s *summary) Write(out *dto.Metric) error {
|
||||
sum := &dto.Summary{}
|
||||
sum := &dto.Summary{
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
qs := make([]*dto.Quantile, 0, len(s.objectives))
|
||||
|
||||
s.bufMtx.Lock()
|
||||
@@ -440,6 +453,8 @@ type noObjectivesSummary struct {
|
||||
counts [2]*summaryCounts
|
||||
|
||||
labelPairs []*dto.LabelPair
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *noObjectivesSummary) Desc() *Desc {
|
||||
@@ -490,8 +505,9 @@ func (s *noObjectivesSummary) Write(out *dto.Metric) error {
|
||||
}
|
||||
|
||||
sum := &dto.Summary{
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
|
||||
out.Summary = sum
|
||||
@@ -681,6 +697,7 @@ type constSummary struct {
|
||||
sum float64
|
||||
quantiles map[float64]float64
|
||||
labelPairs []*dto.LabelPair
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *constSummary) Desc() *Desc {
|
||||
@@ -688,7 +705,9 @@ func (s *constSummary) Desc() *Desc {
|
||||
}
|
||||
|
||||
func (s *constSummary) Write(out *dto.Metric) error {
|
||||
sum := &dto.Summary{}
|
||||
sum := &dto.Summary{
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
qs := make([]*dto.Quantile, 0, len(s.quantiles))
|
||||
|
||||
sum.SampleCount = proto.Uint64(s.count)
|
||||
@@ -737,7 +756,7 @@ func NewConstSummary(
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &constSummary{
|
||||
|
||||
+47
-8
@@ -14,6 +14,7 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -91,7 +92,7 @@ func (v *valueFunc) Desc() *Desc {
|
||||
}
|
||||
|
||||
func (v *valueFunc) Write(out *dto.Metric) error {
|
||||
return populateMetric(v.valType, v.function(), v.labelPairs, nil, out)
|
||||
return populateMetric(v.valType, v.function(), v.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// NewConstMetric returns a metric with one fixed value that cannot be
|
||||
@@ -105,12 +106,12 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metric := &dto.Metric{}
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric); err != nil {
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -130,6 +131,43 @@ func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelVal
|
||||
return m
|
||||
}
|
||||
|
||||
// NewConstMetricWithCreatedTimestamp does the same thing as NewConstMetric, but generates Counters
|
||||
// with created timestamp set and returns an error for other metric types.
|
||||
func NewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) (Metric, error) {
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch valueType {
|
||||
case CounterValue:
|
||||
break
|
||||
default:
|
||||
return nil, errors.New("created timestamps are only supported for counters")
|
||||
}
|
||||
|
||||
metric := &dto.Metric{}
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, timestamppb.New(ct)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &constMetric{
|
||||
desc: desc,
|
||||
metric: metric,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustNewConstMetricWithCreatedTimestamp is a version of NewConstMetricWithCreatedTimestamp that panics where
|
||||
// NewConstMetricWithCreatedTimestamp would have returned an error.
|
||||
func MustNewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) Metric {
|
||||
m, err := NewConstMetricWithCreatedTimestamp(desc, valueType, value, ct, labelValues...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type constMetric struct {
|
||||
desc *Desc
|
||||
metric *dto.Metric
|
||||
@@ -153,11 +191,12 @@ func populateMetric(
|
||||
labelPairs []*dto.LabelPair,
|
||||
e *dto.Exemplar,
|
||||
m *dto.Metric,
|
||||
ct *timestamppb.Timestamp,
|
||||
) error {
|
||||
m.Label = labelPairs
|
||||
switch t {
|
||||
case CounterValue:
|
||||
m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e}
|
||||
m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e, CreatedTimestamp: ct}
|
||||
case GaugeValue:
|
||||
m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
|
||||
case UntypedValue:
|
||||
@@ -176,19 +215,19 @@ func populateMetric(
|
||||
// This function is only needed for custom Metric implementations. See MetricVec
|
||||
// example.
|
||||
func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
|
||||
totalLen := len(desc.variableLabels.names) + len(desc.constLabelPairs)
|
||||
if totalLen == 0 {
|
||||
// Super fast path.
|
||||
return nil
|
||||
}
|
||||
if len(desc.variableLabels) == 0 {
|
||||
if len(desc.variableLabels.names) == 0 {
|
||||
// Moderately fast path.
|
||||
return desc.constLabelPairs
|
||||
}
|
||||
labelPairs := make([]*dto.LabelPair, 0, totalLen)
|
||||
for i, l := range desc.variableLabels {
|
||||
for i, l := range desc.variableLabels.names {
|
||||
labelPairs = append(labelPairs, &dto.LabelPair{
|
||||
Name: proto.String(l.Name),
|
||||
Name: proto.String(l),
|
||||
Value: proto.String(labelValues[i]),
|
||||
})
|
||||
}
|
||||
|
||||
+56
-50
@@ -20,24 +20,6 @@ import (
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
var labelsPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make(Labels)
|
||||
},
|
||||
}
|
||||
|
||||
func getLabelsFromPool() Labels {
|
||||
return labelsPool.Get().(Labels)
|
||||
}
|
||||
|
||||
func putLabelsToPool(labels Labels) {
|
||||
for k := range labels {
|
||||
delete(labels, k)
|
||||
}
|
||||
|
||||
labelsPool.Put(labels)
|
||||
}
|
||||
|
||||
// MetricVec is a Collector to bundle metrics of the same name that differ in
|
||||
// their label values. MetricVec is not used directly but as a building block
|
||||
// for implementations of vectors of a given metric type, like GaugeVec,
|
||||
@@ -91,6 +73,7 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
||||
// See also the CounterVec example.
|
||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
lvs = constrainLabelValues(m.desc, lvs, m.curry)
|
||||
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -110,8 +93,8 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
||||
// there for pros and cons of the two methods.
|
||||
func (m *MetricVec) Delete(labels Labels) bool {
|
||||
labels = constrainLabels(m.desc, labels)
|
||||
defer putLabelsToPool(labels)
|
||||
labels, closer := constrainLabels(m.desc, labels)
|
||||
defer closer()
|
||||
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
@@ -128,8 +111,8 @@ func (m *MetricVec) Delete(labels Labels) bool {
|
||||
// Note that curried labels will never be matched if deleting from the curried vector.
|
||||
// To match curried labels with DeletePartialMatch, it must be called on the base vector.
|
||||
func (m *MetricVec) DeletePartialMatch(labels Labels) int {
|
||||
labels = constrainLabels(m.desc, labels)
|
||||
defer putLabelsToPool(labels)
|
||||
labels, closer := constrainLabels(m.desc, labels)
|
||||
defer closer()
|
||||
|
||||
return m.metricMap.deleteByLabels(labels, m.curry)
|
||||
}
|
||||
@@ -169,11 +152,11 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||
oldCurry = m.curry
|
||||
iCurry int
|
||||
)
|
||||
for i, label := range m.desc.variableLabels {
|
||||
val, ok := labels[label.Name]
|
||||
for i, labelName := range m.desc.variableLabels.names {
|
||||
val, ok := labels[labelName]
|
||||
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
|
||||
if ok {
|
||||
return nil, fmt.Errorf("label name %q is already curried", label.Name)
|
||||
return nil, fmt.Errorf("label name %q is already curried", labelName)
|
||||
}
|
||||
newCurry = append(newCurry, oldCurry[iCurry])
|
||||
iCurry++
|
||||
@@ -181,7 +164,10 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||
if !ok {
|
||||
continue // Label stays uncurried.
|
||||
}
|
||||
newCurry = append(newCurry, curriedLabelValue{i, label.Constrain(val)})
|
||||
newCurry = append(newCurry, curriedLabelValue{
|
||||
i,
|
||||
m.desc.variableLabels.constrain(labelName, val),
|
||||
})
|
||||
}
|
||||
}
|
||||
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
|
||||
@@ -250,8 +236,8 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
// around MetricVec, implementing a vector for a specific Metric implementation,
|
||||
// for example GaugeVec.
|
||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||
labels = constrainLabels(m.desc, labels)
|
||||
defer putLabelsToPool(labels)
|
||||
labels, closer := constrainLabels(m.desc, labels)
|
||||
defer closer()
|
||||
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
@@ -262,7 +248,7 @@ func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||
}
|
||||
|
||||
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
if err := validateLabelValues(vals, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -271,7 +257,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
curry = m.curry
|
||||
iVals, iCurry int
|
||||
)
|
||||
for i := 0; i < len(m.desc.variableLabels); i++ {
|
||||
for i := 0; i < len(m.desc.variableLabels.names); i++ {
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
h = m.hashAdd(h, curry[iCurry].value)
|
||||
iCurry++
|
||||
@@ -285,7 +271,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
}
|
||||
|
||||
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
if err := validateValuesInLabels(labels, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -294,17 +280,17 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
curry = m.curry
|
||||
iCurry int
|
||||
)
|
||||
for i, label := range m.desc.variableLabels {
|
||||
val, ok := labels[label.Name]
|
||||
for i, labelName := range m.desc.variableLabels.names {
|
||||
val, ok := labels[labelName]
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
if ok {
|
||||
return 0, fmt.Errorf("label name %q is already curried", label.Name)
|
||||
return 0, fmt.Errorf("label name %q is already curried", labelName)
|
||||
}
|
||||
h = m.hashAdd(h, curry[iCurry].value)
|
||||
iCurry++
|
||||
} else {
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("label name %q missing in label map", label.Name)
|
||||
return 0, fmt.Errorf("label name %q missing in label map", labelName)
|
||||
}
|
||||
h = m.hashAdd(h, val)
|
||||
}
|
||||
@@ -482,7 +468,7 @@ func valueMatchesVariableOrCurriedValue(targetValue string, index int, values []
|
||||
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
|
||||
for l, v := range labels {
|
||||
// Check if the target label exists in our metrics and get the index.
|
||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.labelNames())
|
||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.names)
|
||||
if validLabel {
|
||||
// Check the value of that label against the target value.
|
||||
// We don't consider curried values in partial matches.
|
||||
@@ -626,7 +612,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||
return false
|
||||
}
|
||||
iCurry := 0
|
||||
for i, k := range desc.variableLabels {
|
||||
for i, k := range desc.variableLabels.names {
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
if values[i] != curry[iCurry].value {
|
||||
return false
|
||||
@@ -634,7 +620,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||
iCurry++
|
||||
continue
|
||||
}
|
||||
if values[i] != labels[k.Name] {
|
||||
if values[i] != labels[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -644,13 +630,13 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
|
||||
labelValues := make([]string, len(labels)+len(curry))
|
||||
iCurry := 0
|
||||
for i, k := range desc.variableLabels {
|
||||
for i, k := range desc.variableLabels.names {
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
labelValues[i] = curry[iCurry].value
|
||||
iCurry++
|
||||
continue
|
||||
}
|
||||
labelValues[i] = labels[k.Name]
|
||||
labelValues[i] = labels[k]
|
||||
}
|
||||
return labelValues
|
||||
}
|
||||
@@ -670,20 +656,37 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
|
||||
return labelValues
|
||||
}
|
||||
|
||||
func constrainLabels(desc *Desc, labels Labels) Labels {
|
||||
constrainedLabels := getLabelsFromPool()
|
||||
for l, v := range labels {
|
||||
if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok {
|
||||
v = desc.variableLabels[i].Constrain(v)
|
||||
}
|
||||
var labelsPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make(Labels)
|
||||
},
|
||||
}
|
||||
|
||||
constrainedLabels[l] = v
|
||||
func constrainLabels(desc *Desc, labels Labels) (Labels, func()) {
|
||||
if len(desc.variableLabels.labelConstraints) == 0 {
|
||||
// Fast path when there's no constraints
|
||||
return labels, func() {}
|
||||
}
|
||||
|
||||
return constrainedLabels
|
||||
constrainedLabels := labelsPool.Get().(Labels)
|
||||
for l, v := range labels {
|
||||
constrainedLabels[l] = desc.variableLabels.constrain(l, v)
|
||||
}
|
||||
|
||||
return constrainedLabels, func() {
|
||||
for k := range constrainedLabels {
|
||||
delete(constrainedLabels, k)
|
||||
}
|
||||
labelsPool.Put(constrainedLabels)
|
||||
}
|
||||
}
|
||||
|
||||
func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {
|
||||
if len(desc.variableLabels.labelConstraints) == 0 {
|
||||
// Fast path when there's no constraints
|
||||
return lvs
|
||||
}
|
||||
|
||||
constrainedValues := make([]string, len(lvs))
|
||||
var iCurry, iLVs int
|
||||
for i := 0; i < len(lvs)+len(curry); i++ {
|
||||
@@ -692,8 +695,11 @@ func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) [
|
||||
continue
|
||||
}
|
||||
|
||||
if i < len(desc.variableLabels) {
|
||||
constrainedValues[iLVs] = desc.variableLabels[i].Constrain(lvs[iLVs])
|
||||
if i < len(desc.variableLabels.names) {
|
||||
constrainedValues[iLVs] = desc.variableLabels.constrain(
|
||||
desc.variableLabels.names[i],
|
||||
lvs[iLVs],
|
||||
)
|
||||
} else {
|
||||
constrainedValues[iLVs] = lvs[iLVs]
|
||||
}
|
||||
|
||||
+196
-155
@@ -215,8 +215,9 @@ type Counter struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||
Exemplar *Exemplar `protobuf:"bytes,2,opt,name=exemplar" json:"exemplar,omitempty"`
|
||||
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||
Exemplar *Exemplar `protobuf:"bytes,2,opt,name=exemplar" json:"exemplar,omitempty"`
|
||||
CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Counter) Reset() {
|
||||
@@ -265,6 +266,13 @@ func (x *Counter) GetExemplar() *Exemplar {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Counter) GetCreatedTimestamp() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedTimestamp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Quantile struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -325,9 +333,10 @@ type Summary struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"`
|
||||
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
|
||||
Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"`
|
||||
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"`
|
||||
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
|
||||
Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"`
|
||||
CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Summary) Reset() {
|
||||
@@ -383,6 +392,13 @@ func (x *Summary) GetQuantile() []*Quantile {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Summary) GetCreatedTimestamp() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedTimestamp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Untyped struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -439,7 +455,8 @@ type Histogram struct {
|
||||
SampleCountFloat *float64 `protobuf:"fixed64,4,opt,name=sample_count_float,json=sampleCountFloat" json:"sample_count_float,omitempty"` // Overrides sample_count if > 0.
|
||||
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
|
||||
// Buckets for the conventional histogram.
|
||||
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"` // Ordered in increasing order of upper_bound, +Inf bucket is optional.
|
||||
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"` // Ordered in increasing order of upper_bound, +Inf bucket is optional.
|
||||
CreatedTimestamp *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=created_timestamp,json=createdTimestamp" json:"created_timestamp,omitempty"`
|
||||
// schema defines the bucket schema. Currently, valid numbers are -4 <= n <= 8.
|
||||
// They are all for base-2 bucket schemas, where 1 is a bucket boundary in each case, and
|
||||
// then each power of two is divided into 2^n logarithmic buckets.
|
||||
@@ -525,6 +542,13 @@ func (x *Histogram) GetBucket() []*Bucket {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Histogram) GetCreatedTimestamp() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedTimestamp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Histogram) GetSchema() int32 {
|
||||
if x != nil && x.Schema != nil {
|
||||
return *x.Schema
|
||||
@@ -972,137 +996,151 @@ var file_io_prometheus_client_metrics_proto_rawDesc = []byte{
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x22, 0x1d, 0x0a, 0x05, 0x47, 0x61, 0x75, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x22, 0x5b, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74,
|
||||
0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65, 0x6d,
|
||||
0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x22, 0x3c,
|
||||
0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x71, 0x75,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x87, 0x01, 0x0a,
|
||||
0x07, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70,
|
||||
0x65, 0x22, 0xa4, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65,
|
||||
0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65,
|
||||
0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x12,
|
||||
0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3c, 0x0a, 0x08, 0x51, 0x75, 0x61, 0x6e,
|
||||
0x74, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x07, 0x53, 0x75, 0x6d, 0x6d, 0x61,
|
||||
0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f,
|
||||
0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73, 0x61, 0x6d, 0x70, 0x6c,
|
||||
0x65, 0x53, 0x75, 0x6d, 0x12, 0x3a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65,
|
||||
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d,
|
||||
0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x51, 0x75,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65,
|
||||
0x12, 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x1f, 0x0a, 0x07, 0x55, 0x6e, 0x74,
|
||||
0x79, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xac, 0x05, 0x0a, 0x09, 0x48,
|
||||
0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b,
|
||||
0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||
0x09, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x3a, 0x0a, 0x08, 0x71, 0x75,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69,
|
||||
0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x2e, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x71, 0x75,
|
||||
0x61, 0x6e, 0x74, 0x69, 0x6c, 0x65, 0x22, 0x1f, 0x0a, 0x07, 0x55, 0x6e, 0x74, 0x79, 0x70, 0x65,
|
||||
0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01,
|
||||
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xe3, 0x04, 0x0a, 0x09, 0x48, 0x69, 0x73, 0x74,
|
||||
0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73, 0x61, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x34, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18,
|
||||
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65,
|
||||
0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75, 0x63,
|
||||
0x6b, 0x65, 0x74, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73,
|
||||
0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x63, 0x68,
|
||||
0x65, 0x6d, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x74, 0x68, 0x72, 0x65,
|
||||
0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x7a, 0x65, 0x72,
|
||||
0x6f, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x7a, 0x65,
|
||||
0x72, 0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09,
|
||||
0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x7a, 0x65, 0x72,
|
||||
0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x08, 0x20,
|
||||
0x01, 0x28, 0x01, 0x52, 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c,
|
||||
0x6f, 0x61, 0x74, 0x12, 0x45, 0x0a, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f,
|
||||
0x73, 0x70, 0x61, 0x6e, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x6e, 0x65,
|
||||
0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65,
|
||||
0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03,
|
||||
0x28, 0x12, 0x52, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c, 0x74,
|
||||
0x61, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74,
|
||||
0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e,
|
||||
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61,
|
||||
0x6e, 0x52, 0x0c, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12,
|
||||
0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74,
|
||||
0x61, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x12, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76,
|
||||
0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69,
|
||||
0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc6, 0x01,
|
||||
0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x6d, 0x75,
|
||||
0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0f, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76,
|
||||
0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x01, 0x52, 0x14, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70,
|
||||
0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a,
|
||||
0x75, 0x70, 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78,
|
||||
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69,
|
||||
0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78,
|
||||
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x22, 0x3c, 0x0a, 0x0a, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74,
|
||||
0x53, 0x70, 0x61, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65,
|
||||
0x6e, 0x67, 0x74, 0x68, 0x22, 0x91, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61,
|
||||
0x72, 0x12, 0x35, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73,
|
||||
0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69,
|
||||
0x72, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38,
|
||||
0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xff, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74,
|
||||
0x72, 0x69, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65,
|
||||
0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50,
|
||||
0x61, 0x69, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x05, 0x67, 0x61,
|
||||
0x75, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x47, 0x61, 0x75, 0x67, 0x65, 0x52, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x12, 0x37, 0x0a,
|
||||
0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
|
||||
0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72,
|
||||
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x53,
|
||||
0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12,
|
||||
0x37, 0x0a, 0x07, 0x75, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73,
|
||||
0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x52,
|
||||
0x07, 0x75, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x09, 0x68, 0x69, 0x73, 0x74,
|
||||
0x6f, 0x67, 0x72, 0x61, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f,
|
||||
0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61,
|
||||
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x43,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x61, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x73,
|
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x34, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b,
|
||||
0x65, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x47,
|
||||
0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||
0x61, 0x6d, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d,
|
||||
0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12,
|
||||
0x25, 0x0a, 0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c,
|
||||
0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x7a, 0x65, 0x72, 0x6f, 0x54, 0x68, 0x72,
|
||||
0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x7a, 0x65, 0x72, 0x6f,
|
||||
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||
0x0e, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12,
|
||||
0x45, 0x0a, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e,
|
||||
0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d,
|
||||
0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x42, 0x75,
|
||||
0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69,
|
||||
0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69,
|
||||
0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x12, 0x52, 0x0d,
|
||||
0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x25, 0x0a,
|
||||
0x0e, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x0b, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65,
|
||||
0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x69, 0x6f,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x09, 0x68, 0x69,
|
||||
0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x22, 0xa2, 0x01, 0x0a, 0x0c, 0x4d,
|
||||
0x65, 0x74, 0x72, 0x69, 0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68,
|
||||
0x65, 0x6c, 0x70, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75,
|
||||
0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54,
|
||||
0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x6d, 0x65, 0x74,
|
||||
0x72, 0x69, 0x63, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2a,
|
||||
0x62, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
|
||||
0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41,
|
||||
0x55, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x4d, 0x4d, 0x41, 0x52, 0x59,
|
||||
0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x54, 0x59, 0x50, 0x45, 0x44, 0x10, 0x03, 0x12,
|
||||
0x0d, 0x0a, 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x04, 0x12, 0x13,
|
||||
0x0a, 0x0f, 0x47, 0x41, 0x55, 0x47, 0x45, 0x5f, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41,
|
||||
0x4d, 0x10, 0x05, 0x42, 0x52, 0x0a, 0x14, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74,
|
||||
0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5a, 0x3a, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65,
|
||||
0x75, 0x73, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f,
|
||||
0x67, 0x6f, 0x3b, 0x69, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73,
|
||||
0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x6e, 0x74, 0x2e, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e, 0x52, 0x0c, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x0d, 0x20,
|
||||
0x03, 0x28, 0x12, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x6c,
|
||||
0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xc6, 0x01, 0x0a, 0x06, 0x42, 0x75,
|
||||
0x63, 0x6b, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69,
|
||||
0x76, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f,
|
||||
0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
|
||||
0x34, 0x0a, 0x16, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||
0x14, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62,
|
||||
0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65,
|
||||
0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x52, 0x08, 0x65, 0x78, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x72, 0x22, 0x3c, 0x0a, 0x0a, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x70, 0x61, 0x6e,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11,
|
||||
0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67,
|
||||
0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68,
|
||||
0x22, 0x91, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x72, 0x12, 0x35, 0x0a,
|
||||
0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69,
|
||||
0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x6c,
|
||||
0x61, 0x62, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x22, 0xff, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12,
|
||||
0x35, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f,
|
||||
0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x52,
|
||||
0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65,
|
||||
0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x61, 0x75,
|
||||
0x67, 0x65, 0x52, 0x05, 0x67, 0x61, 0x75, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x12, 0x37, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68,
|
||||
0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75, 0x6d, 0x6d, 0x61,
|
||||
0x72, 0x79, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x07, 0x75,
|
||||
0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x69,
|
||||
0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74, 0x2e, 0x55, 0x6e, 0x74, 0x79, 0x70, 0x65, 0x64, 0x52, 0x07, 0x75, 0x6e, 0x74,
|
||||
0x79, 0x70, 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x09, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61,
|
||||
0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x48,
|
||||
0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x52, 0x09, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x67,
|
||||
0x72, 0x61, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x5f, 0x6d, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x22, 0xa2, 0x01, 0x0a, 0x0c, 0x4d, 0x65, 0x74, 0x72, 0x69,
|
||||
0x63, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68,
|
||||
0x65, 0x6c, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x12,
|
||||
0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e,
|
||||
0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18,
|
||||
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65,
|
||||
0x74, 0x68, 0x65, 0x75, 0x73, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74,
|
||||
0x72, 0x69, 0x63, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2a, 0x62, 0x0a, 0x0a, 0x4d,
|
||||
0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55,
|
||||
0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10,
|
||||
0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x4d, 0x4d, 0x41, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0b,
|
||||
0x0a, 0x07, 0x55, 0x4e, 0x54, 0x59, 0x50, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x48,
|
||||
0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x47, 0x41,
|
||||
0x55, 0x47, 0x45, 0x5f, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x05, 0x42,
|
||||
0x52, 0x0a, 0x14, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73,
|
||||
0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x2f, 0x63,
|
||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x67, 0x6f, 0x3b, 0x69,
|
||||
0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x5f, 0x63, 0x6c, 0x69,
|
||||
0x65, 0x6e, 0x74,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1137,26 +1175,29 @@ var file_io_prometheus_client_metrics_proto_goTypes = []interface{}{
|
||||
}
|
||||
var file_io_prometheus_client_metrics_proto_depIdxs = []int32{
|
||||
10, // 0: io.prometheus.client.Counter.exemplar:type_name -> io.prometheus.client.Exemplar
|
||||
4, // 1: io.prometheus.client.Summary.quantile:type_name -> io.prometheus.client.Quantile
|
||||
8, // 2: io.prometheus.client.Histogram.bucket:type_name -> io.prometheus.client.Bucket
|
||||
9, // 3: io.prometheus.client.Histogram.negative_span:type_name -> io.prometheus.client.BucketSpan
|
||||
9, // 4: io.prometheus.client.Histogram.positive_span:type_name -> io.prometheus.client.BucketSpan
|
||||
10, // 5: io.prometheus.client.Bucket.exemplar:type_name -> io.prometheus.client.Exemplar
|
||||
1, // 6: io.prometheus.client.Exemplar.label:type_name -> io.prometheus.client.LabelPair
|
||||
13, // 7: io.prometheus.client.Exemplar.timestamp:type_name -> google.protobuf.Timestamp
|
||||
1, // 8: io.prometheus.client.Metric.label:type_name -> io.prometheus.client.LabelPair
|
||||
2, // 9: io.prometheus.client.Metric.gauge:type_name -> io.prometheus.client.Gauge
|
||||
3, // 10: io.prometheus.client.Metric.counter:type_name -> io.prometheus.client.Counter
|
||||
5, // 11: io.prometheus.client.Metric.summary:type_name -> io.prometheus.client.Summary
|
||||
6, // 12: io.prometheus.client.Metric.untyped:type_name -> io.prometheus.client.Untyped
|
||||
7, // 13: io.prometheus.client.Metric.histogram:type_name -> io.prometheus.client.Histogram
|
||||
0, // 14: io.prometheus.client.MetricFamily.type:type_name -> io.prometheus.client.MetricType
|
||||
11, // 15: io.prometheus.client.MetricFamily.metric:type_name -> io.prometheus.client.Metric
|
||||
16, // [16:16] is the sub-list for method output_type
|
||||
16, // [16:16] is the sub-list for method input_type
|
||||
16, // [16:16] is the sub-list for extension type_name
|
||||
16, // [16:16] is the sub-list for extension extendee
|
||||
0, // [0:16] is the sub-list for field type_name
|
||||
13, // 1: io.prometheus.client.Counter.created_timestamp:type_name -> google.protobuf.Timestamp
|
||||
4, // 2: io.prometheus.client.Summary.quantile:type_name -> io.prometheus.client.Quantile
|
||||
13, // 3: io.prometheus.client.Summary.created_timestamp:type_name -> google.protobuf.Timestamp
|
||||
8, // 4: io.prometheus.client.Histogram.bucket:type_name -> io.prometheus.client.Bucket
|
||||
13, // 5: io.prometheus.client.Histogram.created_timestamp:type_name -> google.protobuf.Timestamp
|
||||
9, // 6: io.prometheus.client.Histogram.negative_span:type_name -> io.prometheus.client.BucketSpan
|
||||
9, // 7: io.prometheus.client.Histogram.positive_span:type_name -> io.prometheus.client.BucketSpan
|
||||
10, // 8: io.prometheus.client.Bucket.exemplar:type_name -> io.prometheus.client.Exemplar
|
||||
1, // 9: io.prometheus.client.Exemplar.label:type_name -> io.prometheus.client.LabelPair
|
||||
13, // 10: io.prometheus.client.Exemplar.timestamp:type_name -> google.protobuf.Timestamp
|
||||
1, // 11: io.prometheus.client.Metric.label:type_name -> io.prometheus.client.LabelPair
|
||||
2, // 12: io.prometheus.client.Metric.gauge:type_name -> io.prometheus.client.Gauge
|
||||
3, // 13: io.prometheus.client.Metric.counter:type_name -> io.prometheus.client.Counter
|
||||
5, // 14: io.prometheus.client.Metric.summary:type_name -> io.prometheus.client.Summary
|
||||
6, // 15: io.prometheus.client.Metric.untyped:type_name -> io.prometheus.client.Untyped
|
||||
7, // 16: io.prometheus.client.Metric.histogram:type_name -> io.prometheus.client.Histogram
|
||||
0, // 17: io.prometheus.client.MetricFamily.type:type_name -> io.prometheus.client.MetricType
|
||||
11, // 18: io.prometheus.client.MetricFamily.metric:type_name -> io.prometheus.client.Metric
|
||||
19, // [19:19] is the sub-list for method output_type
|
||||
19, // [19:19] is the sub-list for method input_type
|
||||
19, // [19:19] is the sub-list for extension type_name
|
||||
19, // [19:19] is the sub-list for extension extendee
|
||||
0, // [0:19] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_io_prometheus_client_metrics_proto_init() }
|
||||
|
||||
+11
-6
@@ -1,12 +1,13 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc v4.23.3
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.12.4
|
||||
// source: agent/agent.proto
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@@ -20,6 +21,10 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type RunRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -213,7 +218,7 @@ type DataRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Dataset string `protobuf:"bytes,1,opt,name=dataset,proto3" json:"dataset,omitempty"`
|
||||
Dataset []byte `protobuf:"bytes,1,opt,name=dataset,proto3" json:"dataset,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DataRequest) Reset() {
|
||||
@@ -248,11 +253,11 @@ func (*DataRequest) Descriptor() ([]byte, []int) {
|
||||
return file_agent_agent_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *DataRequest) GetDataset() string {
|
||||
func (x *DataRequest) GetDataset() []byte {
|
||||
if x != nil {
|
||||
return x.Dataset
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
type DataResponse struct {
|
||||
@@ -405,7 +410,7 @@ var file_agent_agent_proto_rawDesc = []byte{
|
||||
0x72, 0x69, 0x74, 0x68, 0x6d, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61,
|
||||
0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x49, 0x44, 0x22, 0x27, 0x0a, 0x0b, 0x44, 0x61,
|
||||
0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x61, 0x74,
|
||||
0x61, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x61, 0x74, 0x61,
|
||||
0x61, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x64, 0x61, 0x74, 0x61,
|
||||
0x73, 0x65, 0x74, 0x22, 0x2c, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x49, 0x44,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x49,
|
||||
|
||||
+2
-4
@@ -19,12 +19,10 @@ message AlgoRequest { bytes algorithm = 1; }
|
||||
|
||||
message AlgoResponse { string algorithmID = 1; }
|
||||
|
||||
message DataRequest { string dataset = 1; }
|
||||
message DataRequest { bytes dataset = 1; }
|
||||
|
||||
message DataResponse { string datasetID = 1; }
|
||||
|
||||
message ResultRequest {}
|
||||
|
||||
message ResultResponse {
|
||||
bytes file = 1;
|
||||
}
|
||||
message ResultResponse { bytes file = 1; }
|
||||
|
||||
+8
-19
@@ -1,8 +1,4 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v4.23.3
|
||||
// source: agent/agent.proto
|
||||
|
||||
package agent
|
||||
|
||||
@@ -18,13 +14,6 @@ import (
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
AgentService_Run_FullMethodName = "/agent.AgentService/Run"
|
||||
AgentService_Algo_FullMethodName = "/agent.AgentService/Algo"
|
||||
AgentService_Data_FullMethodName = "/agent.AgentService/Data"
|
||||
AgentService_Result_FullMethodName = "/agent.AgentService/Result"
|
||||
)
|
||||
|
||||
// AgentServiceClient is the client API for AgentService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
@@ -45,7 +34,7 @@ func NewAgentServiceClient(cc grpc.ClientConnInterface) AgentServiceClient {
|
||||
|
||||
func (c *agentServiceClient) Run(ctx context.Context, in *RunRequest, opts ...grpc.CallOption) (*RunResponse, error) {
|
||||
out := new(RunResponse)
|
||||
err := c.cc.Invoke(ctx, AgentService_Run_FullMethodName, in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/agent.AgentService/Run", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -54,7 +43,7 @@ func (c *agentServiceClient) Run(ctx context.Context, in *RunRequest, opts ...gr
|
||||
|
||||
func (c *agentServiceClient) Algo(ctx context.Context, in *AlgoRequest, opts ...grpc.CallOption) (*AlgoResponse, error) {
|
||||
out := new(AlgoResponse)
|
||||
err := c.cc.Invoke(ctx, AgentService_Algo_FullMethodName, in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/agent.AgentService/Algo", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -63,7 +52,7 @@ func (c *agentServiceClient) Algo(ctx context.Context, in *AlgoRequest, opts ...
|
||||
|
||||
func (c *agentServiceClient) Data(ctx context.Context, in *DataRequest, opts ...grpc.CallOption) (*DataResponse, error) {
|
||||
out := new(DataResponse)
|
||||
err := c.cc.Invoke(ctx, AgentService_Data_FullMethodName, in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/agent.AgentService/Data", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -72,7 +61,7 @@ func (c *agentServiceClient) Data(ctx context.Context, in *DataRequest, opts ...
|
||||
|
||||
func (c *agentServiceClient) Result(ctx context.Context, in *ResultRequest, opts ...grpc.CallOption) (*ResultResponse, error) {
|
||||
out := new(ResultResponse)
|
||||
err := c.cc.Invoke(ctx, AgentService_Result_FullMethodName, in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/agent.AgentService/Result", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -129,7 +118,7 @@ func _AgentService_Run_Handler(srv interface{}, ctx context.Context, dec func(in
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgentService_Run_FullMethodName,
|
||||
FullMethod: "/agent.AgentService/Run",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgentServiceServer).Run(ctx, req.(*RunRequest))
|
||||
@@ -147,7 +136,7 @@ func _AgentService_Algo_Handler(srv interface{}, ctx context.Context, dec func(i
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgentService_Algo_FullMethodName,
|
||||
FullMethod: "/agent.AgentService/Algo",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgentServiceServer).Algo(ctx, req.(*AlgoRequest))
|
||||
@@ -165,7 +154,7 @@ func _AgentService_Data_Handler(srv interface{}, ctx context.Context, dec func(i
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgentService_Data_FullMethodName,
|
||||
FullMethod: "/agent.AgentService/Data",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgentServiceServer).Data(ctx, req.(*DataRequest))
|
||||
@@ -183,7 +172,7 @@ func _AgentService_Result_Handler(srv interface{}, ctx context.Context, dec func
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AgentService_Result_FullMethodName,
|
||||
FullMethod: "/agent.AgentService/Result",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AgentServiceServer).Result(ctx, req.(*ResultRequest))
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package api contains API-related concerns: endpoint definitions, middlewares
|
||||
// and all resource representations.
|
||||
package api
|
||||
+1
-1
@@ -25,7 +25,7 @@ func (req algoReq) validate() error {
|
||||
}
|
||||
|
||||
type dataReq struct {
|
||||
Dataset string `protobuf:"bytes,1,opt,name=dataset,proto3" json:"dataset,omitempty"`
|
||||
Dataset []byte `protobuf:"bytes,1,opt,name=dataset,proto3" json:"dataset,omitempty"`
|
||||
}
|
||||
|
||||
func (req dataReq) validate() error {
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@ type dataRes struct {
|
||||
}
|
||||
|
||||
type resultRes struct {
|
||||
File []byte `json:"-"`
|
||||
File []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package http contains implementation of kit service HTTP API.
|
||||
package http
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
agent "github.com/ultravioletrs/agent/agent"
|
||||
)
|
||||
|
||||
func runEndpoint(svc agent.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(runReq)
|
||||
|
||||
if err := req.validate(); err != nil {
|
||||
return runRes{}, err
|
||||
}
|
||||
cmp := agent.Computation{
|
||||
ID: req.computation.ID,
|
||||
Name: req.computation.Name,
|
||||
Description: req.computation.Description,
|
||||
Status: req.computation.Status,
|
||||
Owner: req.computation.Owner,
|
||||
StartTime: req.computation.StartTime,
|
||||
EndTime: req.computation.EndTime,
|
||||
Datasets: req.computation.Datasets,
|
||||
Algorithms: req.computation.Algorithms,
|
||||
DatasetProviders: req.computation.DatasetProviders,
|
||||
AlgorithmProviders: req.computation.AlgorithmProviders,
|
||||
ResultConsumers: req.computation.ResultConsumers,
|
||||
Ttl: req.computation.Ttl,
|
||||
Metadata: req.computation.Metadata,
|
||||
}
|
||||
|
||||
cmpStr, err := svc.Run(ctx, cmp)
|
||||
if err != nil {
|
||||
return runRes{}, err
|
||||
}
|
||||
|
||||
return runRes{Computation: cmpStr}, nil
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package http
|
||||
|
||||
import agent "github.com/ultravioletrs/agent/agent"
|
||||
|
||||
type runReq struct {
|
||||
computation agent.Computation
|
||||
}
|
||||
|
||||
func (req runReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mainflux/mainflux"
|
||||
)
|
||||
|
||||
var _ mainflux.Response = (*runRes)(nil)
|
||||
|
||||
type runRes struct {
|
||||
Computation string `json:"computation"`
|
||||
}
|
||||
|
||||
func (res runRes) Code() int {
|
||||
return http.StatusOK
|
||||
}
|
||||
|
||||
func (res runRes) Headers() map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
func (res runRes) Empty() bool {
|
||||
return false
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
"github.com/go-zoo/bone"
|
||||
"github.com/mainflux/mainflux"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/ultravioletrs/agent/agent"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
)
|
||||
|
||||
const contentType = "application/json"
|
||||
|
||||
var (
|
||||
errUnsupportedContentType = errors.New("unsupported content type")
|
||||
errInvalidQueryParams = errors.New("invalid query params")
|
||||
)
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc agent.Service, instanceID string) http.Handler {
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorEncoder(encodeError),
|
||||
}
|
||||
|
||||
r := bone.New()
|
||||
|
||||
r.Post("/run", otelhttp.NewHandler(kithttp.NewServer(
|
||||
runEndpoint(svc),
|
||||
decodeRun,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
), "run"))
|
||||
|
||||
r.GetFunc("/health", mainflux.Health("agent", instanceID))
|
||||
r.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func decodeRun(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
var req runReq
|
||||
if err := json.NewDecoder(r.Body).Decode(&req.computation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
if ar, ok := response.(mainflux.Response); ok {
|
||||
for k, v := range ar.Headers() {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
|
||||
w.WriteHeader(ar.Code())
|
||||
|
||||
if ar.Empty() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
switch err {
|
||||
case agent.ErrMalformedEntity:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case agent.ErrUnauthorizedAccess:
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
case errUnsupportedContentType:
|
||||
w.WriteHeader(http.StatusUnsupportedMediaType)
|
||||
case errInvalidQueryParams:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case io.ErrUnexpectedEOF:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case io.EOF:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
default:
|
||||
switch err.(type) {
|
||||
case *json.SyntaxError:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case *json.UnmarshalTypeError:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !test
|
||||
// +build !test
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
log "github.com/mainflux/mainflux/logger"
|
||||
"github.com/ultravioletrs/agent/agent"
|
||||
)
|
||||
|
||||
var _ agent.Service = (*loggingMiddleware)(nil)
|
||||
|
||||
type loggingMiddleware struct {
|
||||
logger log.Logger
|
||||
svc agent.Service
|
||||
}
|
||||
|
||||
// LoggingMiddleware adds logging facilities to the core service.
|
||||
func LoggingMiddleware(svc agent.Service, logger log.Logger) agent.Service {
|
||||
return &loggingMiddleware{logger, svc}
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Run(ctx context.Context, cmp agent.Computation) (response string, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method Run for computation %s took %s to complete", cmp.ID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Run(ctx, cmp)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Algo(ctx context.Context, algorithm []byte) (response string, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method Algo took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Algo(ctx, algorithm)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Data(ctx context.Context, dataset []byte) (response string, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method Data took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Data(ctx, dataset)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Result(ctx context.Context) (response []byte, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method Result took %s to complete", time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s", message, err))
|
||||
return
|
||||
}
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Result(ctx)
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !test
|
||||
// +build !test
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/ultravioletrs/agent/agent"
|
||||
)
|
||||
|
||||
var _ agent.Service = (*metricsMiddleware)(nil)
|
||||
|
||||
type metricsMiddleware struct {
|
||||
counter metrics.Counter
|
||||
latency metrics.Histogram
|
||||
svc agent.Service
|
||||
}
|
||||
|
||||
// MetricsMiddleware instruments core service by tracking request count and
|
||||
// latency.
|
||||
func MetricsMiddleware(svc agent.Service, counter metrics.Counter, latency metrics.Histogram) agent.Service {
|
||||
return &metricsMiddleware{
|
||||
counter: counter,
|
||||
latency: latency,
|
||||
svc: svc,
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Run(ctx context.Context, cmp agent.Computation) (string, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "run").Add(1)
|
||||
ms.latency.With("method", "run").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Run(ctx, cmp)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Algo(ctx context.Context, algorithm []byte) (string, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "algo").Add(1)
|
||||
ms.latency.With("method", "algo").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Algo(ctx, algorithm)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Data(ctx context.Context, dataset []byte) (string, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "data").Add(1)
|
||||
ms.latency.With("method", "data").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Data(ctx, dataset)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Result(ctx context.Context) ([]byte, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "result").Add(1)
|
||||
ms.latency.With("method", "result").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Result(ctx)
|
||||
}
|
||||
+61
-7
@@ -7,6 +7,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
socket "github.com/ultravioletrs/agent/pkg"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -26,13 +30,20 @@ type Metadata map[string]interface{}
|
||||
type Service interface {
|
||||
Run(ctx context.Context, cmp Computation) (string, error)
|
||||
Algo(ctx context.Context, algorithm []byte) (string, error)
|
||||
Data(ctx context.Context, dataset string) (string, error)
|
||||
Data(ctx context.Context, dataset []byte) (string, error)
|
||||
Result(ctx context.Context) ([]byte, error)
|
||||
}
|
||||
|
||||
type agentService struct {
|
||||
computation Computation
|
||||
algorithms [][]byte
|
||||
datasets [][]byte
|
||||
result []byte
|
||||
}
|
||||
|
||||
const socketPath = "unix_socket"
|
||||
const pyRuntime = "python3"
|
||||
|
||||
var _ Service = (*agentService)(nil)
|
||||
|
||||
// New instantiates the agent service implementation.
|
||||
@@ -40,12 +51,14 @@ func New() Service {
|
||||
return &agentService{}
|
||||
}
|
||||
|
||||
func (ks *agentService) Run(ctx context.Context, cmp Computation) (string, error) {
|
||||
func (as *agentService) Run(ctx context.Context, cmp Computation) (string, error) {
|
||||
cmpJSON, err := json.Marshal(cmp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
as.computation = cmp
|
||||
|
||||
return string(cmpJSON), nil // return the JSON string as the function's string return value
|
||||
}
|
||||
|
||||
@@ -53,6 +66,8 @@ func (as *agentService) Algo(ctx context.Context, algorithm []byte) (string, err
|
||||
// Implement the logic for the Algo method based on your requirements
|
||||
// Use the provided ctx and algorithm parameters as needed
|
||||
|
||||
as.algorithms = append(as.algorithms, algorithm)
|
||||
|
||||
// Perform some processing on the algorithm byte array
|
||||
// For example, generate a unique ID for the algorithm
|
||||
algorithmID := "algo123"
|
||||
@@ -61,10 +76,12 @@ func (as *agentService) Algo(ctx context.Context, algorithm []byte) (string, err
|
||||
return algorithmID, nil
|
||||
}
|
||||
|
||||
func (as *agentService) Data(ctx context.Context, dataset string) (string, error) {
|
||||
func (as *agentService) Data(ctx context.Context, dataset []byte) (string, error) {
|
||||
// Implement the logic for the Data method based on your requirements
|
||||
// Use the provided ctx and dataset parameters as needed
|
||||
|
||||
as.datasets = append(as.datasets, dataset)
|
||||
|
||||
// Perform some processing on the dataset string
|
||||
// For example, generate a unique ID for the dataset
|
||||
datasetID := "dataset456"
|
||||
@@ -77,10 +94,47 @@ func (as *agentService) Result(ctx context.Context) ([]byte, error) {
|
||||
// Implement the logic for the Result method based on your requirements
|
||||
// Use the provided ctx parameter as needed
|
||||
|
||||
// Perform some processing to retrieve the computation result file
|
||||
// For example, read the file from storage or generate a dummy result
|
||||
result := []byte("This is the computation result file.")
|
||||
result, err := run(as.algorithms[0], as.datasets[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing computation: %v", err)
|
||||
}
|
||||
as.result = result
|
||||
|
||||
// Return the result file or an error
|
||||
return result, nil
|
||||
return as.result, nil
|
||||
}
|
||||
|
||||
func run(algoContent []byte, dataContent []byte) ([]byte, error) {
|
||||
listener, err := socket.StartUnixSocketServer(socketPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating stdout pipe: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
// Create channels for received data and errors
|
||||
dataChannel := make(chan []byte)
|
||||
errorChannel := make(chan error)
|
||||
go socket.AcceptConnection(listener, dataChannel, errorChannel)
|
||||
|
||||
// Construct the Python script content with CSV data as a command-line argument
|
||||
script := string(algoContent)
|
||||
data := string(dataContent)
|
||||
cmd := exec.Command(pyRuntime, "-c", script, data, socketPath)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("error starting Python script: %v", err)
|
||||
}
|
||||
|
||||
var receivedData []byte
|
||||
select {
|
||||
case receivedData = <-dataChannel:
|
||||
case err = <-errorChannel:
|
||||
return nil, fmt.Errorf("error receiving data: %v", err)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
return nil, fmt.Errorf("python script execution error: %v", err)
|
||||
}
|
||||
|
||||
return receivedData, nil
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Package tracing provides tracing instrumentation for cocos auth service.
|
||||
//
|
||||
// This package provides tracing middleware for cocos auth service.
|
||||
// It can be used to trace incoming requests and add tracing capabilities to
|
||||
// cocos auth service.
|
||||
//
|
||||
// For more details about tracing instrumentation refer
|
||||
// to the documentation at https://docs.mainflux.io/tracing/.
|
||||
package tracing
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ultravioletrs/agent/agent"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var _ agent.Service = (*tracingMiddleware)(nil)
|
||||
|
||||
type tracingMiddleware struct {
|
||||
tracer trace.Tracer
|
||||
svc agent.Service
|
||||
}
|
||||
|
||||
// New returns a new auth service with tracing capabilities.
|
||||
func New(svc agent.Service, tracer trace.Tracer) agent.Service {
|
||||
return &tracingMiddleware{tracer, svc}
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Run(ctx context.Context, cmp agent.Computation) (string, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "run", trace.WithAttributes(
|
||||
attribute.String("id", cmp.ID),
|
||||
attribute.String("name", cmp.Name),
|
||||
attribute.String("description", cmp.Description),
|
||||
attribute.String("status", cmp.Status),
|
||||
attribute.String("start_time", cmp.StartTime.String()),
|
||||
attribute.String("end_time", cmp.EndTime.String()),
|
||||
attribute.StringSlice("dataset_providers", cmp.DatasetProviders),
|
||||
attribute.StringSlice("algorithm_providers", cmp.AlgorithmProviders),
|
||||
attribute.StringSlice("result_consumers", cmp.ResultConsumers),
|
||||
))
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Run(ctx, cmp)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Algo(ctx context.Context, algorithm []byte) (string, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "algo")
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Algo(ctx, algorithm)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Data(ctx context.Context, dataset []byte) (string, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "data")
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Data(ctx, dataset)
|
||||
}
|
||||
|
||||
func (tm *tracingMiddleware) Result(ctx context.Context) ([]byte, error) {
|
||||
ctx, span := tm.tracer.Start(ctx, "result")
|
||||
defer span.End()
|
||||
|
||||
return tm.svc.Result(ctx)
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Agent CLI
|
||||
|
||||
This repository contains the command-line interface (CLI) tool for interacting with the Agent service. The CLI allows you to perform various tasks such as running computations, uploading algorithms and datasets, and retrieving results.
|
||||
|
||||
## Build
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
make cli
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
#### Run Computation
|
||||
|
||||
To run a computation, use the following command:
|
||||
|
||||
```bash
|
||||
./build/cocos-cli run --computation '{"name": "my-computation"}'
|
||||
```
|
||||
|
||||
#### Upload Algorithm
|
||||
|
||||
To upload an algorithm, use the following command:
|
||||
|
||||
```bash
|
||||
./build/cocos-cli algo /path/to/algorithm
|
||||
```
|
||||
|
||||
#### Upload Dataset
|
||||
|
||||
To upload a dataset, use the following command:
|
||||
|
||||
```bash
|
||||
./build/cocos-cli data /path/to/dataset.csv
|
||||
```
|
||||
|
||||
#### Retrieve result
|
||||
|
||||
To retrieve the computation result, use the following command:
|
||||
|
||||
```bash
|
||||
./build/cocos-cli result
|
||||
```
|
||||
|
||||
## Installtion
|
||||
|
||||
To use the CLI, you have the option to install it globally on your system. Here's how:
|
||||
|
||||
### Build the CLI:
|
||||
|
||||
Navigate to the project root and run the following command to build the CLI binary:
|
||||
|
||||
```bash
|
||||
make install-cli
|
||||
```
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
agentsdk "github.com/ultravioletrs/agent/pkg/sdk"
|
||||
)
|
||||
|
||||
func NewAlgorithmsCmd(sdk agentsdk.SDK) *cobra.Command {
|
||||
|
||||
return &cobra.Command{
|
||||
Use: "algo",
|
||||
Short: "Upload an algorithm binary",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
algorithmFile := args[0]
|
||||
|
||||
log.Println("Uploading algorithm binary:", algorithmFile)
|
||||
|
||||
algorithm, err := os.ReadFile(algorithmFile)
|
||||
if err != nil {
|
||||
log.Println("Error reading dataset file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := sdk.UploadAlgorithm(algorithm)
|
||||
if err != nil {
|
||||
log.Println("Error uploading algorithm:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Succesfully uploaded algorithm:", response)
|
||||
},
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
agentsdk "github.com/ultravioletrs/agent/pkg/sdk"
|
||||
)
|
||||
|
||||
func NewDatasetsCmd(sdk agentsdk.SDK) *cobra.Command {
|
||||
|
||||
return &cobra.Command{
|
||||
Use: "data",
|
||||
Short: "Upload a dataset CSV file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
datasetFile := args[0]
|
||||
|
||||
log.Println("Uploading dataset CSV:", datasetFile)
|
||||
|
||||
dataset, err := os.ReadFile(datasetFile)
|
||||
if err != nil {
|
||||
log.Println("Error reading dataset file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := sdk.UploadDataset(dataset)
|
||||
if err != nil {
|
||||
log.Println("Error uploading dataset:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Response:", response)
|
||||
},
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Computation Service API
|
||||
version: 1.0.0
|
||||
servers:
|
||||
- url: https://api.example.com/v1
|
||||
paths:
|
||||
/run:
|
||||
post:
|
||||
summary: Run a computation
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
description: The computation binary file (Linux executable)
|
||||
responses:
|
||||
"200":
|
||||
description: Computation started
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
computationId:
|
||||
type: string
|
||||
description: Identifier for the computation
|
||||
|
||||
/algo:
|
||||
post:
|
||||
summary: Upload algorithm binary
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
description: The algorithm binary file (Linux executable)
|
||||
responses:
|
||||
"200":
|
||||
description: Algorithm binary uploaded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
algorithmId:
|
||||
type: string
|
||||
description: Identifier for the uploaded algorithm binary
|
||||
|
||||
/data:
|
||||
post:
|
||||
summary: Upload dataset CSV file
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
description: The dataset CSV file as a binary
|
||||
responses:
|
||||
"200":
|
||||
description: Dataset CSV uploaded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
datasetId:
|
||||
type: string
|
||||
description: Identifier for the uploaded dataset CSV
|
||||
|
||||
/result:
|
||||
get:
|
||||
summary: Retrieve computation result file
|
||||
responses:
|
||||
"200":
|
||||
description: Computation result file retrieved successfully
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
description: The computation result file
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
agentsdk "github.com/ultravioletrs/agent/pkg/sdk"
|
||||
)
|
||||
|
||||
const resultFilePath = "result.bin"
|
||||
|
||||
func NewResultsCmd(sdk agentsdk.SDK) *cobra.Command {
|
||||
|
||||
return &cobra.Command{
|
||||
Use: "result",
|
||||
Short: "Retrieve computation result file",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("Retrieving computation result file")
|
||||
|
||||
result, err := sdk.Result()
|
||||
if err != nil {
|
||||
log.Println("Error retrieving computation result:", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(resultFilePath, result, 0644)
|
||||
if err != nil {
|
||||
log.Println("Error saving computation result:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Computation result retrieved and saved successfully!")
|
||||
},
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
agentsdk "github.com/ultravioletrs/agent/pkg/sdk"
|
||||
)
|
||||
|
||||
func NewRunCmd(sdk agentsdk.SDK) *cobra.Command {
|
||||
var computationJSON string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run a computation",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Println("Running computation")
|
||||
|
||||
var computation agentsdk.Computation
|
||||
if err := json.Unmarshal([]byte(computationJSON), &computation); err != nil {
|
||||
log.Println("Failed to unmarshal computation JSON:", err)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := sdk.Run(computation)
|
||||
if err != nil {
|
||||
log.Println("Error running computation:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Response:", response)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&computationJSON, "computation", "", "JSON representation of the computation")
|
||||
|
||||
if err := cmd.MarkFlagRequired("computation"); err != nil {
|
||||
log.Fatalf("Failed to mark flag as required: %s", err)
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/ultravioletrs/agent/pkg/sdk"
|
||||
)
|
||||
|
||||
func SetSDK(s sdk.SDK) {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Package env contains utility tools for handling
|
||||
// environment-based service configuration.
|
||||
package env
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package env
|
||||
|
||||
import "github.com/caarlos0/env/v7"
|
||||
|
||||
type Options struct {
|
||||
// Environment keys and values that will be accessible for the service
|
||||
Environment map[string]string
|
||||
|
||||
// TagName specifies another tagname to use rather than the default env
|
||||
TagName string
|
||||
|
||||
// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault'
|
||||
RequiredIfNoDef bool
|
||||
|
||||
// OnSet allows to run a function when a value is set
|
||||
OnSet env.OnSetFn
|
||||
|
||||
// Prefix define a prefix for each key
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func Parse(v interface{}, opts ...Options) error {
|
||||
var actOpts []env.Options
|
||||
|
||||
for _, opt := range opts {
|
||||
actOpts = append(actOpts, env.Options{
|
||||
Environment: opt.Environment,
|
||||
TagName: opt.TagName,
|
||||
RequiredIfNoDef: opt.RequiredIfNoDef,
|
||||
OnSet: opt.OnSet,
|
||||
Prefix: opt.Prefix,
|
||||
})
|
||||
}
|
||||
|
||||
return env.Parse(v, actOpts...)
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Package jaeger contains the domain concept definitions needed to support
|
||||
// Mainflux Jaeger functionality.
|
||||
package jaeger
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
jaegerp "go.opentelemetry.io/contrib/propagators/jaeger"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/jaeger"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoURL = errors.New("URL is empty")
|
||||
errNoSvcName = errors.New("Service Name is empty")
|
||||
)
|
||||
|
||||
// NewProvider initializes Jaeger TraceProvider.
|
||||
func NewProvider(ctx context.Context, svcName, url, instanceID string) (*tracesdk.TracerProvider, error) {
|
||||
if url == "" {
|
||||
return nil, errNoURL
|
||||
}
|
||||
|
||||
if svcName == "" {
|
||||
return nil, errNoSvcName
|
||||
}
|
||||
|
||||
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes := []attribute.KeyValue{
|
||||
semconv.ServiceNameKey.String(svcName),
|
||||
attribute.String("InstanceID", instanceID),
|
||||
}
|
||||
|
||||
hostAttr, err := resource.New(ctx, resource.WithHost(), resource.WithOSDescription(), resource.WithContainer())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attributes = append(attributes, hostAttr.Attributes()...)
|
||||
|
||||
tp := tracesdk.NewTracerProvider(
|
||||
tracesdk.WithSampler(tracesdk.AlwaysSample()),
|
||||
tracesdk.WithBatcher(exporter),
|
||||
tracesdk.WithResource(resource.NewWithAttributes(
|
||||
semconv.SchemaURL,
|
||||
attributes...,
|
||||
)),
|
||||
)
|
||||
otel.SetTracerProvider(tp)
|
||||
otel.SetTextMapPropagator(jaegerp.Jaeger{})
|
||||
|
||||
return tp, nil
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// MakeMetrics returns an instance of metrics.
|
||||
func MakeMetrics(namespace, subsystem string) (*kitprometheus.Counter, *kitprometheus.Summary) {
|
||||
counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"})
|
||||
latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"})
|
||||
|
||||
return counter, latency
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Package server contains the HTTP, gRPC and CoAP server implementation.
|
||||
package server
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Package grpc contains the gRPC server implementation.
|
||||
package grpc
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/ultravioletrs/agent/internal/server"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
stopWaitTime = 5 * time.Second
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
server.BaseServer
|
||||
server *grpc.Server
|
||||
registerService serviceRegister
|
||||
}
|
||||
|
||||
type serviceRegister func(srv *grpc.Server)
|
||||
|
||||
var _ server.Server = (*Server)(nil)
|
||||
|
||||
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, registerService serviceRegister, logger logger.Logger) server.Server {
|
||||
var listenFullAddress = fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||
|
||||
return &Server{
|
||||
BaseServer: server.BaseServer{
|
||||
Ctx: ctx,
|
||||
Cancel: cancel,
|
||||
Name: name,
|
||||
Address: listenFullAddress,
|
||||
Config: config,
|
||||
Logger: logger,
|
||||
},
|
||||
registerService: registerService,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
var errCh = make(chan error)
|
||||
|
||||
listener, err := net.Listen("tcp", s.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case s.Config.CertFile != "" || s.Config.KeyFile != "":
|
||||
creds, err := credentials.NewServerTLSFromFile(s.Config.CertFile, s.Config.KeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load auth certificates: %w", err)
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile))
|
||||
s.server = grpc.NewServer(
|
||||
grpc.Creds(creds),
|
||||
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
|
||||
)
|
||||
default:
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s without TLS", s.Name, s.Address))
|
||||
s.server = grpc.NewServer(
|
||||
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
|
||||
)
|
||||
}
|
||||
|
||||
s.registerService(s.server)
|
||||
|
||||
go func() {
|
||||
errCh <- s.server.Serve(listener)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-s.Ctx.Done():
|
||||
return s.Stop()
|
||||
case err := <-errCh:
|
||||
s.Cancel()
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
defer s.Cancel()
|
||||
var c = make(chan bool)
|
||||
go func() {
|
||||
defer close(c)
|
||||
s.server.GracefulStop()
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
case <-time.After(stopWaitTime):
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s gRPC service shutdown at %s", s.Name, s.Address))
|
||||
|
||||
return nil
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Package http contains the HTTP server implementation.
|
||||
package http
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/ultravioletrs/agent/internal/server"
|
||||
)
|
||||
|
||||
const (
|
||||
stopWaitTime = 5 * time.Second
|
||||
httpProtocol = "http"
|
||||
httpsProtocol = "https"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
server.BaseServer
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
var _ server.Server = (*Server)(nil)
|
||||
|
||||
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, handler http.Handler, logger logger.Logger) server.Server {
|
||||
var listenFullAddress = fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||
var httpServer = &http.Server{Addr: listenFullAddress, Handler: handler}
|
||||
return &Server{
|
||||
BaseServer: server.BaseServer{
|
||||
Ctx: ctx,
|
||||
Cancel: cancel,
|
||||
Name: name,
|
||||
Address: listenFullAddress,
|
||||
Config: config,
|
||||
Logger: logger,
|
||||
},
|
||||
server: httpServer,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
var errCh = make(chan error)
|
||||
s.Protocol = httpProtocol
|
||||
switch {
|
||||
case s.Config.CertFile != "" || s.Config.KeyFile != "":
|
||||
s.Protocol = httpsProtocol
|
||||
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile))
|
||||
go func() {
|
||||
errCh <- s.server.ListenAndServeTLS(s.Config.CertFile, s.Config.KeyFile)
|
||||
}()
|
||||
default:
|
||||
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s without TLS", s.Name, s.Protocol, s.Address))
|
||||
go func() {
|
||||
errCh <- s.server.ListenAndServe()
|
||||
}()
|
||||
}
|
||||
select {
|
||||
case <-s.Ctx.Done():
|
||||
return s.Stop()
|
||||
case err := <-errCh:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
defer s.Cancel()
|
||||
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
|
||||
defer cancelShutdown()
|
||||
if err := s.server.Shutdown(ctxShutdown); err != nil {
|
||||
s.Logger.Error(fmt.Sprintf("%s service %s server error occurred during shutdown at %s: %s", s.Name, s.Protocol, s.Address, err))
|
||||
return fmt.Errorf("%s service %s server error occurred during shutdown at %s: %w", s.Name, s.Protocol, s.Address, err)
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s %s service shutdown of http at %s", s.Name, s.Protocol, s.Address))
|
||||
return nil
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
Start() error
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Host string `env:"HOST" envDefault:""`
|
||||
Port string `env:"PORT" envDefault:""`
|
||||
CertFile string `env:"SERVER_CERT" envDefault:""`
|
||||
KeyFile string `env:"SERVER_KEY" envDefault:""`
|
||||
}
|
||||
|
||||
type BaseServer struct {
|
||||
Ctx context.Context
|
||||
Cancel context.CancelFunc
|
||||
Name string
|
||||
Address string
|
||||
Config Config
|
||||
Logger logger.Logger
|
||||
Protocol string
|
||||
}
|
||||
|
||||
func stopAllServers(servers ...Server) error {
|
||||
var errs []error
|
||||
for _, server := range servers {
|
||||
if err := server.Stop(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("encountered errors while stopping servers: %v", errs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopHandler(ctx context.Context, cancel context.CancelFunc, logger logger.Logger, svcName string, servers ...Server) error {
|
||||
var err error
|
||||
var c = make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT)
|
||||
select {
|
||||
case sig := <-c:
|
||||
defer cancel()
|
||||
err = stopAllServers(servers...)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s service error during shutdown: %v", svcName, err))
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s service shutdown by signal: %s", svcName, sig))
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/ultravioletrs/agent/agent"
|
||||
)
|
||||
|
||||
type SDK interface {
|
||||
Run(computation Computation) (string, error)
|
||||
UploadAlgorithm(algorithm []byte) (string, error)
|
||||
UploadDataset(dataset []byte) (string, error)
|
||||
Result() ([]byte, error)
|
||||
}
|
||||
|
||||
type agentSDK struct {
|
||||
client agent.AgentServiceClient
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type Computation struct {
|
||||
ID string `json:"id,omitempty" db:"id"`
|
||||
Name string `json:"name,omitempty" db:"name"`
|
||||
Description string `json:"description,omitempty" db:"description"`
|
||||
Status string `json:"status,omitempty" db:"status"`
|
||||
Owner string `json:"owner,omitempty" db:"owner"`
|
||||
StartTime time.Time `json:"start_time,omitempty" db:"start_time"`
|
||||
EndTime time.Time `json:"end_time,omitempty" db:"end_time"`
|
||||
Datasets []string `json:"datasets,omitempty" db:"datasets"`
|
||||
Algorithms []string `json:"algorithms,omitempty" db:"algorithms"`
|
||||
DatasetProviders []string `json:"dataset_providers,omitempty" db:"dataset_providers"`
|
||||
AlgorithmProviders []string `json:"algorithm_providers,omitempty" db:"algorithm_providers"`
|
||||
ResultConsumers []string `json:"result_consumers,omitempty" db:"result_consumers"`
|
||||
Ttl int `json:"ttl,omitempty" db:"ttl"`
|
||||
Metadata Metadata `json:"metadata,omitempty" db:"metadata"`
|
||||
}
|
||||
|
||||
type Metadata map[string]interface{}
|
||||
|
||||
func NewAgentSDK(log logger.Logger, agentClient agent.AgentServiceClient) *agentSDK {
|
||||
return &agentSDK{
|
||||
client: agentClient,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (sdk *agentSDK) Run(computation Computation) (string, error) {
|
||||
computationBytes, err := json.Marshal(computation)
|
||||
if err != nil {
|
||||
sdk.logger.Error("Failed to marshal computation")
|
||||
return "", err
|
||||
}
|
||||
|
||||
request := &agent.RunRequest{
|
||||
Computation: computationBytes,
|
||||
}
|
||||
response, err := sdk.client.Run(context.Background(), request)
|
||||
if err != nil {
|
||||
sdk.logger.Error("Failed to call Run RPC")
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.Computation, nil
|
||||
}
|
||||
|
||||
func (sdk *agentSDK) UploadAlgorithm(algorithm []byte) (string, error) {
|
||||
request := &agent.AlgoRequest{
|
||||
Algorithm: algorithm,
|
||||
}
|
||||
|
||||
response, err := sdk.client.Algo(context.Background(), request)
|
||||
if err != nil {
|
||||
sdk.logger.Error("Failed to call Algo RPC")
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.AlgorithmID, nil
|
||||
}
|
||||
|
||||
func (sdk *agentSDK) UploadDataset(dataset []byte) (string, error) {
|
||||
request := &agent.DataRequest{
|
||||
Dataset: dataset,
|
||||
}
|
||||
|
||||
response, err := sdk.client.Data(context.Background(), request)
|
||||
if err != nil {
|
||||
sdk.logger.Error("Failed to call Data RPC")
|
||||
return "", err
|
||||
}
|
||||
|
||||
return response.DatasetID, nil
|
||||
}
|
||||
|
||||
func (sdk *agentSDK) Result() ([]byte, error) {
|
||||
request := &agent.ResultRequest{}
|
||||
|
||||
response, err := sdk.client.Result(context.Background(), request)
|
||||
if err != nil {
|
||||
sdk.logger.Error("Failed to call Result RPC")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return response.File, nil
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func StartUnixSocketServer(socketPath string) (net.Listener, error) {
|
||||
// Remove any existing socket file
|
||||
_ = os.Remove(socketPath)
|
||||
|
||||
// Create a Unix domain socket listener
|
||||
listener, err := net.Listen("unix", socketPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating socket listener: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Unix domain socket server is listening on", socketPath)
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func AcceptConnection(listener net.Listener, dataChannel chan []byte, errorChannel chan error) {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
errorChannel <- fmt.Errorf("error accepting connection:: %v", err)
|
||||
}
|
||||
|
||||
handleConnection(conn, dataChannel, errorChannel)
|
||||
}
|
||||
|
||||
func handleConnection(conn net.Conn, dataChannel chan []byte, errorChannel chan error) {
|
||||
defer conn.Close()
|
||||
|
||||
// Create a dynamic buffer to store incoming data
|
||||
var buffer []byte
|
||||
tmp := make([]byte, 1024)
|
||||
|
||||
for {
|
||||
// Read data into the temporary buffer
|
||||
n, err := conn.Read(tmp)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
errorChannel <- err
|
||||
}
|
||||
buffer = append(buffer, tmp[:n]...)
|
||||
}
|
||||
|
||||
dataChannel <- buffer
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExeShCmdStdout executes a shell command capturing the standard output.
|
||||
func ExeShCmdStdout(command string, args ...string) (string, error) {
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
|
||||
cmd := exec.Command(command, args...)
|
||||
|
||||
// Capture stdout and stderr using buffers
|
||||
cmd.Stdout = io.MultiWriter(&stdoutBuf, os.Stdout)
|
||||
cmd.Stderr = io.MultiWriter(&stderrBuf, os.Stderr)
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error executing command '%s': %s", cmd.String(), err)
|
||||
}
|
||||
|
||||
return stdoutBuf.String(), nil
|
||||
}
|
||||
|
||||
// ExtractCmdAndArgs extracts the command and its arguments from the output string.
|
||||
func ExtractCmdAndArgs(cmdLine string, sudo bool) (string, []string) {
|
||||
lines := strings.Split(cmdLine, "\n")
|
||||
if len(lines) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
parts := strings.Fields(lines[0])
|
||||
if len(parts) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if sudo {
|
||||
parts = append([]string{"sudo"}, parts...)
|
||||
}
|
||||
|
||||
cmd := parts[0]
|
||||
args := parts[1:]
|
||||
|
||||
return cmd, args
|
||||
}
|
||||
|
||||
// RunCmdOutput runs the specified command and returns its standard output as a string.
|
||||
func RunCmdOutput(command string, args ...string) (string, error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error executing command '%s': %s", cmd.String(), err)
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// RunCmdStart starts the specified command and returns the *exec.Cmd for the running process.
|
||||
func RunCmdStart(command string, args ...string) (*exec.Cmd, error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error starting command '%s': %s", cmd.String(), err)
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Package env contains utility tools for handling
|
||||
// environment-based service configuration.
|
||||
package env
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package env
|
||||
|
||||
import "github.com/caarlos0/env/v7"
|
||||
|
||||
type Options struct {
|
||||
// Environment keys and values that will be accessible for the service
|
||||
Environment map[string]string
|
||||
|
||||
// TagName specifies another tagname to use rather than the default env
|
||||
TagName string
|
||||
|
||||
// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault'
|
||||
RequiredIfNoDef bool
|
||||
|
||||
// OnSet allows to run a function when a value is set
|
||||
OnSet env.OnSetFn
|
||||
|
||||
// Prefix define a prefix for each key
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func Parse(v interface{}, opts ...Options) error {
|
||||
var actOpts []env.Options
|
||||
|
||||
for _, opt := range opts {
|
||||
actOpts = append(actOpts, env.Options{
|
||||
Environment: opt.Environment,
|
||||
TagName: opt.TagName,
|
||||
RequiredIfNoDef: opt.RequiredIfNoDef,
|
||||
OnSet: opt.OnSet,
|
||||
Prefix: opt.Prefix,
|
||||
})
|
||||
}
|
||||
|
||||
return env.Parse(v, actOpts...)
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// CopyFile copies a file from srcPath to dstPath.
|
||||
func CopyFile(srcPath, dstPath string) error {
|
||||
src, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.Create(dstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = io.Copy(dst, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteFilesInDir deletes all files in the directory dirPath.
|
||||
func DeleteFilesInDir(dirPath string) error {
|
||||
files, err := filepath.Glob(filepath.Join(dirPath, "*"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
err := os.Remove(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Package jaeger contains the domain concept definitions needed to support
|
||||
// Mainflux Jaeger functionality.
|
||||
package jaeger
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
jaegerp "go.opentelemetry.io/contrib/propagators/jaeger"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/jaeger"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoURL = errors.New("URL is empty")
|
||||
errNoSvcName = errors.New("Service Name is empty")
|
||||
)
|
||||
|
||||
// NewProvider initializes Jaeger TraceProvider.
|
||||
func NewProvider(ctx context.Context, svcName, url, instanceID string) (*tracesdk.TracerProvider, error) {
|
||||
if url == "" {
|
||||
return nil, errNoURL
|
||||
}
|
||||
|
||||
if svcName == "" {
|
||||
return nil, errNoSvcName
|
||||
}
|
||||
|
||||
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes := []attribute.KeyValue{
|
||||
semconv.ServiceNameKey.String(svcName),
|
||||
attribute.String("InstanceID", instanceID),
|
||||
}
|
||||
|
||||
hostAttr, err := resource.New(ctx, resource.WithHost(), resource.WithOSDescription(), resource.WithContainer())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attributes = append(attributes, hostAttr.Attributes()...)
|
||||
|
||||
tp := tracesdk.NewTracerProvider(
|
||||
tracesdk.WithSampler(tracesdk.AlwaysSample()),
|
||||
tracesdk.WithBatcher(exporter),
|
||||
tracesdk.WithResource(resource.NewWithAttributes(
|
||||
semconv.SchemaURL,
|
||||
attributes...,
|
||||
)),
|
||||
)
|
||||
otel.SetTracerProvider(tp)
|
||||
otel.SetTextMapPropagator(jaegerp.Jaeger{})
|
||||
|
||||
return tp, nil
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// MakeMetrics returns an instance of metrics.
|
||||
func MakeMetrics(namespace, subsystem string) (*kitprometheus.Counter, *kitprometheus.Summary) {
|
||||
counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, []string{"method"})
|
||||
latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, []string{"method"})
|
||||
|
||||
return counter, latency
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Package server contains the HTTP, gRPC and CoAP server implementation.
|
||||
package server
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Package grpc contains the gRPC server implementation.
|
||||
package grpc
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/ultravioletrs/manager/internal/server"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
stopWaitTime = 5 * time.Second
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
server.BaseServer
|
||||
server *grpc.Server
|
||||
registerService serviceRegister
|
||||
}
|
||||
|
||||
type serviceRegister func(srv *grpc.Server)
|
||||
|
||||
var _ server.Server = (*Server)(nil)
|
||||
|
||||
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, registerService serviceRegister, logger logger.Logger) server.Server {
|
||||
var listenFullAddress = fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||
|
||||
return &Server{
|
||||
BaseServer: server.BaseServer{
|
||||
Ctx: ctx,
|
||||
Cancel: cancel,
|
||||
Name: name,
|
||||
Address: listenFullAddress,
|
||||
Config: config,
|
||||
Logger: logger,
|
||||
},
|
||||
registerService: registerService,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
var errCh = make(chan error)
|
||||
|
||||
listener, err := net.Listen("tcp", s.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case s.Config.CertFile != "" || s.Config.KeyFile != "":
|
||||
creds, err := credentials.NewServerTLSFromFile(s.Config.CertFile, s.Config.KeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load auth certificates: %w", err)
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile))
|
||||
s.server = grpc.NewServer(
|
||||
grpc.Creds(creds),
|
||||
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
|
||||
)
|
||||
default:
|
||||
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s without TLS", s.Name, s.Address))
|
||||
s.server = grpc.NewServer(
|
||||
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
|
||||
)
|
||||
}
|
||||
|
||||
s.registerService(s.server)
|
||||
|
||||
go func() {
|
||||
errCh <- s.server.Serve(listener)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-s.Ctx.Done():
|
||||
return s.Stop()
|
||||
case err := <-errCh:
|
||||
s.Cancel()
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
defer s.Cancel()
|
||||
var c = make(chan bool)
|
||||
go func() {
|
||||
defer close(c)
|
||||
s.server.GracefulStop()
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
case <-time.After(stopWaitTime):
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s gRPC service shutdown at %s", s.Name, s.Address))
|
||||
|
||||
return nil
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Package http contains the HTTP server implementation.
|
||||
package http
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/ultravioletrs/manager/internal/server"
|
||||
)
|
||||
|
||||
const (
|
||||
stopWaitTime = 5 * time.Second
|
||||
httpProtocol = "http"
|
||||
httpsProtocol = "https"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
server.BaseServer
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
var _ server.Server = (*Server)(nil)
|
||||
|
||||
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, handler http.Handler, logger logger.Logger) server.Server {
|
||||
var listenFullAddress = fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||
var httpServer = &http.Server{Addr: listenFullAddress, Handler: handler}
|
||||
return &Server{
|
||||
BaseServer: server.BaseServer{
|
||||
Ctx: ctx,
|
||||
Cancel: cancel,
|
||||
Name: name,
|
||||
Address: listenFullAddress,
|
||||
Config: config,
|
||||
Logger: logger,
|
||||
},
|
||||
server: httpServer,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
var errCh = make(chan error)
|
||||
s.Protocol = httpProtocol
|
||||
switch {
|
||||
case s.Config.CertFile != "" || s.Config.KeyFile != "":
|
||||
s.Protocol = httpsProtocol
|
||||
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile))
|
||||
go func() {
|
||||
errCh <- s.server.ListenAndServeTLS(s.Config.CertFile, s.Config.KeyFile)
|
||||
}()
|
||||
default:
|
||||
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s without TLS", s.Name, s.Protocol, s.Address))
|
||||
go func() {
|
||||
errCh <- s.server.ListenAndServe()
|
||||
}()
|
||||
}
|
||||
select {
|
||||
case <-s.Ctx.Done():
|
||||
return s.Stop()
|
||||
case err := <-errCh:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
defer s.Cancel()
|
||||
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
|
||||
defer cancelShutdown()
|
||||
if err := s.server.Shutdown(ctxShutdown); err != nil {
|
||||
s.Logger.Error(fmt.Sprintf("%s service %s server error occurred during shutdown at %s: %s", s.Name, s.Protocol, s.Address, err))
|
||||
return fmt.Errorf("%s service %s server error occurred during shutdown at %s: %w", s.Name, s.Protocol, s.Address, err)
|
||||
}
|
||||
s.Logger.Info(fmt.Sprintf("%s %s service shutdown of http at %s", s.Name, s.Protocol, s.Address))
|
||||
return nil
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
Start() error
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Host string `env:"HOST" envDefault:""`
|
||||
Port string `env:"PORT" envDefault:""`
|
||||
CertFile string `env:"SERVER_CERT" envDefault:""`
|
||||
KeyFile string `env:"SERVER_KEY" envDefault:""`
|
||||
}
|
||||
|
||||
type BaseServer struct {
|
||||
Ctx context.Context
|
||||
Cancel context.CancelFunc
|
||||
Name string
|
||||
Address string
|
||||
Config Config
|
||||
Logger logger.Logger
|
||||
Protocol string
|
||||
}
|
||||
|
||||
func stopAllServer(servers ...Server) error {
|
||||
var errs []error
|
||||
for _, server := range servers {
|
||||
if err := server.Stop(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("encountered errors while stopping servers: %v", errs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopHandler(ctx context.Context, cancel context.CancelFunc, logger logger.Logger, svcName string, servers ...Server) error {
|
||||
var err error
|
||||
var c = make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT)
|
||||
select {
|
||||
case sig := <-c:
|
||||
defer cancel()
|
||||
err = stopAllServer(servers...)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("%s service error during shutdown: %v", svcName, err))
|
||||
}
|
||||
logger.Info(fmt.Sprintf("%s service shutdown by signal: %s", svcName, sig))
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Manager
|
||||
|
||||
Manager service provides a barebones HTTP and gRPC API and Service interface implementation for the development of the manager service.
|
||||
|
||||
## Configuration
|
||||
|
||||
The service is configured using the environment variables from the following table. Note that any unset variables will be replaced with their default values.
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------ | -------------------------------------------------------- | --------------------------------- |
|
||||
| MANAGER_LOG_LEVEL | Log level for manager service (debug, info, warn, error) | info |
|
||||
| MANAGER_HTTP_HOST | Manager service HTTP host | |
|
||||
| MANAGER_HTTP_PORT | Manager service HTTP port | 9021 |
|
||||
| MANAGER_HTTP_SERVER_CERT | Path to server certificate in pem format | |
|
||||
| MANAGER_HTTP_SERVER_KEY | Path to server key in pem format | |
|
||||
| MANAGER_GRPC_HOST | Manager service gRPC host | |
|
||||
| MANAGER_GRPC_PORT | Manager service gRPC port | 7001 |
|
||||
| MANAGER_GRPC_SERVER_CERT | Path to server certificate in pem format | |
|
||||
| MANAGER_GRPC_SERVER_KEY | Path to server key in pem format | |
|
||||
| AGENT_GRPC_URL | Agent service gRPC URL | localhost:7002 |
|
||||
| AGENT_GRPC_TIMEOUT | Agent service gRPC timeout | 1s |
|
||||
| AGENT_GRPC_CA_CERTS | Agent service gRPC CA certificates | |
|
||||
| AGENT_GRPC_CLIENT_TLS | Agent service gRPC client TLS | false |
|
||||
| MANAGER_JAEGER_URL | Jaeger server URL | http://localhost:14268/api/traces |
|
||||
| MANAGER_INSTANCE_ID | Manager service instance ID | |
|
||||
|
||||
## Deployment
|
||||
|
||||
To start the service outside of the container, execute the following shell script:
|
||||
|
||||
```bash
|
||||
# download the latest version of the service
|
||||
go get github.com/ultravioletrs/manager
|
||||
|
||||
cd $GOPATH/src/github.com/ultravioletrs/manager
|
||||
|
||||
# compile the manager
|
||||
make manager
|
||||
|
||||
# copy binary to bin
|
||||
make install
|
||||
|
||||
# set the environment variables and run the service
|
||||
./build/cocos-manager
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
For more information about service capabilities and its usage, please check out the [README documentation](../README.md).
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package api contains API-related concerns: endpoint definitions, middlewares
|
||||
// and all resource representations.
|
||||
package api
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
kitgrpc "github.com/go-kit/kit/transport/grpc"
|
||||
"github.com/ultravioletrs/manager/manager"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const svcName = "manager.ManagerService"
|
||||
|
||||
type grpcClient struct {
|
||||
run endpoint.Endpoint
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewClient returns new gRPC client instance.
|
||||
func NewClient(conn *grpc.ClientConn, timeout time.Duration) manager.ManagerServiceClient {
|
||||
return &grpcClient{
|
||||
run: kitgrpc.NewClient(
|
||||
conn,
|
||||
svcName,
|
||||
"Run",
|
||||
encodeRunRequest,
|
||||
decodeRunResponse,
|
||||
manager.RunResponse{},
|
||||
).Endpoint(),
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// encodeRunRequest is a transport/grpc.EncodeRequestFunc that
|
||||
// converts a user-domain runReq to a gRPC request.
|
||||
func encodeRunRequest(_ context.Context, request interface{}) (interface{}, error) {
|
||||
req, ok := request.(runReq)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid request type: %T", request)
|
||||
}
|
||||
return &manager.RunRequest{
|
||||
Computation: req.Computation,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// decodeRunResponse is a transport/grpc.DecodeResponseFunc that
|
||||
// converts a gRPC RunResponse to a user-domain response.
|
||||
func decodeRunResponse(_ context.Context, grpcResponse interface{}) (interface{}, error) {
|
||||
response, ok := grpcResponse.(*manager.RunResponse)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid response type: %T", grpcResponse)
|
||||
}
|
||||
return runRes{
|
||||
ID: response.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client grpcClient) Run(ctx context.Context, req *manager.RunRequest, _ ...grpc.CallOption) (*manager.RunResponse, error) {
|
||||
ctx, close := context.WithTimeout(ctx, client.timeout)
|
||||
defer close()
|
||||
|
||||
runReq := runReq{
|
||||
Computation: req.GetComputation(),
|
||||
}
|
||||
|
||||
res, err := client.run(ctx, runReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
runRes := res.(runRes)
|
||||
return &manager.RunResponse{ID: runRes.ID}, nil
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Mainflux
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package grpc contains implementation of kit service gRPC API.
|
||||
package grpc
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user