Skip to content

Commit

Permalink
Add azure app security groups resource type.
Browse files Browse the repository at this point in the history
- TODO: Unpend acceptance test, create app sg, figure out how to say No
to the resource group prompt but Yes to the app sg prompt. Maybe only
test it when deleting by type?
- TODO: Wire up the app security groups collector to each respective
resource group since it has a dependency on the rg name.

For #84
  • Loading branch information
Genevieve Lesperance committed Sep 15, 2019
1 parent 990caff commit 563acce
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 11 deletions.
19 changes: 19 additions & 0 deletions acceptance/azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,24 @@ var _ = Describe("Azure", func() {
Expect(stdout.String()).To(ContainSubstring("[Resource Group: %s] Deleting...", filter))
Expect(stdout.String()).To(ContainSubstring("[Resource Group: %s] Deleted!", filter))
})

Context("when the user wants to delete subresources of the resource group", func() {
BeforeEach(func() {
filter = "leftovers-acc-sub-delete"
acc.CreateResourceGroup(filter)
// acc.CreateAppSecurityGroup(filter)
})

PIt("prompts them for subresources after they say no to the resource group", func() {
err := deleter.Delete(filter)
Expect(err).NotTo(HaveOccurred())

Expect(stdout.String()).NotTo(ContainSubstring("[Resource Group: %s] Deleting...", filter))
Expect(stdout.String()).NotTo(ContainSubstring("[Resource Group: %s] Deleted!", filter))

Expect(stdout.String()).To(ContainSubstring("[Application Security Group: %s] Deleting...", filter))
Expect(stdout.String()).To(ContainSubstring("[Application Security Group: %s] Deleted!", filter))
})
})
})
})
36 changes: 36 additions & 0 deletions azure/app_security_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package azure

import "fmt"

type AppSecurityGroup struct {
client appSecurityGroupsClient
name string
rgName string
}

// AppSecurityGroup represents an Azure application security group.
func NewAppSecurityGroup(client appSecurityGroupsClient, rgName, name string) AppSecurityGroup {
return AppSecurityGroup{
client: client,
rgName: rgName,
name: name,
}
}

// Delete deletes an Azure application security group.
func (g AppSecurityGroup) Delete() error {
err := g.client.DeleteAppSecurityGroup(g.rgName, g.name)
if err != nil {
return fmt.Errorf("Delete: %s", err)
}

return nil
}

func (g AppSecurityGroup) Name() string {
return g.name
}

func (g AppSecurityGroup) Type() string {
return "Application Security Group"
}
56 changes: 56 additions & 0 deletions azure/app_security_group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package azure_test

import (
"errors"

"github.com/genevieve/leftovers/azure"
"github.com/genevieve/leftovers/azure/fakes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("AppSecurityGroup", func() {
var (
client *fakes.AppSecurityGroupsClient
name string
rgName string

group azure.AppSecurityGroup
)

BeforeEach(func() {
client = &fakes.AppSecurityGroupsClient{}
name = "banana-group"
rgName = "major-banana-group"

group = azure.NewAppSecurityGroup(client, rgName, name)
})

Describe("Delete", func() {
It("deletes resource groups", func() {
err := group.Delete()
Expect(err).NotTo(HaveOccurred())

Expect(client.DeleteAppSecurityGroupCall.CallCount).To(Equal(1))
Expect(client.DeleteAppSecurityGroupCall.Receives.Name).To(Equal(name))
Expect(client.DeleteAppSecurityGroupCall.Receives.ResourceGroupName).To(Equal(rgName))
})

Context("when client fails to delete the app security group", func() {
BeforeEach(func() {
client.DeleteAppSecurityGroupCall.Returns.Error = errors.New("some error")
})

It("logs the error", func() {
err := group.Delete()
Expect(err).To(MatchError("Delete: some error"))
})
})
})

Describe("Type", func() {
It("returns the type", func() {
Expect(group.Type()).To(Equal("Application Security Group"))
})
})
})
56 changes: 56 additions & 0 deletions azure/app_security_groups.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package azure

import (
"fmt"
"strings"

"github.com/genevieve/leftovers/common"
)

type appSecurityGroupsClient interface {
ListAppSecurityGroups(rgName string) ([]string, error)
DeleteAppSecurityGroup(rgName string, name string) error
}

type AppSecurityGroups struct {
client appSecurityGroupsClient
rgName string
logger logger
}

func NewAppSecurityGroups(client appSecurityGroupsClient, rgName string, logger logger) AppSecurityGroups {
return AppSecurityGroups{
client: client,
rgName: rgName,
logger: logger,
}
}

func (g AppSecurityGroups) List(filter string) ([]common.Deletable, error) {
groups, err := g.client.ListAppSecurityGroups(g.rgName)
if err != nil {
return nil, fmt.Errorf("Listing Application Security Groups: %s", err)
}

var resources []common.Deletable
for _, group := range groups {
r := NewAppSecurityGroup(g.client, g.rgName, group)

if !strings.Contains(r.Name(), filter) {
continue
}

proceed := g.logger.PromptWithDetails(r.Type(), r.Name())
if !proceed {
continue
}

resources = append(resources, r)
}

return resources, nil
}

func (g AppSecurityGroups) Type() string {
return "app-security-group"
}
86 changes: 86 additions & 0 deletions azure/app_security_groups_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package azure_test

import (
"errors"

"github.com/genevieve/leftovers/azure"
"github.com/genevieve/leftovers/azure/fakes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("AppSecurityGroups", func() {
var (
client *fakes.AppSecurityGroupsClient
logger *fakes.Logger
filter string
rgName string

groups azure.AppSecurityGroups
)

BeforeEach(func() {
client = &fakes.AppSecurityGroupsClient{}
logger = &fakes.Logger{}
filter = "banana"
rgName = "resource-group"

groups = azure.NewAppSecurityGroups(client, rgName, logger)
})

Describe("List", func() {
BeforeEach(func() {
logger.PromptWithDetailsCall.Returns.Proceed = true
client.ListAppSecurityGroupsCall.Returns.List = []string{"banana-group", "kiwi-group"}
})

It("returns a list of resources to delete", func() {
items, err := groups.List(filter)
Expect(err).NotTo(HaveOccurred())

Expect(client.ListAppSecurityGroupsCall.CallCount).To(Equal(1))
Expect(client.ListAppSecurityGroupsCall.Receives.ResourceGroupName).To(Equal(rgName))

Expect(logger.PromptWithDetailsCall.CallCount).To(Equal(1))

Expect(items).To(HaveLen(1))
})

Context("when client fails to list the resource", func() {
BeforeEach(func() {
client.ListAppSecurityGroupsCall.Returns.Error = errors.New("some error")
})

It("returns the error", func() {
_, err := groups.List(filter)
Expect(err).To(MatchError("Listing Application Security Groups: some error"))
})
})

Context("when the user responds no to the prompt", func() {
BeforeEach(func() {
logger.PromptWithDetailsCall.Returns.Proceed = false
})

It("does not return it in the list", func() {
items, err := groups.List(filter)
Expect(err).NotTo(HaveOccurred())

Expect(logger.PromptWithDetailsCall.Receives.Type).To(Equal("Application Security Group"))
Expect(logger.PromptWithDetailsCall.Receives.Name).To(Equal("banana-group"))

Expect(items).To(HaveLen(0))
})
})

Context("when the resource group name does not contain the filter", func() {
It("does not return it in the list", func() {
items, err := groups.List("grape")
Expect(err).NotTo(HaveOccurred())

Expect(logger.PromptWithDetailsCall.CallCount).To(Equal(0))
Expect(items).To(HaveLen(0))
})
})
})
})
48 changes: 41 additions & 7 deletions azure/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,32 @@ import (
"context"
"fmt"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-04-01/network"
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources"
"github.com/Azure/go-autorest/autorest"
)

type client struct {
groupsClient resources.GroupsClient
rgClient resources.GroupsClient
sgClient network.ApplicationSecurityGroupsClient
autorestClient autorest.Client
}

func NewClient(groupsClient resources.GroupsClient) client {
func NewClient(rg resources.GroupsClient, sg network.ApplicationSecurityGroupsClient) client {
return client{
groupsClient: groupsClient,
autorestClient: groupsClient.Client,
rgClient: rg,
sgClient: sg,
autorestClient: rg.Client,
}
}

func (c client) ListGroups() ([]string, error) {
ctx := context.Background()
groups := []string{}

for list, err := c.groupsClient.ListComplete(ctx, "", nil); list.NotDone(); err = list.Next() {
for list, err := c.rgClient.ListComplete(ctx, "", nil); list.NotDone(); err = list.Next() {
if err != nil {
return []string{}, fmt.Errorf("List Groups: %s", err)
return []string{}, fmt.Errorf("List Complete Resource Groups: %s", err)
}

groups = append(groups, *list.Value().Name)
Expand All @@ -38,7 +41,38 @@ func (c client) ListGroups() ([]string, error) {
func (c client) DeleteGroup(name string) error {
ctx := context.Background()

future, err := c.groupsClient.Delete(ctx, name)
future, err := c.rgClient.Delete(ctx, name)
if err != nil {
return err
}

err = future.WaitForCompletionRef(ctx, c.autorestClient)
if err != nil {
return fmt.Errorf("Waiting for completion: %s", err)
}

return nil
}

func (c client) ListAppSecurityGroups(rgName string) ([]string, error) {
ctx := context.Background()
groups := []string{}

for list, err := c.sgClient.ListComplete(ctx, rgName); list.NotDone(); err = list.Next() {
if err != nil {
return groups, fmt.Errorf("List Complete App Security Groups: %s", err)
}

groups = append(groups, *list.Value().Name)
}

return groups, nil
}

func (c client) DeleteAppSecurityGroup(rgName, name string) error {
ctx := context.Background()

future, err := c.sgClient.Delete(ctx, rgName, name)
if err != nil {
return err
}
Expand Down
39 changes: 39 additions & 0 deletions azure/fakes/app_security_groups_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package fakes

type AppSecurityGroupsClient struct {
ListAppSecurityGroupsCall struct {
CallCount int
Receives struct {
ResourceGroupName string
}
Returns struct {
List []string
Error error
}
}

DeleteAppSecurityGroupCall struct {
CallCount int
Receives struct {
ResourceGroupName string
Name string
}
Returns struct {
Error error
}
}
}

func (c *AppSecurityGroupsClient) ListAppSecurityGroups(rgName string) ([]string, error) {
c.ListAppSecurityGroupsCall.CallCount++
c.ListAppSecurityGroupsCall.Receives.ResourceGroupName = rgName
return c.ListAppSecurityGroupsCall.Returns.List, c.ListAppSecurityGroupsCall.Returns.Error
}

func (c *AppSecurityGroupsClient) DeleteAppSecurityGroup(rgName, name string) error {
c.DeleteAppSecurityGroupCall.CallCount++
c.DeleteAppSecurityGroupCall.Receives.ResourceGroupName = rgName
c.DeleteAppSecurityGroupCall.Receives.Name = name

return c.DeleteAppSecurityGroupCall.Returns.Error
}
Loading

0 comments on commit 563acce

Please sign in to comment.