Compare commits

18 Commits

Author SHA1 Message Date
52758c6e1f Use proper image for tests.
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-29 15:06:32 +05:00
9e5f98a413 Move to mirrorred images for Drone and Dockerfile, linting fixes.
All checks were successful
continuous-integration/drone/push Build is passing
2022-06-29 15:04:57 +05:00
899c406f09 README update. 2021-11-20 23:10:59 +05:00
f0c5fbf68d Update golang version in Dockerfile. 2021-11-20 23:08:14 +05:00
d5fcc5cef9 Linter and Drone config fixes, code linting. 2021-11-20 23:06:40 +05:00
9c045e9fd3 Drone fixes. 2021-11-20 02:11:03 +05:00
24a402ba7a Really push back whole configuration.
Related to #15
2020-04-27 03:51:24 +05:00
412e404192 Return whole configuration to client.
Related to #15
2020-04-27 03:45:43 +05:00
66ec7f86ab Renamed 'configuration' to 'serverconfig'.
Related to #15
2020-04-27 03:37:42 +05:00
2e07cb9726 Linting fixes and fixed HTTP requests logger. 2020-04-25 13:47:19 +05:00
bbb68c48a1 Use latest golang image for testing.
Fixes #20
2020-04-25 13:41:00 +05:00
85c60fd106 README linting. 2019-12-22 04:19:30 +05:00
c910f12eae Merge branch 'package-creation-fix' of pztrn/giredore into master 2019-10-21 23:15:56 +00:00
68ad99b767 Ensure that leading slash is added to package's original path. Fixes #18. 2019-10-22 04:13:27 +05:00
5acc64de59 Drone CI and linting (#14) 2019-10-16 18:32:21 +00:00
ef597063cf Merge branch 'drone-and-import-paths-fixing' of pztrn/giredore into master 2019-10-13 10:15:39 +00:00
ce3c10c8a5 Copypast is good source of typos. Fixed docker hub repo where we will push images. 2019-10-13 15:14:19 +05:00
28adacf53e Moved to go.dev.pztrn.name and updated README with badges. 2019-10-13 15:12:54 +05:00
25 changed files with 261 additions and 208 deletions

View File

@@ -4,39 +4,31 @@ type: docker
name: build name: build
steps: steps:
- name: notify-start - name: lint
image: appleboy/drone-discord image: code.pztrn.name/containers/mirror/golangci/golangci-lint:v1.46.2
settings: environment:
webhook_id: CGO_ENABLED: 0
from_secret: discord_webhook_id commands:
webhook_token: - golangci-lint run
from_secret: discord_webhook_secret
message: 'Starting building **{{repo.name}}#{{build.number}}@{{build.commit}}** @ {{datetime build.started "02-Jan-2006 15:04:05 MST" "Asia/Yekaterinburg"}} (See {{build.link}} for logs).' - name: test
image: code.pztrn.name/containers/mirror/golang:1.18.3-alpine
environment:
CGO_ENABLED: 0
commands:
- go test ./...
- name: docker - name: docker
image: plugins/docker image: code.pztrn.name/containers/mirror/plugins/docker:20.13.0
settings:
username:
from_secret: dockerhub_user
password:
from_secret: dockerhub_password
repo: pztrn/giredore
auto_tag: true
- name: notify-end
when: when:
status: branch: ["master"]
- success
- failure
image: appleboy/drone-discord
settings: settings:
webhook_id: registry: code.pztrn.name
from_secret: discord_webhook_id username: drone
webhook_token: password:
from_secret: discord_webhook_secret from_secret: drone_secret
message: " repo: code.pztrn.name/apps/giredore
{{#success build.status}} auto_tag: true
**{{repo.name}}#{{build.number}}@{{build.commit}}** deployed. depends_on:
{{ else }} - lint
**{{repo.name}}#{{build.number}}@{{build.commit}}** failed. See {{build.link}}. - test
{{/success}}"

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*DS_Store* *DS_Store*
data/* data/*
.idea .idea
.vscode

View File

@@ -10,9 +10,19 @@ linters:
- gocritic - gocritic
# Complains about main() lengths, which isn't an issue. # Complains about main() lengths, which isn't an issue.
- funlen - funlen
# Magic numbers might be everywhere. Disabled for now.
- gomnd
# Deprecated.
- exhaustivestruct
linters-settings: linters-settings:
lll: lll:
line-length: 420 line-length: 420
gocyclo: cyclop:
min-complexity: 40 max-complexity: 25
issues:
exclude-rules:
# There will be some ToDos.
- linters:
- godox
text: "TODO"

View File

@@ -1,15 +1,16 @@
FROM golang:1.13.1-alpine AS build FROM code.pztrn.name/containers/mirror/golang:1.18.3-alpine AS build
WORKDIR /go/src/sources.dev.pztrn.name/pztrn/giredore WORKDIR /go/src/sources.dev.pztrn.name/pztrn/giredore
COPY . . COPY . .
RUN cd /go/src/sources.dev.pztrn.name/pztrn/giredore/cmd/giredored && go build && cd ../giredorectl && go build ENV CGO_ENABLED=0
RUN cd /go/src/sources.dev.pztrn.name/pztrn/giredore/cmd/giredored && go build -tags netgo -ldflags '-w -extldflags "-static"' && cd ../giredorectl && go build -tags netgo -ldflags '-w -extldflags "-static"'
FROM alpine:latest FROM code.pztrn.name/containers/mirror/alpine:3.16.0
LABEL maintainer "Stanislav N. <pztrn@pztrn.name>" LABEL maintainer "Stanislav N. <pztrn@pztrn.name>"
COPY --from=build /go/src/sources.dev.pztrn.name/pztrn/giredore/cmd/giredored/giredored /usr/local/bin/giredored COPY --from=build /go/src/sources.dev.pztrn.name/pztrn/giredore/cmd/giredored/giredored /usr/local/bin/giredored
COPY --from=build /go/src/sources.dev.pztrn.name/pztrn/giredore/cmd/giredorectl/giredorectl /usr/local/bin/giredorectl COPY --from=build /go/src/sources.dev.pztrn.name/pztrn/giredore/cmd/giredorectl/giredorectl /usr/local/bin/giredorectl
EXPOSE 62222 EXPOSE 62222
ENTRYPOINT [ "/usr/local/bin/giredored" ] ENTRYPOINT [ "/usr/local/bin/giredored" ]

View File

@@ -1,12 +1,10 @@
# Giredore # Giredore
[![Build Status](https://github-ci.pztrn.name/api/badges/pztrn/giredore/status.svg)](https://github-ci.pztrn.name/pztrn/giredore) ![Keybase XLM](https://img.shields.io/keybase/xlm/pztrn)
**Giredore** is an utility that does go-import redirects right. Main idea of this project is to provide easy-to-use yet powerful tool for Go developers to serve they packages right and help them with getting nice and very static URLs for their packages. **Giredore** is an utility that does go-import redirects right. Main idea of this project is to provide easy-to-use yet powerful tool for Go developers to serve they packages right and help them with getting nice and very static URLs for their packages.
## What it able to do ## What it able to do
* Be a HTTP server. *If you want HTTPS (and you normally should want it) - put giredore behind proxy or submit merge request that implements this feature)* * Be a HTTP server. *If you want HTTPS (and you normally should want it) - put giredore behind proxy or submit merge request that implements this feature)*
* Provide simple CLI client for controlling things. * Provide simple CLI client for controlling things.
## Documentation
Documentation is available [here](https://docs.dev.pztrn.name/s/bmdesotu0rrje1og7s5g/giredore).

View File

@@ -1,20 +1,16 @@
package main package main
import ( import (
// stdlib
"os" "os"
// local
clientv1 "sources.dev.pztrn.name/pztrn/giredore/domains/client/v1"
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
// other
"github.com/teris-io/cli" "github.com/teris-io/cli"
clientv1 "go.dev.pztrn.name/giredore/domains/client/v1"
"go.dev.pztrn.name/giredore/internal/logger"
) )
func main() { func main() {
config := cli.NewCommand("configuration", "work with giredore server configuration"). config := cli.NewCommand("serverconfig", "work with giredore server configuration").
WithShortcut("conf"). WithShortcut("serverconf").
WithCommand( WithCommand(
cli.NewCommand("get", "gets and prints out current giredore server configuration"). cli.NewCommand("get", "gets and prints out current giredore server configuration").
WithAction(func(args []string, options map[string]string) int { WithAction(func(args []string, options map[string]string) int {
@@ -34,6 +30,7 @@ func main() {
logger.Initialize() logger.Initialize()
clientv1.Initialize() clientv1.Initialize()
clientv1.SetAllowedIPs(args, options) clientv1.SetAllowedIPs(args, options)
return 0 return 0
}), }),
), ),

View File

@@ -1,16 +1,14 @@
package main package main
import ( import (
// stdlib
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
// local serverv1 "go.dev.pztrn.name/giredore/domains/server/v1"
serverv1 "sources.dev.pztrn.name/pztrn/giredore/domains/server/v1" "go.dev.pztrn.name/giredore/internal/configuration"
"sources.dev.pztrn.name/pztrn/giredore/internal/configuration" "go.dev.pztrn.name/giredore/internal/httpserver"
"sources.dev.pztrn.name/pztrn/giredore/internal/httpserver" "go.dev.pztrn.name/giredore/internal/logger"
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
) )
func main() { func main() {
@@ -29,7 +27,9 @@ func main() {
// CTRL+C handler. // CTRL+C handler.
signalHandler := make(chan os.Signal, 1) signalHandler := make(chan os.Signal, 1)
shutdownDone := make(chan bool, 1) shutdownDone := make(chan bool, 1)
signal.Notify(signalHandler, os.Interrupt, syscall.SIGTERM) signal.Notify(signalHandler, os.Interrupt, syscall.SIGTERM)
go func() { go func() {
<-signalHandler <-signalHandler
httpserver.Shutdown() httpserver.Shutdown()

View File

@@ -1,16 +1,15 @@
package clientv1 package clientv1
import ( import (
// stdlib
"strings" "strings"
// local "go.dev.pztrn.name/giredore/internal/requester"
"sources.dev.pztrn.name/pztrn/giredore/internal/requester" "go.dev.pztrn.name/giredore/internal/structs"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
) )
func GetConfiguration(options map[string]string) { func GetConfiguration(options map[string]string) {
url := "http://" + options["server"] + "/_api/configuration" url := "http://" + options["server"] + "/_api/configuration"
log.Info().Msg("Getting configuration from giredore server...") log.Info().Msg("Getting configuration from giredore server...")
data, err := requester.Get(url) data, err := requester.Get(url)
@@ -23,6 +22,7 @@ func GetConfiguration(options map[string]string) {
func SetAllowedIPs(args []string, options map[string]string) { func SetAllowedIPs(args []string, options map[string]string) {
url := "http://" + options["server"] + "/_api/configuration/allowedips" url := "http://" + options["server"] + "/_api/configuration/allowedips"
log.Info().Str("allowed IPs", args[0]).Msg("Setting allowed IPs for API interaction...") log.Info().Str("allowed IPs", args[0]).Msg("Setting allowed IPs for API interaction...")
req := &structs.AllowedIPsSetRequest{ req := &structs.AllowedIPsSetRequest{

View File

@@ -1,17 +1,12 @@
package clientv1 package clientv1
import ( import (
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
"sources.dev.pztrn.name/pztrn/giredore/internal/requester"
// other
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.dev.pztrn.name/giredore/internal/logger"
"go.dev.pztrn.name/giredore/internal/requester"
) )
var ( var log zerolog.Logger
log zerolog.Logger
)
func Initialize() { func Initialize() {
log = logger.Logger.With().Str("type", "domain").Str("package", "client").Int("version", 1).Logger() log = logger.Logger.With().Str("type", "domain").Str("package", "client").Int("version", 1).Logger()

View File

@@ -1,12 +1,10 @@
package clientv1 package clientv1
import ( import (
// stdlib
"strings" "strings"
// local "go.dev.pztrn.name/giredore/internal/requester"
"sources.dev.pztrn.name/pztrn/giredore/internal/requester" "go.dev.pztrn.name/giredore/internal/structs"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
) )
func DeletePackage(args []string, options map[string]string) { func DeletePackage(args []string, options map[string]string) {
@@ -17,6 +15,7 @@ func DeletePackage(args []string, options map[string]string) {
log.Info().Str("original path", req.OriginalPath).Msg("Sending package deletion request to giredored...") log.Info().Str("original path", req.OriginalPath).Msg("Sending package deletion request to giredored...")
url := "http://" + options["server"] + "/_api/packages" url := "http://" + options["server"] + "/_api/packages"
data, err := requester.Delete(url, req) data, err := requester.Delete(url, req)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Failed to send package deletion request to giredored") log.Fatal().Err(err).Msg("Failed to send package deletion request to giredored")
@@ -28,6 +27,7 @@ func DeletePackage(args []string, options map[string]string) {
func GetPackages(args []string, options map[string]string) { func GetPackages(args []string, options map[string]string) {
pkgs := strings.Split(args[0], ",") pkgs := strings.Split(args[0], ",")
// nolint:exhaustruct
req := &structs.PackageGetRequest{} req := &structs.PackageGetRequest{}
if pkgs[0] == "all" { if pkgs[0] == "all" {
req.All = true req.All = true
@@ -36,6 +36,7 @@ func GetPackages(args []string, options map[string]string) {
} }
url := "http://" + options["server"] + "/_api/packages" url := "http://" + options["server"] + "/_api/packages"
log.Info().Msg("Getting packages data from giredore server...") log.Info().Msg("Getting packages data from giredore server...")
data, err := requester.Post(url, req) data, err := requester.Post(url, req)
@@ -54,9 +55,16 @@ func SetPackage(args []string, options map[string]string) {
VCS: args[3], VCS: args[3],
} }
// Execute some necessary checks.
// If package's original path isn't starting with "/" - add it.
if !strings.HasPrefix(pkg.OriginalPath, "/") {
pkg.OriginalPath = "/" + pkg.OriginalPath
}
log.Info().Str("description", pkg.Description).Str("original path", pkg.OriginalPath).Str("real path", pkg.RealPath).Str("VCS", pkg.VCS).Msg("Sending set/update request to giredored...") log.Info().Str("description", pkg.Description).Str("original path", pkg.OriginalPath).Str("real path", pkg.RealPath).Str("VCS", pkg.VCS).Msg("Sending set/update request to giredored...")
url := "http://" + options["server"] + "/_api/packages" url := "http://" + options["server"] + "/_api/packages"
data, err := requester.Put(url, pkg) data, err := requester.Put(url, pkg)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Failed to send package update/set request to giredored") log.Fatal().Err(err).Msg("Failed to send package update/set request to giredored")

View File

@@ -1,32 +1,32 @@
package serverv1 package serverv1
import ( import (
// stdlib
"net/http" "net/http"
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/configuration"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
// other
"github.com/labstack/echo" "github.com/labstack/echo"
"go.dev.pztrn.name/giredore/internal/configuration"
"go.dev.pztrn.name/giredore/internal/structs"
) )
// This function responsible for getting runtime configuration. // This function responsible for getting runtime configuration.
func configurationGET(ec echo.Context) error { func configurationGET(ec echo.Context) error {
return ec.JSON(http.StatusOK, map[string]string{"result": "success"}) // nolint:wrapcheck
return ec.JSON(http.StatusOK, configuration.Cfg)
} }
func configurationAllowedIPsSET(ec echo.Context) error { func configurationAllowedIPsSET(ectx echo.Context) error {
// nolint:exhaustruct
req := &structs.AllowedIPsSetRequest{} req := &structs.AllowedIPsSetRequest{}
if err := ec.Bind(req); err != nil { if err := ectx.Bind(req); err != nil {
log.Error().Err(err).Msg("Failed to parse allowed IPs set request") log.Error().Err(err).Msg("Failed to parse allowed IPs set request")
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrParsingAllowedIPsSetRequest}}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrParsingAllowedIPsSetRequest}})
} }
log.Debug().Msgf("Got set allowed IPs request: %+v", req) log.Debug().Msgf("Got set allowed IPs request: %+v", req)
configuration.Cfg.SetAllowedIPs(req.AllowedIPs) configuration.Cfg.SetAllowedIPs(req.AllowedIPs)
return ec.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess})
} }

View File

@@ -1,17 +1,12 @@
package serverv1 package serverv1
import ( import (
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/httpserver"
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
// other
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.dev.pztrn.name/giredore/internal/httpserver"
"go.dev.pztrn.name/giredore/internal/logger"
) )
var ( var log zerolog.Logger
log zerolog.Logger
)
func Initialize() { func Initialize() {
log = logger.Logger.With().Str("type", "domain").Str("package", "server").Int("version", 1).Logger() log = logger.Logger.With().Str("type", "domain").Str("package", "server").Int("version", 1).Logger()

View File

@@ -1,41 +1,43 @@
package serverv1 package serverv1
import ( import (
// stdlib
"net/http" "net/http"
"strings" "strings"
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/configuration"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
// other
"github.com/labstack/echo" "github.com/labstack/echo"
"go.dev.pztrn.name/giredore/internal/configuration"
"go.dev.pztrn.name/giredore/internal/structs"
) )
func throwGoImports(ec echo.Context) error { func throwGoImports(ectx echo.Context) error {
// Getting real path. This might be the package itself, or namespace // Getting real path. This might be the package itself, or namespace
// to list available packages. // to list available packages.
// For now only package itself is supported, all other features in ToDo. // For now only package itself is supported, all other features in ToDo.
packageNameRaw := ec.Request().URL.Path packageNameRaw := ectx.Request().URL.Path
pkgs, errs := configuration.Cfg.GetPackagesInfo([]string{packageNameRaw}) pkgs, errs := configuration.Cfg.GetPackagesInfo([]string{packageNameRaw})
if errs != nil { if errs != nil {
log.Error().Str("package", packageNameRaw).Msgf("Failed to get package information: %+v", errs) log.Error().Str("package", packageNameRaw).Msgf("Failed to get package information: %+v", errs)
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: errs})
// nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: errs})
} }
if len(pkgs) == 0 { if len(pkgs) == 0 {
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrNoPackagesFound}}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrNoPackagesFound}})
} }
pkg, found := pkgs[packageNameRaw] pkg, found := pkgs[packageNameRaw]
if !found { if !found {
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrNoPackagesFound}}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrNoPackagesFound}})
} }
// We should compose package name using our domain under which giredore // We should compose package name using our domain under which giredore
// is working. // is working.
domain := ec.Request().Host domain := ectx.Request().Host
packageName := domain + packageNameRaw packageName := domain + packageNameRaw
tmpl := singlePackageTemplate tmpl := singlePackageTemplate
@@ -43,5 +45,6 @@ func throwGoImports(ec echo.Context) error {
tmpl = strings.Replace(tmpl, "{VCS}", pkg.VCS, 1) tmpl = strings.Replace(tmpl, "{VCS}", pkg.VCS, 1)
tmpl = strings.Replace(tmpl, "{REPOPATH}", pkg.RealPath, 1) tmpl = strings.Replace(tmpl, "{REPOPATH}", pkg.RealPath, 1)
return ec.HTML(http.StatusOK, tmpl) // nolint:wrapcheck
return ectx.HTML(http.StatusOK, tmpl)
} }

View File

@@ -1,30 +1,31 @@
package serverv1 package serverv1
import ( import (
// stdlib
"net/http" "net/http"
"strings" "strings"
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/configuration"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
// other
"github.com/labstack/echo" "github.com/labstack/echo"
"go.dev.pztrn.name/giredore/internal/configuration"
"go.dev.pztrn.name/giredore/internal/structs"
) )
// This function responsible for getting packages configuration. // This function responsible for getting packages configuration.
func packagesGET(ec echo.Context) error { func packagesGET(ectx echo.Context) error {
// nolint:exhaustruct
req := &structs.PackageGetRequest{} req := &structs.PackageGetRequest{}
if err := ec.Bind(req); err != nil { if err := ectx.Bind(req); err != nil {
log.Error().Err(err).Msg("Failed to parse package get request") log.Error().Err(err).Msg("Failed to parse package get request")
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrParsingPackagesGetRequest}})
// nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrParsingPackagesGetRequest}})
} }
log.Info().Msgf("Received package(s) info get request: %+v", req) log.Info().Msgf("Received package(s) info get request: %+v", req)
var pkgs map[string]*structs.Package var pkgs map[string]*structs.Package
var errors []structs.Error var errors []structs.Error
if req.All { if req.All {
pkgs = configuration.Cfg.GetAllPackagesInfo() pkgs = configuration.Cfg.GetAllPackagesInfo()
} else { } else {
@@ -32,18 +33,23 @@ func packagesGET(ec echo.Context) error {
} }
if len(errors) > 0 { if len(errors) > 0 {
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: errors, Data: pkgs}) // nolint:wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: errors, Data: pkgs})
} }
return ec.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess, Data: pkgs}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess, Data: pkgs})
} }
// This function responsible for deleting package. // This function responsible for deleting package.
func packagesDELETE(ec echo.Context) error { func packagesDELETE(ectx echo.Context) error {
// nolint:exhaustruct
req := &structs.PackageDeleteRequest{} req := &structs.PackageDeleteRequest{}
if err := ec.Bind(req); err != nil { if err := ectx.Bind(req); err != nil {
log.Error().Err(err).Msg("Failed to parse package delete request") log.Error().Err(err).Msg("Failed to parse package delete request")
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrParsingDeleteRequest}})
// nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrParsingDeleteRequest}})
} }
log.Info().Msgf("Received package delete request: %+v", req) log.Info().Msgf("Received package delete request: %+v", req)
@@ -51,28 +57,35 @@ func packagesDELETE(ec echo.Context) error {
errs := configuration.Cfg.DeletePackage(req) errs := configuration.Cfg.DeletePackage(req)
if len(errs) > 0 { if len(errs) > 0 {
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: errs}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: errs})
} }
return ec.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess})
} }
// This function responsible for setting or updating packages. // This function responsible for setting or updating packages.
func packagesSET(ec echo.Context) error { func packagesSET(ectx echo.Context) error {
// nolint:exhaustruct
req := &structs.Package{} req := &structs.Package{}
if err := ec.Bind(req); err != nil { if err := ectx.Bind(req); err != nil {
log.Error().Err(err).Msg("Failed to parse package data") log.Error().Err(err).Msg("Failed to parse package data")
return ec.JSON(http.StatusBadRequest, nil)
// nolint:wrapcheck
return ectx.JSON(http.StatusBadRequest, nil)
} }
log.Info().Msgf("Received package set/update request: %+v", req) log.Info().Msgf("Received package set/update request: %+v", req)
// Validate passed package data. // Validate passed package data.
if !strings.HasPrefix(req.OriginalPath, "/") { if !strings.HasPrefix(req.OriginalPath, "/") {
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrPackageOrigPathShouldStartWithSlash}}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrPackageOrigPathShouldStartWithSlash}})
} }
configuration.Cfg.AddOrUpdatePackage(req) configuration.Cfg.AddOrUpdatePackage(req)
return ec.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusOK, &structs.Reply{Status: structs.StatusSuccess})
} }

2
go.mod
View File

@@ -1,4 +1,4 @@
module sources.dev.pztrn.name/pztrn/giredore module go.dev.pztrn.name/giredore
go 1.13 go 1.13

View File

@@ -1,7 +1,6 @@
package configuration package configuration
import ( import (
// other
"github.com/vrischmann/envconfig" "github.com/vrischmann/envconfig"
) )

View File

@@ -1,11 +1,8 @@
package configuration package configuration
import ( import (
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
// other
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.dev.pztrn.name/giredore/internal/logger"
) )
var ( var (
@@ -19,9 +16,11 @@ func Initialize() {
log = logger.Logger.With().Str("type", "internal").Str("package", "configuration").Logger() log = logger.Logger.With().Str("type", "internal").Str("package", "configuration").Logger()
log.Info().Msg("Initializing...") log.Info().Msg("Initializing...")
// nolint:exhaustruct
envCfg = &envConfig{} envCfg = &envConfig{}
envCfg.Initialize() envCfg.Initialize()
// nolint:exhaustruct
Cfg = &fileConfig{} Cfg = &fileConfig{}
Cfg.Initialize() Cfg.Initialize()

View File

@@ -1,7 +1,6 @@
package configuration package configuration
import ( import (
// stdlib
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -9,8 +8,7 @@ import (
"strings" "strings"
"sync" "sync"
// local "go.dev.pztrn.name/giredore/internal/structs"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
) )
// This structure represents configuration that will be parsed via file. // This structure represents configuration that will be parsed via file.
@@ -19,22 +17,22 @@ import (
// may be accesses concurrently. In other words DO NOT USE EXPORTED FIELDS // may be accesses concurrently. In other words DO NOT USE EXPORTED FIELDS
// DIRECTLY! // DIRECTLY!
type fileConfig struct { type fileConfig struct {
packagesMutex sync.RWMutex
// Packages describes packages mapping.
Packages map[string]*structs.Package
// HTTP describes HTTP server configuration. // HTTP describes HTTP server configuration.
HTTP struct { HTTP struct {
// AllowedIPs is a list of IPs that allowed to access API.
// There might be other authentication implemented in future.
AllowedIPs []string
allowedipsmutex sync.RWMutex allowedipsmutex sync.RWMutex
// Listen is an address on which HTTP server will listen. // Listen is an address on which HTTP server will listen.
Listen string Listen string
// AllowedIPs is a list of IPs that allowed to access API.
// There might be other authentication implemented in future.
AllowedIPs []string
// WaitForSeconds is a timeout during which we will wait for // WaitForSeconds is a timeout during which we will wait for
// HTTP server be up. If timeout will pass and HTTP server won't // HTTP server be up. If timeout will pass and HTTP server won't
// start processing requests - giredore will exit. // start processing requests - giredore will exit.
WaitForSeconds int WaitForSeconds int
} }
// Packages describes packages mapping.
Packages map[string]*structs.Package
packagesMutex sync.RWMutex
} }
func (fc *fileConfig) AddOrUpdatePackage(pkg *structs.Package) { func (fc *fileConfig) AddOrUpdatePackage(pkg *structs.Package) {
@@ -52,6 +50,7 @@ func (fc *fileConfig) DeletePackage(req *structs.PackageDeleteRequest) []structs
if !found { if !found {
errors = append(errors, structs.ErrPackageWasntDefined) errors = append(errors, structs.ErrPackageWasntDefined)
return errors return errors
} }
@@ -62,6 +61,7 @@ func (fc *fileConfig) DeletePackage(req *structs.PackageDeleteRequest) []structs
func (fc *fileConfig) GetAllowedIPs() []string { func (fc *fileConfig) GetAllowedIPs() []string {
var allowedIPs []string var allowedIPs []string
fc.HTTP.allowedipsmutex.RLock() fc.HTTP.allowedipsmutex.RLock()
allowedIPs = append(allowedIPs, fc.HTTP.AllowedIPs...) allowedIPs = append(allowedIPs, fc.HTTP.AllowedIPs...)
fc.HTTP.allowedipsmutex.RUnlock() fc.HTTP.allowedipsmutex.RUnlock()
@@ -83,6 +83,7 @@ func (fc *fileConfig) GetAllPackagesInfo() map[string]*structs.Package {
func (fc *fileConfig) GetPackagesInfo(packages []string) (map[string]*structs.Package, []structs.Error) { func (fc *fileConfig) GetPackagesInfo(packages []string) (map[string]*structs.Package, []structs.Error) {
pkgs := make(map[string]*structs.Package) pkgs := make(map[string]*structs.Package)
var errors []structs.Error var errors []structs.Error
fc.packagesMutex.Lock() fc.packagesMutex.Lock()
@@ -114,6 +115,7 @@ func (fc *fileConfig) Initialize() {
// exists. // exists.
if _, err2 := os.Stat(configPath); os.IsNotExist(err2) { if _, err2 := os.Stat(configPath); os.IsNotExist(err2) {
cfgLoadLog.Error().Msg("Unable to load configuration from filesystem.") cfgLoadLog.Error().Msg("Unable to load configuration from filesystem.")
return return
} }
@@ -135,15 +137,18 @@ func (fc *fileConfig) Initialize() {
// Ensure that localhost (127.0.0.1) are defined in AllowedIPs. // Ensure that localhost (127.0.0.1) are defined in AllowedIPs.
var localhostIsAllowed bool var localhostIsAllowed bool
for _, ip := range fc.HTTP.AllowedIPs { for _, ip := range fc.HTTP.AllowedIPs {
if strings.Contains(ip, "127.0.0.1") { if strings.Contains(ip, "127.0.0.1") {
localhostIsAllowed = true localhostIsAllowed = true
break break
} }
} }
if !localhostIsAllowed { if !localhostIsAllowed {
cfgLoadLog.Warn().Msg("Localhost (127.0.0.1) wasn't allowed to access configuration API, adding it to list of allowed IP addresses") cfgLoadLog.Warn().Msg("Localhost (127.0.0.1) wasn't allowed to access configuration API, adding it to list of allowed IP addresses")
fc.HTTP.AllowedIPs = append(fc.HTTP.AllowedIPs, "127.0.0.1") fc.HTTP.AllowedIPs = append(fc.HTTP.AllowedIPs, "127.0.0.1")
} else { } else {
cfgLoadLog.Debug().Msg("Localhost (127.0.0.1) is allowed to access configuration API") cfgLoadLog.Debug().Msg("Localhost (127.0.0.1) is allowed to access configuration API")
@@ -159,13 +164,16 @@ func (fc *fileConfig) normalizePath(configPath string) (string, error) {
if strings.Contains(configPath, "~") { if strings.Contains(configPath, "~") {
homeDir, err := os.UserHomeDir() homeDir, err := os.UserHomeDir()
if err != nil { if err != nil {
// nolint:wrapcheck
return "", err return "", err
} }
configPath = strings.Replace(configPath, "~", homeDir, 1) configPath = strings.Replace(configPath, "~", homeDir, 1)
} }
absPath, err1 := filepath.Abs(configPath) absPath, err1 := filepath.Abs(configPath)
if err1 != nil { if err1 != nil {
// nolint:wrapcheck
return "", err1 return "", err1
} }
@@ -181,6 +189,7 @@ func (fc *fileConfig) Save() {
data, err := json.Marshal(fc) data, err := json.Marshal(fc)
if err != nil { if err != nil {
cfgSaveLog.Fatal().Err(err).Msg("Failed to encode data into JSON. Configuration file won't be saved!") cfgSaveLog.Fatal().Err(err).Msg("Failed to encode data into JSON. Configuration file won't be saved!")
return return
} }

View File

@@ -1,33 +1,30 @@
package httpserver package httpserver
import ( import (
// stdlib
"net" "net"
"net/http" "net/http"
"strings" "strings"
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/configuration"
"sources.dev.pztrn.name/pztrn/giredore/internal/structs"
// other
"github.com/labstack/echo" "github.com/labstack/echo"
"go.dev.pztrn.name/giredore/internal/configuration"
"go.dev.pztrn.name/giredore/internal/structs"
) )
func checkAllowedIPs() echo.MiddlewareFunc { func checkAllowedIPs() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ec echo.Context) error { return func(ectx echo.Context) error {
// Do nothing if request came not in "/_api" namespace. // Do nothing if request came not in "/_api" namespace.
if !strings.HasPrefix(ec.Request().RequestURI, "/_api") { if !strings.HasPrefix(ectx.Request().RequestURI, "/_api") {
_ = next(ec) return next(ectx)
return nil
} }
// Get IPs and subnets from configuration and parse them // Get IPs and subnets from configuration and parse them
// into comparable things. // into comparable things.
// If IP address was specified without network mask - assume /32. // If IP address was specified without network mask - assume /32.
var subnets []*net.IPNet var subnets []*net.IPNet
allowedIPs := configuration.Cfg.GetAllowedIPs() allowedIPs := configuration.Cfg.GetAllowedIPs()
for _, ip := range allowedIPs { for _, ip := range allowedIPs {
ipToParse := ip ipToParse := ip
if !strings.Contains(ip, "/") { if !strings.Contains(ip, "/") {
@@ -37,7 +34,9 @@ func checkAllowedIPs() echo.MiddlewareFunc {
_, net, err := net.ParseCIDR(ipToParse) _, net, err := net.ParseCIDR(ipToParse)
if err != nil { if err != nil {
log.Error().Err(err).Str("subnet", ipToParse).Msg("Failed to parse CIDR. /_api/ endpoint won't be accessible, this should be fixed manually in configuration file!") log.Error().Err(err).Str("subnet", ipToParse).Msg("Failed to parse CIDR. /_api/ endpoint won't be accessible, this should be fixed manually in configuration file!")
return ec.JSON(http.StatusInternalServerError, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrInvalidAllowedIPDefined}})
// nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusInternalServerError, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrInvalidAllowedIPDefined}})
} }
subnets = append(subnets, net) subnets = append(subnets, net)
@@ -45,21 +44,24 @@ func checkAllowedIPs() echo.MiddlewareFunc {
// Check if requester's IP address are within allowed IP // Check if requester's IP address are within allowed IP
// subnets. // subnets.
ipToCheck := net.ParseIP(ec.RealIP()) ipToCheck := net.ParseIP(ectx.RealIP())
var allowed bool var allowed bool
for _, subnet := range subnets { for _, subnet := range subnets {
if subnet.Contains(ipToCheck) { if subnet.Contains(ipToCheck) {
allowed = true allowed = true
break break
} }
} }
if allowed { if allowed {
_ = next(ec) return next(ectx)
return nil
} }
return ec.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrIPAddressNotAllowed}}) // nolint:exhaustruct,wrapcheck
return ectx.JSON(http.StatusBadRequest, &structs.Reply{Status: structs.StatusFailure, Errors: []structs.Error{structs.ErrIPAddressNotAllowed}})
} }
} }
} }

View File

@@ -1,21 +1,17 @@
package httpserver package httpserver
import ( import (
// stdlib
"context" "context"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "time"
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/configuration"
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
// other
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/labstack/echo/middleware" "github.com/labstack/echo/middleware"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.dev.pztrn.name/giredore/internal/configuration"
"go.dev.pztrn.name/giredore/internal/logger"
) )
var ( var (
@@ -44,10 +40,12 @@ func Initialize() {
// Shutdown stops HTTP server. Returns true on success and false on failure. // Shutdown stops HTTP server. Returns true on success and false on failure.
func Shutdown() { func Shutdown() {
log.Info().Msg("Shutting down HTTP server...") log.Info().Msg("Shutting down HTTP server...")
err := Srv.Shutdown(context.Background()) err := Srv.Shutdown(context.Background())
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Failed to stop HTTP server") log.Fatal().Err(err).Msg("Failed to stop HTTP server")
} }
log.Info().Msg("HTTP server shutted down") log.Info().Msg("HTTP server shutted down")
} }
@@ -59,50 +57,75 @@ func Start() {
go func() { go func() {
err := Srv.Start(configuration.Cfg.HTTP.Listen) err := Srv.Start(configuration.Cfg.HTTP.Listen)
if !strings.Contains(err.Error(), "Server closed") { if !strings.Contains(err.Error(), "Server closed") {
log.Fatal().Err(err).Msg("HTTP server critial error occurred") log.Fatal().Err(err).Msg("HTTP server critical error occurred")
} }
}() }()
// Check that HTTP server was started. // Check that HTTP server was started.
// nolint:exhaustruct
httpc := &http.Client{Timeout: time.Second * 1} httpc := &http.Client{Timeout: time.Second * 1}
checks := 0 checks := 0
for { for {
checks++ checks++
if checks >= configuration.Cfg.HTTP.WaitForSeconds { if checks >= configuration.Cfg.HTTP.WaitForSeconds {
log.Fatal().Int("seconds passed", checks).Msg("HTTP server isn't up") log.Fatal().Int("seconds passed", checks).Msg("HTTP server isn't up")
} }
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
resp, err := httpc.Get("http://" + configuration.Cfg.HTTP.Listen + "/_internal/waitForOnline")
localCtx, cancelFunc := context.WithTimeout(context.Background(), time.Second*1)
req, err := http.NewRequestWithContext(localCtx, "GET", "http://"+configuration.Cfg.HTTP.Listen+"/_internal/waitForOnline", nil)
if err != nil {
log.Panic().Err(err).Msg("Failed to create HTTP request!")
}
resp, err := httpc.Do(req)
if err != nil { if err != nil {
log.Debug().Err(err).Msg("HTTP error occurred, HTTP server isn't ready, waiting...") log.Debug().Err(err).Msg("HTTP error occurred, HTTP server isn't ready, waiting...")
continue continue
} }
response, err := ioutil.ReadAll(resp.Body) response, err := ioutil.ReadAll(resp.Body)
resp.Body.Close() resp.Body.Close()
if err != nil { if err != nil {
log.Debug().Err(err).Msg("Failed to read response body, HTTP server isn't ready, waiting...") log.Debug().Err(err).Msg("Failed to read response body, HTTP server isn't ready, waiting...")
continue continue
} }
log.Debug().Str("status", resp.Status).Int("body length", len(response)).Msg("HTTP response received") log.Debug().Str("status", resp.Status).Int("body length", len(response)).Msg("HTTP response received")
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
if len(response) == 0 { if len(response) == 0 {
log.Debug().Msg("Response is empty, HTTP server isn't ready, waiting...") log.Debug().Msg("Response is empty, HTTP server isn't ready, waiting...")
continue continue
} }
log.Debug().Int("status code", resp.StatusCode).Msgf("Response: %+v", string(response)) log.Debug().Int("status code", resp.StatusCode).Msgf("Response: %+v", string(response))
if len(response) == 17 { if len(response) == 17 {
// This is useless context cancel function call. Thanks to lostcancel linter.
cancelFunc()
break break
} }
} }
} }
log.Info().Msg("HTTP server is ready to process requests") log.Info().Msg("HTTP server is ready to process requests")
} }
func waitForHTTPServerToBeUpHandler(ec echo.Context) error { func waitForHTTPServerToBeUpHandler(ectx echo.Context) error {
response := map[string]string{ response := map[string]string{
"error": "None", "error": "None",
} }
return ec.JSON(200, response)
// nolint:wrapcheck
return ectx.JSON(200, response)
} }

View File

@@ -1,24 +1,29 @@
package httpserver package httpserver
import ( import (
// other "time"
"github.com/labstack/echo" "github.com/labstack/echo"
) )
func requestLogger() echo.MiddlewareFunc { func requestLogger() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ec echo.Context) error { return func(ectx echo.Context) error {
startTime := time.Now()
err := next(ectx)
log.Info(). log.Info().
Str("From", ec.RealIP()). Str("From", ectx.RealIP()).
Str("To", ec.Request().Host). Str("To", ectx.Request().Host).
Str("Method", ec.Request().Method). Str("Method", ectx.Request().Method).
Str("Path", ec.Request().URL.Path). Str("Path", ectx.Request().URL.Path).
Int64("Length", ec.Request().ContentLength). Int64("Length", ectx.Request().ContentLength).
Str("UA", ec.Request().UserAgent()). Str("UA", ectx.Request().UserAgent()).
TimeDiff("TimeMS", time.Now(), startTime).
Msg("HTTP request") Msg("HTTP request")
_ = next(ec) return err
return nil
} }
} }
} }

View File

@@ -1,12 +1,10 @@
package httpserver package httpserver
import ( import (
// stdlib
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
// other
"github.com/labstack/echo" "github.com/labstack/echo"
) )
@@ -15,7 +13,7 @@ import (
type StrictJSONBinder struct{} type StrictJSONBinder struct{}
// Bind parses JSON input. // Bind parses JSON input.
func (sjb *StrictJSONBinder) Bind(i interface{}, c echo.Context) error { func (sjb *StrictJSONBinder) Bind(data interface{}, c echo.Context) error {
req := c.Request() req := c.Request()
if req.ContentLength == 0 { if req.ContentLength == 0 {
return echo.NewHTTPError(http.StatusBadRequest, "Request body can't be empty") return echo.NewHTTPError(http.StatusBadRequest, "Request body can't be empty")
@@ -24,7 +22,10 @@ func (sjb *StrictJSONBinder) Bind(i interface{}, c echo.Context) error {
// Decode it. // Decode it.
decoder := json.NewDecoder(req.Body) decoder := json.NewDecoder(req.Body)
decoder.DisallowUnknownFields() decoder.DisallowUnknownFields()
if err := decoder.Decode(i); err != nil {
// ToDo: rework this code.
// nolint:errorlint
if err := decoder.Decode(data); err != nil {
if ute, ok := err.(*json.UnmarshalTypeError); ok { if ute, ok := err.(*json.UnmarshalTypeError); ok {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset)) return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset))
} else if se, ok := err.(*json.SyntaxError); ok { } else if se, ok := err.(*json.SyntaxError); ok {

View File

@@ -1,13 +1,11 @@
package logger package logger
import ( import (
// stdlib
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time" "time"
// other
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
@@ -17,12 +15,14 @@ var (
) )
// Initialize initializes zerolog with proper formatting and log level. // Initialize initializes zerolog with proper formatting and log level.
// nolint:forbidigo
func Initialize() { func Initialize() {
// Check environment for logger level. // Check environment for logger level.
// Defaulting to INFO. // Defaulting to INFO.
loggerLevel, loggerLevelFound := os.LookupEnv("LOGGER_LEVEL") loggerLevel, loggerLevelFound := os.LookupEnv("LOGGER_LEVEL")
if loggerLevelFound { if loggerLevelFound {
fmt.Println("Setting logger level to:", loggerLevel) fmt.Println("Setting logger level to:", loggerLevel)
switch strings.ToUpper(loggerLevel) { switch strings.ToUpper(loggerLevel) {
case "DEBUG": case "DEBUG":
zerolog.SetGlobalLevel(zerolog.DebugLevel) zerolog.SetGlobalLevel(zerolog.DebugLevel)
@@ -36,7 +36,7 @@ func Initialize() {
zerolog.SetGlobalLevel(zerolog.FatalLevel) zerolog.SetGlobalLevel(zerolog.FatalLevel)
default: default:
fmt.Println("Invalid logger level passed:", loggerLevel) fmt.Println("Invalid logger level passed:", loggerLevel)
fmt.Println("Fofcing INFO") fmt.Println("Forcing INFO")
zerolog.SetGlobalLevel(zerolog.InfoLevel) zerolog.SetGlobalLevel(zerolog.InfoLevel)
} }
} else { } else {
@@ -44,29 +44,32 @@ func Initialize() {
zerolog.SetGlobalLevel(zerolog.InfoLevel) zerolog.SetGlobalLevel(zerolog.InfoLevel)
} }
// nolint:exhaustruct
output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: false, TimeFormat: time.RFC3339} output := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: false, TimeFormat: time.RFC3339}
output.FormatLevel = func(i interface{}) string { output.FormatLevel = func(lvlRaw interface{}) string {
var v string var formattedLvl string
if ii, ok := i.(string); ok {
ii = strings.ToUpper(ii) if lvl, ok := lvlRaw.(string); ok {
switch ii { lvl = strings.ToUpper(lvl)
switch lvl {
case "DEBUG": case "DEBUG":
v = fmt.Sprintf("\x1b[30m%-5s\x1b[0m", ii) formattedLvl = fmt.Sprintf("\x1b[30m%-5s\x1b[0m", lvl)
case "ERROR": case "ERROR":
v = fmt.Sprintf("\x1b[31m%-5s\x1b[0m", ii) formattedLvl = fmt.Sprintf("\x1b[31m%-5s\x1b[0m", lvl)
case "FATAL": case "FATAL":
v = fmt.Sprintf("\x1b[35m%-5s\x1b[0m", ii) formattedLvl = fmt.Sprintf("\x1b[35m%-5s\x1b[0m", lvl)
case "INFO": case "INFO":
v = fmt.Sprintf("\x1b[32m%-5s\x1b[0m", ii) formattedLvl = fmt.Sprintf("\x1b[32m%-5s\x1b[0m", lvl)
case "PANIC": case "PANIC":
v = fmt.Sprintf("\x1b[36m%-5s\x1b[0m", ii) formattedLvl = fmt.Sprintf("\x1b[36m%-5s\x1b[0m", lvl)
case "WARN": case "WARN":
v = fmt.Sprintf("\x1b[33m%-5s\x1b[0m", ii) formattedLvl = fmt.Sprintf("\x1b[33m%-5s\x1b[0m", lvl)
default: default:
v = ii formattedLvl = lvl
} }
} }
return fmt.Sprintf("| %s |", v)
return fmt.Sprintf("| %s |", formattedLvl)
} }
Logger = zerolog.New(output).With().Timestamp().Logger() Logger = zerolog.New(output).With().Timestamp().Logger()

View File

@@ -1,22 +1,17 @@
package requester package requester
import ( import (
// stdlib
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
// local
"sources.dev.pztrn.name/pztrn/giredore/internal/logger"
// other
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.dev.pztrn.name/giredore/internal/logger"
) )
var ( var log zerolog.Logger
log zerolog.Logger
)
func Initialize() { func Initialize() {
log = logger.Logger.With().Str("type", "internal").Str("package", "requester").Logger() log = logger.Logger.With().Str("type", "internal").Str("package", "requester").Logger()
@@ -27,6 +22,7 @@ func Delete(url string, data interface{}) ([]byte, error) {
return execRequest("DELETE", url, data) return execRequest("DELETE", url, data)
} }
// nolint:wrapcheck
func execRequest(method string, url string, data interface{}) ([]byte, error) { func execRequest(method string, url string, data interface{}) ([]byte, error) {
log.Debug().Str("method", method).Str("URL", url).Msg("Trying to execute HTTP request...") log.Debug().Str("method", method).Str("URL", url).Msg("Trying to execute HTTP request...")
@@ -34,11 +30,12 @@ func execRequest(method string, url string, data interface{}) ([]byte, error) {
var dataToSend []byte var dataToSend []byte
if data != nil { if data != nil {
// nolint:errchkjson
dataToSend, _ = json.Marshal(data) dataToSend, _ = json.Marshal(data)
} }
// Compose HTTP request. // Compose HTTP request.
httpReq, err := http.NewRequest(method, url, bytes.NewReader(dataToSend)) httpReq, err := http.NewRequestWithContext(context.Background(), method, url, bytes.NewReader(dataToSend))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -52,6 +49,7 @@ func execRequest(method string, url string, data interface{}) ([]byte, error) {
if err2 != nil { if err2 != nil {
return nil, err2 return nil, err2
} }
response.Body.Close() response.Body.Close()
log.Debug().Int("response body length (bytes)", len(bodyBytes)).Msg("Got response") log.Debug().Int("response body length (bytes)", len(bodyBytes)).Msg("Got response")

View File

@@ -1,14 +1,15 @@
package requester package requester
import ( import (
// stdlib
"net" "net"
"net/http" "net/http"
"time" "time"
) )
// nolint:exhaustivestruct
func getHTTPClient() *http.Client { func getHTTPClient() *http.Client {
c := &http.Client{ // nolint:exhaustruct
client := &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
ExpectContinueTimeout: time.Second * 5, ExpectContinueTimeout: time.Second * 5,
DialContext: (&net.Dialer{ DialContext: (&net.Dialer{
@@ -19,5 +20,5 @@ func getHTTPClient() *http.Client {
}, },
} }
return c return client
} }