From 4860e7ce32162ffe0cf3f84f269600a4b76677d0 Mon Sep 17 00:00:00 2001 From: ParthaI <47887552+ParthaI@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:38:15 +0530 Subject: [PATCH] Add table gcp_app_engine_application Closes #528 (#537) --- docs/tables/gcp_app_engine_application.md | 100 ++++++++++++++ gcp/plugin.go | 1 + gcp/service.go | 22 +++ gcp/table_gcp_app_engine_application.go | 157 ++++++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 docs/tables/gcp_app_engine_application.md create mode 100644 gcp/table_gcp_app_engine_application.go diff --git a/docs/tables/gcp_app_engine_application.md b/docs/tables/gcp_app_engine_application.md new file mode 100644 index 00000000..def48b86 --- /dev/null +++ b/docs/tables/gcp_app_engine_application.md @@ -0,0 +1,100 @@ +--- +title: "Steampipe Table: gcp_app_engine_application - Query App Engine Application using SQL" +description: "Allows users to query App Engine Application in Google Cloud Platform (GCP), specifically the details about the application, including their name, location, service account, storage bucket, database type and serving_status." +--- + +# Table: gcp_app_engine_application - Query App Engine Application using SQL + +Google Cloud Platform's (GCP) App Engine is a fully managed, serverless platform for developing and hosting web applications at scale. An App Engine application refers to the specific application you deploy on this platform. + +## Table Usage Guide + +The `gcp_app_engine_application` table provides insights into the App Engine abstracts away the infrastructure, allowing developers to focus on code. It manages the hardware and networking infrastructure required to run your code. + +## Examples + +### Basic info +Explore the basic details of your Google Cloud Platform's App Engine Application such as their names, storage bucket, database type, default hostname, and serving status. This information can help you manage and monitor your application more effectively. + +```sql+postgres +select + name, + id, + code_bucket, + database_type, + default_hostname, + gcr_domain, + serving_status +from + gcp_app_engine_application; +``` + +```sql+sqlite +select + name, + id, + code_bucket, + database_type, + default_hostname, + gcr_domain, + serving_status +from + gcp_app_engine_application; +``` + +### Get feature setting details of an application +This is designed to retrieve specific configuration details from App Engine applications within a Google Cloud Platform (GCP) environment. + +```sql+postgres +select + name, + id, + location, + feature_settings -> 'SplitHealthChecks' as split_health_checks, + feature_settings -> 'UseContainerOptimizedOs' as use_container_optimized_os +from + gcp_app_engine_application; +``` + +```sql+sqlite +select + name, + id, + location, + json_extract(feature_settings, '$.SplitHealthChecks') as split_health_checks, + json_extract(feature_settings, '$.UseContainerOptimizedOs') as use_container_optimized_os +from + gcp_app_engine_application; +``` + +### Get service account details for the application +Explore the details about the service account that has been associated with the application. + +```sql+postgres +select + a.name, + a.service_account, + s.email, + s.disabled, + s.oauth2_client_id, + s.iam_policy +from + gcp_app_engine_application as a, + gcp_service_account as s +where + s.name = a.service_account; +``` + +```sql+sqlite +select + a.name, + a.service_account, + s.email, + s.disabled, + s.oauth2_client_id, + s.iam_policy +from + gcp_app_engine_application as a +join + gcp_service_account as s ON s.name = a.service_account; +``` \ No newline at end of file diff --git a/gcp/plugin.go b/gcp/plugin.go index d035dd95..4e28e6bc 100644 --- a/gcp/plugin.go +++ b/gcp/plugin.go @@ -32,6 +32,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { }, TableMap: map[string]*plugin.Table{ "gcp_apikeys_key": tableGcpApiKeysKey(ctx), + "gcp_app_engine_application": tableGcpAppEngineApplication(ctx), "gcp_artifact_registry_repository": tableGcpArtifactRegistryRepository(ctx), "gcp_audit_policy": tableGcpAuditPolicy(ctx), "gcp_bigquery_dataset": tableGcpBigQueryDataset(ctx), diff --git a/gcp/service.go b/gcp/service.go index 05cc9f3e..57709a8b 100644 --- a/gcp/service.go +++ b/gcp/service.go @@ -8,6 +8,7 @@ import ( "github.com/turbot/steampipe-plugin-sdk/v5/plugin" "google.golang.org/api/accessapproval/v1" "google.golang.org/api/apikeys/v2" + "google.golang.org/api/appengine/v1" "google.golang.org/api/artifactregistry/v1" "google.golang.org/api/bigquery/v2" "google.golang.org/api/bigtableadmin/v2" @@ -139,6 +140,27 @@ func APIKeysService(ctx context.Context, d *plugin.QueryData) (*apikeys.Service, return svc, nil } +// AppEngineService returns the service connection for GCP App Engine service +func AppEngineService(ctx context.Context, d *plugin.QueryData) (*appengine.APIService, error) { + // have we already created and cached the service? + serviceCacheKey := "BillingBudgetsService" + if cachedData, ok := d.ConnectionManager.Cache.Get(serviceCacheKey); ok { + return cachedData.(*appengine.APIService), nil + } + + // To get config arguments from plugin config file + opts := setSessionConfig(ctx, d.Connection) + + // so it was not in cache - create service + svc, err := appengine.NewService(ctx, opts...) + if err != nil { + return nil, err + } + + d.ConnectionManager.Cache.Set(serviceCacheKey, svc) + return svc, nil +} + // BillingBudgetsService returns the service connection for GCP Billing Budgets service func BillingBudgetsService(ctx context.Context, d *plugin.QueryData) (*billingbudgets.Service, error) { // have we already created and cached the service? diff --git a/gcp/table_gcp_app_engine_application.go b/gcp/table_gcp_app_engine_application.go new file mode 100644 index 00000000..5b89bceb --- /dev/null +++ b/gcp/table_gcp_app_engine_application.go @@ -0,0 +1,157 @@ +package gcp + +import ( + "context" + + "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" +) + +//// TABLE DEFINITION + +// We can have only one Application per project. The App ID would be the Project ID. +// https://cloud.google.com/appengine/docs/flexible/managing-projects-apps-billing#:~:text=Important%3A%20Each%20Google%20Cloud%20project,of%20your%20App%20Engine%20application +func tableGcpAppEngineApplication(ctx context.Context) *plugin.Table { + return &plugin.Table{ + Name: "gcp_app_engine_application", + Description: "GCP App Engine Application", + List: &plugin.ListConfig{ + Hydrate: getAppEngineApplication, + }, + Columns: []*plugin.Column{ + { + Name: "id", + Description: "Identifier of the Application resource. This identifier is equivalent to the project ID of the Google Cloud Platform project where you want to deploy your application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "name", + Description: "Full path to the Application resource in the API.", + Type: proto.ColumnType_STRING, + }, + { + Name: "service_account", + Description: "The service account associated with the application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "auth_domain", + Description: "Google Apps authentication domain that controls which users can access this application.Defaults to open access for any Google Account.", + Type: proto.ColumnType_STRING, + }, + { + Name: "code_bucket", + Description: "Google Cloud Storage bucket that can be used for storing files associated with this application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "database_type", + Description: "The type of the Cloud Firestore or Cloud Datastore database associated with this application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "default_bucket", + Description: "Google Cloud Storage bucket that can be used by this application to store content.@OutputOnly.", + Type: proto.ColumnType_STRING, + }, + { + Name: "default_cookie_expiration", + Description: "Cookie expiration policy for this application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "default_hostname", + Description: "Hostname used to reach this application, as resolved by App Engine.@OutputOnly.", + Type: proto.ColumnType_STRING, + }, + { + Name: "gcr_domain", + Description: "The Google Container Registry domain used for storing managed build docker images for this application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "serving_status", + Description: "Serving status of this application.", + Type: proto.ColumnType_STRING, + }, + { + Name: "dispatch_rules", + Description: "HTTP path dispatch rules for requests to the application that do not explicitly target a service or version.", + Type: proto.ColumnType_JSON, + }, + { + Name: "feature_settings", + Description: "The feature specific settings to be used in the application.", + Type: proto.ColumnType_JSON, + }, + { + Name: "iap", + Description: "Identity-Aware Proxy.", + Type: proto.ColumnType_JSON, + }, + + // Steampipe standard columns + { + Name: "title", + Description: ColumnDescriptionTitle, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Name"), + }, + + // GCP standard columns + { + Name: "location", + Description: ColumnDescriptionLocation, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("LocationId"), + }, + { + Name: "project", + Description: ColumnDescriptionProject, + Type: proto.ColumnType_STRING, + Hydrate: getProject, + Transform: transform.FromValue(), + }, + }, + } +} + +//// LIST FUNCTION + +func getAppEngineApplication(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + + // Create Service Connection + service, err := AppEngineService(ctx, d) + if err != nil { + return nil, err + } + + // Get project details + getProjectCached := plugin.HydrateFunc(getProject).WithCache() + projectId, err := getProjectCached(ctx, d, h) + if err != nil { + return nil, err + } + project := projectId.(string) + + // In Google Cloud Platform (GCP), the structure is such that each project + // can contain only one App Engine application. This means the number of + // App Engine applications is directly related to the number of projects you have. + + // Each GCP project can support various services and features from Google Cloud, + // but when it comes to App Engine, it is restricted to one application per project. + + // If multiple applications are needed, you will need to create additional GCP projects, + // with one project for each App Engine application you wish to deploy. This approach + // Available APIs: https://cloud.google.com/appengine/docs/admin-api/reference/rest/v1/apps + + resp, err := service.Apps.Get(project).Do() + if err != nil { + return nil, err + } + + d.StreamListItem(ctx, resp) + + return resp, nil +}