diff --git a/aws/disk.go b/aws/disk.go index 77da98e..51f0d27 100644 --- a/aws/disk.go +++ b/aws/disk.go @@ -9,16 +9,25 @@ import ( var _ unused.Disk = &Disk{} +// Disk holds information about an AWS EC2 volume. type Disk struct { types.Volume provider *Provider meta unused.Meta } +// ID returns the volume ID of this AWS EC2 volume. func (d *Disk) ID() string { return *d.Volume.VolumeId } +// Provider returns a reference to the provider used to instantiate +// this disk. func (d *Disk) Provider() unused.Provider { return d.provider } +// Name returns the name of this AWS EC2 volume. +// +// AWS EC2 volumes do not have a name property, instead they store the +// name in tags. This method will try to find the Name or +// CSIVolumeName, otherwise it will return empty. func (d *Disk) Name() string { for _, t := range d.Volume.Tags { if *t.Key == "Name" || *t.Key == "CSIVolumeName" { @@ -28,8 +37,12 @@ func (d *Disk) Name() string { return "" } +// CreatedAt returns the time when the AWS EC2 volume was created. func (d *Disk) CreatedAt() time.Time { return *d.Volume.CreateTime } +// Meta returns the disk metadata. func (d *Disk) Meta() unused.Meta { return d.meta } +// LastUsedAt returns a zero [time.Time] value, as AWS does not +// provide this information. func (d *Disk) LastUsedAt() time.Time { return time.Time{} } diff --git a/aws/provider.go b/aws/provider.go index a6f8cce..e1649ec 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -12,15 +12,23 @@ import ( var _ unused.Provider = &Provider{} +// Provider implements [unused.Provider] for AWS. type Provider struct { client *ec2.Client meta unused.Meta } +// Name returns AWS. func (p *Provider) Name() string { return "AWS" } +// Meta returns the provider metadata. func (p *Provider) Meta() unused.Meta { return p.meta } +// NewProvider creates a new AWS [unused.Provider]. +// +// A valid EC2 client must be supplied in order to list the unused +// resources. The metadata passed will be used to identify the +// provider. func NewProvider(client *ec2.Client, meta unused.Meta) (*Provider, error) { if meta == nil { meta = make(unused.Meta) @@ -32,6 +40,8 @@ func NewProvider(client *ec2.Client, meta unused.Meta) (*Provider, error) { }, nil } +// ListUnusedDisks returns all the AWS EC2 volumes that are available, +// ie. not used by any other resource. func (p *Provider) ListUnusedDisks(ctx context.Context) (unused.Disks, error) { params := &ec2.DescribeVolumesInput{ Filters: []types.Filter{ @@ -72,6 +82,7 @@ func (p *Provider) ListUnusedDisks(ctx context.Context) (unused.Disks, error) { return upds, nil } +// Delete deletes the given disk from AWS. func (p *Provider) Delete(ctx context.Context, disk unused.Disk) error { _, err := p.client.DeleteVolume(ctx, &ec2.DeleteVolumeInput{ VolumeId: aws.String(disk.ID()), diff --git a/azure/disk.go b/azure/disk.go index 44ed5f1..2aa53c5 100644 --- a/azure/disk.go +++ b/azure/disk.go @@ -9,20 +9,30 @@ import ( var _ unused.Disk = &Disk{} +// Disk holds information about an Azure compute disk. type Disk struct { compute.Disk provider *Provider meta unused.Meta } +// ID returns the Azure compute disk ID. func (d *Disk) ID() string { return *d.Disk.ID } +// Provider returns a reference to the provider used to instantiate +// this disk. func (d *Disk) Provider() unused.Provider { return d.provider } +// Name returns the name of this Azure compute disk. func (d *Disk) Name() string { return *d.Disk.Name } +// CreatedAt returns the time when this Azure compute disk was +// created. func (d *Disk) CreatedAt() time.Time { return d.Disk.TimeCreated.ToTime() } +// Meta returns the disk metadata. func (d *Disk) Meta() unused.Meta { return d.meta } +// LastUsedAt returns a zero [time.Time] value, as Azure does not +// provide this information. func (d *Disk) LastUsedAt() time.Time { return time.Time{} } diff --git a/azure/provider.go b/azure/provider.go index 7736444..f3d85b0 100644 --- a/azure/provider.go +++ b/azure/provider.go @@ -13,15 +13,22 @@ var _ unused.Provider = &Provider{} const ResourceGroupMetaKey = "resource-group" +// Providcer implements [unused.Provider] for Azure. type Provider struct { client compute.DisksClient meta unused.Meta } +// Name returns Azure. func (p *Provider) Name() string { return "Azure" } +// Meta returns the provider metadata. func (p *Provider) Meta() unused.Meta { return p.meta } +// NewProvider creates a new Azure [unused.Provider]. +// +// A valid Azure compute disks client must be supplied in order to +// list the unused resources. func NewProvider(client compute.DisksClient, meta unused.Meta) (*Provider, error) { if meta == nil { meta = make(unused.Meta) @@ -30,6 +37,8 @@ func NewProvider(client compute.DisksClient, meta unused.Meta) (*Provider, error return &Provider{client: client, meta: meta}, nil } +// ListUnusedDisks returns all the Azure compute disks that are not +// managed by other resources. func (p *Provider) ListUnusedDisks(ctx context.Context) (unused.Disks, error) { var upds unused.Disks @@ -69,6 +78,7 @@ func (p *Provider) ListUnusedDisks(ctx context.Context) (unused.Disks, error) { return upds, nil } +// Delete deletes the given disk from Azure. func (p *Provider) Delete(ctx context.Context, disk unused.Disk) error { _, err := p.client.Delete(ctx, disk.Meta()[ResourceGroupMetaKey], disk.Name()) if err != nil { diff --git a/gcp/disk.go b/gcp/disk.go index 8c3694f..6be8bf8 100644 --- a/gcp/disk.go +++ b/gcp/disk.go @@ -11,18 +11,24 @@ import ( // ensure we are properly defining the interface var _ unused.Disk = &Disk{} +// Disk holds information about a GCP compute disk. type Disk struct { *compute.Disk provider *Provider meta unused.Meta } -func (d *Disk) ID() string { return fmt.Sprintf("gcp-disk-%d", d.Disk.Id) } +// ID returns the GCP compute disk ID, prefixed by gcp-disk. +func (d *Disk) ID() string { return fmt.Sprintf("gcp-disk-%d", d.Disk.Id) } // TODO remove prefix +// Provider returns a reference to the provider used to instantiate +// this disk. func (d *Disk) Provider() unused.Provider { return d.provider } +// Name returns the name of the GCP compute disk. func (d *Disk) Name() string { return d.Disk.Name } +// CreatedAt returns the time when the GCP compute disk was created. func (d *Disk) CreatedAt() time.Time { // it's safe to assume GCP will send a valid timestamp c, _ := time.Parse(time.RFC3339, d.Disk.CreationTimestamp) @@ -30,8 +36,11 @@ func (d *Disk) CreatedAt() time.Time { return c } +// Meta returns the disk metadata. func (d *Disk) Meta() unused.Meta { return d.meta } +// LastUsedAt returns the time when the GCP compute disk was last +// detached. func (d *Disk) LastUsedAt() time.Time { // it's safe to assume GCP will send a valid timestamp t, _ := time.Parse(time.RFC3339, d.Disk.LastDetachTimestamp) diff --git a/gcp/provider.go b/gcp/provider.go index 706a66f..be69eea 100644 --- a/gcp/provider.go +++ b/gcp/provider.go @@ -11,20 +11,30 @@ import ( "google.golang.org/api/compute/v1" ) +// ErrMissingProject is the error used when no project ID is provided +// when trying to create a provider. var ErrMissingProject = errors.New("missing project id") var _ unused.Provider = &Provider{} +// Provider implements [unused.Provider] for GCP. type Provider struct { project string svc *compute.Service meta unused.Meta } +// Name returns GCP. func (p *Provider) Name() string { return "GCP" } +// Meta returns the provider metadata. func (p *Provider) Meta() unused.Meta { return p.meta } +// NewProvider creates a new GCP [unused.Provider]. +// +// A valid GCP compute service must be supplied in order to listed the +// unused resources. It also requires a valid project ID which should +// be the project where the disks were created. func NewProvider(svc *compute.Service, project string, meta unused.Meta) (*Provider, error) { if project == "" { return nil, ErrMissingProject @@ -41,6 +51,8 @@ func NewProvider(svc *compute.Service, project string, meta unused.Meta) (*Provi }, nil } +// ListUnusedDisks returns all the GCP compute disks that aren't +// associated to any users, meaning that are not being in use. func (p *Provider) ListUnusedDisks(ctx context.Context) (unused.Disks, error) { var disks unused.Disks @@ -85,6 +97,7 @@ func diskMetadata(d *compute.Disk) (unused.Meta, error) { return m, nil } +// Delete deletes the given disk from GCP. func (p *Provider) Delete(ctx context.Context, disk unused.Disk) error { _, err := p.svc.Disks.Delete(p.project, disk.Meta()["zone"], disk.Name()).Do() if err != nil {