Skip to content
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

Cherry pick unassign roles panic fix into 0.17.x #214

Merged
merged 3 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
"github.com/google/uuid"
"github.com/hashicorp/go-multierror"
Expand Down Expand Up @@ -214,11 +214,10 @@ func (c *client) unassignRoles(ctx context.Context, roleIDs []string) error {
var merr *multierror.Error

for _, id := range roleIDs {
var rawResponse *http.Response
ctxWithResp := policy.WithCaptureResponse(ctx, &rawResponse)
if _, err := c.provider.DeleteRoleAssignmentByID(ctxWithResp, id); err != nil {
// If a role was deleted manually then Azure returns a error and status 204
if rawResponse != nil && rawResponse.StatusCode == http.StatusNoContent || rawResponse.StatusCode == http.StatusNotFound {
if _, err := c.provider.DeleteRoleAssignmentByID(ctx, id); err != nil {
// If a role was deleted out-of-band then Azure returns an error and status 204
respErr := new(azcore.ResponseError)
if errors.As(err, &respErr) && (respErr.StatusCode == http.StatusNoContent || respErr.StatusCode == http.StatusNotFound) {
continue
}

Expand Down
58 changes: 58 additions & 0 deletions path_service_principal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,64 @@ func TestRoleAssignmentWALRollback(t *testing.T) {
})
}

// TestUnassignRoleFailures ensures that the plugin can
// cleanly resolve any errors received when unassigning roles
func TestUnassignRoleFailures(t *testing.T) {
b, s := getTestBackendMocked(t, true)

mp := newMockProvider()
mp.(*mockProvider).failUnassignRoles = true
mp.(*mockProvider).unassignRolesFailureParams = failureParams{
expectError: true,
}
b.getProvider = func(s *clientSettings, p api.Passwords) (AzureProvider, error) {
return mp, nil
}

c, err := b.getClient(context.Background(), s)
if err != nil {
t.Fatalf("error getting client: %s", err.Error())
}

// verify error is received
t.Run("Role unassign fail", func(t *testing.T) {
testAssignmentIDs := []string{"test-1", "test-2"}
// Remove one of the RA IDs to simulate a failure to assign a role
if err := c.unassignRoles(context.Background(), testAssignmentIDs); err == nil {
t.Fatalf("expected error but got nil")
}
})

mp.(*mockProvider).unassignRolesFailureParams = failureParams{
expectError: false,
statusCode: 204,
}

// verify the error is properly handled and ignored in 204 case
t.Run("Role unassign error handled for 204", func(t *testing.T) {
testAssignmentIDs := []string{"test-1", "test-2"}
// Remove one of the RA IDs to simulate a failure to assign a role
if err := c.unassignRoles(context.Background(), testAssignmentIDs); err != nil {
t.Fatalf("error unassigning Role: %s", err.Error())
}
})

mp.(*mockProvider).unassignRolesFailureParams = failureParams{
expectError: false,
statusCode: 404,
}

// verify the error is properly handled and ignored in 404 case
t.Run("Role unassign error handled for 404", func(t *testing.T) {
testAssignmentIDs := []string{"test-1", "test-2"}
// Remove one of the RA IDs to simulate a failure to assign a role
if err := c.unassignRoles(context.Background(), testAssignmentIDs); err != nil {
t.Fatalf("error unassigning Role: %s", err.Error())
}
})

}

// This is an integration test against the live Azure service. It requires
// valid, sufficiently-privileged Azure credentials in env variables.
func TestCredentialInteg_msgraph(t *testing.T) {
Expand Down
37 changes: 30 additions & 7 deletions provider_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sync"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
"github.com/google/uuid"

Expand All @@ -20,13 +21,20 @@ import (

// mockProvider is a Provider that provides stubs and simple, deterministic responses.
type mockProvider struct {
applications map[string]string
servicePrincipals map[string]bool
deletedObjects map[string]bool
passwords map[string]string
failNextCreateApplication bool
ctxTimeout time.Duration
lock sync.Mutex
applications map[string]string
servicePrincipals map[string]bool
deletedObjects map[string]bool
passwords map[string]string
failNextCreateApplication bool
failUnassignRoles bool
unassignRolesFailureParams failureParams
ctxTimeout time.Duration
lock sync.Mutex
}

type failureParams struct {
statusCode int
expectError bool
}

func newMockProvider() AzureProvider {
Expand Down Expand Up @@ -224,6 +232,21 @@ func (m *mockProvider) CreateRoleAssignment(_ context.Context, scope string, nam
}

func (m *mockProvider) DeleteRoleAssignmentByID(_ context.Context, _ string) (armauthorization.RoleAssignmentsClientDeleteByIDResponse, error) {
if m.failUnassignRoles {
if m.unassignRolesFailureParams.expectError {
// return empty response and no 200 status codes to throw error
return armauthorization.RoleAssignmentsClientDeleteByIDResponse{}, &azcore.ResponseError{
ErrorCode: "mock: fail to delete role assignment",
}
}

// return empty response and with status code; will ignore error and assume role
// assignment was manually deleted based on status code
return armauthorization.RoleAssignmentsClientDeleteByIDResponse{}, &azcore.ResponseError{
StatusCode: m.unassignRolesFailureParams.statusCode,
ErrorCode: "mock: fail to delete role assignment",
}
}
return armauthorization.RoleAssignmentsClientDeleteByIDResponse{}, nil
}

Expand Down