Skip to content

Commit df210de

Browse files
authored
Allow Machine Learning jobs to be specified using datasource UID (#707)
The Grafana Machine Learning API now supports jobs to specify their datasource using UID; this PR updates the Terraform provider to also allow this.
1 parent e29384e commit df210de

File tree

7 files changed

+80
-15
lines changed

7 files changed

+80
-15
lines changed

docs/resources/machine_learning_job.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ A job defines the queries and model parameters for a machine learning task.
1717

1818
### Required
1919

20-
- `datasource_id` (Number) The id of the datasource to query.
2120
- `datasource_type` (String) The type of datasource being queried. Currently allowed values are prometheus, graphite, loki, postgres, and datadog.
2221
- `metric` (String) The metric used to query the job results.
2322
- `name` (String) The name of the job.
2423
- `query_params` (Map of String) An object representing the query params to query Grafana with.
2524

2625
### Optional
2726

27+
- `datasource_id` (Number) The id of the datasource to query.
28+
- `datasource_uid` (String) The uid of the datasource to query.
2829
- `description` (String) A description of the job.
2930
- `hyper_params` (Map of String) The hyperparameters used to fine tune the algorithm. See https://grafana.com/docs/grafana-cloud/machine-learning/models/ for the full list of available hyperparameters. Defaults to `map[]`.
3031
- `interval` (Number) The data interval in seconds to train the data on. Defaults to `300`.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
resource "grafana_machine_learning_job" "test_job" {
2+
name = "Test Job"
3+
metric = "tf_test_job"
4+
datasource_type = "prometheus"
5+
datasource_uid = "grafanacloud-usage"
6+
query_params = {
7+
expr = "grafanacloud_grafana_instance_active_user_count"
8+
}
9+
}

examples/resources/grafana_machine_learning_job/tuned_job.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ resource "grafana_machine_learning_job" "test_job" {
22
name = "Test Job"
33
metric = "tf_test_job"
44
datasource_type = "prometheus"
5-
datasource_id = 10
5+
datasource_uid = "grafanacloud-usage"
66
query_params = {
77
expr = "grafanacloud_grafana_instance_active_user_count"
88
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/Masterminds/semver/v3 v3.1.1
77
github.com/grafana/amixr-api-go-client v0.0.5
88
github.com/grafana/grafana-api-golang-client v0.13.0
9-
github.com/grafana/machine-learning-go-client v0.1.1
9+
github.com/grafana/machine-learning-go-client v0.2.0
1010
github.com/grafana/synthetic-monitoring-agent v0.9.4
1111
github.com/grafana/synthetic-monitoring-api-go-client v0.6.3
1212
github.com/hashicorp/go-cleanhttp v0.5.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ github.com/grafana/amixr-api-go-client v0.0.5 h1:jqmljnd5FozuOsCNuyhZVpooxmj0BW9
117117
github.com/grafana/amixr-api-go-client v0.0.5/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE=
118118
github.com/grafana/grafana-api-golang-client v0.13.0 h1:o/gL3F7EjBSBKgrpjH9/+sYFJ0HBUofvAYlKhrT2opE=
119119
github.com/grafana/grafana-api-golang-client v0.13.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
120-
github.com/grafana/machine-learning-go-client v0.1.1 h1:Gw6cX8xAd6IVF2LApkXOIdBK8Gzz07B3jQPukecw7fc=
121-
github.com/grafana/machine-learning-go-client v0.1.1/go.mod h1:QFfZz8NkqVF8++skjkKQXJEZfpCYd8S0yTWJUpsLLTA=
120+
github.com/grafana/machine-learning-go-client v0.2.0 h1:5JgfJn5Q72D0jZlXnM0gZ9lV4Q4zzq9X0GVfPu8Vxis=
121+
github.com/grafana/machine-learning-go-client v0.2.0/go.mod h1:QFfZz8NkqVF8++skjkKQXJEZfpCYd8S0yTWJUpsLLTA=
122122
github.com/grafana/synthetic-monitoring-agent v0.9.4 h1:Enx5s6gFbc/RAzL5KDX/00catAlbcY7/1IFPBe5lo/c=
123123
github.com/grafana/synthetic-monitoring-agent v0.9.4/go.mod h1:PGcL9plaDkqkWenFE53vCfLamfws3yFba4jgWEt9kGw=
124124
github.com/grafana/synthetic-monitoring-api-go-client v0.6.3 h1:3jb4v5W8cEEr5zp5kVAzyV0UGF7mm06iY3nBpN4ZCDU=

grafana/resource_machine_learning_job.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package grafana
22

33
import (
44
"context"
5+
"fmt"
56
"time"
67

78
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -49,7 +50,12 @@ A job defines the queries and model parameters for a machine learning task.
4950
"datasource_id": {
5051
Description: "The id of the datasource to query.",
5152
Type: schema.TypeInt,
52-
Required: true,
53+
Optional: true,
54+
},
55+
"datasource_uid": {
56+
Description: "The uid of the datasource to query.",
57+
Type: schema.TypeString,
58+
Optional: true,
5359
},
5460
"datasource_type": {
5561
Description: "The type of datasource being queried. Currently allowed values are prometheus, graphite, loki, postgres, and datadog.",
@@ -85,8 +91,11 @@ A job defines the queries and model parameters for a machine learning task.
8591

8692
func resourceMachineLearningJobCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
8793
c := meta.(*client).mlapi
88-
job := makeMLJob(d, meta)
89-
job, err := c.NewJob(ctx, job)
94+
job, err := makeMLJob(d, meta)
95+
if err != nil {
96+
return diag.FromErr(err)
97+
}
98+
job, err = c.NewJob(ctx, job)
9099
if err != nil {
91100
return diag.FromErr(err)
92101
}
@@ -104,7 +113,16 @@ func resourceMachineLearningJobRead(ctx context.Context, d *schema.ResourceData,
104113
d.Set("name", job.Name)
105114
d.Set("metric", job.Metric)
106115
d.Set("description", job.Description)
107-
d.Set("datasource_id", job.DatasourceID)
116+
if job.DatasourceID != 0 {
117+
d.Set("datasource_id", job.DatasourceID)
118+
} else {
119+
d.Set("datasource_id", nil)
120+
}
121+
if job.DatasourceUID != "" {
122+
d.Set("datasource_uid", job.DatasourceUID)
123+
} else {
124+
d.Set("datasource_uid", nil)
125+
}
108126
d.Set("datasource_type", job.DatasourceType)
109127
d.Set("query_params", job.QueryParams)
110128
d.Set("interval", job.Interval)
@@ -116,8 +134,11 @@ func resourceMachineLearningJobRead(ctx context.Context, d *schema.ResourceData,
116134

117135
func resourceMachineLearningJobUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
118136
c := meta.(*client).mlapi
119-
j := makeMLJob(d, meta)
120-
_, err := c.UpdateJob(ctx, j)
137+
job, err := makeMLJob(d, meta)
138+
if err != nil {
139+
return diag.FromErr(err)
140+
}
141+
_, err = c.UpdateJob(ctx, job)
121142
if err != nil {
122143
return diag.FromErr(err)
123144
}
@@ -134,20 +155,26 @@ func resourceMachineLearningJobDelete(ctx context.Context, d *schema.ResourceDat
134155
return nil
135156
}
136157

137-
func makeMLJob(d *schema.ResourceData, meta interface{}) mlapi.Job {
158+
func makeMLJob(d *schema.ResourceData, meta interface{}) (mlapi.Job, error) {
159+
datasourceID := uint(d.Get("datasource_id").(int))
160+
datasourceUID := d.Get("datasource_uid").(string)
161+
if datasourceID == 0 && datasourceUID == "" {
162+
return mlapi.Job{}, fmt.Errorf("either datasource_id or datasource_uid must be set")
163+
}
138164
return mlapi.Job{
139165
ID: d.Id(),
140166
Name: d.Get("name").(string),
141167
Metric: d.Get("metric").(string),
142168
Description: d.Get("description").(string),
143169
GrafanaURL: meta.(*client).gapiURL,
144-
DatasourceID: uint(d.Get("datasource_id").(int)),
170+
DatasourceID: datasourceID,
171+
DatasourceUID: datasourceUID,
145172
DatasourceType: d.Get("datasource_type").(string),
146173
QueryParams: d.Get("query_params").(map[string]interface{}),
147174
Interval: uint(d.Get("interval").(int)),
148175
Algorithm: "Prophet",
149176
HyperParams: d.Get("hyper_params").(map[string]interface{}),
150177
TrainingWindow: uint(d.Get("training_window").(int)),
151178
TrainingFrequency: uint(24 * time.Hour / time.Second),
152-
}
179+
}, nil
153180
}

grafana/resource_machine_learning_job_test.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,27 @@ func TestAccResourceMachineLearningJob(t *testing.T) {
3333
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "training_window", "7776000"),
3434
),
3535
},
36+
{
37+
Config: testAccExample(t, "resources/grafana_machine_learning_job/datasource_uid_job.tf"),
38+
Check: resource.ComposeTestCheckFunc(
39+
resource.TestCheckResourceAttrSet("grafana_machine_learning_job.test_job", "id"),
40+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "name", "Test Job"),
41+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "metric", "tf_test_job"),
42+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "datasource_type", "prometheus"),
43+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "datasource_uid", "grafanacloud-usage"),
44+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "query_params.expr", "grafanacloud_grafana_instance_active_user_count"),
45+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "interval", "300"),
46+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "training_window", "7776000"),
47+
),
48+
},
3649
{
3750
Config: testAccExample(t, "resources/grafana_machine_learning_job/tuned_job.tf"),
3851
Check: resource.ComposeTestCheckFunc(
3952
resource.TestCheckResourceAttrSet("grafana_machine_learning_job.test_job", "id"),
4053
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "name", "Test Job"),
4154
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "metric", "tf_test_job"),
4255
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "datasource_type", "prometheus"),
43-
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "datasource_id", "10"),
56+
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "datasource_uid", "grafanacloud-usage"),
4457
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "query_params.expr", "grafanacloud_grafana_instance_active_user_count"),
4558
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "interval", "300"),
4659
resource.TestCheckResourceAttr("grafana_machine_learning_job.test_job", "training_window", "7776000"),
@@ -103,6 +116,17 @@ resource "grafana_machine_learning_job" "invalid" {
103116
}
104117
`
105118

119+
const machineLearningJobMissingDatasourceIDOrUID = `
120+
resource "grafana_machine_learning_job" "invalid" {
121+
name = "Test Job"
122+
metric = "tf_test_job"
123+
datasource_type = "prometheus"
124+
query_params = {
125+
expr = "grafanacloud_grafana_instance_active_user_count"
126+
}
127+
}
128+
`
129+
106130
func TestAccResourceInvalidMachineLearningJob(t *testing.T) {
107131
CheckCloudInstanceTestsEnabled(t)
108132

@@ -113,6 +137,10 @@ func TestAccResourceInvalidMachineLearningJob(t *testing.T) {
113137
Config: machineLearningJobInvalid,
114138
ExpectError: regexp.MustCompile(".*datasourceType.*"),
115139
},
140+
{
141+
Config: machineLearningJobMissingDatasourceIDOrUID,
142+
ExpectError: regexp.MustCompile(".*datasource_id or datasource_uid.*"),
143+
},
116144
},
117145
})
118146
}

0 commit comments

Comments
 (0)