Repository: cycloidio/terracost
Pull request #59 (2021-09-24 14:33:57)

Changed: Backend is now on it's own package
It was repeated in `cost.Backend` and `terracost.Backend` so it's now abstracted to it's own package and used instead of redeclared.

Also did some minor fixes but the main one would be the `//go:generate` functions that where using `go run {pkg}` instead of the binary alone, which was not working and it's not how it's suppose to be used either. Also did the same no the Makefile removing all the `go run` and just used the binary of the lib.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e266b5e..4531ae5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 ## [Unreleased]
 
+### Changed
+
+- **[breaking]** Remove the repeated Backend and abstracted it to it's own package
+  ([Pull #60](https://github.com/cycloidio/terracost/pull/59))
+
+## [0.4.4] _2021-09-31_
+
 ### Fixed
 
 - Ignore providers related to constraints
diff --git a/Makefile b/Makefile
index 249ec1a..4f30785 100644
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,6 @@ DOCKER_COMPOSE_CMD := docker-compose
 GO_CMD := go
 GO_TEST_CMD := $(GO_CMD) test
 GO_RUN_CMD := $(GO_CMD) run
-GOLINT_CMD := $(GO_RUN_CMD) golang.org/x/lint/golint
-GOIMPORTS_CMD := $(GO_RUN_CMD) golang.org/x/tools/cmd/goimports
 
 MYSQL_USER := root
 MYSQL_PASS := terracost
@@ -29,7 +27,7 @@ db-inject:
 
 .PHONY: lint
 lint:
-	@$(GOLINT_CMD) -set_exit_status ./... && test -z "`$(GO_CMD) list -f {{.Dir}} ./... | xargs $(GOIMPORTS_CMD) -l | tee /dev/stderr`"
+	@golint -set_exit_status ./... && test -z "`$(GO_CMD) list -f {{.Dir}} ./... | xargs goimports -l | tee /dev/stderr`"
 
 .PHONY: db-up
 db-up: # Start the DB server
@@ -54,8 +52,8 @@ db-cli:
 generate:
 	@rm -rf ./mock/
 	@$(GO_CMD) generate ./...
-	@$(GOIMPORTS_CMD) -w ./mock
+	@goimports -w ./mock
 
 .PHOHY: goimports
 goimports:
-	@$(GOIMPORTS_CMD) -w ./
+	@goimports -w ./
diff --git a/README.md b/README.md
index 45e09e8..4895143 100644
--- a/README.md
+++ b/README.md
@@ -107,3 +107,15 @@ For Contributing Guide, please read [CONTIBUTING.md](CONTRIBUTING.md).
 ## License
 
 This project is licensed under the MIT license. Please see [LICENSE](LICENSE) for more details.
+
+## Meet Cycloid
+
+[Cycloid](https://www.cycloid.io/) is a hybrid cloud DevOps collaboration platform providing end-to-end frameworks to accelerate and industrialize software delivery.
+
+As of now, we have three open-source tools:
+
+* [TerraCognita](https://github.com/cycloidio/terracognita): Read from your existing cloud providers and generate IaC in Terraform 
+* [InfraMap](https://github.com/cycloidio/inframap): Reads .tfstate or HCL to generate a graph specific for each provider
+* [TerraCost](https://github.com/cycloidio/terracost): Cloud cost estimation for Terraform in the CLI
+
+...and the functionality of each is also embedded in our DevOps solution, which you can find out more about [here](https://www.cycloid.io/hybrid-cloud-devops-platform).
diff --git a/aws/field/field.go b/aws/field/field.go
index 9f02cb2..14b7af8 100644
--- a/aws/field/field.go
+++ b/aws/field/field.go
@@ -1,6 +1,6 @@
 package field
 
-//go:generate go run github.com/dmarkham/enumer -type=Field -output=field_gen.go -linecomment
+//go:generate enumer -type=Field -output=field_gen.go -linecomment
 
 // Field represents a single column in an AWS offer file.
 type Field uint8
diff --git a/aws/field/field_gen.go b/aws/field/field_gen.go
index 6c2a27b..03c887d 100644
--- a/aws/field/field_gen.go
+++ b/aws/field/field_gen.go
@@ -160,8 +160,8 @@ func FieldString(s string) (Field, error) {
 	if val, ok := _FieldNameToValueMap[s]; ok {
 		return val, nil
 	}
-	s = strings.ToLower(s)
-	if val, ok := _FieldNameToValueMap[s]; ok {
+
+	if val, ok := _FieldNameToValueMap[strings.ToLower(s)]; ok {
 		return val, nil
 	}
 	return 0, fmt.Errorf("%s does not belong to Field values", s)
diff --git a/aws/ingester.go b/aws/ingester.go
index c1d74b9..b7c696b 100644
--- a/aws/ingester.go
+++ b/aws/ingester.go
@@ -54,7 +54,7 @@ func NewIngester(service, region string, options ...Option) (*Ingester, error) {
 	}
 
 	ing := &Ingester{
-		httpClient:      &http.Client{},
+		httpClient:      http.DefaultClient,
 		pricingURL:      defaultPricingURL,
 		bufferSize:      defaultBufferSize,
 		service:         service,
diff --git a/aws/options.go b/aws/options.go
index 448fb07..b3732fd 100644
--- a/aws/options.go
+++ b/aws/options.go
@@ -7,7 +7,7 @@ import (
 	"github.com/machinebox/progress"
 )
 
-//go:generate go run github.com/golang/mock/mockgen -destination=../mock/http_client.go -mock_names=HTTPClient=HTTPClient -package mock github.com/cycloidio/terracost/aws HTTPClient
+//go:generate mockgen -destination=../mock/http_client.go -mock_names=HTTPClient=HTTPClient -package mock github.com/cycloidio/terracost/aws HTTPClient
 
 // HTTPClient is an interface of a client that is able to Do HTTP requests
 type HTTPClient interface {
diff --git a/backend/backend.go b/backend/backend.go
new file mode 100644
index 0000000..d754cc3
--- /dev/null
+++ b/backend/backend.go
@@ -0,0 +1,15 @@
+package backend
+
+import (
+	"github.com/cycloidio/terracost/price"
+	"github.com/cycloidio/terracost/product"
+)
+
+//go:generate mockgen -destination=../mock/backend.go -mock_names=Backend=Backend -package mock github.com/cycloidio/terracost/backend Backend
+
+// Backend represents a storage method used to store pricing data. It must include concrete implementations
+// of all repositories.
+type Backend interface {
+	Products() product.Repository
+	Prices() price.Repository
+}
diff --git a/cost/plan.go b/cost/plan.go
index b4e801a..2250328 100644
--- a/cost/plan.go
+++ b/cost/plan.go
@@ -4,6 +4,8 @@ import (
 	"sort"
 )
 
+var isPlanned = true
+
 // Plan is the cost difference between two State instances. It is not tied to any specific cloud provider or IaC tool.
 // Instead, it is a representation of the differences between two snapshots of cloud resources, with their associated
 // costs. The Plan instance can be used to calculate the total cost difference of a plan, as well as cost differences
@@ -39,10 +41,10 @@ func (p Plan) ResourceDifferences() []ResourceDiff {
 	rdmap := make(map[string]ResourceDiff)
 
 	if p.Prior != nil {
-		mergeResourceDiffsFromState(rdmap, p.Prior, false)
+		mergeResourceDiffsFromState(rdmap, p.Prior, !isPlanned)
 	}
 	if p.Planned != nil {
-		mergeResourceDiffsFromState(rdmap, p.Planned, true)
+		mergeResourceDiffsFromState(rdmap, p.Planned, isPlanned)
 	}
 
 	rds := make([]ResourceDiff, 0, len(rdmap))
diff --git a/cost/state.go b/cost/state.go
index 0c7f246..8cc25eb 100644
--- a/cost/state.go
+++ b/cost/state.go
@@ -4,19 +4,11 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/cycloidio/terracost/price"
-	"github.com/cycloidio/terracost/product"
+	"github.com/cycloidio/terracost/backend"
 	"github.com/cycloidio/terracost/query"
 	"github.com/cycloidio/terracost/terraform"
 )
 
-// Backend represents a storage method used to query pricing data. It must include concrete implementations
-// of all repositories.
-type Backend interface {
-	Product() product.Repository
-	Price() price.Repository
-}
-
 // State represents a collection of all the Resource costs (either prior or planned.) It is not tied to any specific
 // cloud provider or IaC tool. Instead, it is a representation of a snapshot of cloud resources at a given point
 // in time, with their associated costs.
@@ -31,7 +23,7 @@ var (
 )
 
 // NewState returns a new State from a query.Resource slice by using the Backend to fetch the pricing data.
-func NewState(ctx context.Context, backend Backend, queries []query.Resource) (*State, error) {
+func NewState(ctx context.Context, backend backend.Backend, queries []query.Resource) (*State, error) {
 	state := &State{Resources: make(map[string]Resource)}
 
 	if len(queries) == 0 {
@@ -42,7 +34,7 @@ func NewState(ctx context.Context, backend Backend, queries []query.Resource) (*
 		state.ensureResource(res.Address, res.Provider, res.Type, len(res.Components) == 0)
 
 		for _, comp := range res.Components {
-			prods, err := backend.Product().Filter(ctx, comp.ProductFilter)
+			prods, err := backend.Products().Filter(ctx, comp.ProductFilter)
 			if err != nil {
 				state.addComponent(res.Address, comp.Name, Component{Error: err})
 				continue
@@ -51,7 +43,7 @@ func NewState(ctx context.Context, backend Backend, queries []query.Resource) (*
 				state.addComponent(res.Address, comp.Name, Component{Error: ErrProductNotFound})
 				continue
 			}
-			prices, err := backend.Price().Filter(ctx, prods[0].ID, comp.PriceFilter)
+			prices, err := backend.Prices().Filter(ctx, prods[0].ID, comp.PriceFilter)
 			if err != nil {
 				state.addComponent(res.Address, comp.Name, Component{Error: err})
 				continue
diff --git a/estimation.go b/estimation.go
index f47580e..8f92e87 100644
--- a/estimation.go
+++ b/estimation.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/spf13/afero"
 
+	"github.com/cycloidio/terracost/backend"
 	"github.com/cycloidio/terracost/cost"
 	"github.com/cycloidio/terracost/terraform"
 )
@@ -13,7 +14,7 @@ import (
 // EstimateTerraformPlan is a helper function that reads a Terraform plan using the provided io.Reader,
 // generates the prior and planned cost.State, and then creates a cost.Plan from them that is returned.
 // It uses the Backend to retrieve the pricing data.
-func EstimateTerraformPlan(ctx context.Context, backend Backend, plan io.Reader, providerInitializers ...terraform.ProviderInitializer) (*cost.Plan, error) {
+func EstimateTerraformPlan(ctx context.Context, be backend.Backend, plan io.Reader, providerInitializers ...terraform.ProviderInitializer) (*cost.Plan, error) {
 	if len(providerInitializers) == 0 {
 		providerInitializers = getDefaultProviders()
 	}
@@ -27,7 +28,7 @@ func EstimateTerraformPlan(ctx context.Context, backend Backend, plan io.Reader,
 	if err != nil {
 		return nil, err
 	}
-	prior, err := cost.NewState(ctx, backend, priorQueries)
+	prior, err := cost.NewState(ctx, be, priorQueries)
 	if err != nil {
 		return nil, err
 	}
@@ -36,7 +37,7 @@ func EstimateTerraformPlan(ctx context.Context, backend Backend, plan io.Reader,
 	if err != nil {
 		return nil, err
 	}
-	planned, err := cost.NewState(ctx, backend, plannedQueries)
+	planned, err := cost.NewState(ctx, be, plannedQueries)
 	if err != nil {
 		return nil, err
 	}
@@ -47,7 +48,7 @@ func EstimateTerraformPlan(ctx context.Context, backend Backend, plan io.Reader,
 // EstimateHCL is a helper function that recursively reads Terraform modules from a directory at the
 // given path and generates a planned cost.State that is returned wrapped in a cost.Plan.
 // It uses the Backend to retrieve the pricing data.
-func EstimateHCL(ctx context.Context, backend Backend, fs afero.Fs, path string, providerInitializers ...terraform.ProviderInitializer) (*cost.Plan, error) {
+func EstimateHCL(ctx context.Context, be backend.Backend, fs afero.Fs, path string, providerInitializers ...terraform.ProviderInitializer) (*cost.Plan, error) {
 	if len(providerInitializers) == 0 {
 		providerInitializers = getDefaultProviders()
 	}
@@ -56,7 +57,7 @@ func EstimateHCL(ctx context.Context, backend Backend, fs afero.Fs, path string,
 	if err != nil {
 		return nil, err
 	}
-	planned, err := cost.NewState(ctx, backend, plannedQueries)
+	planned, err := cost.NewState(ctx, be, plannedQueries)
 	if err != nil {
 		return nil, err
 	}
diff --git a/go.sum b/go.sum
index 8053258..b4727d2 100644
--- a/go.sum
+++ b/go.sum
@@ -508,7 +508,6 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@@ -629,7 +628,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
 golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -680,7 +678,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb h1:KVWk3RW1AZlxWum4tYqegLgwJHb5oouozcGM8HfNQaw=
 golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/ingestion.go b/ingestion.go
index adb5877..da25de4 100644
--- a/ingestion.go
+++ b/ingestion.go
@@ -4,12 +4,12 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/cycloidio/terracost/backend"
 	"github.com/cycloidio/terracost/price"
 	"github.com/cycloidio/terracost/product"
 )
 
-//go:generate go run github.com/golang/mock/mockgen -destination=mock/ingester.go -mock_names=Ingester=Ingester -package mock github.com/cycloidio/terracost Ingester
-//go:generate go run github.com/golang/mock/mockgen -destination=mock/backend.go -mock_names=Backend=Backend -package mock github.com/cycloidio/terracost Backend
+//go:generate mockgen -destination=mock/ingester.go -mock_names=Ingester=Ingester -package mock github.com/cycloidio/terracost Ingester
 
 // Ingester represents a vendor-specific mechanism to load pricing data.
 type Ingester interface {
@@ -21,15 +21,8 @@ type Ingester interface {
 	Err() error
 }
 
-// Backend represents a storage method used to store pricing data. It must include concrete implementations
-// of all repositories.
-type Backend interface {
-	Product() product.Repository
-	Price() price.Repository
-}
-
 // IngestPricing uses the Ingester to load the pricing data and stores it into the Backend.
-func IngestPricing(ctx context.Context, backend Backend, ingester Ingester) error {
+func IngestPricing(ctx context.Context, be backend.Backend, ingester Ingester) error {
 	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
 
@@ -39,14 +32,14 @@ func IngestPricing(ctx context.Context, backend Backend, ingester Ingester) erro
 			pp.Product.ID = id
 		} else {
 			var err error
-			pp.Product.ID, err = backend.Product().Upsert(ctx, pp.Product)
+			pp.Product.ID, err = be.Products().Upsert(ctx, pp.Product)
 			if err != nil {
 				return fmt.Errorf("failed to upsert product (SKU=%q): %w", pp.Product.SKU, err)
 			}
 			skuProductID[pp.Product.SKU] = pp.Product.ID
 		}
 
-		if _, err := backend.Price().Upsert(ctx, pp); err != nil {
+		if _, err := be.Prices().Upsert(ctx, pp); err != nil {
 			return fmt.Errorf("failed to upsert price (SKU=%q): %w", pp.Product.SKU, err)
 		}
 	}
diff --git a/mock/backend.go b/mock/backend.go
index 6bc3cba..4252908 100644
--- a/mock/backend.go
+++ b/mock/backend.go
@@ -1,5 +1,5 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/cycloidio/terracost (interfaces: Backend)
+// Source: github.com/cycloidio/terracost/backend (interfaces: Backend)
 
 // Package mock is a generated GoMock package.
 package mock
@@ -12,53 +12,53 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// Backend is a mock of Backend interface
+// Backend is a mock of Backend interface.
 type Backend struct {
 	ctrl     *gomock.Controller
 	recorder *BackendMockRecorder
 }
 
-// BackendMockRecorder is the mock recorder for Backend
+// BackendMockRecorder is the mock recorder for Backend.
 type BackendMockRecorder struct {
 	mock *Backend
 }
 
-// NewBackend creates a new mock instance
+// NewBackend creates a new mock instance.
 func NewBackend(ctrl *gomock.Controller) *Backend {
 	mock := &Backend{ctrl: ctrl}
 	mock.recorder = &BackendMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *Backend) EXPECT() *BackendMockRecorder {
 	return m.recorder
 }
 
-// Price mocks base method
-func (m *Backend) Price() price.Repository {
+// Prices mocks base method.
+func (m *Backend) Prices() price.Repository {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Price")
+	ret := m.ctrl.Call(m, "Prices")
 	ret0, _ := ret[0].(price.Repository)
 	return ret0
 }
 
-// Price indicates an expected call of Price
-func (mr *BackendMockRecorder) Price() *gomock.Call {
+// Prices indicates an expected call of Prices.
+func (mr *BackendMockRecorder) Prices() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Price", reflect.TypeOf((*Backend)(nil).Price))
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prices", reflect.TypeOf((*Backend)(nil).Prices))
 }
 
-// Product mocks base method
-func (m *Backend) Product() product.Repository {
+// Products mocks base method.
+func (m *Backend) Products() product.Repository {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Product")
+	ret := m.ctrl.Call(m, "Products")
 	ret0, _ := ret[0].(product.Repository)
 	return ret0
 }
 
-// Product indicates an expected call of Product
-func (mr *BackendMockRecorder) Product() *gomock.Call {
+// Products indicates an expected call of Products.
+func (mr *BackendMockRecorder) Products() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Product", reflect.TypeOf((*Backend)(nil).Product))
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Products", reflect.TypeOf((*Backend)(nil).Products))
 }
diff --git a/mock/http_client.go b/mock/http_client.go
index cdd5878..616e9c4 100644
--- a/mock/http_client.go
+++ b/mock/http_client.go
@@ -11,30 +11,30 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// HTTPClient is a mock of HTTPClient interface
+// HTTPClient is a mock of HTTPClient interface.
 type HTTPClient struct {
 	ctrl     *gomock.Controller
 	recorder *HTTPClientMockRecorder
 }
 
-// HTTPClientMockRecorder is the mock recorder for HTTPClient
+// HTTPClientMockRecorder is the mock recorder for HTTPClient.
 type HTTPClientMockRecorder struct {
 	mock *HTTPClient
 }
 
-// NewHTTPClient creates a new mock instance
+// NewHTTPClient creates a new mock instance.
 func NewHTTPClient(ctrl *gomock.Controller) *HTTPClient {
 	mock := &HTTPClient{ctrl: ctrl}
 	mock.recorder = &HTTPClientMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *HTTPClient) EXPECT() *HTTPClientMockRecorder {
 	return m.recorder
 }
 
-// Do mocks base method
+// Do mocks base method.
 func (m *HTTPClient) Do(arg0 *http.Request) (*http.Response, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Do", arg0)
@@ -43,7 +43,7 @@ func (m *HTTPClient) Do(arg0 *http.Request) (*http.Response, error) {
 	return ret0, ret1
 }
 
-// Do indicates an expected call of Do
+// Do indicates an expected call of Do.
 func (mr *HTTPClientMockRecorder) Do(arg0 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*HTTPClient)(nil).Do), arg0)
diff --git a/mock/ingester.go b/mock/ingester.go
index 8189a62..135ba40 100644
--- a/mock/ingester.go
+++ b/mock/ingester.go
@@ -12,30 +12,30 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// Ingester is a mock of Ingester interface
+// Ingester is a mock of Ingester interface.
 type Ingester struct {
 	ctrl     *gomock.Controller
 	recorder *IngesterMockRecorder
 }
 
-// IngesterMockRecorder is the mock recorder for Ingester
+// IngesterMockRecorder is the mock recorder for Ingester.
 type IngesterMockRecorder struct {
 	mock *Ingester
 }
 
-// NewIngester creates a new mock instance
+// NewIngester creates a new mock instance.
 func NewIngester(ctrl *gomock.Controller) *Ingester {
 	mock := &Ingester{ctrl: ctrl}
 	mock.recorder = &IngesterMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *Ingester) EXPECT() *IngesterMockRecorder {
 	return m.recorder
 }
 
-// Err mocks base method
+// Err mocks base method.
 func (m *Ingester) Err() error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Err")
@@ -43,13 +43,13 @@ func (m *Ingester) Err() error {
 	return ret0
 }
 
-// Err indicates an expected call of Err
+// Err indicates an expected call of Err.
 func (mr *IngesterMockRecorder) Err() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Err", reflect.TypeOf((*Ingester)(nil).Err))
 }
 
-// Ingest mocks base method
+// Ingest mocks base method.
 func (m *Ingester) Ingest(arg0 context.Context, arg1 int) <-chan *price.WithProduct {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Ingest", arg0, arg1)
@@ -57,7 +57,7 @@ func (m *Ingester) Ingest(arg0 context.Context, arg1 int) <-chan *price.WithProd
 	return ret0
 }
 
-// Ingest indicates an expected call of Ingest
+// Ingest indicates an expected call of Ingest.
 func (mr *IngesterMockRecorder) Ingest(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ingest", reflect.TypeOf((*Ingester)(nil).Ingest), arg0, arg1)
diff --git a/mock/price_repository.go b/mock/price_repository.go
index 851dd6d..e62687f 100644
--- a/mock/price_repository.go
+++ b/mock/price_repository.go
@@ -13,30 +13,30 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// PriceRepository is a mock of Repository interface
+// PriceRepository is a mock of Repository interface.
 type PriceRepository struct {
 	ctrl     *gomock.Controller
 	recorder *PriceRepositoryMockRecorder
 }
 
-// PriceRepositoryMockRecorder is the mock recorder for PriceRepository
+// PriceRepositoryMockRecorder is the mock recorder for PriceRepository.
 type PriceRepositoryMockRecorder struct {
 	mock *PriceRepository
 }
 
-// NewPriceRepository creates a new mock instance
+// NewPriceRepository creates a new mock instance.
 func NewPriceRepository(ctrl *gomock.Controller) *PriceRepository {
 	mock := &PriceRepository{ctrl: ctrl}
 	mock.recorder = &PriceRepositoryMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *PriceRepository) EXPECT() *PriceRepositoryMockRecorder {
 	return m.recorder
 }
 
-// DeleteByProductWithKeep mocks base method
+// DeleteByProductWithKeep mocks base method.
 func (m *PriceRepository) DeleteByProductWithKeep(arg0 context.Context, arg1 product.ID, arg2 []price.ID) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "DeleteByProductWithKeep", arg0, arg1, arg2)
@@ -44,13 +44,13 @@ func (m *PriceRepository) DeleteByProductWithKeep(arg0 context.Context, arg1 pro
 	return ret0
 }
 
-// DeleteByProductWithKeep indicates an expected call of DeleteByProductWithKeep
+// DeleteByProductWithKeep indicates an expected call of DeleteByProductWithKeep.
 func (mr *PriceRepositoryMockRecorder) DeleteByProductWithKeep(arg0, arg1, arg2 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteByProductWithKeep", reflect.TypeOf((*PriceRepository)(nil).DeleteByProductWithKeep), arg0, arg1, arg2)
 }
 
-// Filter mocks base method
+// Filter mocks base method.
 func (m *PriceRepository) Filter(arg0 context.Context, arg1 product.ID, arg2 *price.Filter) ([]*price.Price, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Filter", arg0, arg1, arg2)
@@ -59,13 +59,13 @@ func (m *PriceRepository) Filter(arg0 context.Context, arg1 product.ID, arg2 *pr
 	return ret0, ret1
 }
 
-// Filter indicates an expected call of Filter
+// Filter indicates an expected call of Filter.
 func (mr *PriceRepositoryMockRecorder) Filter(arg0, arg1, arg2 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*PriceRepository)(nil).Filter), arg0, arg1, arg2)
 }
 
-// Upsert mocks base method
+// Upsert mocks base method.
 func (m *PriceRepository) Upsert(arg0 context.Context, arg1 *price.WithProduct) (price.ID, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Upsert", arg0, arg1)
@@ -74,7 +74,7 @@ func (m *PriceRepository) Upsert(arg0 context.Context, arg1 *price.WithProduct)
 	return ret0, ret1
 }
 
-// Upsert indicates an expected call of Upsert
+// Upsert indicates an expected call of Upsert.
 func (mr *PriceRepositoryMockRecorder) Upsert(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*PriceRepository)(nil).Upsert), arg0, arg1)
diff --git a/mock/product_repository.go b/mock/product_repository.go
index f40b5a8..47e67f5 100644
--- a/mock/product_repository.go
+++ b/mock/product_repository.go
@@ -12,30 +12,30 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// ProductRepository is a mock of Repository interface
+// ProductRepository is a mock of Repository interface.
 type ProductRepository struct {
 	ctrl     *gomock.Controller
 	recorder *ProductRepositoryMockRecorder
 }
 
-// ProductRepositoryMockRecorder is the mock recorder for ProductRepository
+// ProductRepositoryMockRecorder is the mock recorder for ProductRepository.
 type ProductRepositoryMockRecorder struct {
 	mock *ProductRepository
 }
 
-// NewProductRepository creates a new mock instance
+// NewProductRepository creates a new mock instance.
 func NewProductRepository(ctrl *gomock.Controller) *ProductRepository {
 	mock := &ProductRepository{ctrl: ctrl}
 	mock.recorder = &ProductRepositoryMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *ProductRepository) EXPECT() *ProductRepositoryMockRecorder {
 	return m.recorder
 }
 
-// Filter mocks base method
+// Filter mocks base method.
 func (m *ProductRepository) Filter(arg0 context.Context, arg1 *product.Filter) ([]*product.Product, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Filter", arg0, arg1)
@@ -44,13 +44,13 @@ func (m *ProductRepository) Filter(arg0 context.Context, arg1 *product.Filter) (
 	return ret0, ret1
 }
 
-// Filter indicates an expected call of Filter
+// Filter indicates an expected call of Filter.
 func (mr *ProductRepositoryMockRecorder) Filter(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*ProductRepository)(nil).Filter), arg0, arg1)
 }
 
-// FindByVendorAndSKU mocks base method
+// FindByVendorAndSKU mocks base method.
 func (m *ProductRepository) FindByVendorAndSKU(arg0 context.Context, arg1, arg2 string) (*product.Product, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "FindByVendorAndSKU", arg0, arg1, arg2)
@@ -59,13 +59,13 @@ func (m *ProductRepository) FindByVendorAndSKU(arg0 context.Context, arg1, arg2
 	return ret0, ret1
 }
 
-// FindByVendorAndSKU indicates an expected call of FindByVendorAndSKU
+// FindByVendorAndSKU indicates an expected call of FindByVendorAndSKU.
 func (mr *ProductRepositoryMockRecorder) FindByVendorAndSKU(arg0, arg1, arg2 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByVendorAndSKU", reflect.TypeOf((*ProductRepository)(nil).FindByVendorAndSKU), arg0, arg1, arg2)
 }
 
-// Upsert mocks base method
+// Upsert mocks base method.
 func (m *ProductRepository) Upsert(arg0 context.Context, arg1 *product.Product) (product.ID, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Upsert", arg0, arg1)
@@ -74,7 +74,7 @@ func (m *ProductRepository) Upsert(arg0 context.Context, arg1 *product.Product)
 	return ret0, ret1
 }
 
-// Upsert indicates an expected call of Upsert
+// Upsert indicates an expected call of Upsert.
 func (mr *ProductRepositoryMockRecorder) Upsert(arg0, arg1 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*ProductRepository)(nil).Upsert), arg0, arg1)
diff --git a/mock/terraform_provider.go b/mock/terraform_provider.go
index 4f3c356..58e86a8 100644
--- a/mock/terraform_provider.go
+++ b/mock/terraform_provider.go
@@ -12,30 +12,30 @@ import (
 	gomock "github.com/golang/mock/gomock"
 )
 
-// TerraformProvider is a mock of Provider interface
+// TerraformProvider is a mock of Provider interface.
 type TerraformProvider struct {
 	ctrl     *gomock.Controller
 	recorder *TerraformProviderMockRecorder
 }
 
-// TerraformProviderMockRecorder is the mock recorder for TerraformProvider
+// TerraformProviderMockRecorder is the mock recorder for TerraformProvider.
 type TerraformProviderMockRecorder struct {
 	mock *TerraformProvider
 }
 
-// NewTerraformProvider creates a new mock instance
+// NewTerraformProvider creates a new mock instance.
 func NewTerraformProvider(ctrl *gomock.Controller) *TerraformProvider {
 	mock := &TerraformProvider{ctrl: ctrl}
 	mock.recorder = &TerraformProviderMockRecorder{mock}
 	return mock
 }
 
-// EXPECT returns an object that allows the caller to indicate expected use
+// EXPECT returns an object that allows the caller to indicate expected use.
 func (m *TerraformProvider) EXPECT() *TerraformProviderMockRecorder {
 	return m.recorder
 }
 
-// Name mocks base method
+// Name mocks base method.
 func (m *TerraformProvider) Name() string {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Name")
@@ -43,13 +43,13 @@ func (m *TerraformProvider) Name() string {
 	return ret0
 }
 
-// Name indicates an expected call of Name
+// Name indicates an expected call of Name.
 func (mr *TerraformProviderMockRecorder) Name() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*TerraformProvider)(nil).Name))
 }
 
-// ResourceComponents mocks base method
+// ResourceComponents mocks base method.
 func (m *TerraformProvider) ResourceComponents(arg0 terraform.Resource) []query.Component {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ResourceComponents", arg0)
@@ -57,7 +57,7 @@ func (m *TerraformProvider) ResourceComponents(arg0 terraform.Resource) []query.
 	return ret0
 }
 
-// ResourceComponents indicates an expected call of ResourceComponents
+// ResourceComponents indicates an expected call of ResourceComponents.
 func (mr *TerraformProviderMockRecorder) ResourceComponents(arg0 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceComponents", reflect.TypeOf((*TerraformProvider)(nil).ResourceComponents), arg0)
diff --git a/mysql/backend.go b/mysql/backend.go
index 726eb04..156bec3 100644
--- a/mysql/backend.go
+++ b/mysql/backend.go
@@ -24,8 +24,8 @@ func NewBackend(querier sqlr.Querier) *Backend {
 	}
 }
 
-// Product returns the product.Repository that uses the Backend's querier.
-func (b *Backend) Product() product.Repository { return b.productRepo }
+// Products returns the product.Repository that uses the Backend's querier.
+func (b *Backend) Products() product.Repository { return b.productRepo }
 
-// Price returns the price.Repository that uses the Backend's querier.
-func (b *Backend) Price() price.Repository { return b.priceRepo }
+// Prices returns the price.Repository that uses the Backend's querier.
+func (b *Backend) Prices() price.Repository { return b.priceRepo }
diff --git a/price/repository.go b/price/repository.go
index 41ac87e..77ad8ff 100644
--- a/price/repository.go
+++ b/price/repository.go
@@ -6,7 +6,7 @@ import (
 	"github.com/cycloidio/terracost/product"
 )
 
-//go:generate go run github.com/golang/mock/mockgen -destination=../mock/price_repository.go -mock_names=Repository=PriceRepository -package mock github.com/cycloidio/terracost/price Repository
+//go:generate mockgen -destination=../mock/price_repository.go -mock_names=Repository=PriceRepository -package mock github.com/cycloidio/terracost/price Repository
 
 // Repository describes interactions with a storage system to deal with Price entries.
 type Repository interface {
diff --git a/product/repository.go b/product/repository.go
index 15a93a5..9ea0ddc 100644
--- a/product/repository.go
+++ b/product/repository.go
@@ -4,7 +4,7 @@ import (
 	"context"
 )
 
-//go:generate go run github.com/golang/mock/mockgen -destination=../mock/product_repository.go -mock_names=Repository=ProductRepository -package mock github.com/cycloidio/terracost/product Repository
+//go:generate mockgen -destination=../mock/product_repository.go -mock_names=Repository=ProductRepository -package mock github.com/cycloidio/terracost/product Repository
 
 // Repository describes interactions with a storage system to deal with Product entries.
 type Repository interface {
diff --git a/terraform/provider.go b/terraform/provider.go
index fd182da..f9e7561 100644
--- a/terraform/provider.go
+++ b/terraform/provider.go
@@ -4,7 +4,7 @@ import (
 	"github.com/cycloidio/terracost/query"
 )
 
-//go:generate go run github.com/golang/mock/mockgen -destination=../mock/terraform_provider.go -mock_names=Provider=TerraformProvider -package mock github.com/cycloidio/terracost/terraform Provider
+//go:generate mockgen -destination=../mock/terraform_provider.go -mock_names=Provider=TerraformProvider -package mock github.com/cycloidio/terracost/terraform Provider
 
 // Provider represents a Terraform provider. It extracts price queries from Terraform resources.
 type Provider interface {


Test patch:
diff --git a/cost/state_test.go b/cost/state_test.go
index ad6b3ce..64ef196 100644
--- a/cost/state_test.go
+++ b/cost/state_test.go
@@ -52,8 +52,8 @@ func TestNewState(t *testing.T) {
 		productRepo := mock.NewProductRepository(ctrl)
 		priceRepo := mock.NewPriceRepository(ctrl)
 		backend := mock.NewBackend(ctrl)
-		backend.EXPECT().Product().AnyTimes().Return(productRepo)
-		backend.EXPECT().Price().AnyTimes().Return(priceRepo)
+		backend.EXPECT().Products().AnyTimes().Return(productRepo)
+		backend.EXPECT().Prices().AnyTimes().Return(priceRepo)
 
 		prod1 := &product.Product{ID: product.ID(1)}
 		productRepo.EXPECT().Filter(ctx, queries[0].Components[0].ProductFilter).Return([]*product.Product{prod1}, nil)
@@ -89,8 +89,8 @@ func TestNewState(t *testing.T) {
 		productRepo := mock.NewProductRepository(ctrl)
 		priceRepo := mock.NewPriceRepository(ctrl)
 		backend := mock.NewBackend(ctrl)
-		backend.EXPECT().Product().AnyTimes().Return(productRepo)
-		backend.EXPECT().Price().AnyTimes().Return(priceRepo)
+		backend.EXPECT().Products().AnyTimes().Return(productRepo)
+		backend.EXPECT().Prices().AnyTimes().Return(priceRepo)
 
 		productRepo.EXPECT().Filter(ctx, queries[0].Components[0].ProductFilter).Return(nil, errors.New("repo fail"))
 
@@ -107,8 +107,8 @@ func TestNewState(t *testing.T) {
 		productRepo := mock.NewProductRepository(ctrl)
 		priceRepo := mock.NewPriceRepository(ctrl)
 		backend := mock.NewBackend(ctrl)
-		backend.EXPECT().Product().AnyTimes().Return(productRepo)
-		backend.EXPECT().Price().AnyTimes().Return(priceRepo)
+		backend.EXPECT().Products().AnyTimes().Return(productRepo)
+		backend.EXPECT().Prices().AnyTimes().Return(priceRepo)
 
 		prod1 := &product.Product{ID: product.ID(1)}
 		productRepo.EXPECT().Filter(ctx, queries[0].Components[0].ProductFilter).Return([]*product.Product{prod1}, nil)
diff --git a/e2e/aws_estimation_test.go b/e2e/aws_estimation_test.go
index b862818..127a914 100644
--- a/e2e/aws_estimation_test.go
+++ b/e2e/aws_estimation_test.go
@@ -108,7 +108,7 @@ func TestAWSEstimation(t *testing.T) {
 
 	for _, p := range prods {
 		var err error
-		p.ID, err = backend.Product().Upsert(ctx, p)
+		p.ID, err = backend.Products().Upsert(ctx, p)
 		require.NoError(t, err)
 	}
 
@@ -149,7 +149,7 @@ func TestAWSEstimation(t *testing.T) {
 	}
 
 	for _, p := range prices {
-		_, err := backend.Price().Upsert(ctx, p)
+		_, err := backend.Prices().Upsert(ctx, p)
 		require.NoError(t, err)
 	}
 
diff --git a/e2e/aws_ingestion_test.go b/e2e/aws_ingestion_test.go
index 068b0b1..9afe9c5 100644
--- a/e2e/aws_ingestion_test.go
+++ b/e2e/aws_ingestion_test.go
@@ -47,12 +47,12 @@ func TestAWSIngestion(t *testing.T) {
 	err = costestimation.IngestPricing(ctx, backend, ingester)
 	require.NoError(t, err)
 
-	allProds, err := backend.Product().Filter(ctx, &product.Filter{Provider: util.StringPtr("aws"), Service: util.StringPtr("AmazonEC2-test")})
+	allProds, err := backend.Products().Filter(ctx, &product.Filter{Provider: util.StringPtr("aws"), Service: util.StringPtr("AmazonEC2-test")})
 	require.NoError(t, err)
 	assert.Len(t, allProds, 5)
 
 	for _, prod := range allProds {
-		prices, err := backend.Price().Filter(ctx, prod.ID, nil)
+		prices, err := backend.Prices().Filter(ctx, prod.ID, nil)
 		require.NoError(t, err)
 		assert.Len(t, prices, 1)
 	}
diff --git a/ingestion_test.go b/ingestion_test.go
index 8da3f72..bd751ba 100644
--- a/ingestion_test.go
+++ b/ingestion_test.go
@@ -51,8 +51,8 @@ func TestIngestPricing(t *testing.T) {
 	backend := mock.NewBackend(ctrl)
 	ingester := mock.NewIngester(ctrl)
 
-	backend.EXPECT().Product().AnyTimes().Return(productRepo)
-	backend.EXPECT().Price().AnyTimes().Return(priceRepo)
+	backend.EXPECT().Products().AnyTimes().Return(productRepo)
+	backend.EXPECT().Prices().AnyTimes().Return(priceRepo)
 
 	ingester.EXPECT().Ingest(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, chSize int) <-chan *price.WithProduct {
 		results := make(chan *price.WithProduct, chSize)