|
@@ -13,37 +13,89 @@ import (
|
|
|
|
|
|
|
|
// Exporter[T] is a generic interface for exporting T instances to a specific storage destination.
|
|
// Exporter[T] is a generic interface for exporting T instances to a specific storage destination.
|
|
|
type Exporter[T any] interface {
|
|
type Exporter[T any] interface {
|
|
|
- // Export performs the export operation for the given window and data.
|
|
|
|
|
|
|
+ // Export performs the export operation for the provided data.
|
|
|
|
|
+ Export(data *T) error
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// StorageExporter[T] is an implementation of an Exporter[T] that writes data to a storage backend using
|
|
|
|
|
+// the `github.com/opencost/opencost/core/pkg/storage` package, a pathing strategy, and an encoder.
|
|
|
|
|
+type StorageExporter[T any] struct {
|
|
|
|
|
+ pipeline string
|
|
|
|
|
+ paths pathing.StoragePathFormatter[time.Time]
|
|
|
|
|
+ encoder Encoder[T]
|
|
|
|
|
+ storage storage.Storage
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// NewStorageExporter creates a new StorageExporter instance, which is responsible for exporting data to a storage backend.
|
|
|
|
|
+// It uses a pathing strategy to determine the storage location, an encoder to convert the data to binary format, and
|
|
|
|
|
+// a storage backend to write the data.
|
|
|
|
|
+func NewStorageExporter[T any](
|
|
|
|
|
+ pipeline string,
|
|
|
|
|
+ paths pathing.StoragePathFormatter[time.Time],
|
|
|
|
|
+ encoder Encoder[T],
|
|
|
|
|
+ storage storage.Storage,
|
|
|
|
|
+) *StorageExporter[T] {
|
|
|
|
|
+ return &StorageExporter[T]{
|
|
|
|
|
+ pipeline: pipeline,
|
|
|
|
|
+ paths: paths,
|
|
|
|
|
+ encoder: encoder,
|
|
|
|
|
+ storage: storage,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Export performs the export operation for the provided data. It encodes the data using the encoder and writes it to
|
|
|
|
|
+// the storage backend using the pathing strategy.
|
|
|
|
|
+func (se *StorageExporter[T]) Export(data *T) error {
|
|
|
|
|
+ t := time.Now().UTC()
|
|
|
|
|
+ path := se.paths.ToFullPath("", t, se.encoder.FileExt())
|
|
|
|
|
+
|
|
|
|
|
+ bin, err := se.encoder.Encode(data)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to encode data: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.Debugf("writing new binary data to storage %s", path)
|
|
|
|
|
+ err = se.storage.Write(path, bin)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to write binary data to file '%s': %w", path, err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ComputeExporter[T] is an interface that exports windowed data of type T using a specific resolution.
|
|
|
|
|
+type ComputeExporter[T any] interface {
|
|
|
|
|
+ // Export performs the export operation for the provided data.
|
|
|
Export(window opencost.Window, data *T) error
|
|
Export(window opencost.Window, data *T) error
|
|
|
|
|
|
|
|
// Resolution contains the resolution of the data being exported
|
|
// Resolution contains the resolution of the data being exported
|
|
|
Resolution() time.Duration
|
|
Resolution() time.Duration
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// StorageExporter[T] is an implementation of Exporter[T] that writes data to a storage backend using
|
|
|
|
|
|
|
+// ComputeStorageExporter[T] is an implementation of ComputeExporter[T] that writes data to a storage backend using
|
|
|
// `github.com/opencost/opencost/core/pkg/storage`, a pathing strategy, and an encoder.
|
|
// `github.com/opencost/opencost/core/pkg/storage`, a pathing strategy, and an encoder.
|
|
|
-type StorageExporter[T any] struct {
|
|
|
|
|
|
|
+type ComputeStorageExporter[T any] struct {
|
|
|
pipeline string
|
|
pipeline string
|
|
|
resolution time.Duration
|
|
resolution time.Duration
|
|
|
- paths pathing.StoragePathFormatter
|
|
|
|
|
|
|
+ paths pathing.StoragePathFormatter[opencost.Window]
|
|
|
encoder Encoder[T]
|
|
encoder Encoder[T]
|
|
|
storage storage.Storage
|
|
storage storage.Storage
|
|
|
validator validator.ExportValidator[T]
|
|
validator validator.ExportValidator[T]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// NewStorageExporter creates a new StorageExporter instance, which is responsible for exporting data for
|
|
|
|
|
-// a specific window to a storage backend. It uses a pathing strategy to determine the storage location,
|
|
|
|
|
-// an encoder to convert the data to binary format, and a validator to check the data before export.
|
|
|
|
|
-// The pipeline name and resolution are also provided to help identify the data being exported.
|
|
|
|
|
-func NewStorageExporter[T any](
|
|
|
|
|
|
|
+// NewComputeStorageExporter creates a new ComputeStorageExporter instance, which is responsible for exporting
|
|
|
|
|
+// data for a specific window to a storage backend. It uses a pathing strategy to determine the storage location,
|
|
|
|
|
+// an encoder to convert the data to binary format, and a validator to check the data before export. The pipeline
|
|
|
|
|
+// name and resolution are also provided to help identify the data being exported.
|
|
|
|
|
+func NewComputeStorageExporter[T any](
|
|
|
pipeline string,
|
|
pipeline string,
|
|
|
resolution time.Duration,
|
|
resolution time.Duration,
|
|
|
- paths pathing.StoragePathFormatter,
|
|
|
|
|
|
|
+ paths pathing.StoragePathFormatter[opencost.Window],
|
|
|
encoder Encoder[T],
|
|
encoder Encoder[T],
|
|
|
storage storage.Storage,
|
|
storage storage.Storage,
|
|
|
validator validator.ExportValidator[T],
|
|
validator validator.ExportValidator[T],
|
|
|
-) *StorageExporter[T] {
|
|
|
|
|
- return &StorageExporter[T]{
|
|
|
|
|
|
|
+) *ComputeStorageExporter[T] {
|
|
|
|
|
+ return &ComputeStorageExporter[T]{
|
|
|
pipeline: pipeline,
|
|
pipeline: pipeline,
|
|
|
resolution: resolution,
|
|
resolution: resolution,
|
|
|
paths: paths,
|
|
paths: paths,
|
|
@@ -55,7 +107,7 @@ func NewStorageExporter[T any](
|
|
|
|
|
|
|
|
// Export performs validation on the provided window and data, determines if it should overwrite existing data,
|
|
// Export performs validation on the provided window and data, determines if it should overwrite existing data,
|
|
|
// and stores the data in the location specified by the pathing formatter.
|
|
// and stores the data in the location specified by the pathing formatter.
|
|
|
-func (se *StorageExporter[T]) Export(window opencost.Window, data *T) error {
|
|
|
|
|
|
|
+func (se *ComputeStorageExporter[T]) Export(window opencost.Window, data *T) error {
|
|
|
if se.validator != nil {
|
|
if se.validator != nil {
|
|
|
err := se.validator.Validate(window, data)
|
|
err := se.validator.Validate(window, data)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -63,8 +115,7 @@ func (se *StorageExporter[T]) Export(window opencost.Window, data *T) error {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- s, e := *window.Start(), *window.End()
|
|
|
|
|
- path := se.paths.ToFullPath("", s, e)
|
|
|
|
|
|
|
+ path := se.paths.ToFullPath("", window, se.encoder.FileExt())
|
|
|
|
|
|
|
|
currentExists, err := se.storage.Exists(path)
|
|
currentExists, err := se.storage.Exists(path)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -91,6 +142,6 @@ func (se *StorageExporter[T]) Export(window opencost.Window, data *T) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Resolution returns the resolution of the data being exported.
|
|
// Resolution returns the resolution of the data being exported.
|
|
|
-func (se *StorageExporter[T]) Resolution() time.Duration {
|
|
|
|
|
|
|
+func (se *ComputeStorageExporter[T]) Resolution() time.Duration {
|
|
|
return se.resolution
|
|
return se.resolution
|
|
|
}
|
|
}
|