/
get_ec2_data.go
136 lines (127 loc) · 5.58 KB
/
get_ec2_data.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2018 MSolution.IO
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ec2
import (
"context"
"database/sql"
"fmt"
"net/http"
"strings"
"errors"
"gopkg.in/olivere/elastic.v5"
"github.com/trackit/jsonlog"
"github.com/trackit/trackit-server/aws/usageReports/ec2"
terrors "github.com/trackit/trackit-server/errors"
"github.com/trackit/trackit-server/es"
"github.com/trackit/trackit-server/users"
)
// makeElasticSearchRequest prepares and run an ES request
// based on the ec2QueryParams and search params
// It will return the data, an http status code (as int) and an error.
// Because an error can be generated, but is not critical and is not needed to be known by
// the user (e.g if the index does not exists because it was not yet indexed ) the error will
// be returned, but instead of having a 500 status code, it will return the provided status code
// with empty data
func makeElasticSearchRequest(ctx context.Context, parsedParams Ec2QueryParams,
esSearchParams func(Ec2QueryParams, *elastic.Client, string) *elastic.SearchService) (*elastic.SearchResult, int, error) {
l := jsonlog.LoggerFromContextOrDefault(ctx)
index := strings.Join(parsedParams.IndexList, ",")
searchService := esSearchParams(
parsedParams,
es.Client,
index,
)
res, err := searchService.Do(ctx)
if err != nil {
if elastic.IsNotFound(err) {
l.Warning("Query execution failed, ES index does not exists", map[string]interface{}{
"index": index,
"error": err.Error(),
})
return nil, http.StatusOK, terrors.GetErrorMessage(ctx, err)
} else if cast, ok := err.(*elastic.Error); ok && cast.Details.Type == "search_phase_execution_exception" {
l.Error("Error while getting data from ES", map[string]interface{}{
"type": fmt.Sprintf("%T", err),
"error": err,
})
} else {
l.Error("Query execution failed", map[string]interface{}{"error": err.Error()})
}
return nil, http.StatusInternalServerError, terrors.GetErrorMessage(ctx, err)
}
return res, http.StatusOK, nil
}
// GetEc2MonthlyInstances does an elastic request and returns an array of instances monthly report based on query params
func GetEc2MonthlyInstances(ctx context.Context, params Ec2QueryParams) (int, []InstanceReport, error) {
res, returnCode, err := makeElasticSearchRequest(ctx, params, getElasticSearchEc2MonthlyParams)
if err != nil {
return returnCode, nil, err
} else if res == nil {
return http.StatusInternalServerError, nil, errors.New("Error while getting data. Please check again in few hours.")
}
instances, err := prepareResponseEc2Monthly(ctx, res)
if err != nil {
return http.StatusInternalServerError, nil, err
}
return http.StatusOK, instances, nil
}
// GetEc2DailyInstances does an elastic request and returns an array of instances daily report based on query params
func GetEc2DailyInstances(ctx context.Context, params Ec2QueryParams, user users.User, tx *sql.Tx) (int, []InstanceReport, error) {
res, returnCode, err := makeElasticSearchRequest(ctx, params, getElasticSearchEc2DailyParams)
if err != nil {
return returnCode, nil, err
} else if res == nil {
return http.StatusInternalServerError, nil, errors.New("Error while getting data. Please check again in few hours.")
}
accountsAndIndexes, returnCode, err := es.GetAccountsAndIndexes(params.AccountList, user, tx, es.IndexPrefixLineItems)
if err != nil {
return returnCode, nil, err
}
params.AccountList = accountsAndIndexes.Accounts
params.IndexList = accountsAndIndexes.Indexes
costRes, _, _ := makeElasticSearchRequest(ctx, params, getElasticSearchCostParams)
instances, err := prepareResponseEc2Daily(ctx, res, costRes)
if err != nil {
return http.StatusInternalServerError, nil, err
}
return http.StatusOK, instances, nil
}
// GetEc2Data gets EC2 monthly reports based on query params, if there isn't a monthly report, it gets daily reports
func GetEc2Data(ctx context.Context, parsedParams Ec2QueryParams, user users.User, tx *sql.Tx) (int, []InstanceReport, error) {
accountsAndIndexes, returnCode, err := es.GetAccountsAndIndexes(parsedParams.AccountList, user, tx, ec2.IndexPrefixEC2Report)
if err != nil {
return returnCode, nil, err
}
parsedParams.AccountList = accountsAndIndexes.Accounts
parsedParams.IndexList = accountsAndIndexes.Indexes
returnCode, monthlyInstances, err := GetEc2MonthlyInstances(ctx, parsedParams)
if err != nil {
return returnCode, nil, err
} else if monthlyInstances != nil && len(monthlyInstances) > 0 {
return returnCode, monthlyInstances, nil
}
returnCode, dailyInstances, err := GetEc2DailyInstances(ctx, parsedParams, user, tx)
if err != nil {
return returnCode, nil, err
}
return returnCode, dailyInstances, nil
}
// GetEc2UnusedData gets EC2 reports and parse them based on query params to have an array of unused instances
func GetEc2UnusedData(ctx context.Context, params Ec2UnusedQueryParams, user users.User, tx *sql.Tx) (int, []InstanceReport, error) {
returnCode, instances, err := GetEc2Data(ctx, Ec2QueryParams{params.AccountList, nil, params.Date}, user, tx)
if err != nil {
return returnCode, nil, err
}
return prepareResponseEc2Unused(params, instances)
}