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

Add table azure_lb_probe. Closes #189 #238

Merged
merged 16 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from 13 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
Empty file.
9 changes: 9 additions & 0 deletions azure-test/tests/azure_lb_probe/test-get-expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"id": "{{ output.resource_id.value }}",
"name": "{{ resourceName }}",
"resource_group": "{{ resourceName }}",
"subscription_id": "{{ output.subscription_id.value }}",
"type": "Microsoft.Network/loadBalancers/probes"
}
]
3 changes: 3 additions & 0 deletions azure-test/tests/azure_lb_probe/test-get-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
select name, id, type, resource_group, subscription_id
from azure.azure_lb_probe
where name = '{{ resourceName }}' and resource_group = '{{ resourceName }}';
6 changes: 6 additions & 0 deletions azure-test/tests/azure_lb_probe/test-list-expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"id": "{{ output.resource_id.value }}",
"name": "{{ resourceName }}"
}
]
3 changes: 3 additions & 0 deletions azure-test/tests/azure_lb_probe/test-list-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
select name, id
from azure.azure_lb_probe
where name = '{{ resourceName }}';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
null
3 changes: 3 additions & 0 deletions azure-test/tests/azure_lb_probe/test-not-found-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
select name, id
from azure.azure_lb_probe
where name = 'dummy-test-{{ resourceName }}' and resource_group = '{{ resourceName }}';
10 changes: 10 additions & 0 deletions azure-test/tests/azure_lb_probe/test-turbot-expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"akas": [
"{{ output.resource_aka.value }}",
"{{ output.resource_aka_lower.value }}"
],
"name": "{{ resourceName }}",
"title": "{{ resourceName }}"
}
]
3 changes: 3 additions & 0 deletions azure-test/tests/azure_lb_probe/test-turbot-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
select name, akas, title
from azure.azure_lb_probe
where name = '{{ resourceName }}' and resource_group = '{{ resourceName }}';
1 change: 1 addition & 0 deletions azure-test/tests/azure_lb_probe/variables.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
77 changes: 77 additions & 0 deletions azure-test/tests/azure_lb_probe/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
variable "resource_name" {
type = string
default = "turbot-test-azure-lb-probe-20210806"
description = "Name of the resource used throughout the test."
}

variable "azure_environment" {
type = string
default = "public"
description = "Azure environment used for the test."
}

variable "azure_subscription" {
type = string
default = "3510ae4d-530b-497d-8f30-53c0616fc6c1"
description = "Azure environment used for the test."
}

provider "azurerm" {
# Cannot be passed as a variable
version = "=1.36.0"
environment = var.azure_environment
subscription_id = var.azure_subscription
}

data "azuread_client_config" "current" {}

resource "azurerm_resource_group" "named_test_resource" {
name = var.resource_name
location = "West US"
}

resource "azurerm_public_ip" "named_test_resource" {
name = var.resource_name
location = azurerm_resource_group.named_test_resource.location
resource_group_name = azurerm_resource_group.named_test_resource.name
allocation_method = "Static"
}

resource "azurerm_lb" "named_test_resource" {
name = var.resource_name
location = azurerm_resource_group.named_test_resource.location
resource_group_name = azurerm_resource_group.named_test_resource.name

frontend_ip_configuration {
name = var.resource_name
public_ip_address_id = azurerm_public_ip.named_test_resource.id
}
}

resource "azurerm_lb_probe" "named_test_resource" {
resource_group_name = azurerm_resource_group.named_test_resource.name
loadbalancer_id = azurerm_lb.named_test_resource.id
name = var.resource_name
port = 22
}

output "resource_aka" {
depends_on = [azurerm_lb_probe.named_test_resource]
value = "azure://${azurerm_lb_probe.named_test_resource.id}"
}

output "resource_aka_lower" {
value = "azure://${lower(azurerm_lb_probe.named_test_resource.id)}"
}

output "resource_name" {
value = var.resource_name
}

output "resource_id" {
value = azurerm_lb_probe.named_test_resource.id
}

output "subscription_id" {
value = var.azure_subscription
}
1 change: 1 addition & 0 deletions azure/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"azure_key_vault_secret": tableAzureKeyVaultSecret(ctx),
"azure_kubernetes_cluster": tableAzureKubernetesCluster(ctx),
"azure_lb": tableAzureLoadBalancer(ctx),
"azure_lb_probe": tableAzureLoadBalancerProbe(ctx),
"azure_location": tableAzureLocation(ctx),
"azure_log_alert": tableAzureLogAlert(ctx),
"azure_log_profile": tableAzureLogProfile(ctx),
Expand Down
212 changes: 212 additions & 0 deletions azure/table_azure_lb_probe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package azure

import (
"context"
"strings"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network"
"github.com/turbot/steampipe-plugin-sdk/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/plugin/transform"

"github.com/turbot/steampipe-plugin-sdk/plugin"
)

//// TABLE DEFINITION

func tableAzureLoadBalancerProbe(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "azure_lb_probe",
Description: "Azure Load Balancer Probe",
Get: &plugin.GetConfig{
KeyColumns: plugin.AllColumns([]string{"load_balancer_name", "name", "resource_group"}),
Hydrate: getLoadBalancerProbe,
ShouldIgnoreError: isNotFoundError([]string{"ResourceNotFound", "ResourceGroupNotFound", "404"}),
},
List: &plugin.ListConfig{
Hydrate: listLoadBalancerProbes,
ParentHydrate: listLoadBalancers,
},
Columns: []*plugin.Column{
{
Name: "name",
Description: "The name of the resource that is unique within the set of probes used by the load balancer. This name can be used to access the resource.",
Type: proto.ColumnType_STRING,
},
{
Name: "id",
Description: "The resource ID.",
Type: proto.ColumnType_STRING,
Transform: transform.FromGo(),
},
{
Name: "load_balancer_name",
Description: "The friendly name that identifies the load balancer.",
Type: proto.ColumnType_STRING,
Transform: transform.From(extractLoadBalancerNameFromProbeID),
},
{
Name: "provisioning_state",
Description: "The provisioning state of the probe resource. Possible values include: 'Succeeded', 'Updating', 'Deleting', 'Failed'.",
Type: proto.ColumnType_STRING,
Transform: transform.FromField("ProbePropertiesFormat.ProvisioningState"),
},
{
Name: "type",
Description: "Type of the resource.",
Type: proto.ColumnType_STRING,
},
{
Name: "etag",
Description: "A unique read-only string that changes whenever the resource is updated.",
Type: proto.ColumnType_STRING,
},
{
Name: "interval_in_seconds",
Description: "The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5.",
Type: proto.ColumnType_INT,
Transform: transform.FromField("ProbePropertiesFormat.IntervalInSeconds"),
},
{
Name: "number_of_probes",
Description: "The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure.",
Type: proto.ColumnType_INT,
Transform: transform.FromField("ProbePropertiesFormat.NumberOfProbes"),
},
{
Name: "port",
Description: "The port for communicating the probe. Possible values range from 1 to 65535, inclusive.",
Type: proto.ColumnType_INT,
Transform: transform.FromField("ProbePropertiesFormat.Port"),
},
{
Name: "protocol",
Description: "The protocol of the end point. If 'Tcp' is specified, a received ACK is required for the probe to be successful. If 'Http' or 'Https' is specified, a 200 OK response from the specifies URI is required for the probe to be successful. Possible values include: 'HTTP', 'TCP', 'HTTPS'.",
Type: proto.ColumnType_STRING,
Transform: transform.FromField("ProbePropertiesFormat.Protocol"),
},
{
Name: "request_path",
Description: "The URI used for requesting health status from the VM. Path is required if a protocol is set to http. Otherwise, it is not allowed. There is no default value.",
Type: proto.ColumnType_STRING,
Transform: transform.FromField("ProbePropertiesFormat.RequestPath"),
},
{
Name: "load_balancing_rules",
Description: "The load balancer rules that use this probe.",
Type: proto.ColumnType_JSON,
Transform: transform.FromField("ProbePropertiesFormat.LoadBalancingRules"),
},

// 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.FromField("ID").Transform(idToAkas),
},

// Azure standard columns
{
Name: "resource_group",
Description: ColumnDescriptionResourceGroup,
Type: proto.ColumnType_STRING,
Transform: transform.FromField("ID").Transform(extractResourceGroupFromID),
},
{
Name: "subscription_id",
Description: ColumnDescriptionSubscription,
Type: proto.ColumnType_STRING,
Transform: transform.FromField("ID").Transform(idToSubscriptionID),
},
},
}
}

//// LIST FUNCTION

func listLoadBalancerProbes(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
// Get the details of load balancer
loadBalancer := h.Item.(network.LoadBalancer)

// Create session
session, err := GetNewSession(ctx, d, "MANAGEMENT")
if err != nil {
return nil, err
}
subscriptionID := session.SubscriptionID
resourceGroup := strings.Split(*loadBalancer.ID, "/")[4]

listLoadBalancerProbesClient := network.NewLoadBalancerProbesClient(subscriptionID)
listLoadBalancerProbesClient.Authorizer = session.Authorizer

result, err := listLoadBalancerProbesClient.List(ctx, resourceGroup, *loadBalancer.Name)
if err != nil {
return nil, err
}
for _, probe := range result.Values() {
d.StreamListItem(ctx, probe)
}

for result.NotDone() {
err = result.NextWithContext(ctx)
if err != nil {
return nil, err
}
for _, probe := range result.Values() {
d.StreamListItem(ctx, probe)
}
}

return nil, err
}

//// HYDRATE FUNCTION

func getLoadBalancerProbe(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
plugin.Logger(ctx).Trace("getLoadBalancerProbe")

loadBalancerName := d.KeyColumnQuals["load_balancer_name"].GetStringValue()
probeName := d.KeyColumnQuals["name"].GetStringValue()
resourceGroup := d.KeyColumnQuals["resource_group"].GetStringValue()

// Handle empty loadBalancerName, probeName or resourceGroup
if loadBalancerName == "" || probeName == "" || resourceGroup == "" {
return nil, nil
}

session, err := GetNewSession(ctx, d, "MANAGEMENT")
if err != nil {
return nil, err
}
subscriptionID := session.SubscriptionID

LoadBalancerProbeClient := network.NewLoadBalancerProbesClient(subscriptionID)
LoadBalancerProbeClient.Authorizer = session.Authorizer

op, err := LoadBalancerProbeClient.Get(ctx, resourceGroup, loadBalancerName, probeName)
if err != nil {
return nil, err
}

// In some cases resource does not give any notFound error
// instead of notFound error, it returns empty data
if op.ID != nil {
return op, nil
}

return nil, nil
}

//// TRANSFORM FUNCTION

func extractLoadBalancerNameFromProbeID(ctx context.Context, d *transform.TransformData) (interface{}, error) {
data := d.HydrateItem.(network.Probe)
vaultName := strings.Split(*data.ID, "/")[8]
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
vaultName := strings.Split(*data.ID, "/")[8]
load_balancer_name := strings.Split(*data.ID, "/")[8]

return vaultName, nil
}
47 changes: 47 additions & 0 deletions docs/tables/azure_lb_probe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Table: azure_lb_probe

A health probe is used to determine the health status of the instances in the backend pool. It will determine if an instance is healthy and can receive traffic.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
A health probe is used to determine the health status of the instances in the backend pool. It will determine if an instance is healthy and can receive traffic.
When using load-balancing rules with Azure Load Balancer, you need to specify health probes to allow Load Balancer to detect the backend endpoint status. The configuration of the health probe and probe responses determine which backend pool instances will receive new flows. You can use health probes to detect the failure of an application on a backend endpoint.


## Examples

### Basic info

```sql
select
id,
name,
type,
provisioning_state,
load_balancer_name,
port
from
azure_lb_probe;
```

### List succeeded load balancer probe

```sql
select
id,
name,
type,
provisioning_state
from
azure_lb_probe
where
provisioning_state = 'Succeeded';
```

### List load balancer probe order by interval

```sql
select
id,
name,
type,
interval_in_seconds
from
azure_lb_probe
order by
interval_in_seconds;
```