From 81ba94eb639f792f5bf158bd86ba64aa9b944d3a Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Fri, 26 Jul 2024 18:31:26 +0300 Subject: [PATCH 1/2] first version --- .github/dependabot.yml | 12 +++ .github/workflows/golangci-lint.yml | 26 +++++ .github/workflows/release.yaml | 11 ++ .github/workflows/unit.yml | 23 ++++ .golangci.yml | 38 +++++++ Makefile | 10 ++ README.md | 21 +++- cmd/agent.go | 54 ++++++++++ cmd/root.go | 20 ++++ go.mod | 24 +++++ go.sum | 52 +++++++++ internal/app/agent.go | 159 ++++++++++++++++++++++++++++ internal/build/agent.go | 36 +++++++ internal/build/builder.go | 17 +++ internal/build/logger.go | 42 ++++++++ internal/config/cfg.go | 11 ++ main.go | 22 ++++ pkg/ctxid/id.go | 40 +++++++ 18 files changed, 617 insertions(+), 1 deletion(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/golangci-lint.yml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/unit.yml create mode 100644 .golangci.yml create mode 100644 Makefile create mode 100644 cmd/agent.go create mode 100644 cmd/root.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/app/agent.go create mode 100644 internal/build/agent.go create mode 100644 internal/build/builder.go create mode 100644 internal/build/logger.go create mode 100644 internal/config/cfg.go create mode 100644 main.go create mode 100644 pkg/ctxid/id.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..e9c7a2e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: gomod + directory: / + schedule: + interval: daily + open-pull-requests-limit: 10 + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..facb82e --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,26 @@ +name: golangci-lint +on: + push: + branches: + - master + pull_request: +permissions: + contents: read +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + strategy: + matrix: + go-version: [ '1.22' ] + steps: + - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + cache: true + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: v1.59.1 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9fbf698 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,11 @@ +name: go release + +on: + release: + types: [created] + +jobs: + build: + permissions: + contents: write + uses: bavix/.github/.github/workflows/go-release-binary.yml@0.3.0 diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml new file mode 100644 index 0000000..3a20294 --- /dev/null +++ b/.github/workflows/unit.yml @@ -0,0 +1,23 @@ +name: Unit +on: + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + go-version: [ '1.22' ] + steps: + - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go-version }} + cache: true + - name: Install dependencies + run: go get . + - name: Test with Go + run: go test -json ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..6d90f4c --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,38 @@ +run: + timeout: 1m +linters: + enable-all: true + disable: + # turn on later + # - godox + # deprecated + - gomnd + - execinquery + # not relevant + - varnamelen + - wrapcheck + # - paralleltest + # - exhaustruct +linters-settings: + lll: + line-length: 140 + gci: + sections: + - Standard + - Default + - Prefix(github.com/bavix) + depguard: + rules: + main: + allow: + - $gostd + - github.com +issues: + exclude-rules: + - path: cmd/* + linters: + - gochecknoglobals + - gochecknoinits + - path: (.+)_test.go + linters: + - dupl \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..66b6495 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: * + +test: + go test -tags mock -race -cover ./... + +lint: + go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1 run --color always ${args} + +lint-fix: + make lint args=--fix diff --git a/README.md b/README.md index 7561502..5d8468e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,21 @@ # vakeel -An agent sending an event about its work + +vakeel is a service that allows you to send updates from multiple clients to a single server. + +## Description of the service + +The vakeel service is built using [gRPC](https://grpc.io/) and [zerolog](https://github.com/rs/zerolog). + +### Agent +The agent is a client that sends updates to the server. + +### Server +The server is a gRPC server that receives updates from multiple clients and stores them in memory. + +### Protocol +The protocol used by the service is defined in the [protobuf definition](https://github.com/bavix/vakeel-way/blob/master/api/vakeel_way/state.proto). + +## Run the service +``` +LOG_LEVEL=info go run main.go agent --id=224f8a59-6705-4f3e-b7de-177757932aad +``` diff --git a/cmd/agent.go b/cmd/agent.go new file mode 100644 index 0000000..65eadd6 --- /dev/null +++ b/cmd/agent.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "github.com/google/uuid" + "github.com/spf13/cobra" + + "github.com/bavix/vakeel/internal/build" + "github.com/bavix/vakeel/internal/config" + "github.com/bavix/vakeel/pkg/ctxid" +) + +// cfg is the configuration for the Vakeel agent. +// +//nolint:exhaustruct +var cfg *config.Config = &config.Config{} + +// agentCmd is the command for the Vakeel agent. +// +//nolint:exhaustruct +var agentCmd = &cobra.Command{ + Use: "agent", + Short: "Run the Vakeel agent", + // RunE is the function that will be executed when the agent command is called. + // It creates a new builder with the configuration and calls the AgentApp method of the builder. + // The AgentApp method establishes a connection to the Vakeel server and starts sending update requests. + RunE: func(cmd *cobra.Command, _ []string) error { + // Create a new context with the ID value from the configuration. + ctx := ctxid.WithID(cmd.Context(), cfg.ID) + + // Create a new builder with the configuration. + builder := build.New(cfg) + + // Call the AgentApp method of the builder and pass the context of the command. + // The AgentApp method returns an error if the connection or the update service call fails. + return builder.AgentApp(builder.Logger(ctx)) + }, +} + +// init registers the agent command to the root command. +// +//nolint:mnd +func init() { + rootCmd.AddCommand(agentCmd) + + // Set the default value of the host flag to "127.0.0.1". + agentCmd.Flags(). + StringVarP(&cfg.Host, "host", "H", "127.0.0.1", "Host for agent, i.e. the IP address of the Vakeel server.") + // Set the default value of the port flag to 4643. + agentCmd.Flags(). + IntVarP(&cfg.Port, "port", "p", 4643, "Port for agent, i.e. the port number of the Vakeel server.") + // Set the default value of the id flag to uuid.Nil.String(). + agentCmd.Flags(). + StringVar(&cfg.ID, "id", uuid.Nil.String(), "ID of agent, i.e. the UUID of the Vakeel agent.") +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..d857545 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "context" + "os" + + "github.com/spf13/cobra" +) + +//nolint:exhaustruct +var rootCmd = &cobra.Command{ + Use: "vakeel", + Short: "Agent for vakeel-way", +} + +func Execute(ctx context.Context) { + if err := rootCmd.ExecuteContext(ctx); err != nil { + os.Exit(1) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..baf28e1 --- /dev/null +++ b/go.mod @@ -0,0 +1,24 @@ +module github.com/bavix/vakeel + +go 1.22.5 + +require ( + github.com/bavix/apis v1.0.0 + github.com/bavix/vakeel-way v1.0.1 + github.com/google/uuid v1.6.0 + github.com/rs/zerolog v1.33.0 + github.com/spf13/cobra v1.8.1 + google.golang.org/grpc v1.65.0 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f // indirect + google.golang.org/protobuf v1.34.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c117189 --- /dev/null +++ b/go.sum @@ -0,0 +1,52 @@ +github.com/bavix/apis v1.0.0 h1:VqRiXAjVZ8q2Vrwp1/fPT3jXWHwfReFzawxErSymbhI= +github.com/bavix/apis v1.0.0/go.mod h1:lL40JQEilatN4osf1UkweZcKSDWaL5JC8IZilArLOUA= +github.com/bavix/vakeel-way v1.0.1 h1:a2cnjhqUp+rhiv9dZa2tUl7uGlMUeJU9S3fUiWLKM0Q= +github.com/bavix/vakeel-way v1.0.1/go.mod h1:iuYM5/vxGibbsyCHf4Y+symCV7y4tUEk9T9Cpq+7UEs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f h1:RARaIm8pxYuxyNPbBQf5igT7XdOyCNtat1qAT2ZxjU4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/app/agent.go b/internal/app/agent.go new file mode 100644 index 0000000..91c4b2e --- /dev/null +++ b/internal/app/agent.go @@ -0,0 +1,159 @@ +package app + +import ( + "context" + "time" + + "github.com/rs/zerolog" + + apiv1 "github.com/bavix/apis/pkg/bavix/api/v1" + "github.com/bavix/apis/pkg/uuidconv" + "github.com/bavix/vakeel-way/pkg/api/vakeel_way" + "github.com/bavix/vakeel/pkg/ctxid" +) + +// duration is the duration between update requests sent by the agent. +// It is set to 15 seconds. +// +// Update requests are sent continuously to the server with this duration. +// The agent will send an update request to the server every 15 seconds. +const duration = 15 * time.Second + +// Agent sends update requests to the given state service client. +// It continuously sends update requests until the context is cancelled. +// Returns an error if sending the update request fails. +// +// Parameters: +// - ctx: The context.Context to use for the gRPC call. +// - stateServiceClient: The client for the state service. +// +// Returns: +// - error: An error if sending the update request fails. +func Agent( + ctx context.Context, + stateServiceClient vakeel_way.StateServiceClient, +) error { + // Loop until the context is cancelled. + for { + select { + case <-ctx.Done(): + // If the context is cancelled, return the error from the context. + return ctx.Err() + default: + // Create a client stream to send updates to the server. + // This method establishes a connection with the server and returns a client stream. + // If the connection fails, an error is returned. + updateClient, err := stateServiceClient.Update(ctx) + if err != nil { + // Log the error and sleep for a duration before continuing. + logError(ctx, err, "failed to create client stream") + time.Sleep(duration) + continue + } + + // Send an update request to the server. + // This function sends an update request to the server using the client stream. + // If sending the update request fails, an error is returned. + if err := stream(ctx, updateClient); err != nil { + // Log the error and continue. + logError(ctx, err, "failed to send update request") + } + + // Close the update stream to free resources. + // This method closes the client stream and waits for the response from the server. + // If the response is not received, an error is returned. + if _, err := updateClient.CloseAndRecv(); err != nil { + // Log the error and continue. + logError(ctx, err, "failed to close update stream") + } + } + } +} + +// logError logs the error with the given message. +// +// It takes a context, an error, and a message as parameters. +// The function logs the error with the given message using the zerolog logger. +// The logger is obtained from the context and the error is logged with the message. +// +// Parameters: +// - ctx: The context.Context used for logging. +// - err: The error to log. +// - msg: The message to log along with the error. +func logError(ctx context.Context, err error, msg string) { + // Get the logger from the context. + logger := zerolog.Ctx(ctx) + + // Log the error with the message. + logger.Error().Err(err).Msg(msg) +} + +// stream sends an update request to the server at regular intervals. +// +// It takes a context and a client for the update service as parameters. +// The function sends an update request to the server with the ID extracted from the context. +// It also logs a message indicating that an update request is being sent. +// The function returns an error if sending the update request fails. +func stream( + ctx context.Context, + client vakeel_way.StateService_UpdateClient, +) error { + // Get the high and low parts of the UUID from the context. + // The UUID is extracted from the context using the ctxid.ID function. + high, low := uuidconv.UUID2DoubleInt(ctxid.ID(ctx)) + + // Send an initial update request to the server with the given UUID. + // The sendUpdateRequest function logs a message indicating that an update request is being sent + // and returns an error if sending the update request fails. + if err := sendUpdateRequest(ctx, client, high, low); err != nil { + return err + } + + // Create a ticker to send update requests at regular intervals. + // The duration between update requests is set to 15 seconds. + ticker := time.NewTicker(duration) + defer ticker.Stop() + + // Loop until the context is cancelled. + for { + select { + // If the context is cancelled, return nil. + case <-ctx.Done(): + return nil + + // If the ticker fires, send an update request to the server with the given UUID. + case <-ticker.C: + // The sendUpdateRequest function logs a message indicating that an update request is being sent + // and returns an error if sending the update request fails. + if err := sendUpdateRequest(ctx, client, high, low); err != nil { + return err + } + } + } +} + +// sendUpdateRequest sends an update request to the server with the given UUID. +// It takes a context, a client for the server's update service, and the high and low parts of the UUID. +// The function logs a message indicating that an update request is being sent +// and returns an error if sending the update request fails. +func sendUpdateRequest( + ctx context.Context, + client vakeel_way.StateService_UpdateClient, + high int64, + low int64, +) error { + // Create an update request with the UUID extracted from the context. + updateRequest := &vakeel_way.UpdateRequest{ + Ids: []*apiv1.UUID{ + {High: high, Low: low}, + }, + } + + // Log a message indicating that an update request is being sent. + // The message includes the UUID that is being sent. + zerolog.Ctx(ctx).Info().Msgf("sending update request: %s", uuidconv.DoubleInt2UUID(high, low)) + + // Send the update request to the server. + // The function returns an error if sending the update request fails. + return client.Send(updateRequest) +} diff --git a/internal/build/agent.go b/internal/build/agent.go new file mode 100644 index 0000000..f550075 --- /dev/null +++ b/internal/build/agent.go @@ -0,0 +1,36 @@ +package build + +import ( + "context" + "net" + "strconv" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/bavix/vakeel-way/pkg/api/vakeel_way" + "github.com/bavix/vakeel/internal/app" +) + +// AgentApp creates a gRPC client and connects to the server's update service. +// It returns an error if the connection or the update service call fails. +// +// ctx: The context.Context to use for the gRPC call. +// Returns: An error if the connection or update service call fails. +func (b *Builder) AgentApp(ctx context.Context) error { + // Create a gRPC client insecure connection to the server. + conn, err := grpc.NewClient( + net.JoinHostPort(b.config.Host, strconv.Itoa(b.config.Port)), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return err + } + + // Close the connection when the function returns. + defer conn.Close() + + serviceClient := vakeel_way.NewStateServiceClient(conn) + + return app.Agent(ctx, serviceClient) +} diff --git a/internal/build/builder.go b/internal/build/builder.go new file mode 100644 index 0000000..e993d23 --- /dev/null +++ b/internal/build/builder.go @@ -0,0 +1,17 @@ +package build + +import "github.com/bavix/vakeel/internal/config" + +// Builder is a structure that provides methods to build the agent and the client. +type Builder struct { + // config is the configuration for the agent and the client. + config *config.Config +} + +// New creates a new Builder instance with the given configuration. +// It returns a pointer to the Builder instance. +func New(config *config.Config) *Builder { + return &Builder{ + config: config, + } +} diff --git a/internal/build/logger.go b/internal/build/logger.go new file mode 100644 index 0000000..25cae94 --- /dev/null +++ b/internal/build/logger.go @@ -0,0 +1,42 @@ +package build + +import ( + "context" + "log" + "os" + "time" + + "github.com/rs/zerolog" +) + +// Logger creates a new context with a logger attached to it. +// +// It creates a logger with the log level specified in the environment variable LOG_LEVEL. +// The logger is then attached to the given context. +// +// Parameters: +// - ctx: The context to attach the logger to. +// +// Returns: +// - The context with the logger attached. +func (b *Builder) Logger(ctx context.Context) context.Context { + // Parse the log level from the environment variable LOG_LEVEL. + level, err := zerolog.ParseLevel(os.Getenv("LOG_LEVEL")) + if err != nil { + // If the log level is invalid, log the error and stop the application. + log.Fatal(err) + } + + // Create a new logger with the specified log level and time format. + // The time format is set to RFC3339Nano, which is the most precise time format. + logger := zerolog.New(zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { + w.TimeFormat = time.RFC3339Nano + })). + Level(level). + With(). + Timestamp(). + Logger() + + // Attach the logger to the given context and return the new context. + return logger.WithContext(ctx) +} diff --git a/internal/config/cfg.go b/internal/config/cfg.go new file mode 100644 index 0000000..b2b3c0c --- /dev/null +++ b/internal/config/cfg.go @@ -0,0 +1,11 @@ +package config + +// Config holds the configuration for the vakeel agent. +type Config struct { + // Host is the host address of the vakeel-way server. + Host string + // Port is the port number of the vakeel-way server. + Port int + // ID is the agent ID. + ID string +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..2d3d588 --- /dev/null +++ b/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "context" + + "github.com/bavix/vakeel/cmd" +) + +// main is the entry point of the application. +// It executes the command with an empty context. +// The context is used to pass the configuration to the command. +// The configuration is used to set the host and port of the server. +func main() { + // Create an empty context. + // The context is used to pass configuration settings to the command. + ctx := context.Background() + + // Execute the command with the context. + // The command is responsible for setting up and running the server. + // It takes the context as a parameter and uses it to configure the server. + cmd.Execute(ctx) +} diff --git a/pkg/ctxid/id.go b/pkg/ctxid/id.go new file mode 100644 index 0000000..7241080 --- /dev/null +++ b/pkg/ctxid/id.go @@ -0,0 +1,40 @@ +package ctxid + +import ( + "context" + + "github.com/google/uuid" +) + +// idKey is the key type used to store the ID value in the context. +type idKey struct{} + +// WithID returns a new context with the provided ID value. +// +// ctx: The parent context. +// id: The ID value to attach to the context. +// Returns: A new context with the ID value attached. +func WithID(ctx context.Context, id string) context.Context { + return context.WithValue(ctx, &idKey{}, uuid.MustParse(id)) +} + +// ID retrieves the ID value from the provided context. +// +// ctx: The context to retrieve the ID from. +// Returns: The ID value from the context, or an empty UUID if the context doesn't have an ID. +func ID(ctx context.Context) uuid.UUID { + // Get the value associated with the idKey from the context. + // The value is either a uuid.UUID or nil. + value := ctx.Value(&idKey{}) + + // If the value is not a uuid.UUID, return an empty UUID. + // This is the default value for the ID if it is not set in the context. + vid, ok := value.(uuid.UUID) + if !ok { + return uuid.Nil + } + + // Return the value as a uuid.UUID. + // This is the value that is associated with the idKey in the context. + return vid +} From 3c4b65bc95d3866f2d73c5b92bfc302fd9b57d1c Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Fri, 26 Jul 2024 18:36:43 +0300 Subject: [PATCH 2/2] lint fix --- internal/app/agent.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/agent.go b/internal/app/agent.go index 91c4b2e..40cba8d 100644 --- a/internal/app/agent.go +++ b/internal/app/agent.go @@ -48,6 +48,7 @@ func Agent( // Log the error and sleep for a duration before continuing. logError(ctx, err, "failed to create client stream") time.Sleep(duration) + continue }