-
Notifications
You must be signed in to change notification settings - Fork 223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove leaking logs from SDK #868
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,6 @@ import ( | |
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"time" | ||
|
||
|
@@ -57,29 +56,48 @@ func generateFuncStr(spec *DeployFunctionSpec) string { | |
return spec.FunctionName | ||
} | ||
|
||
type DeployResponse struct { | ||
Message string | ||
RollingUpdate bool | ||
URL string | ||
} | ||
|
||
// DeployFunction first tries to deploy a function and if it exists will then attempt | ||
// a rolling update. Warnings are suppressed for the second API call (if required.) | ||
func (c *Client) DeployFunction(context context.Context, spec *DeployFunctionSpec) int { | ||
func (c *Client) DeployFunction(context context.Context, spec *DeployFunctionSpec) (*DeployResponse, *http.Response, error) { | ||
|
||
rollingUpdateInfo := fmt.Sprintf("Function %s already exists, attempting rolling-update.", spec.FunctionName) | ||
viveksyngh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
statusCode, deployOutput := c.deploy(context, spec, spec.Update) | ||
res, err := c.deploy(context, spec, spec.Update) | ||
|
||
if err != nil && IsUnknown(err) { | ||
return nil, res, err | ||
} | ||
|
||
var deployResponse DeployResponse | ||
if err == nil { | ||
deployResponse.Message = fmt.Sprintf("Deployed. %s.", res.Status) | ||
deployResponse.URL = fmt.Sprintf("%s/function/%s", c.GatewayURL.String(), generateFuncStr(spec)) | ||
} | ||
|
||
if spec.Update == true && statusCode == http.StatusNotFound { | ||
if spec.Update == true && IsNotFound(err) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Example of checking types error. |
||
// Re-run the function with update=false | ||
res, err = c.deploy(context, spec, false) | ||
if err == nil { | ||
deployResponse.Message = fmt.Sprintf("Deployed. %s.", res.Status) | ||
deployResponse.URL = fmt.Sprintf("%s/function/%s", c.GatewayURL.String(), generateFuncStr(spec)) | ||
} | ||
|
||
} else if res.StatusCode == http.StatusOK { | ||
deployResponse.Message += rollingUpdateInfo | ||
deployResponse.RollingUpdate = true | ||
|
||
statusCode, deployOutput = c.deploy(context, spec, false) | ||
} else if statusCode == http.StatusOK { | ||
fmt.Println(rollingUpdateInfo) | ||
} | ||
fmt.Println() | ||
fmt.Println(deployOutput) | ||
return statusCode | ||
|
||
return &deployResponse, res, err | ||
} | ||
|
||
// deploy a function to an OpenFaaS gateway over REST | ||
func (c *Client) deploy(context context.Context, spec *DeployFunctionSpec, update bool) (int, string) { | ||
|
||
var deployOutput string | ||
func (c *Client) deploy(context context.Context, spec *DeployFunctionSpec, update bool) (*http.Response, error) { | ||
// Need to alter Gateway to allow nil/empty string as fprocess, to avoid this repetition. | ||
var fprocessTemplate string | ||
if len(spec.FProcess) > 0 { | ||
|
@@ -146,37 +164,20 @@ func (c *Client) deploy(context context.Context, spec *DeployFunctionSpec, updat | |
request, err = c.newRequest(method, "/system/functions", reader) | ||
|
||
if err != nil { | ||
deployOutput += fmt.Sprintln(err) | ||
return http.StatusInternalServerError, deployOutput | ||
return nil, err | ||
} | ||
|
||
res, err := c.doRequest(context, request) | ||
|
||
if err != nil { | ||
deployOutput += fmt.Sprintln("Is OpenFaaS deployed? Do you need to specify the --gateway flag?") | ||
deployOutput += fmt.Sprintln(err) | ||
return http.StatusInternalServerError, deployOutput | ||
} | ||
|
||
if res.Body != nil { | ||
defer res.Body.Close() | ||
errMessage := fmt.Sprintln("Is OpenFaaS deployed? Do you need to specify the --gateway flag?") | ||
errMessage += fmt.Sprintln(err) | ||
return res, NewUnknown(errMessage, 0) | ||
} | ||
|
||
switch res.StatusCode { | ||
case http.StatusOK, http.StatusCreated, http.StatusAccepted: | ||
deployOutput += fmt.Sprintf("Deployed. %s.\n", res.Status) | ||
|
||
deployedURL := fmt.Sprintf("URL: %s/function/%s", c.GatewayURL.String(), generateFuncStr(spec)) | ||
deployOutput += fmt.Sprintln(deployedURL) | ||
case http.StatusUnauthorized: | ||
deployOutput += fmt.Sprintln("unauthorized access, run \"faas-cli login\" to setup authentication for this server") | ||
|
||
default: | ||
bytesOut, err := ioutil.ReadAll(res.Body) | ||
if err == nil { | ||
deployOutput += fmt.Sprintf("Unexpected status: %d, message: %s\n", res.StatusCode, string(bytesOut)) | ||
} | ||
err = checkForAPIError(res) | ||
if err != nil { | ||
return res, err | ||
} | ||
|
||
return res.StatusCode, deployOutput | ||
return res, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
package proxy | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"io/ioutil" | ||
"net/http" | ||
) | ||
|
||
type APIError struct { | ||
Reason StatusReason | ||
Code int | ||
Status string | ||
Message string | ||
} | ||
|
||
func (e *APIError) Error() string { | ||
return e.Message | ||
} | ||
|
||
const ( | ||
StatusSuccess = "Success" | ||
StatusFailure = "Failure" | ||
) | ||
|
||
type StatusReason string | ||
|
||
const ( | ||
StatusReasonUnknown StatusReason = "Unknown" | ||
|
||
// Status code 500 | ||
StatusReasonInternalError StatusReason = "InternalError" | ||
|
||
// Status code 401 | ||
StatusReasonUnauthorized StatusReason = "Unauthorized" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could it be http.StatusUnauthorized? https://golang.org/src/net/http/status.go There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can but we will have challenge in solving two problems, if we are not using
If we remove StatusReason, to figure out UnknownError we will have to check for list of all HTTP status for which we have typed error. |
||
|
||
// Status code 403 | ||
StatusReasonForbidden StatusReason = "Forbidden" | ||
|
||
// Status code 404 | ||
StatusReasonNotFound StatusReason = "NotFound" | ||
|
||
// Status code 409 | ||
StatusReasonConflict StatusReason = "Conflict" | ||
|
||
// Status code 504 | ||
StatusReasonGatewayTimeout StatusReason = "Timeout" | ||
|
||
// Status code 429 | ||
StatusReasonTooManyRquests StatusReason = "TooManyRequests" | ||
|
||
// Status code 400 | ||
StatusReasonBadRequest StatusReason = "BadRequest" | ||
|
||
// Status code 405 | ||
StatusReasonMethodNotAllowed StatusReason = "MethodNotAllowed" | ||
|
||
// Status code 503 | ||
StatusReasonServiceUnavailable StatusReason = "ServiceUnavailable" | ||
) | ||
|
||
func NewInternalServer(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonInternalError, | ||
Code: http.StatusInternalServerError, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewUnauthorized(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonUnauthorized, | ||
Code: http.StatusUnauthorized, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewForbidden(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonForbidden, | ||
Code: http.StatusForbidden, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewNotFound(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonNotFound, | ||
Code: http.StatusNotFound, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewConflict(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonConflict, | ||
Code: http.StatusConflict, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewGatewayTimeout(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonGatewayTimeout, | ||
Code: http.StatusGatewayTimeout, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewTooManyRequests(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonTooManyRquests, | ||
Code: http.StatusTooManyRequests, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewBadRequest(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonBadRequest, | ||
Code: http.StatusBadRequest, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewMethodNotAllowed(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonMethodNotAllowed, | ||
Code: http.StatusMethodNotAllowed, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewServiceUnavailable(message string) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonServiceUnavailable, | ||
Code: http.StatusServiceUnavailable, | ||
Status: StatusFailure, | ||
Message: message, | ||
} | ||
} | ||
|
||
func NewUnknown(message string, statusCode int) *APIError { | ||
return &APIError{ | ||
Reason: StatusReasonUnknown, | ||
Code: statusCode, | ||
Message: message, | ||
Status: StatusFailure, | ||
} | ||
} | ||
|
||
func checkForAPIError(resp *http.Response) error { | ||
//HTTP status in 2xx range are success | ||
if c := resp.StatusCode; 200 <= c && c <= 299 { | ||
return nil | ||
} | ||
|
||
if resp.Body != nil { | ||
defer resp.Body.Close() | ||
} | ||
|
||
respBytes, err := ioutil.ReadAll(resp.Body) | ||
|
||
// Re-construct response body in case of error | ||
resp.Body = ioutil.NopCloser(bytes.NewBuffer(respBytes)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
message := string(respBytes) | ||
|
||
switch resp.StatusCode { | ||
|
||
case http.StatusInternalServerError: | ||
return NewInternalServer(message) | ||
|
||
case http.StatusUnauthorized: | ||
return NewUnauthorized(message) | ||
|
||
case http.StatusForbidden: | ||
return NewForbidden(message) | ||
|
||
case http.StatusNotFound: | ||
return NewNotFound(message) | ||
|
||
case http.StatusConflict: | ||
return NewConflict(message) | ||
|
||
case http.StatusGatewayTimeout: | ||
return NewGatewayTimeout(message) | ||
|
||
case http.StatusTooManyRequests: | ||
return NewTooManyRequests(message) | ||
|
||
case http.StatusMethodNotAllowed: | ||
return NewMethodNotAllowed(message) | ||
|
||
case http.StatusBadRequest: | ||
return NewBadRequest(message) | ||
|
||
case http.StatusServiceUnavailable: | ||
return NewServiceUnavailable(message) | ||
|
||
default: | ||
return NewUnknown(message, resp.StatusCode) | ||
} | ||
} | ||
|
||
func IsUnknown(err error) bool { | ||
return ReasonForError(err) == StatusReasonUnknown | ||
} | ||
|
||
func IsNotFound(err error) bool { | ||
return ReasonForError(err) == StatusReasonNotFound | ||
} | ||
|
||
func IsUnauthorized(err error) bool { | ||
return ReasonForError(err) == StatusReasonUnauthorized | ||
} | ||
|
||
func IsBadRequest(err error) bool { | ||
return ReasonForError(err) == StatusReasonBadRequest | ||
} | ||
|
||
func IsForbidden(err error) bool { | ||
return ReasonForError(err) == StatusReasonForbidden | ||
} | ||
|
||
func IsInternalServerError(err error) bool { | ||
return ReasonForError(err) == StatusReasonInternalError | ||
} | ||
|
||
func IsServiceUnavailable(err error) bool { | ||
return ReasonForError(err) == StatusReasonServiceUnavailable | ||
} | ||
|
||
func IsMethodNotAllowed(err error) bool { | ||
return ReasonForError(err) == StatusReasonMethodNotAllowed | ||
} | ||
|
||
func IsTooManyRequests(err error) bool { | ||
return ReasonForError(err) == StatusReasonTooManyRquests | ||
} | ||
|
||
func IsGatewayTimeout(err error) bool { | ||
return ReasonForError(err) == StatusReasonGatewayTimeout | ||
} | ||
|
||
func IsConflict(err error) bool { | ||
return ReasonForError(err) == StatusReasonConflict | ||
} | ||
|
||
func ReasonForError(err error) StatusReason { | ||
var e *APIError | ||
|
||
if errors.As(err, &e) { | ||
return e.Reason | ||
} | ||
|
||
return StatusReasonUnknown | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A generic error message functions which can be used in all commands to print error message for generic APIError like 403, 500 etc