Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

retrieving deployment error details during upgrade #1995

Merged
merged 17 commits into from
Jan 27, 2018
2 changes: 2 additions & 0 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ const (
Migrating ProvisioningState = "Migrating"
// Upgrading means an existing ContainerService resource is being upgraded
Upgrading ProvisioningState = "Upgrading"
// Canceled means a deployment has been canceled
Canceled ProvisioningState = "Canceled"
)

// OrchestratorProfile contains Orchestrator properties
Expand Down
16 changes: 16 additions & 0 deletions pkg/apierror/apierror.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

package apierror

// New creates an ErrorResponse
func New(errorCategory ErrorCategory, errorCode ErrorCode, message string) *ErrorResponse {
return &ErrorResponse{
Body: Error{
Code: errorCode,
Message: message,
Category: errorCategory,
},
}
}
33 changes: 33 additions & 0 deletions pkg/apierror/apierror_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

package apierror

import (
"testing"

. "github.com/onsi/gomega"
)

func TestNewAPIError(t *testing.T) {
RegisterTestingT(t)

apiError := New(
ClientError,
InvalidParameter,
"error test")

Expect(apiError.Body.Code).Should(Equal(ErrorCode("InvalidParameter")))
}

func TestAcsNewAPIError(t *testing.T) {
RegisterTestingT(t)

apiError := New(
ClientError,
InvalidSubscriptionStateTransition,
"error test")

Expect(apiError.Body.Code).Should(Equal(ErrorCode("InvalidSubscriptionStateTransition")))
}
71 changes: 71 additions & 0 deletions pkg/apierror/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

package apierror

// ErrorCategory indicates the kind of error
type ErrorCategory string

const (
// ClientError is expected error
ClientError ErrorCategory = "ClientError"

// InternalError is system or internal error
InternalError ErrorCategory = "InternalError"
)

// ErrorCode is Common Azure Resource Provider API error code
type ErrorCode string

const (
// From Microsoft.Azure.ResourceProvider.API.ErrorCode

// InvalidParameter error
InvalidParameter ErrorCode = "InvalidParameter"
// BadRequest error
BadRequest ErrorCode = "BadRequest"
// NotFound error
NotFound ErrorCode = "NotFound"
// Conflict error
Conflict ErrorCode = "Conflict"
// PreconditionFailed error
PreconditionFailed ErrorCode = "PreconditionFailed"
// OperationNotAllowed error
OperationNotAllowed ErrorCode = "OperationNotAllowed"
// OperationPreempted error
OperationPreempted ErrorCode = "OperationPreempted"
// PropertyChangeNotAllowed error
PropertyChangeNotAllowed ErrorCode = "PropertyChangeNotAllowed"
// InternalOperationError error
InternalOperationError ErrorCode = "InternalOperationError"
// InvalidSubscriptionStateTransition error
InvalidSubscriptionStateTransition ErrorCode = "InvalidSubscriptionStateTransition"
// UnregisterWithResourcesNotAllowed error
UnregisterWithResourcesNotAllowed ErrorCode = "UnregisterWithResourcesNotAllowed"
// InvalidParameterConflictingProperties error
InvalidParameterConflictingProperties ErrorCode = "InvalidParameterConflictingProperties"
// SubscriptionNotRegistered error
SubscriptionNotRegistered ErrorCode = "SubscriptionNotRegistered"
// ConflictingUserInput error
ConflictingUserInput ErrorCode = "ConflictingUserInput"
// ProvisioningInternalError error
ProvisioningInternalError ErrorCode = "ProvisioningInternalError"
// ProvisioningFailed error
ProvisioningFailed ErrorCode = "ProvisioningFailed"
// NetworkingInternalOperationError error
NetworkingInternalOperationError ErrorCode = "NetworkingInternalOperationError"
// QuotaExceeded error
QuotaExceeded ErrorCode = "QuotaExceeded"
// Unauthorized error
Unauthorized ErrorCode = "Unauthorized"
// ResourcesOverConstrained error
ResourcesOverConstrained ErrorCode = "ResourcesOverConstrained"

// ResourceDeploymentFailure error
ResourceDeploymentFailure ErrorCode = "ResourceDeploymentFailure"
// InvalidTemplateDeployment error
InvalidTemplateDeployment ErrorCode = "InvalidTemplateDeployment"
// DeploymentFailed error
DeploymentFailed ErrorCode = "DeploymentFailed"
)
47 changes: 47 additions & 0 deletions pkg/apierror/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package apierror

import (
"encoding/json"
"net/http"

"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
)

// ExtractCodeFromARMHttpResponse returns the ARM error's Code field
// If not found return defaultCode
func ExtractCodeFromARMHttpResponse(resp *http.Response, defaultCode ErrorCode) ErrorCode {
if resp == nil {
return defaultCode
}
decoder := json.NewDecoder(resp.Body)
errorJSON := ErrorResponse{}
if err := decoder.Decode(&errorJSON); err != nil {
return defaultCode
}

if errorJSON.Body.Code == "" {
return defaultCode
}
return ErrorCode(errorJSON.Body.Code)
}

//ConvertToAPIError turns a ManagementErrorWithDetails into a apierror.Error
func ConvertToAPIError(mError *resources.ManagementErrorWithDetails) *Error {
retVal := &Error{}
if mError.Code != nil {
retVal.Code = ErrorCode(*mError.Code)
}
if mError.Message != nil {
retVal.Message = *mError.Message
}
if mError.Target != nil {
retVal.Target = *mError.Target
}
if mError.Details != nil {
retVal.Details = []Error{}
for _, me := range *mError.Details {
retVal.Details = append(retVal.Details, *ConvertToAPIError(&me))
}
}
return retVal
}
21 changes: 21 additions & 0 deletions pkg/apierror/helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package apierror

import (
"bytes"
"io/ioutil"
"net/http"
"testing"

. "github.com/onsi/gomega"
)

func TestExtractCodeFromARMHttpResponse(t *testing.T) {
RegisterTestingT(t)

resp := &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(`{"error":{"code":"ResourceGroupNotFound","message":"Resource group 'jiren-fakegroup' could not be found."}}`)),
}

code := ExtractCodeFromARMHttpResponse(resp, "")
Expect(code).To(Equal(ErrorCode("ResourceGroupNotFound")))
}
39 changes: 39 additions & 0 deletions pkg/apierror/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

package apierror

import "encoding/json"

// Error is the OData v4 format, used by the RPC and
// will go into the v2.2 Azure REST API guidelines
type Error struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Target string `json:"target,omitempty"`
Details []Error `json:"details,omitempty"`

Category ErrorCategory `json:"-"`
ExceptionType string `json:"-"`
InternalDetail string `json:"-"`
}

// ErrorResponse defines Resource Provider API 2.0 Error Response Content structure
type ErrorResponse struct {
Body Error `json:"error"`
}

// Error implements error interface to return error in json
func (e *ErrorResponse) Error() string {
return e.Body.Error()
}

// Error implements error interface to return error in json
func (e *Error) Error() string {
output, err := json.MarshalIndent(e, " ", " ")
if err != nil {
return err.Error()
}
return string(output)
}
Loading