// 2017 Rackspace, Inc. // // 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 tools serves as helper or utility functions for other applications within the Migrations App. package tools import ( "encoding/json" "fmt" "net/http" "os" "strings" "time" "github.com/gocarina/gocsv" "github.com/sirupsen/logrus" "github.rackspace.com/DCM/migration/pkg/collection" "github.rackspace.com/DCM/migration/pkg/tools/journal" raxidentity "github.rackspace.com/gorax/clients/raxIdentity" "github.rackspace.com/gorax/clients/raxNewrelic" "github.rackspace.com/gorax/libs/nsiuuid" "github.rackspace.com/gorax/tools/lagger" ) // T struct defines the transaction client // used for logging and pushing to new relic // // swagger:ignore type T struct { start time.Time name string collection string rw http.ResponseWriter r *http.Request headers map[string]string } // rHandle type used for to return a like typed response format // // swagger:model responseDef type rHandle map[string]interface{} // Transaction function will initiate a new transaction client to use within an // http router func Transaction(name, collection string, rw http.ResponseWriter, r *http.Request) T { return T{ start: time.Now(), name: name, rw: rw, r: r, collection: collection, headers: map[string]string{ "Content-Type": "application/json", "Server": os.Getenv("SERVER"), "X-Request-Id": nsiuuid.FromContext(r.Context()).String(), "WWW-Authenticate": fmt.Sprintf("Keystone uri=%s", raxidentity.FromContext(r.Context()).URL), }, } } // Log method will create a new relic transaction log if the correct // query parameter is set. func (t *T) Log(resp collection.UpdateResponse, err error) { logger := lagger.FromContext(t.r.Context()) // Return without logging if the logNewRelic query param is not set. if t.r.URL.Query().Get("logNewRelic") != "true" { return } var e string if err != nil { e = err.Error() logger.Error(logger.WebLog(t.r, raxidentity.FromContext(t.r.Context()).Access.User.Name, e)) } if err := journal.Notifications([]journal.Notification{ { EventType: t.name, StartTime: t.start, Duration: time.Since(t.start), Updated: resp.Updated, Errors: e, Missed: len(resp.Missed), }, }, raxNewrelic.FromContext(t.r.Context())); err != nil { logrus.Error(err) } } // LogAudit method will create a new relic transaction log if the correct // query parameter is set. func (t *T) LogAudit(resp []journal.Changes, err error) { logger := lagger.FromContext(t.r.Context()) // Return without logging if the logNewRelic query param is not set. if t.r.URL.Query().Get("logNewRelic") != "true" { return } var e string if err != nil { e = err.Error() logger.Error(logger.WebLog(t.r, raxidentity.FromContext(t.r.Context()).Access.User.Name, e)) } if err := journal.Notifications([]journal.Notification{{ EventType: t.name, StartTime: t.start, Duration: time.Since(t.start), Errors: e, }}, raxNewrelic.FromContext(t.r.Context())); err != nil { logrus.Error(err) } // Send an alert message to NewRelic API to log in the dashboard. if err := journal.Audits(resp, t.collection, raxNewrelic.FromContext(t.r.Context())); err != nil { logrus.Error(err) } } // Complete method will write the response data and return func (t *T) Complete(resp interface{}, code int, err error) { switch { case err != nil: logrus.Error(err) t.handle(rHandle{"data": resp, "error": err.Error()}, code) return case strings.Contains(t.r.Header.Get("Accept"), "text/plain"): t.headers["Content-Type"] = "text/plain" t.handle(resp, code) return case strings.Contains(t.r.Header.Get("Accept"), "text/csv"): t.headers["Content-Type"] = "text/csv" csvContent, _ := gocsv.MarshalString(&resp) t.handle(csvContent, code) return default: t.handle(rHandle{"data": resp, "error": nil}, code) return } } // CompleteRaw method will write the response data and return func (t *T) CompleteRaw(resp interface{}, code int, err error) { switch { case err != nil: logrus.Error(err) t.handle(err, code) return case strings.Contains(t.r.Header.Get("Accept"), "text/plain"): t.headers["Content-Type"] = "text/plain" t.handle(resp, code) return case strings.Contains(t.r.Header.Get("Accept"), "text/csv"): t.headers["Content-Type"] = "text/csv" csvContent, _ := gocsv.MarshalString(&resp) t.handle(csvContent, code) return default: t.handle(resp, code) return } } func (t *T) handle(resp interface{}, code int) { for key, value := range t.headers { t.rw.Header().Set(key, value) } t.rw.WriteHeader(code) if stringType, ok := resp.(string); ok { fmt.Fprint(t.rw, stringType) } else { //nolint:errchkjson b, _ := json.Marshal(resp) fmt.Fprint(t.rw, strings.Replace(string(b), "%", "%%", -1)) } }