Skip to content

Commit

Permalink
feat: add gcp_compute_instance_group_manager table
Browse files Browse the repository at this point in the history
  • Loading branch information
pdecat committed Oct 19, 2024
1 parent 5bbdb77 commit d2851b6
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 7 deletions.
4 changes: 2 additions & 2 deletions docs/tables/gcp_compute_instance_group.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Allows users to query Google Cloud Compute Engine Instance Groups,

# Table: gcp_compute_instance_group - Query Google Cloud Compute Engine Instance Groups using SQL

Google Cloud Compute Engine Instance Groups are collections of virtual machine (VM) instances that you can manage as a single entity. These groups are ideal for applications that require a lot of computing power and need to scale rapidly to meet demand. They offer a range of features including autoscaling, load balancing, and rolling updates.
Google Cloud Compute Engine Instance Groups are collections of virtual machine (VM) instances that you can manage as a single entity. When they are managed by Instance Group Managers, these groups are called [Managed Instance Groups (MIG)](https://cloud.google.com/compute/docs/instance-groups#managed_instance_groups), and are ideal for highly available applications that require a lot of computing power and need to scale rapidly to meet demand. They offer a range of features including autoscaling, autohealing, regional (multiple zone) deployment, and automatic updating. Otherwise, these groups are called [Unmanaged Instance Groups](https://cloud.google.com/compute/docs/instance-groups#unmanaged_instance_groups), and can contain heterogeneous instances that you can arbitrarily add and remove from them, but do not offer autoscaling, autohealing, rolling update support, multi-zone support, or the use of instance templates and are not a good fit for deploying highly available and scalable workloads, they can just be used for load balancing.

## Table Usage Guide

Expand Down Expand Up @@ -125,4 +125,4 @@ from
where
g.network = n.self_link
and g.subnetwork = s.self_link;
```
```
71 changes: 71 additions & 0 deletions docs/tables/gcp_compute_instance_group_manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: "Steampipe Table: gcp_compute_instance_group_manager - Query Google Cloud Compute Engine Instance Group Managers using SQL"
description: "Allows users to query Google Cloud Compute Engine Instance Group Managers, providing insights into the configuration, status, and properties of these group managers."
---

# Table: gcp_compute_instance_group_manager - Query Google Cloud Compute Engine Instance Group Managers using SQL

Google Cloud Compute Engine Instance Group Managers manage [Managed Instance Groups (MIG)](https://cloud.google.com/compute/docs/instance-groups#managed_instance_groups). are ideal for highly available applications that require a lot of computing power and need to scale rapidly to meet demand. They offer a range of features including autoscaling, autohealing, regional (multiple zone) deployment, and automatic updating.

## Table Usage Guide

The `gcp_compute_instance_group_manager` table provides insights into instance group managers within Google Cloud Compute Engine. As a system administrator, you can explore group-specific details through this table, including configuration, associated instances, and autoscaling policies. Utilize it to monitor the status of your instance groups, manage load balancing, and plan for capacity adjustments.

## Examples

### Basic Info
Discover the segments of your Google Cloud Platform (GCP) that contain instance group managers, gaining insights into aspects like size and location. This can help in project management and resource allocation within the GCP infrastructure.

```sql+postgres
select
name,
description,
self_link,
instance_group,
location,
akas,
project
from
gcp_compute_instance_group_manager;
```

```sql+sqlite
select
name,
description,
self_link,
instance_group,
location,
akas,
project
from
gcp_compute_instance_group_manager;
```


### Get instance group details of each instance group manager
Get the size of the instance groups managed by instance group managers.

```sql+postgres
select
m.name,
g.name as group_name,
g.size as group_size
from
gcp_compute_instance_group_manager as m,
gcp_compute_instance_group as g
where
m.instance_group ->> 'name' = g.name;
```

```sql+sqlite
select
m.name,
g.name as group_name,
g.size as group_size
from
gcp_compute_instance_group_manager as m,
gcp_compute_instance_group as g
where
m.instance_group -> 'name' = g.name;
```
1 change: 1 addition & 0 deletions gcp/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"gcp_compute_image": tableGcpComputeImage(ctx),
"gcp_compute_instance": tableGcpComputeInstance(ctx),
"gcp_compute_instance_group": tableGcpComputeInstanceGroup(ctx),
"gcp_compute_instance_group_manager": tableGcpComputeInstanceGroupManager(ctx),
"gcp_compute_instance_metric_cpu_utilization": tableGcpComputeInstanceMetricCpuUtilization(ctx),
"gcp_compute_instance_metric_cpu_utilization_daily": tableGcpComputeInstanceMetricCpuUtilizationDaily(ctx),
"gcp_compute_instance_metric_cpu_utilization_hourly": tableGcpComputeInstanceMetricCpuUtilizationHourly(ctx),
Expand Down
7 changes: 2 additions & 5 deletions gcp/table_gcp_compute_instance_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func tableGcpComputeInstanceGroup(ctx context.Context) *plugin.Table {
},
{
Name: "fingerprint",
Description: "The fingerprint of the named ports.",
Description: "The fingerprint of the instance group.",
Type: proto.ColumnType_STRING,
},
{
Expand Down Expand Up @@ -89,7 +89,7 @@ func tableGcpComputeInstanceGroup(ctx context.Context) *plugin.Table {
},
{
Name: "zone",
Description: "URL of the zone where the instance group resides.",
Description: "The URL of the zone where the instance group resides.",
Type: proto.ColumnType_STRING,
},
{
Expand Down Expand Up @@ -148,7 +148,6 @@ func tableGcpComputeInstanceGroup(ctx context.Context) *plugin.Table {
//// LIST FUNCTIONS

func listComputeInstanceGroup(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {

// Max limit is set as per documentation
pageSize := types.Int64(500)
limit := d.QueryContext.Limit
Expand All @@ -159,7 +158,6 @@ func listComputeInstanceGroup(ctx context.Context, d *plugin.QueryData, h *plugi
}

// Get project details

projectId, err := getProject(ctx, d, h)
if err != nil {
return nil, err
Expand Down Expand Up @@ -202,7 +200,6 @@ func getComputeInstanceGroup(ctx context.Context, d *plugin.QueryData, h *plugin
plugin.Logger(ctx).Trace("getComputeInstanceGroup")

// Get project details

projectId, err := getProject(ctx, d, h)
if err != nil {
return nil, err
Expand Down
261 changes: 261 additions & 0 deletions gcp/table_gcp_compute_instance_group_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package gcp

import (
"context"
"strings"

"github.com/turbot/go-kit/types"
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
"google.golang.org/api/compute/v1"
)

func tableGcpComputeInstanceGroupManager(ctx context.Context) *plugin.Table {
return &plugin.Table{
Name: "gcp_compute_instance_group_manager",
Description: "GCP Compute Instance Group Manager",
Get: &plugin.GetConfig{
KeyColumns: plugin.SingleColumn("name"),
Hydrate: getComputeInstanceGroupManager,
},
List: &plugin.ListConfig{
Hydrate: listComputeInstanceGroupManager,
},
Columns: []*plugin.Column{
{
Name: "name",
Description: "The name of the instance group manager.",
Type: proto.ColumnType_STRING,
},
{
Name: "id",
Description: "The unique identifier for this instance group manager. This identifier is defined by the server.",
Type: proto.ColumnType_INT,
},
{
Name: "creation_timestamp",
Description: "The timestamp when the instance group manager was created.",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "description",
Description: "An optional description of this resource. Provide this property when you create the resource.",
Type: proto.ColumnType_STRING,
},
{
Name: "fingerprint",
Description: "The fingerprint of the instance group manager.",
Type: proto.ColumnType_STRING,
},
{
Name: "kind",
Description: "The type of the resource. Always compute#instanceGroupManager for instance group managers.",
Type: proto.ColumnType_STRING,
},
{
Name: "region",
Description: "The URL of the region where the instance group manager resides. Only applicable for regional resources.",
Type: proto.ColumnType_STRING,
},
{
Name: "region_name",
Description: "The name of the region where the instance group manager resides. Only applicable for regional resources.",
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Region").Transform(lastPathElement),
},
{
Name: "self_link",
Description: "The server-defined fully-qualified URL for this resource.",
Type: proto.ColumnType_STRING,
},
{
Name: "zone",
Description: "The URL of the zone where the instance group manager resides.",
Type: proto.ColumnType_STRING,
},
{
Name: "named_ports",
Description: "The named ports configured for the Instance Groups complementary to this Instance Group Manager.",
Type: proto.ColumnType_JSON,
Transform: transform.FromGo().NullIfZero(),
},
{
Name: "instance_group",
Description: "The instance group that is managed by this group manager.",
Type: proto.ColumnType_JSON,
Transform: transform.FromValue(),
},

// zone_name is a simpler view of the zone, without the full path
{
Name: "zone_name",
Description: "The zone name in which the instance group manager resides.",
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Zone").Transform(lastPathElement),
},

// Steampipe standard columns
{
Name: "title",
Description: ColumnDescriptionTitle,
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Name"),
},
{
Name: "akas",
Description: ColumnDescriptionAkas,
Type: proto.ColumnType_JSON,
Transform: transform.From(instanceGroupManagerAka),
},

// GCP standard columns
{
Name: "location",
Description: ColumnDescriptionLocation,
Type: proto.ColumnType_STRING,
Transform: transform.FromP(instanceGroupManagerLocation, "Location"),
},
{
Name: "project",
Description: ColumnDescriptionProject,
Type: proto.ColumnType_STRING,
Transform: transform.FromP(instanceGroupManagerLocation, "Project"),
},
},
}
}

//// LIST FUNCTIONS

func listComputeInstanceGroupManager(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
// Max limit is set as per documentation
pageSize := types.Int64(500)
limit := d.QueryContext.Limit
if d.QueryContext.Limit != nil {
if *limit < *pageSize {
pageSize = limit
}
}

// Get project details

projectId, err := getProject(ctx, d, h)
if err != nil {
return nil, err
}
project := projectId.(string)

// Create Service Connection
service, err := ComputeService(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("gcp_compute_instance_group_manager.listComputeInstanceGroupManager", "service_creation_err", err)
return nil, err
}

resp := service.InstanceGroupManagers.AggregatedList(project).MaxResults(*pageSize)
if err := resp.Pages(ctx, func(page *compute.InstanceGroupManagerAggregatedList) error {
for _, item := range page.Items {
for _, group := range item.InstanceGroupManagers {
d.StreamListItem(ctx, group)

// Check if context has been cancelled or if the limit has been hit (if specified)
// if there is a limit, it will return the number of rows required to reach this limit
if d.RowsRemaining(ctx) == 0 {
page.NextPageToken = ""
return nil
}
}
}
return nil
}); err != nil {
plugin.Logger(ctx).Error("gcp_compute_instance_group_manager.listComputeInstanceGroupManager", "api_err", err)
return nil, err
}

return nil, nil
}

//// HYDRATE FUNCTIONS

func getComputeInstanceGroupManager(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
// Get project details
projectId, err := getProject(ctx, d, h)
if err != nil {
return nil, err
}
project := projectId.(string)

var group compute.InstanceGroupManager
name := d.EqualsQuals["name"].GetStringValue()

// Create Service Connection
service, err := ComputeService(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("gcp_compute_instance_group_manager.getComputeInstanceGroupManager", "service_creation_err", err)
return nil, err
}

resp := service.InstanceGroupManagers.AggregatedList(project).Filter("name=" + name)
if err := resp.Pages(ctx, func(page *compute.InstanceGroupManagerAggregatedList) error {
for _, item := range page.Items {
for _, i := range item.InstanceGroupManagers {
group = *i
}
}
return nil
},
); err != nil {
plugin.Logger(ctx).Error("gcp_compute_instance_group_manager.getComputeInstanceGroupManager", "api_err", err)
return nil, err
}

// If the specified resource is not present, API does not return any not found errors
if len(group.Name) < 1 {
return nil, nil
}

return &group, nil
}

//// TRANSFORM FUNCTIONS

func instanceGroupManagerAka(_ context.Context, d *transform.TransformData) (interface{}, error) {
i := d.HydrateItem.(*compute.InstanceGroupManager)

zoneName := getLastPathElement(types.SafeString(i.Zone))
regionName := getLastPathElement(types.SafeString(i.Region))
project := strings.Split(i.SelfLink, "/")[6]
instanceGroupManagerName := types.SafeString(i.Name)

var akas []string
if zoneName == "" {
akas = []string{"gcp://compute.googleapis.com/projects/" + project + "/regions/" + regionName + "/instanceGroupManagers/" + instanceGroupManagerName}
} else {
akas = []string{"gcp://compute.googleapis.com/projects/" + project + "/zones/" + zoneName + "/instanceGroupManagers/" + instanceGroupManagerName}
}

return akas, nil
}

func instanceGroupManagerLocation(_ context.Context, d *transform.TransformData) (interface{}, error) {
i := d.HydrateItem.(*compute.InstanceGroupManager)
param := d.Param.(string)

zoneName := getLastPathElement(types.SafeString(i.Zone))
regionName := getLastPathElement(types.SafeString(i.Region))
project := strings.Split(i.SelfLink, "/")[6]

locationData := map[string]string{
"Type": "ZONAL",
"Location": zoneName,
"Project": project,
}

if zoneName == "" {
locationData["Type"] = "REGIONAL"
locationData["Location"] = regionName
}

return locationData[param], nil
}

0 comments on commit d2851b6

Please sign in to comment.