storj/cmd/satellite/reports/attribution.go
ethanadams 0e528bc56e
Add attribution report to the satellite CLI (#2288)
* added satalite partner value attribution report. WIP

* WIP

* basic attribution report test completed. still a WIP

* cleanup

* fixed projectID conversion

* report display cleanup

* cleanup .added more test data

* added partnerID to query results

* fixed lint issues

* fix import order

* suggestions from PR review

* updated doc to reflect implementation

* clarification comments in the report SQL

* Changed based on PR suggestion

* More changes based on PR suggestions

* Changes based on PR suggestions

* reordered tests to make consistant with previous 2

* small comments cleanup

* More PR suggestions

* fixed lint issue and removed printf

* fixed var name

* Updates based on PR suggestions

* fixed message

* fixed test

* changes required after merge from master
2019-06-25 16:58:38 -04:00

108 lines
2.5 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package reports
import (
"context"
"encoding/csv"
"fmt"
"io"
"os"
"strconv"
"time"
"github.com/skyrings/skyring-common/tools/uuid"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/storj/internal/memory"
"storj.io/storj/satellite/attribution"
"storj.io/storj/satellite/satellitedb"
)
var headers = []string{
"projectID",
"bucketName",
"byte-hours:Remote",
"byte-hours:Inline",
"bytes:BWEgress",
}
// GenerateAttributionCSV creates a report with
func GenerateAttributionCSV(ctx context.Context, database string, partnerID uuid.UUID, start time.Time, end time.Time, output io.Writer) error {
log := zap.L().Named("db")
db, err := satellitedb.New(log, database)
if err != nil {
return errs.New("error connecting to master database on satellite: %+v", err)
}
defer func() {
err = errs.Combine(err, db.Close())
if err != nil {
log.Sugar().Errorf("error closing satellite DB connection after retrieving partner value attribution data: %+v", err)
}
}()
rows, err := db.Attribution().QueryAttribution(ctx, partnerID, start, end)
if err != nil {
return errs.Wrap(err)
}
w := csv.NewWriter(output)
defer func() {
w.Flush()
}()
if err := w.Write(headers); err != nil {
return errs.Wrap(err)
}
for _, row := range rows {
record, err := csvRowToStringSlice(row)
if err != nil {
return errs.Wrap(err)
}
if err := w.Write(record); err != nil {
return errs.Wrap(err)
}
}
if err := w.Error(); err != nil {
return errs.Wrap(err)
}
if output != os.Stdout {
fmt.Println("Generated report for partner attribution")
}
return errs.Wrap(err)
}
func csvRowToStringSlice(p *attribution.CSVRow) ([]string, error) {
projectID, err := bytesToUUID(p.ProjectID)
if err != nil {
return nil, errs.New("Invalid Project ID")
}
remoteGBPerHour := memory.Size(p.RemoteBytesPerHour).GB()
inlineGBPerHour := memory.Size(p.InlineBytesPerHour).GB()
egressGBData := memory.Size(p.EgressData).GB()
record := []string{
projectID.String(),
string(p.BucketName),
strconv.FormatFloat(remoteGBPerHour, 'f', 4, 64),
strconv.FormatFloat(inlineGBPerHour, 'f', 4, 64),
strconv.FormatFloat(egressGBData, 'f', 4, 64),
}
return record, nil
}
// bytesToUUID is used to convert []byte to UUID
func bytesToUUID(data []byte) (uuid.UUID, error) {
var id uuid.UUID
copy(id[:], data)
if len(id) != len(data) {
return uuid.UUID{}, errs.New("Invalid uuid")
}
return id, nil
}