Skip to content
This repository has been archived by the owner on May 11, 2022. It is now read-only.

Commit

Permalink
fix critical bug with cache layer
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilsk committed Jun 16, 2020
1 parent 6572885 commit 8372586
Show file tree
Hide file tree
Showing 29 changed files with 536 additions and 131 deletions.
6 changes: 4 additions & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ brews:
system "#{bin}/grafaman --version"
builds:
- binary: grafaman
- id: grafaman
binary: grafaman
env:
- CGO_ENABLED=0
flags:
Expand All @@ -47,7 +48,8 @@ builds:
goos:
- darwin
- linux
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
main: .

checksum: { name_template: checksums.txt }
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ require (
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.6.1
go.octolab.org v0.0.27
go.octolab.org/toolkit/cli v0.0.10
go.octolab.org/toolkit/cli v0.0.11
go.octolab.org/toolkit/config v0.0.2
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
)

Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,10 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.octolab.org v0.0.27 h1:R3z2XaKwqMmJ+3EoccIAxiNLMu83BjIPMVk6SRJChU4=
go.octolab.org v0.0.27/go.mod h1:WpDp9ii9NV/PXRTHSgde3W/VVINi3McOuYe5Gkx+ALM=
go.octolab.org/toolkit/cli v0.0.10 h1:9mOVhw7/kOR8kuUMQwOPDRoGz9pW8PHS4UD0KzuzrMo=
go.octolab.org/toolkit/cli v0.0.10/go.mod h1:xyPJgY86Ig0anaovmv/s5sLlmXGLSRs3oz+WLM+0HxQ=
go.octolab.org/toolkit/cli v0.0.11 h1:9CPtShCwFeOA58bgt0f1AD2ukS1kK9IJfNCnTh/+5q0=
go.octolab.org/toolkit/cli v0.0.11/go.mod h1:d9Bsj1h4gBrJtLZEkYGgZA1yBuA6ingjF7nnhEzUe7k=
go.octolab.org/toolkit/config v0.0.2 h1:Fdd0jg4oJ4shhzgejUBPT5PPe8z9BALkgktweKG9c80=
go.octolab.org/toolkit/config v0.0.2/go.mod h1:dI3TlD3b4t0ilP7T+q0BYs6mhYBXm8i4TCLqCabykBA=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
Expand Down Expand Up @@ -438,6 +440,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
10 changes: 10 additions & 0 deletions internal/cache/graphite.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ func (decorator *graphite) Fetch(ctx context.Context, prefix string, last time.D
return nil, errors.Wrap(err, "cache: fetch data")
}
data.TTL = now.Add(xtime.Day).Unix()
{
if err := file.Truncate(0); err != nil {
logger.WithError(err).Error("reset cache")
return nil, errors.Wrap(err, "cache: reset")
}
if _, err := file.Seek(0, io.SeekStart); err != nil {
logger.WithError(err).Error("prepare cache to write")
return nil, errors.Wrap(err, "cache: prepare to write")
}
}
if err := json.NewEncoder(file).Encode(data); err != nil {
logger.WithError(err).Error("write data to cache")
return nil, errors.Wrap(err, "cache: write data")
Expand Down
28 changes: 14 additions & 14 deletions internal/cmd/coverage.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"golang.org/x/sync/errgroup"

"github.com/kamilsk/grafaman/internal/cache"
"github.com/kamilsk/grafaman/internal/config"
"github.com/kamilsk/grafaman/internal/cnf"
"github.com/kamilsk/grafaman/internal/filter"
entity "github.com/kamilsk/grafaman/internal/provider"
"github.com/kamilsk/grafaman/internal/provider/grafana"
Expand All @@ -24,7 +24,7 @@ import (

// NewCoverageCommand returns command to calculate metrics coverage by queries.
func NewCoverageCommand(
cfg *config.Config,
config *cnf.Config,
logger *logrus.Logger,
printer interface {
SetPrefix(string)
Expand All @@ -49,23 +49,23 @@ func NewCoverageCommand(
func() error { return viper.BindPFlag("graphite_url", flags.Lookup("graphite")) },
func() error { return viper.BindPFlag("graphite_metrics", flags.Lookup("metrics")) },
func() error { return viper.BindPFlag("filter", flags.Lookup("filter")) },
func() error { return viper.Unmarshal(cfg) },
func() error { return viper.Unmarshal(config) },
)

if cfg.Grafana.URL == "" {
if config.Grafana.URL == "" {
return errors.New("please provide Grafana API endpoint")
}
if cfg.Grafana.Dashboard == "" {
if config.Grafana.Dashboard == "" {
return errors.New("please provide a dashboard unique identifier")
}
if cfg.Graphite.Prefix == "" {
if config.Graphite.Prefix == "" {
return errors.New("please provide metric prefix")
}
checker := validator.Metric()
if !checker(cfg.Graphite.Prefix) {
if !checker(config.Graphite.Prefix) {
return errors.Errorf(
"invalid metric prefix: %s; it must be simple, e.g. apps.services.name",
cfg.Graphite.Prefix,
config.Graphite.Prefix,
)
}
return nil
Expand All @@ -79,24 +79,24 @@ func NewCoverageCommand(
g, ctx := errgroup.WithContext(cmd.Context())
g.Go(func() error {
var provider entity.Graphite
provider, err := graphite.New(cfg.Graphite.URL, logger)
provider, err := graphite.New(config.Graphite.URL, logger)
if err != nil {
return err
}
provider = cache.Wrap(provider, afero.NewOsFs(), logger)
metrics, err = provider.Fetch(ctx, cfg.Graphite.Prefix, last)
metrics, err = provider.Fetch(ctx, config.Graphite.Prefix, last)
if err != nil {
return err
}
metrics, err = filter.Filter(metrics, cfg.Graphite.Filter, cfg.Graphite.Prefix)
metrics, err = filter.Filter(metrics, config.Graphite.Filter, config.Graphite.Prefix)
return err
})
g.Go(func() error {
provider, err := grafana.New(cfg.Grafana.URL, logger)
provider, err := grafana.New(config.Grafana.URL, logger)
if err != nil {
return err
}
dashboard, err = provider.Fetch(ctx, cfg.Grafana.Dashboard)
dashboard, err = provider.Fetch(ctx, config.Grafana.Dashboard)
return err
})
if err := g.Wait(); err != nil {
Expand All @@ -120,7 +120,7 @@ func NewCoverageCommand(
return err
}

printer.SetPrefix(cfg.Graphite.Prefix)
printer.SetPrefix(config.Graphite.Prefix)
return printer.PrintCoverage(report)
},
}
Expand Down
22 changes: 11 additions & 11 deletions internal/cmd/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
xtime "go.octolab.org/time"

"github.com/kamilsk/grafaman/internal/cache"
"github.com/kamilsk/grafaman/internal/config"
"github.com/kamilsk/grafaman/internal/cnf"
"github.com/kamilsk/grafaman/internal/filter"
entity "github.com/kamilsk/grafaman/internal/provider"
"github.com/kamilsk/grafaman/internal/provider/graphite"
Expand All @@ -22,7 +22,7 @@ import (

// NewMetricsCommand returns command to fetch metrics from Graphite.
func NewMetricsCommand(
cfg *config.Config,
config *cnf.Config,
logger *logrus.Logger,
printer interface {
SetPrefix(string)
Expand All @@ -44,43 +44,43 @@ func NewMetricsCommand(
func() error { return viper.BindPFlag("graphite_url", flags.Lookup("graphite")) },
func() error { return viper.BindPFlag("graphite_metrics", flags.Lookup("metrics")) },
func() error { return viper.BindPFlag("filter", flags.Lookup("filter")) },
func() error { return viper.Unmarshal(cfg) },
func() error { return viper.Unmarshal(config) },
)

if cfg.Graphite.URL == "" {
if config.Graphite.URL == "" {
return errors.New("please provide Graphite API endpoint")
}
if cfg.Graphite.Prefix == "" {
if config.Graphite.Prefix == "" {
return errors.New("please provide metric prefix")
}
checker := validator.Metric()
if !checker(cfg.Graphite.Prefix) {
if !checker(config.Graphite.Prefix) {
return errors.Errorf(
"invalid metric prefix: %s; it must be simple, e.g. apps.services.name",
cfg.Graphite.Prefix,
config.Graphite.Prefix,
)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
var provider entity.Graphite
provider, err := graphite.New(cfg.Graphite.URL, logger)
provider, err := graphite.New(config.Graphite.URL, logger)
if err != nil {
return err
}
provider = cache.Wrap(provider, afero.NewOsFs(), logger)

metrics, err := provider.Fetch(cmd.Context(), cfg.Graphite.Prefix, last)
metrics, err := provider.Fetch(cmd.Context(), config.Graphite.Prefix, last)
if err != nil {
return err
}
metrics, err = filter.Filter(metrics, cfg.Graphite.Filter, cfg.Graphite.Prefix)
metrics, err = filter.Filter(metrics, config.Graphite.Filter, config.Graphite.Prefix)
if err != nil {
return err
}
sort.Sort(metrics)

printer.SetPrefix(cfg.Graphite.Prefix)
printer.SetPrefix(config.Graphite.Prefix)
return printer.PrintMetrics(metrics)
},
}
Expand Down
18 changes: 9 additions & 9 deletions internal/cmd/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (
"github.com/spf13/viper"
"go.octolab.org/fn"

"github.com/kamilsk/grafaman/internal/config"
"github.com/kamilsk/grafaman/internal/cnf"
entity "github.com/kamilsk/grafaman/internal/provider"
"github.com/kamilsk/grafaman/internal/provider/grafana"
"github.com/kamilsk/grafaman/internal/validator"
)

// NewQueriesCommand returns command to fetch queries from a Grafana dashboard.
func NewQueriesCommand(
cfg *config.Config,
config *cnf.Config,
logger *logrus.Logger,
printer interface{ PrintQueries(entity.Queries) error },
) *cobra.Command {
Expand All @@ -36,31 +36,31 @@ func NewQueriesCommand(
func() error { return viper.BindPFlag("grafana_url", flags.Lookup("grafana")) },
func() error { return viper.BindPFlag("grafana_dashboard", flags.Lookup("dashboard")) },
func() error { return viper.BindPFlag("graphite_metrics", flags.Lookup("metrics")) },
func() error { return viper.Unmarshal(cfg) },
func() error { return viper.Unmarshal(config) },
)

if cfg.Grafana.URL == "" {
if config.Grafana.URL == "" {
return errors.New("please provide Grafana API endpoint")
}
if cfg.Grafana.Dashboard == "" {
if config.Grafana.Dashboard == "" {
return errors.New("please provide a dashboard unique identifier")
}
if metrics, checker := cfg.Graphite.Prefix, validator.Metric(); metrics != "" && !checker(metrics) {
if metrics, checker := config.Graphite.Prefix, validator.Metric(); metrics != "" && !checker(metrics) {
return errors.Errorf("invalid metric prefix: %s; it must be simple, e.g. apps.services.name", metrics)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
provider, err := grafana.New(cfg.Grafana.URL, logger)
provider, err := grafana.New(config.Grafana.URL, logger)
if err != nil {
return err
}
dashboard, err := provider.Fetch(cmd.Context(), cfg.Grafana.Dashboard)
dashboard, err := provider.Fetch(cmd.Context(), config.Grafana.Dashboard)
if err != nil {
return err
}

dashboard.Prefix = cfg.Graphite.Prefix
dashboard.Prefix = config.Graphite.Prefix
queries, err := dashboard.Queries(entity.Transform{
SkipRaw: raw,
SkipDuplicates: duplicates,
Expand Down
10 changes: 5 additions & 5 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/spf13/viper"
"go.octolab.org/fn"

"github.com/kamilsk/grafaman/internal/config"
"github.com/kamilsk/grafaman/internal/cnf"
"github.com/kamilsk/grafaman/internal/presenter"
)

Expand All @@ -21,7 +21,7 @@ func New() *cobra.Command {
debug bool
format string
verbose int
cfg = new(config.Config)
config = new(cnf.Config)
logger = logrus.New()
printer = new(presenter.Printer)
)
Expand Down Expand Up @@ -75,9 +75,9 @@ func New() *cobra.Command {
SilenceUsage: true,
}
command.AddCommand(
NewCoverageCommand(cfg, logger, printer),
NewMetricsCommand(cfg, logger, printer),
NewQueriesCommand(cfg, logger, printer),
NewCoverageCommand(config, logger, printer),
NewMetricsCommand(config, logger, printer),
NewQueriesCommand(config, logger, printer),
)
flags := command.PersistentFlags()
{
Expand Down
3 changes: 2 additions & 1 deletion internal/config/cli.go → internal/cnf/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package config
package cnf

// Config contains all necessary tool configuration.
type Config struct {
Grafana struct {
URL string `mapstructure:"grafana"`
Expand Down
15 changes: 15 additions & 0 deletions internal/cnf/features.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cnf

import "go.octolab.org/toolkit/config"

// Features defines a list of available features.
var Features = config.Features{
{
Name: "paas",
Enabled: true,
},
{
Name: "cache",
Enabled: true,
},
}
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"go.octolab.org/unsafe"

"github.com/kamilsk/grafaman/internal/cmd"
"github.com/kamilsk/grafaman/internal/cnf"
)

const unknown = "unknown"
Expand Down Expand Up @@ -41,7 +42,7 @@ func main() {
root.SetOut(stdout)
root.AddCommand(
cobra.NewCompletionCommand(),
cobra.NewVersionCommand(version, date, commit),
cobra.NewVersionCommand(version, date, commit, cnf.Features...),
)
safe.Do(func() error { return root.ExecuteContext(ctx) }, shutdown)
}
Expand Down
4 changes: 2 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ func TestExecution(t *testing.T) {
t.Run("success", func(t *testing.T) {
exit = func(code int) { assert.Equal(t, 0, code) }
stderr, stdout = ioutil.Discard, ioutil.Discard
os.Args = []string{"version"}
os.Args = []string{"root", "version"}
main()
})
t.Run("failure", func(t *testing.T) {
exit = func(code int) { assert.Equal(t, 1, code) }
stderr, stdout = ioutil.Discard, ioutil.Discard
os.Args = []string{"unknown"}
os.Args = []string{"root", "unknown"}
main()
})
t.Run("shutdown with panic", func(t *testing.T) {
Expand Down
4 changes: 0 additions & 4 deletions tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ go 1.13
require (
github.com/golang/mock v1.4.3
github.com/golangci/golangci-lint v1.27.0
github.com/kamilsk/egg v0.0.16
golang.org/x/exp v0.0.0-20200513190911-00229845015e
golang.org/x/tools v0.3.3
)

replace github.com/izumin5210/gex => github.com/kamilsk/gex v0.6.0-e4

replace golang.org/x/tools => github.com/kamilsk/go-tools v0.0.3
Loading

0 comments on commit 8372586

Please sign in to comment.