Skip to content

Commit

Permalink
stackdriver dashboards resource (#3490)
Browse files Browse the repository at this point in the history
  • Loading branch information
danawillow authored May 20, 2020
1 parent e808ad1 commit 47e1d5e
Show file tree
Hide file tree
Showing 8 changed files with 641 additions and 18 deletions.
30 changes: 15 additions & 15 deletions products/monitoring/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ display_name: Cloud (Stackdriver) Monitoring
versions:
- !ruby/object:Api::Product::Version
name: ga
base_url: https://monitoring.googleapis.com/v3/
base_url: https://monitoring.googleapis.com/
scopes:
- https://www.googleapis.com/auth/cloud-platform
apis_required:
Expand All @@ -26,8 +26,8 @@ apis_required:
objects:
- !ruby/object:Api::Resource
name: 'AlertPolicy'
base_url: projects/{{project}}/alertPolicies
self_link: "{{name}}"
base_url: v3/projects/{{project}}/alertPolicies
self_link: "v3/{{name}}"
update_verb: :PATCH
update_mask: true
description: |
Expand Down Expand Up @@ -741,8 +741,8 @@ objects:
- !ruby/object:Api::Resource
name: 'Group'
base_url: projects/{{project}}/groups
self_link: "{{name}}"
base_url: v3/projects/{{project}}/groups
self_link: "v3/{{name}}"
update_verb: :PUT
description: |
The description of a dynamic collection of monitored resources. Each group
Expand Down Expand Up @@ -789,8 +789,8 @@ objects:
- !ruby/object:Api::Resource
name: NotificationChannel
base_url: projects/{{project}}/notificationChannels
self_link: "{{name}}"
base_url: v3/projects/{{project}}/notificationChannels
self_link: "v3/{{name}}"
update_verb: :PATCH
description: |
A NotificationChannel is a medium through which an alert is delivered
Expand Down Expand Up @@ -916,9 +916,9 @@ objects:

- !ruby/object:Api::Resource
name: Service
base_url: projects/{{project}}/services
create_url: projects/{{project}}/services?serviceId={{service_id}}
self_link: "{{name}}"
base_url: v3/projects/{{project}}/services
create_url: v3/projects/{{project}}/services?serviceId={{service_id}}
self_link: "v3/{{name}}"
update_verb: :PATCH
update_mask: true
description: |
Expand Down Expand Up @@ -963,10 +963,10 @@ objects:
- !ruby/object:Api::Resource
name: Slo
base_url: projects/{{project}}/services/{{service}}/serviceLevelObjectives
base_url: v3/projects/{{project}}/services/{{service}}/serviceLevelObjectives
# name = projects/{{project}}/services/{{service}}/serviceLevelObjectives/{{slo_id}}
self_link: "{{name}}"
create_url: projects/{{project}}/services/{{service}}/serviceLevelObjectives?serviceLevelObjectiveId={{slo_id}}
self_link: "v3/{{name}}"
create_url: v3/projects/{{project}}/services/{{service}}/serviceLevelObjectives?serviceLevelObjectiveId={{slo_id}}
update_verb: :PATCH
update_mask: true
description: |
Expand Down Expand Up @@ -1578,8 +1578,8 @@ objects:
'Official Documentation':
'https://cloud.google.com/monitoring/uptime-checks/'
api: 'https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.uptimeCheckConfigs'
base_url: projects/{{project}}/uptimeCheckConfigs
self_link: "{{name}}"
base_url: v3/projects/{{project}}/uptimeCheckConfigs
self_link: "v3/{{name}}"
description: This message configures which resources and services to monitor for
availability.
properties:
Expand Down
2 changes: 1 addition & 1 deletion products/monitoring/inspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides
additional_functions: third_party/inspec/custom_functions/alert_policy.erb
singular_extra_examples: third_party/inspec/documentation/google_project_alert_policy.md
plural_extra_examples: third_party/inspec/documentation/google_project_alert_policies.md
self_link: projects/{{project}}/alertPolicies/{{name}}
self_link: v3/projects/{{project}}/alertPolicies/{{name}}
properties:
name: !ruby/object:Overrides::Inspec::PropertyOverride
override_name: policy_names
Expand Down
4 changes: 3 additions & 1 deletion templates/terraform/objectlib/base.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ package google
<%
resource_name = product_ns + object.name
properties = object.all_user_properties
api_version = @base_url.split("/")[-1]
# See discussion on asset name here: https://github.com/GoogleCloudPlatform/magic-modules/pull/1520
asset_name_template = '//' + product_ns.downcase + '.googleapis.com/' + (!object.self_link.nil? && !object.self_link.empty? ? object.self_link : object.base_url + '/{{name}}')
version_regex = /\/(v\d[^\/]*)\//
api_version = version_regex.match?(asset_name_template) ? version_regex.match(asset_name_template)[1] : @base_url.split("/")[-1]
asset_name_template.gsub!(version_regex, '/')
%>
<%= lines(compile(pwd + '/' + object.custom_code.constants)) if object.custom_code.constants -%>
Expand Down
193 changes: 193 additions & 0 deletions third_party/terraform/resources/resource_monitoring_dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package google

import (
"fmt"
"reflect"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func monitoringDashboardDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
computedFields := []string{"etag", "name"}

oldMap, err := structure.ExpandJsonFromString(old)
if err != nil {
return false
}

newMap, err := structure.ExpandJsonFromString(new)
if err != nil {
return false
}

for _, f := range computedFields {
delete(oldMap, f)
delete(newMap, f)
}

return reflect.DeepEqual(oldMap, newMap)
}

func resourceMonitoringDashboard() *schema.Resource {
return &schema.Resource{
Create: resourceMonitoringDashboardCreate,
Read: resourceMonitoringDashboardRead,
Update: resourceMonitoringDashboardUpdate,
Delete: resourceMonitoringDashboardDelete,

Importer: &schema.ResourceImporter{
State: resourceMonitoringDashboardImport,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(4 * time.Minute),
Update: schema.DefaultTimeout(4 * time.Minute),
Delete: schema.DefaultTimeout(4 * time.Minute),
},

Schema: map[string]*schema.Schema{
"dashboard_json": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.ValidateJsonString,
DiffSuppressFunc: monitoringDashboardDiffSuppress,
StateFunc: func(v interface{}) string {
json, _ := structure.NormalizeJsonString(v)
return json
},
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
}
}

func resourceMonitoringDashboardCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

obj, err := structure.ExpandJsonFromString(d.Get("dashboard_json").(string))
if err != nil {
return err
}

project, err := getProject(d, config)
if err != nil {
return err
}

url, err := replaceVars(d, config, "{{MonitoringBasePath}}v1/projects/{{project}}/dashboards")
if err != nil {
return err
}
res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate), isMonitoringRetryableError)
if err != nil {
return fmt.Errorf("Error creating Dashboard: %s", err)
}

name, ok := res["name"]
if !ok {
return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.")
}
d.SetId(name.(string))

return resourceMonitoringDashboardRead(d, config)
}

func resourceMonitoringDashboardRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

url := config.MonitoringBasePath + "v1/" + d.Id()

project, err := getProject(d, config)
if err != nil {
return err
}

res, err := sendRequest(config, "GET", project, url, nil, isMonitoringRetryableError)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("MonitoringDashboard %q", d.Id()))
}

if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error reading Dashboard: %s", err)
}

str, err := structure.FlattenJsonToString(res)
if err != nil {
return fmt.Errorf("Error reading Dashboard: %s", err)
}
if err = d.Set("dashboard_json", str); err != nil {
return fmt.Errorf("Error reading Dashboard: %s", err)
}

return nil
}

func resourceMonitoringDashboardUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

o, n := d.GetChange("dashboard_json")
oObj, err := structure.ExpandJsonFromString(o.(string))
if err != nil {
return err
}
nObj, err := structure.ExpandJsonFromString(n.(string))
if err != nil {
return err
}

nObj["etag"] = oObj["etag"]

project, err := getProject(d, config)
if err != nil {
return err
}

url := config.MonitoringBasePath + "v1/" + d.Id()
_, err = sendRequestWithTimeout(config, "PATCH", project, url, nObj, d.Timeout(schema.TimeoutUpdate), isMonitoringRetryableError)
if err != nil {
return fmt.Errorf("Error updating Dashboard %q: %s", d.Id(), err)
}

return resourceMonitoringDashboardRead(d, config)
}

func resourceMonitoringDashboardDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

url := config.MonitoringBasePath + "v1/" + d.Id()

project, err := getProject(d, config)
if err != nil {
return err
}

_, err = sendRequestWithTimeout(config, "DELETE", project, url, nil, d.Timeout(schema.TimeoutDelete), isMonitoringRetryableError)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("MonitoringDashboard %q", d.Id()))
}

return nil
}

func resourceMonitoringDashboardImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(*Config)

// current import_formats can't import fields with forward slashes in their value
parts, err := getImportIdQualifiers([]string{"projects/(?P<project>[^/]+)/dashboards/(?P<id>[^/]+)", "(?P<id>[^/]+)"}, d, config, d.Id())
if err != nil {
return nil, err
}

d.Set("project", parts["project"])
d.SetId(fmt.Sprintf("projects/%s/dashboards/%s", parts["project"], parts["id"]))

return []*schema.ResourceData{d}, nil
}
Loading

0 comments on commit 47e1d5e

Please sign in to comment.