feat: implement memestore pkg

main
sundowndev 2021-06-25 17:14:50 +02:00
parent a07f7d2544
commit d1ed5e18d9
8 changed files with 183 additions and 34 deletions

View File

@ -9,6 +9,7 @@ import (
"syscall"
"time"
"github.com/cloudskiff/driftctl/pkg/memstore"
"github.com/cloudskiff/driftctl/pkg/remote/common"
"github.com/cloudskiff/driftctl/pkg/telemetry"
"github.com/fatih/color"
@ -179,6 +180,7 @@ func NewScanCmd() *cobra.Command {
}
func scanRun(opts *pkg.ScanOptions) error {
store := memstore.New()
selectedOutput := output.GetOutput(opts.Output, opts.Quiet)
c := make(chan os.Signal)
@ -216,7 +218,7 @@ func scanRun(opts *pkg.ScanOptions) error {
return err
}
ctl := pkg.NewDriftCTL(scanner, iacSupplier, alerter, resFactory, opts, scanProgress, iacProgress, resourceSchemaRepository)
ctl := pkg.NewDriftCTL(scanner, iacSupplier, alerter, resFactory, opts, scanProgress, iacProgress, resourceSchemaRepository, store)
go func() {
<-c
@ -237,7 +239,7 @@ func scanRun(opts *pkg.ScanOptions) error {
globaloutput.Printf(color.WhiteString("Scan duration: %s\n", analysis.Duration.Round(time.Second)))
if !opts.DisableTelemetry {
telemetry.SendTelemetry(analysis)
telemetry.SendTelemetry(store)
}
globaloutput.Printf(color.WhiteString("Provider version used to scan: %s. Use --tf-provider-version to use another version.\n"), resourceSchemaRepository.ProviderVersion.String())

View File

@ -2,9 +2,12 @@ package pkg
import (
"fmt"
"runtime"
"time"
"github.com/cloudskiff/driftctl/pkg/memstore"
globaloutput "github.com/cloudskiff/driftctl/pkg/output"
"github.com/cloudskiff/driftctl/pkg/version"
"github.com/jmespath/go-jmespath"
"github.com/sirupsen/logrus"
@ -45,6 +48,7 @@ type DriftCTL struct {
iacProgress globaloutput.Progress
resourceSchemaRepository resource.SchemaRepositoryInterface
opts *ScanOptions
s memstore.Store
}
func NewDriftCTL(remoteSupplier resource.Supplier,
@ -54,7 +58,8 @@ func NewDriftCTL(remoteSupplier resource.Supplier,
opts *ScanOptions,
scanProgress globaloutput.Progress,
iacProgress globaloutput.Progress,
resourceSchemaRepository resource.SchemaRepositoryInterface) *DriftCTL {
resourceSchemaRepository resource.SchemaRepositoryInterface,
store memstore.Store) *DriftCTL {
return &DriftCTL{
remoteSupplier,
iacSupplier,
@ -65,6 +70,7 @@ func NewDriftCTL(remoteSupplier resource.Supplier,
iacProgress,
resourceSchemaRepository,
opts,
store,
}
}
@ -138,6 +144,13 @@ func (d DriftCTL) Run() (*analyser.Analysis, error) {
analysis.Duration = time.Since(start)
analysis.Date = time.Now()
d.s.Bucket(memstore.TelemetryBucket).Set("version", version.Current())
d.s.Bucket(memstore.TelemetryBucket).Set("os", runtime.GOOS)
d.s.Bucket(memstore.TelemetryBucket).Set("arch", runtime.GOARCH)
d.s.Bucket(memstore.TelemetryBucket).Set("total_resources", analysis.Summary().TotalResources)
d.s.Bucket(memstore.TelemetryBucket).Set("total_managed", analysis.Summary().TotalManaged)
d.s.Bucket(memstore.TelemetryBucket).Set("duration", uint(analysis.Duration.Seconds()+0.5))
return &analysis, nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/cloudskiff/driftctl/pkg/alerter"
"github.com/cloudskiff/driftctl/pkg/analyser"
"github.com/cloudskiff/driftctl/pkg/filter"
"github.com/cloudskiff/driftctl/pkg/memstore"
"github.com/cloudskiff/driftctl/pkg/output"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
@ -98,7 +99,7 @@ func runTest(t *testing.T, cases TestCases) {
iacProgress.On("Start").Return().Once()
iacProgress.On("Stop").Return().Once()
driftctl := pkg.NewDriftCTL(remoteSupplier, stateSupplier, testAlerter, resourceFactory, c.options, scanProgress, iacProgress, repo)
driftctl := pkg.NewDriftCTL(remoteSupplier, stateSupplier, testAlerter, resourceFactory, c.options, scanProgress, iacProgress, repo, memstore.New())
analysis, err := driftctl.Run()

6
pkg/memstore/buckets.go Normal file
View File

@ -0,0 +1,6 @@
package memstore
const (
// TelemetryBucket is the name of the store bucket used by the telemetry service
TelemetryBucket = "telemetry"
)

63
pkg/memstore/memstore.go Normal file
View File

@ -0,0 +1,63 @@
package memstore
import (
"encoding/json"
"sync"
)
type Bucket interface {
Set(string, interface{})
Get(string) interface{}
MarshallJSON() ([]byte, error)
}
type Store interface {
Bucket(string) Bucket
}
type store struct {
m *sync.Mutex
s map[string]*bucket
}
type bucket struct {
m *sync.RWMutex
s map[string]interface{}
}
func New() Store {
return &store{
m: &sync.Mutex{},
s: map[string]*bucket{},
}
}
func (s store) Bucket(name string) Bucket {
s.m.Lock()
defer s.m.Unlock()
if _, ok := s.s[name]; !ok {
s.s[name] = &bucket{
m: &sync.RWMutex{},
s: map[string]interface{}{},
}
}
return s.s[name]
}
func (b bucket) Set(key string, value interface{}) {
b.m.Lock()
defer b.m.Unlock()
b.s[key] = value
}
func (b bucket) Get(key string) interface{} {
b.m.RLock()
defer b.m.RUnlock()
return b.s[key]
}
func (b bucket) MarshallJSON() ([]byte, error) {
return json.Marshal(b.s)
}

View File

@ -0,0 +1,70 @@
package memstore
import (
"sync"
"testing"
"github.com/cloudskiff/driftctl/test/resource"
"github.com/stretchr/testify/assert"
)
func TestStore(t *testing.T) {
cases := []struct {
name string
bucket string
values map[string]interface{}
expectedJSON string
}{
{
name: "test basic store usage",
bucket: "test-bucket-1",
values: map[string]interface{}{
"test-value_|)": 13,
"duration_key": "23",
"null": nil,
"res": &resource.FakeResource{Id: "id", Type: "type"},
},
expectedJSON: `{"duration_key":"23","null":null,"res":{"Id":"id","Type":"type","Attrs":null},"test-value_|)":13}`,
},
{
name: "test empty bucket",
bucket: "test-bucket-empty",
values: map[string]interface{}{},
expectedJSON: `{}`,
},
{
name: "test bucket with nil values",
bucket: "test-bucket-empty",
values: map[string]interface{}{
"version": nil,
"total_resources": nil,
"total_managed": nil,
},
expectedJSON: `{"total_managed":null,"total_resources":null,"version":null}`,
},
}
for _, tt := range cases {
kv := New()
t.Run(tt.name, func(t *testing.T) {
var wg sync.WaitGroup
for key, val := range tt.values {
wg.Add(1)
go func(key string, val interface{}, wg *sync.WaitGroup) {
defer wg.Done()
kv.Bucket(tt.bucket).Set(key, val)
assert.Equal(t, val, kv.Bucket(tt.bucket).Get(key))
assert.Equal(t, nil, kv.Bucket("dummybucketname").Get(key))
}(key, val, &wg)
}
wg.Wait()
b, err := kv.Bucket(tt.bucket).MarshallJSON()
assert.Nil(t, err)
assert.Equal(t, tt.expectedJSON, string(b))
})
}
}

View File

@ -2,40 +2,14 @@ package telemetry
import (
"bytes"
"encoding/json"
"net/http"
"runtime"
"github.com/cloudskiff/driftctl/pkg/analyser"
"github.com/cloudskiff/driftctl/pkg/version"
"github.com/cloudskiff/driftctl/pkg/memstore"
"github.com/sirupsen/logrus"
)
type telemetry struct {
Version string `json:"version"`
Os string `json:"os"`
Arch string `json:"arch"`
TotalResources int `json:"total_resources"`
TotalManaged int `json:"total_managed"`
Duration uint `json:"duration"`
}
func SendTelemetry(analysis *analyser.Analysis) {
if analysis == nil {
return
}
t := telemetry{
Version: version.Current(),
Os: runtime.GOOS,
Arch: runtime.GOARCH,
TotalResources: analysis.Summary().TotalResources,
TotalManaged: analysis.Summary().TotalManaged,
Duration: uint(analysis.Duration.Seconds() + 0.5),
}
body, err := json.Marshal(t)
func SendTelemetry(s memstore.Store) {
body, err := s.Bucket(memstore.TelemetryBucket).MarshallJSON()
if err != nil {
logrus.Debug(err)
return

View File

@ -8,6 +8,7 @@ import (
"testing"
"github.com/cloudskiff/driftctl/pkg/analyser"
"github.com/cloudskiff/driftctl/pkg/memstore"
"github.com/cloudskiff/driftctl/pkg/version"
"github.com/cloudskiff/driftctl/test/resource"
"github.com/jarcoal/httpmock"
@ -15,6 +16,14 @@ import (
)
func TestSendTelemetry(t *testing.T) {
type telemetry struct {
Version string `json:"version"`
Os string `json:"os"`
Arch string `json:"arch"`
TotalResources int `json:"total_resources"`
TotalManaged int `json:"total_managed"`
Duration uint `json:"duration"`
}
httpmock.Activate()
defer httpmock.DeactivateAndReset()
@ -64,6 +73,17 @@ func TestSendTelemetry(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := memstore.New()
if tt.analysis != nil {
s.Bucket(memstore.TelemetryBucket).Set("version", version.Current())
s.Bucket(memstore.TelemetryBucket).Set("os", runtime.GOOS)
s.Bucket(memstore.TelemetryBucket).Set("arch", runtime.GOARCH)
s.Bucket(memstore.TelemetryBucket).Set("total_resources", tt.analysis.Summary().TotalResources)
s.Bucket(memstore.TelemetryBucket).Set("total_managed", tt.analysis.Summary().TotalManaged)
s.Bucket(memstore.TelemetryBucket).Set("duration", uint(tt.analysis.Duration.Seconds()+0.5))
}
httpmock.Reset()
if tt.expectedBody != nil {
httpmock.RegisterResponder(
@ -91,7 +111,7 @@ func TestSendTelemetry(t *testing.T) {
},
)
}
SendTelemetry(tt.analysis)
SendTelemetry(s)
})
}
}