diff --git a/datasources/generic/settings/data_source.go b/datasources/generic/settings/data_source.go new file mode 100644 index 000000000..88c530e36 --- /dev/null +++ b/datasources/generic/settings/data_source.go @@ -0,0 +1,109 @@ +/** +* @license +* Copyright 2020 Dynatrace LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ + +package settings + +import ( + "context" + "fmt" + + "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/api" + srv "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/api/builtin/generic" + generic "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/api/builtin/generic/settings" + "github.com/dynatrace-oss/terraform-provider-dynatrace/terraform/hcl" + + "github.com/dynatrace-oss/terraform-provider-dynatrace/provider/config" + "github.com/dynatrace-oss/terraform-provider-dynatrace/provider/logging" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: logging.EnableDSCtx(DataSourceRead), + Schema: map[string]*schema.Schema{ + "schema": { + Type: schema.TypeString, + Description: "Schema IDs to which the requested objects belong", + Optional: true, + Computed: true, + AtLeastOneOf: []string{"schema", "scope"}, + }, + "scope": { + Type: schema.TypeString, + Description: "Scope that the requested objects target", + Optional: true, + Computed: true, + AtLeastOneOf: []string{"schema", "scope"}, + }, + "filter": { + Type: schema.TypeString, + Description: "Filter of the requested objects", + Optional: true, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func DataSourceRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + var query srv.QueryParams + if v, ok := d.GetOk("schema"); ok { + query.Schema = v.(string) + } + if v, ok := d.GetOk("scope"); ok { + query.Scope = v.(string) + } + if v, ok := d.GetOk("filter"); ok { + query.Filter = v.(string) + } + + d.SetId(fmt.Sprintf("generic_settings[%s][%s][%s]", query.Schema, query.Scope, query.Filter)) + + creds, err := config.Credentials(m, config.CredValDefault) + if err != nil { + return diag.FromErr(err) + } + + var stubs api.Stubs + myService := srv.Service(creds) + if spService, ok := myService.(SpecificLister); ok { + stubs, err = spService.ListSpecific(query) + } + if err != nil { + return diag.FromErr(err) + } + + if len(stubs) > 0 { + value := stubs[0].Value.(*generic.Settings) + + marshalled := hcl.Properties{} + if err := value.MarshalHCL(marshalled); err != nil { + return diag.FromErr(err) + } + + for k, v := range marshalled { + d.Set(k, v) + } + } + + return diag.Diagnostics{} +} diff --git a/datasources/generic/settings/data_source_multiple.go b/datasources/generic/settings/data_source_multiple.go new file mode 100644 index 000000000..333683160 --- /dev/null +++ b/datasources/generic/settings/data_source_multiple.go @@ -0,0 +1,116 @@ +/** +* @license +* Copyright 2020 Dynatrace LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ + +package settings + +import ( + "context" + "fmt" + + "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/api" + srv "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/api/builtin/generic" + generic "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/api/builtin/generic/settings" + "github.com/dynatrace-oss/terraform-provider-dynatrace/terraform/hcl" + + "github.com/dynatrace-oss/terraform-provider-dynatrace/provider/config" + "github.com/dynatrace-oss/terraform-provider-dynatrace/provider/logging" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceMultiple() *schema.Resource { + return &schema.Resource{ + ReadContext: logging.EnableDSCtx(DataSourceReadMultiple), + Schema: map[string]*schema.Schema{ + "schema": { + Type: schema.TypeString, + Description: "Schema IDs to which the requested objects belong", + Optional: true, + Computed: true, + AtLeastOneOf: []string{"schema", "scope"}, + }, + "scope": { + Type: schema.TypeString, + Description: "Scope that the requested objects target", + Optional: true, + Computed: true, + AtLeastOneOf: []string{"schema", "scope"}, + }, + "filter": { + Type: schema.TypeString, + Description: "Filter of the requested objects", + Optional: true, + }, + "values": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{Schema: new(generic.Settings).Schema()}, + }, + }, + } +} + +func DataSourceReadMultiple(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + var query srv.QueryParams + if v, ok := d.GetOk("schema"); ok { + query.Schema = v.(string) + } + if v, ok := d.GetOk("scope"); ok { + query.Scope = v.(string) + } + if v, ok := d.GetOk("filter"); ok { + query.Filter = v.(string) + } + + d.SetId(fmt.Sprintf("generic_settings[%s][%s][%s]", query.Schema, query.Scope, query.Filter)) + + creds, err := config.Credentials(m, config.CredValDefault) + if err != nil { + return diag.FromErr(err) + } + + var stubs api.Stubs + myService := srv.Service(creds) + if spService, ok := myService.(SpecificLister); ok { + stubs, err = spService.ListSpecific(query) + } + if err != nil { + return diag.FromErr(err) + } + + if len(stubs) > 0 { + values := []*generic.Settings{} + for _, stub := range stubs { + value := stub.Value.(*generic.Settings) + values = append(values, value) + } + + marshalled := hcl.Properties{} + if marshalled.EncodeSlice("values", values); err != nil { + return diag.FromErr(err) + } + + d.Set("values", marshalled["values"]) + } + + return diag.Diagnostics{} +} + +type SpecificLister interface { + ListSpecific(query srv.QueryParams) (api.Stubs, error) +} diff --git a/dynatrace/api/builtin/generic/service.go b/dynatrace/api/builtin/generic/service.go index a0b7c0b17..e80b614f7 100644 --- a/dynatrace/api/builtin/generic/service.go +++ b/dynatrace/api/builtin/generic/service.go @@ -33,6 +33,7 @@ import ( "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/rest" "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/settings" "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/settings/services/settings20" + "github.com/dynatrace-oss/terraform-provider-dynatrace/dynatrace/shutdown" "github.com/dynatrace/dynatrace-configuration-as-code-core/api/auth" crest "github.com/dynatrace/dynatrace-configuration-as-code-core/api/rest" "golang.org/x/oauth2" @@ -308,3 +309,63 @@ func (me *service) delete(ctx context.Context, id string, numRetries int) error func (me *service) SchemaID() string { return "generic" } + +type QueryParams struct { + Schema string + Scope string + Filter string +} + +func (me *service) ListSpecific(query QueryParams) (api.Stubs, error) { + client := me.TokenClient() + + stubs := api.Stubs{} + nextPage := true + var nextPageKey *string + for nextPage { + var sol settings20.SettingsObjectList + var options crest.RequestOptions + if nextPageKey == nil { + options.QueryParams = url.Values{ + "fields": []string{"objectId,scope,value,schemaId"}, + "pageSize": []string{"100"}, + "schemaIds": []string{query.Schema}, + "scopes": []string{query.Scope}, + "filter": []string{query.Filter}, + } + } else { + options.QueryParams = url.Values{ + "nextPageKey": []string{*nextPageKey}, + } + } + + response, err := client.GET(context.TODO(), "api/v2/settings/objects", options) + if err != nil { + return nil, err + } + + if response.Body != nil { + json.NewDecoder(response.Body).Decode(&sol) + } + if len(sol.Items) == 0 { + return api.Stubs{}, nil + } + if shutdown.System.Stopped() { + return stubs, nil + } + + if len(sol.Items) > 0 { + for _, item := range sol.Items { + newItem := new(generic.Settings) + newItem.Value = string(item.Value) + newItem.Scope = item.Scope + newItem.SchemaID = item.SchemaID + stubs = append(stubs, &api.Stub{ID: item.ObjectID, Name: item.ObjectID, Value: newItem}) + } + } + nextPageKey = sol.NextPageKey + nextPage = (nextPageKey != nil) + } + + return stubs, nil +} diff --git a/dynatrace/settings/services/settings20/settings_object.go b/dynatrace/settings/services/settings20/settings_object.go index a638a339b..ed8e53461 100644 --- a/dynatrace/settings/services/settings20/settings_object.go +++ b/dynatrace/settings/services/settings20/settings_object.go @@ -37,5 +37,6 @@ type SettingsObjectListItem struct { ObjectID string `json:"objectId"` Scope string `json:"scope"` SchemaVersion string `json:"schemaVersion"` + SchemaID string `json:"schemaId"` Value json.RawMessage `json:"value"` } diff --git a/provider/provider.go b/provider/provider.go index e357ed463..15f45ad4a 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -37,6 +37,7 @@ import ( "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/entities" "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/entity" failure_detection_parameters "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/failuredetection/parameters" + genericsettingsds "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/generic/settings" "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/host" "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/hub/items" ds_iam_groups "github.com/dynatrace-oss/terraform-provider-dynatrace/datasources/iam/groups" @@ -225,6 +226,8 @@ func Provider() *schema.Provider { "dynatrace_iam_policy": ds_iam_policies.DataSourceSingle(), "dynatrace_lambda_agent_version": lambdaagent.DataSource(), "dynatrace_autotag": autotag.DataSource(), + "dynatrace_generic_settings": genericsettingsds.DataSourceMultiple(), + "dynatrace_generic_setting": genericsettingsds.DataSource(), }, ResourcesMap: map[string]*schema.Resource{ "dynatrace_custom_service": resources.NewGeneric(export.ResourceTypes.CustomService).Resource(), diff --git a/templates/data-sources/generic_setting.md.tmpl b/templates/data-sources/generic_setting.md.tmpl new file mode 100644 index 000000000..e50d95a83 --- /dev/null +++ b/templates/data-sources/generic_setting.md.tmpl @@ -0,0 +1,28 @@ +--- +layout: "" +page_title: "dynatrace_generic_setting Data Source - terraform-provider-dynatrace" +subcategory: "Platform" +description: |- + The data source `dynatrace_generic_setting` covers queries for a Settings 2.0 object +--- + +# dynatrace_generic_setting (Data Source) + +The generic setting data source allows a single Settings 2.0 object to be retrieved by its schema ID, scope, and/or filter. + +If multiple objects match the given criteria, the first result will be retrieved. + +## Example Usage + +```terraform +data "dynatrace_generic_setting" "example" { + schema = "builtin:alerting.maintenance-window" + filter = "value.generalProperties.name = 'Terraform Example'" +} + +output "generic_setting" { + value = data.dynatrace_generic_setting.example +} +``` + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file diff --git a/templates/data-sources/generic_settings.md.tmpl b/templates/data-sources/generic_settings.md.tmpl new file mode 100644 index 000000000..6fb946efa --- /dev/null +++ b/templates/data-sources/generic_settings.md.tmpl @@ -0,0 +1,25 @@ +--- +layout: "" +page_title: "dynatrace_generic_settings Data Source - terraform-provider-dynatrace" +subcategory: "Platform" +description: |- + The data source `dynatrace_generic_settings` covers queries for a list of Settings 2.0 objects +--- + +# dynatrace_generic_settings (Data Source) + +The generic settings data source allows Settings 2.0 objects to be retrieved by its schema ID, scope, and/or filter. + +## Example Usage + +```terraform +data "dynatrace_generic_settings" "example" { + schema = "builtin:alerting.maintenance-window" +} + +output "generic_settings" { + value = data.dynatrace_generic_settings.example +} +``` + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file