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

Delete role assignments when deleting a VM #2934

Merged
merged 8 commits into from
May 23, 2018
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ test/e2e/openshift/translations/
# test outputs
cmd/_test_output

.idea
2 changes: 1 addition & 1 deletion cmd/scale.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ func (sc *scaleCmd) run(cmd *cobra.Command, args []string) error {
}
}

errList := operations.ScaleDownVMs(sc.client, sc.logger, sc.resourceGroupName, vmsToDelete...)
errList := operations.ScaleDownVMs(sc.client, sc.logger, sc.SubscriptionID.String(), sc.resourceGroupName, vmsToDelete...)
if errList != nil {
errorMessage := ""
for element := errList.Front(); element != nil; element = element.Next() {
Expand Down
11 changes: 11 additions & 0 deletions pkg/armhelpers/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ func (az *AzureClient) CreateRoleAssignment(scope string, roleAssignmentName str
return az.authorizationClient.Create(scope, roleAssignmentName, parameters)
}

// DeleteRoleAssignmentByID deletes a roleAssignment via its unique identifier
func (az *AzureClient) DeleteRoleAssignmentByID(roleAssignmentID string) (authorization.RoleAssignment, error) {
return az.authorizationClient.DeleteByID(roleAssignmentID)
}

// ListRoleAssignmentsForPrincipal (e.g. a VM) via the scope and the unique identifier of the principal
func (az *AzureClient) ListRoleAssignmentsForPrincipal(scope string, principalID string) (authorization.RoleAssignmentListResult, error) {
filter := fmt.Sprintf("principalId eq '%s'", principalID)
return az.authorizationClient.ListForScope(scope, filter)
}

// CreateApp is a simpler method for creating an application
func (az *AzureClient) CreateApp(appName, appURL string, replyURLs *[]string, requiredResourceAccess *[]graphrbac.RequiredResourceAccess) (applicationID, servicePrincipalObjectID, servicePrincipalClientSecret string, err error) {
notBefore := time.Now()
Expand Down
2 changes: 2 additions & 0 deletions pkg/armhelpers/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ type ACSEngineClient interface {
// RBAC
CreateRoleAssignment(scope string, roleAssignmentName string, parameters authorization.RoleAssignmentCreateParameters) (authorization.RoleAssignment, error)
CreateRoleAssignmentSimple(applicationID, roleID string) error
DeleteRoleAssignmentByID(roleAssignmentNameID string) (authorization.RoleAssignment, error)
ListRoleAssignmentsForPrincipal(scope string, principalID string) (authorization.RoleAssignmentListResult, error)

// MANAGED DISKS
DeleteManagedDisk(resourceGroupName string, diskName string, cancel <-chan struct{}) (<-chan disk.OperationStatusResponse, <-chan error)
Expand Down
16 changes: 16 additions & 0 deletions pkg/armhelpers/mockclients.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ func (mc *MockACSEngineClient) GetVirtualMachine(resourceGroup, name string) (co
resourceNameSuffix := "12345678"
poolname := "agentpool1"

principalID := "00000000-1111-2222-3333-444444444444"

tags := map[string]*string{
creationSourceString: &creationSource,
orchestratorString: &orchestrator,
Expand All @@ -284,6 +286,8 @@ func (mc *MockACSEngineClient) GetVirtualMachine(resourceGroup, name string) (co
return compute.VirtualMachine{
Name: &vm1Name,
Tags: &tags,
Identity: &compute.VirtualMachineIdentity{
PrincipalID: &principalID},
VirtualMachineProperties: &compute.VirtualMachineProperties{
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
Expand Down Expand Up @@ -462,3 +466,15 @@ func (mc *MockACSEngineClient) ListDeploymentOperations(resourceGroupName string
func (mc *MockACSEngineClient) ListDeploymentOperationsNextResults(lastResults resources.DeploymentOperationsListResult) (result resources.DeploymentOperationsListResult, err error) {
return resources.DeploymentOperationsListResult{}, nil
}

// DeleteRoleAssignmentByID deletes a roleAssignment via its unique identifier
func (mc *MockACSEngineClient) DeleteRoleAssignmentByID(roleAssignmentID string) (authorization.RoleAssignment, error) {
return authorization.RoleAssignment{}, nil
}

// ListRoleAssignmentsForPrincipal (e.g. a VM) via the scope and the unique identifier of the principal
func (mc *MockACSEngineClient) ListRoleAssignmentsForPrincipal(scope string, principalID string) (authorization.RoleAssignmentListResult, error) {
roleAssignments := []authorization.RoleAssignment{}
return authorization.RoleAssignmentListResult{
Value: &roleAssignments}, nil
}
27 changes: 26 additions & 1 deletion pkg/operations/deletevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import (
log "github.com/sirupsen/logrus"
)

const (
// AADRoleResourceGroupScopeTemplate is a template for a roleDefinition scope
AADRoleResourceGroupScopeTemplate = "/subscriptions/%s/resourceGroups/%s"
)

// CleanDeleteVirtualMachine deletes a VM and any associated OS disk
func CleanDeleteVirtualMachine(az armhelpers.ACSEngineClient, logger *log.Entry, resourceGroup, name string) error {
func CleanDeleteVirtualMachine(az armhelpers.ACSEngineClient, logger *log.Entry, subscriptionID, resourceGroup, name string) error {
logger.Infof("fetching VM: %s/%s", resourceGroup, name)
vm, err := az.GetVirtualMachine(resourceGroup, name)
if err != nil {
Expand Down Expand Up @@ -86,5 +91,25 @@ func CleanDeleteVirtualMachine(az armhelpers.ACSEngineClient, logger *log.Entry,
}
}

// Role assignments are not deleted if the VM is destroyed, so we must cleanup ourselves!
// The role assignments should only be relevant if managed identities are used,
// but always cleaning them up is easier than adding rule based logic here and there.
scope := fmt.Sprintf(AADRoleResourceGroupScopeTemplate, subscriptionID, resourceGroup)
logger.Infof("fetching roleAssignments: %s with principal %s", scope, *vm.Identity.PrincipalID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran an upgrade test against this branch and got the following nil pointer panic:

time="2018-05-15T19:03:56Z" level=info msg="deleting managed disk: kubernetes-koreasouth-74251/k8s-master-15172440-0_OsDisk_1_dc7dab13d2354a4f875595aa83b99d3c"
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x138a30f]

goroutine 1 [running]:
github.com/Azure/acs-engine/pkg/operations.CleanDeleteVirtualMachine(0x186eca0, 0xc4205f4900, 0xc42031c780, 0xc4208ade60, 0x24, 0x7fff98924da7, 0x1b, 0xc42034bc60, 0x15, 0x148c120, ...)
	/go/src/github.com/Azure/acs-engine/pkg/operations/deletevm.go:98 +0xa2f
github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade.(*UpgradeMasterNode).DeleteNode(0xc4201622a0, 0xc42086eb20, 0xc42034bc00, 0x15, 0x0)
	/go/src/github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade/upgrademasternode.go:39 +0x7f
github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade.(*Upgrader).upgradeMasterNodes(0xc4208be210, 0x148c101, 0xc4208be210)
	/go/src/github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade/upgrader.go:137 +0xa98
github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade.(*Upgrader).RunUpgrade(0xc4208be210, 0x0, 0x0)
	/go/src/github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade/upgrader.go:54 +0x2f
github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade.(*UpgradeCluster).UpgradeCluster(0xc420a13c60, 0x804f1c7d6b541430, 0x6afe76994af22385, 0xc420b40800, 0x26be, 0x7fff98924da7, 0x1b, 0xc42073b6e0, 0xc420ac9940, 0x8, ...)
	/go/src/github.com/Azure/acs-engine/pkg/operations/kubernetesupgrade/upgradecluster.go:115 +0x660
github.com/Azure/acs-engine/cmd.(*upgradeCmd).run(0xc4200914a0, 0xc42016a900, 0xc420487d00, 0x0, 0x10, 0x0, 0x0)
	/go/src/github.com/Azure/acs-engine/cmd/upgrade.go:225 +0x455
github.com/Azure/acs-engine/cmd.newUpgradeCmd.func1(0xc42016a900, 0xc420487d00, 0x0, 0x10, 0x0, 0x0)
	/go/src/github.com/Azure/acs-engine/cmd/upgrade.go:58 +0x52
github.com/Azure/acs-engine/vendor/github.com/spf13/cobra.(*Command).execute(0xc42016a900, 0xc420487c00, 0x10, 0x10, 0xc42016a900, 0xc420487c00)
	/go/src/github.com/Azure/acs-engine/vendor/github.com/spf13/cobra/command.go:647 +0x3e4
github.com/Azure/acs-engine/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0xc42021b8c0, 0xc42016ab40, 0xc42016a900, 0xc42016a480)
	/go/src/github.com/Azure/acs-engine/vendor/github.com/spf13/cobra/command.go:726 +0x2d4
github.com/Azure/acs-engine/vendor/github.com/spf13/cobra.(*Command).Execute(0xc42021b8c0, 0xc42000e018, 0x13a4b83)
	/go/src/github.com/Azure/acs-engine/vendor/github.com/spf13/cobra/command.go:685 +0x2b
main.main()
	/go/src/github.com/Azure/acs-engine/main.go:12 +0x74

vmRoleAssignments, listRoleAssingmentsError := az.ListRoleAssignmentsForPrincipal(scope, *vm.Identity.PrincipalID)
if listRoleAssingmentsError != nil {
logger.Errorf("failed to list role assignments: %s/%s: %s", scope, *vm.Identity.PrincipalID, listRoleAssingmentsError.Error())
return listRoleAssingmentsError
}

for _, roleAssignment := range *vmRoleAssignments.Value {
logger.Infof("deleting role assignment: %s", *roleAssignment.ID)
_, deleteRoleAssignmentErr := az.DeleteRoleAssignmentByID(*roleAssignment.ID)
if deleteRoleAssignmentErr != nil {
logger.Errorf("failed to delete role assignment: %s: %s", *roleAssignment.ID, deleteRoleAssignmentErr.Error())
return deleteRoleAssignmentErr
}
}

return nil
}
3 changes: 2 additions & 1 deletion pkg/operations/kubernetesupgrade/upgradeagentnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type UpgradeAgentNode struct {
TemplateMap map[string]interface{}
ParametersMap map[string]interface{}
UpgradeContainerService *api.ContainerService
SubscriptionID string
ResourceGroup string
Client armhelpers.ACSEngineClient
kubeConfig string
Expand Down Expand Up @@ -62,7 +63,7 @@ func (kan *UpgradeAgentNode) DeleteNode(vmName *string, drain bool) error {
}
}
// Delete VM in ARM
if err := operations.CleanDeleteVirtualMachine(kan.Client, kan.logger, kan.ResourceGroup, *vmName); err != nil {
if err := operations.CleanDeleteVirtualMachine(kan.Client, kan.logger, kan.SubscriptionID, kan.ResourceGroup, *vmName); err != nil {
return err
}
// Delete VM in api server
Expand Down
10 changes: 6 additions & 4 deletions pkg/operations/kubernetesupgrade/upgradecluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import (
// ClusterTopology contains resources of the cluster the upgrade operation
// is targeting
type ClusterTopology struct {
DataModel *api.ContainerService
Location string
ResourceGroup string
NameSuffix string
DataModel *api.ContainerService
SubscriptionID string
Location string
ResourceGroup string
NameSuffix string

AgentPoolsToUpgrade map[string]bool
AgentPools map[string]*AgentPoolTopology
Expand Down Expand Up @@ -59,6 +60,7 @@ const MasterPoolName = "master"
func (uc *UpgradeCluster) UpgradeCluster(subscriptionID uuid.UUID, kubeConfig, resourceGroup string,
cs *api.ContainerService, nameSuffix string, agentPoolsToUpgrade []string, acsengineVersion string) error {
uc.ClusterTopology = ClusterTopology{}
uc.SubscriptionID = subscriptionID.String()
uc.ResourceGroup = resourceGroup
uc.DataModel = cs
uc.NameSuffix = nameSuffix
Expand Down
3 changes: 2 additions & 1 deletion pkg/operations/kubernetesupgrade/upgrademasternode.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type UpgradeMasterNode struct {
TemplateMap map[string]interface{}
ParametersMap map[string]interface{}
UpgradeContainerService *api.ContainerService
SubscriptionID string
ResourceGroup string
Client armhelpers.ACSEngineClient
kubeConfig string
Expand All @@ -35,7 +36,7 @@ type UpgradeMasterNode struct {
// the node.
// The 'drain' flag is not used for deleting master nodes.
func (kmn *UpgradeMasterNode) DeleteNode(vmName *string, drain bool) error {
return operations.CleanDeleteVirtualMachine(kmn.Client, kmn.logger, kmn.ResourceGroup, *vmName)
return operations.CleanDeleteVirtualMachine(kmn.Client, kmn.logger, kmn.SubscriptionID, kmn.ResourceGroup, *vmName)
}

// CreateNode creates a new master/agent node with the targeted version of Kubernetes
Expand Down
2 changes: 2 additions & 0 deletions pkg/operations/kubernetesupgrade/upgrader.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func (ku *Upgrader) upgradeMasterNodes() error {
upgradeMasterNode.ParametersMap = parametersMap
upgradeMasterNode.UpgradeContainerService = ku.ClusterTopology.DataModel
upgradeMasterNode.ResourceGroup = ku.ClusterTopology.ResourceGroup
upgradeMasterNode.SubscriptionID = ku.ClusterTopology.SubscriptionID
upgradeMasterNode.Client = ku.Client
upgradeMasterNode.kubeConfig = ku.kubeConfig
if ku.stepTimeout == nil {
Expand Down Expand Up @@ -242,6 +243,7 @@ func (ku *Upgrader) upgradeAgentPools() error {
upgradeAgentNode.TemplateMap = templateMap
upgradeAgentNode.ParametersMap = parametersMap
upgradeAgentNode.UpgradeContainerService = ku.ClusterTopology.DataModel
upgradeAgentNode.SubscriptionID = ku.ClusterTopology.SubscriptionID
upgradeAgentNode.ResourceGroup = ku.ClusterTopology.ResourceGroup
upgradeAgentNode.Client = ku.Client
upgradeAgentNode.kubeConfig = ku.kubeConfig
Expand Down
4 changes: 2 additions & 2 deletions pkg/operations/scaledownagentpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ type VMScalingErrorDetails struct {

// ScaleDownVMs removes the vms in the provided list. Returns a list with details on each failure.
// all items in the list will always be of type *VMScalingErrorDetails
func ScaleDownVMs(az armhelpers.ACSEngineClient, logger *log.Entry, resourceGroup string, vmNames ...string) *list.List {
func ScaleDownVMs(az armhelpers.ACSEngineClient, logger *log.Entry, subscriptionID string, resourceGroup string, vmNames ...string) *list.List {
numVmsToDelete := len(vmNames)
errChan := make(chan *VMScalingErrorDetails, numVmsToDelete)
defer close(errChan)
for _, vmName := range vmNames {
go func(vmName string) {
err := CleanDeleteVirtualMachine(az, logger, resourceGroup, vmName)
err := CleanDeleteVirtualMachine(az, logger, subscriptionID, resourceGroup, vmName)
if err != nil {
errChan <- &VMScalingErrorDetails{Name: vmName, Error: err}
return
Expand Down
4 changes: 2 additions & 2 deletions pkg/operations/scaledownagentpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var _ = Describe("Scale down vms operation tests", func() {
It("Should return error messages for failing vms", func() {
mockClient := armhelpers.MockACSEngineClient{}
mockClient.FailGetVirtualMachine = true
errs := ScaleDownVMs(&mockClient, log.NewEntry(log.New()), "rg", "vm1", "vm2", "vm3", "vm5")
errs := ScaleDownVMs(&mockClient, log.NewEntry(log.New()), "sid", "rg", "vm1", "vm2", "vm3", "vm5")
Expect(errs.Len()).To(Equal(4))
for e := errs.Front(); e != nil; e = e.Next() {
output := e.Value.(*VMScalingErrorDetails)
Expand All @@ -28,7 +28,7 @@ var _ = Describe("Scale down vms operation tests", func() {
})
It("Should return nil for errors if all deletes successful", func() {
mockClient := armhelpers.MockACSEngineClient{}
errs := ScaleDownVMs(&mockClient, log.NewEntry(log.New()), "rg", "k8s-agent-F8EADCCF-0", "k8s-agent-F8EADCCF-3", "k8s-agent-F8EADCCF-2", "k8s-agent-F8EADCCF-4")
errs := ScaleDownVMs(&mockClient, log.NewEntry(log.New()), "sid", "rg", "k8s-agent-F8EADCCF-0", "k8s-agent-F8EADCCF-3", "k8s-agent-F8EADCCF-2", "k8s-agent-F8EADCCF-4")
Expect(errs).To(BeNil())
})
})