Skip to content

Commit eb087b6

Browse files
Merge pull request #835 from SumoLogic/rk-ot-collector-update
SUMO-253272: Added update support for sumologic_ot_collector and enable/disable sumologic_source_template resource
2 parents d36ce06 + a7e5aee commit eb087b6

8 files changed

+224
-14
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## X.Y.Z (Unreleased)
2-
* Fix for failing content test case for scheduled searches
2+
ENHANCEMENTS:
3+
* Added update support for `sumologic_ot_collector` resource.
4+
* Suppressed system-managed `sumo.disco.*` tags so they no longer appear in Terraform plans or state when reading OT Collectors.
5+
* Added IsEnabled field in `sumologic_source_template` resource.
6+
* Added enable/disable support to the `sumologic_source_template` resource.
7+
* Updated endpoint for `sumologic_source_template` resource to use the open API standard.
8+
* Fix for failing content test case for scheduled searches
39

410
## 3.2.2 (January 6, 2026)
511
DOCS:

sumologic/resource_sumologic_ot_collector.go

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package sumologic
22

33
import (
4-
"errors"
54
"fmt"
65
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
76
"log"
7+
"strings"
88
)
99

1010
func resourceSumologicOTCollector() *schema.Resource {
@@ -78,6 +78,9 @@ func resourceSumologicOTCollector() *schema.Resource {
7878
Type: schema.TypeString,
7979
},
8080
Optional: true,
81+
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
82+
return strings.HasPrefix(strings.ToLower(k), "tags.sumo.disco")
83+
},
8184
},
8285
},
8386
}
@@ -98,7 +101,7 @@ func resourceSumologicOTCollectorRead(d *schema.ResourceData, meta interface{})
98101
return nil
99102
}
100103

101-
if err := d.Set("tags", otCollector.Tags); err != nil {
104+
if err := d.Set("tags", filterUpdatableTags(otCollector.Tags)); err != nil {
102105
return fmt.Errorf("error setting fields for resource %s: %s", d.Id(), err)
103106
}
104107

@@ -124,7 +127,49 @@ func resourceSumologicOTCollectorDelete(d *schema.ResourceData, meta interface{}
124127
}
125128

126129
func resourceSumologicOTCollectorUpdate(d *schema.ResourceData, meta interface{}) error {
127-
return errors.New("Terraform does not support OTel collector updates")
130+
c := meta.(*Client)
131+
id := d.Id()
132+
req := make(map[string]interface{})
133+
if d.HasChange("time_zone") {
134+
req["timezone"] = d.Get("time_zone")
135+
}
136+
137+
if d.HasChange("name") {
138+
req["name"] = d.Get("name")
139+
}
140+
141+
if d.HasChange("tags") {
142+
newTags := d.Get("tags").(map[string]interface{})
143+
filtered := filterUpdatableTags(newTags)
144+
145+
if len(filtered) > 0 {
146+
req["tags"] = filtered
147+
}
148+
}
149+
150+
if len(req) == 0 {
151+
return resourceSumologicOTCollectorRead(d, meta)
152+
}
153+
if err := c.UpdateOTCollector(id, req); err != nil {
154+
return fmt.Errorf("failed to update OTel collector %s: %w", id, err)
155+
}
156+
157+
return resourceSumologicOTCollectorRead(d, meta)
158+
}
159+
160+
func filterUpdatableTags(tags map[string]interface{}) map[string]interface{} {
161+
if tags == nil {
162+
return nil
163+
}
164+
165+
updatable := make(map[string]interface{})
166+
for k, v := range tags {
167+
if strings.HasPrefix(k, "sumo.disco") {
168+
continue
169+
}
170+
updatable[k] = v
171+
}
172+
return updatable
128173
}
129174

130175
func resourceToOTCollector(d *schema.ResourceData) OTCollector {
@@ -142,6 +187,6 @@ func resourceToOTCollector(d *schema.ResourceData) OTCollector {
142187
TimeZone: d.Get("time_zone").(string),
143188
ID: d.Id(),
144189
Name: d.Get("name").(string),
145-
Tags: d.Get("fields").(map[string]interface{}),
190+
Tags: d.Get("tags").(map[string]interface{}),
146191
}
147192
}

sumologic/resource_sumologic_source_template.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ func resourceSumologicSourceTemplate() *schema.Resource {
122122
Computed: true,
123123
},
124124

125+
"is_enabled": {
126+
Type: schema.TypeBool,
127+
Optional: true,
128+
Computed: true,
129+
Description: "Enable or disable the source template",
130+
},
131+
125132
"input_json": {
126133
Type: schema.TypeString,
127134
Description: "inputJson of source template",
@@ -143,7 +150,15 @@ func resourceSumologicSourceTemplateRead(d *schema.ResourceData, meta interface{
143150
}
144151

145152
if sourceTemplate == nil {
146-
log.Printf("[WARN] SourceTemplate not found, removing from state: %v - %v", id, err)
153+
// API returns 404 for both disabled and deleted source templates
154+
// Check if we expect this to be disabled - if so, keep it in state
155+
// If we expect it to be enabled, then it was actually deleted - remove from state
156+
isEnabledInState := d.Get("is_enabled").(bool)
157+
if !isEnabledInState {
158+
log.Printf("[INFO] SourceTemplate not found (likely disabled): %v - keeping in state", id)
159+
return nil
160+
}
161+
log.Printf("[WARN] SourceTemplate not found (actually deleted): %v - removing from state", id)
147162
d.SetId("")
148163
return nil
149164
}
@@ -160,6 +175,10 @@ func resourceSumologicSourceTemplateRead(d *schema.ResourceData, meta interface{
160175
return fmt.Errorf("Error setting input_json for resource %s: %s", d.Id(), err)
161176
}
162177

178+
if sourceTemplate.IsEnabled != nil {
179+
d.Set("is_enabled", sourceTemplate.IsEnabled)
180+
}
181+
163182
d.Set("created_by", sourceTemplate.CreatedBy)
164183
d.Set("modified_by", sourceTemplate.ModifiedBy)
165184
d.Set("created_at", sourceTemplate.CreatedAt)
@@ -216,6 +235,11 @@ func resourceToSourceTemplate(d *schema.ResourceData) SourceTemplate {
216235
log.Println("Unable to unmarshal the input json configuration")
217236
return SourceTemplate{}
218237
}
238+
var isEnabled *bool
239+
if v, ok := d.GetOkExists("is_enabled"); ok {
240+
val := v.(bool)
241+
isEnabled = &val
242+
}
219243

220244
return SourceTemplate{
221245
Config: d.Get("config").(string),
@@ -224,6 +248,7 @@ func resourceToSourceTemplate(d *schema.ResourceData) SourceTemplate {
224248
TotalCollectorLinked: d.Get("total_collector_linked").(int),
225249
Selector: selector,
226250
InputJson: jsonRawConf,
251+
IsEnabled: isEnabled,
227252
CreatedBy: d.Get("created_by").(string),
228253
ModifiedBy: d.Get("modified_by").(string),
229254
ModifiedAt: d.Get("modified_at").(string),

sumologic/resource_sumologic_source_template_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,87 @@ func TestAccSumologicSourceTemplate_update(t *testing.T) {
9797
})
9898
}
9999

100+
func TestAccSumologicSourceTemplate_enableDisable(t *testing.T) {
101+
var sourceTemplate SourceTemplate
102+
103+
testSchemaRef := `type = "Mac"`
104+
testSelector := `tags = [
105+
[
106+
{
107+
key = "tag"
108+
values = ["Value"]
109+
}
110+
]
111+
]`
112+
113+
testInputJson := `jsonencode({
114+
"name": "hostmetrics_test_source_template_enable_disable",
115+
"description": "Host metric source",
116+
"receivers": {
117+
"hostmetrics": {
118+
"receiverType": "hostmetrics",
119+
"collection_interval": "5m"
120+
}
121+
}
122+
})`
123+
124+
resource.Test(t, resource.TestCase{
125+
PreCheck: func() { testAccPreCheck(t) },
126+
Providers: testAccProviders,
127+
CheckDestroy: testAccCheckSourceTemplateDestroy(sourceTemplate),
128+
Steps: []resource.TestStep{
129+
{
130+
Config: testAccSumologicSourceTemplateWithEnabled(
131+
testSchemaRef,
132+
testSelector,
133+
testInputJson,
134+
true,
135+
),
136+
Check: resource.ComposeTestCheckFunc(
137+
testAccCheckSourceTemplateExists("sumologic_source_template.test", &sourceTemplate, t),
138+
resource.TestCheckResourceAttr(
139+
"sumologic_source_template.test",
140+
"is_enabled",
141+
"true",
142+
),
143+
),
144+
},
145+
// Step 2: Disable via update
146+
{
147+
Config: testAccSumologicSourceTemplateWithEnabled(
148+
testSchemaRef,
149+
testSelector,
150+
testInputJson,
151+
false,
152+
),
153+
Check: resource.ComposeTestCheckFunc(
154+
resource.TestCheckResourceAttr(
155+
"sumologic_source_template.test",
156+
"is_enabled",
157+
"false",
158+
),
159+
),
160+
},
161+
// Step 3: Re-enable via update
162+
{
163+
Config: testAccSumologicSourceTemplateWithEnabled(
164+
testSchemaRef,
165+
testSelector,
166+
testInputJson,
167+
true,
168+
),
169+
Check: resource.ComposeTestCheckFunc(
170+
resource.TestCheckResourceAttr(
171+
"sumologic_source_template.test",
172+
"is_enabled",
173+
"true",
174+
),
175+
),
176+
},
177+
},
178+
})
179+
}
180+
100181
func testAccCheckSourceTemplateDestroy(sourceTemplate SourceTemplate) resource.TestCheckFunc {
101182
return func(s *terraform.State) error {
102183
client := testAccProvider.Meta().(*Client)
@@ -196,3 +277,25 @@ func testAccCheckSourceTemplateAttributes(name string) resource.TestCheckFunc {
196277
return f(s)
197278
}
198279
}
280+
281+
func testAccSumologicSourceTemplateWithEnabled(
282+
schemaRef string,
283+
selector string,
284+
inputJson string,
285+
isEnabled bool,
286+
) string {
287+
return fmt.Sprintf(`
288+
resource "sumologic_source_template" "test" {
289+
schema_ref {
290+
%s
291+
}
292+
293+
selector {
294+
%s
295+
}
296+
297+
is_enabled = %t
298+
input_json = %s
299+
}
300+
`, schemaRef, selector, isEnabled, inputJson)
301+
}

sumologic/sumologic_ot_collector.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,39 @@ func (s *Client) DeleteOTCollector(id string) error {
4343
return err
4444
}
4545

46+
func (s *Client) UpdateOTCollector(id string, req map[string]interface{}) error {
47+
url := fmt.Sprintf("v1/otCollectors?id=%s", id)
48+
49+
data, err := s.Put(url, req)
50+
if err != nil {
51+
return err
52+
}
53+
54+
if len(data) == 0 {
55+
return nil
56+
}
57+
var resp UpdateOTCollectorResponse
58+
if err := json.Unmarshal(data, &resp); err != nil {
59+
return err
60+
}
61+
if len(resp.Errors) > 0 {
62+
return fmt.Errorf("update ot collector failed: %v", resp.Errors)
63+
}
64+
return nil
65+
}
66+
67+
type UpdateOTCollectorResponse struct {
68+
ID string `json:"id"`
69+
Errors []map[string]interface{} `json:"errors,omitempty"`
70+
}
71+
4672
type OTCollector struct {
4773
Category string `json:"category"`
4874
CreatedAt string `json:"createdAt"`
4975
CreatedBy string `json:"createdBy"`
5076
ModifiedBy string `json:"modifiedBy"`
5177
ModifiedAt string `json:"modifiedAt"`
52-
TimeZone string `json:"timeZone"`
78+
TimeZone string `json:"timezone"`
5379
IsAlive bool `json:"alive"`
5480
IsRemotelyManaged bool `json:"isRemotelyManaged"`
5581
ID string `json:"id,omitempty"`

sumologic/sumologic_source_template.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
)
77

88
func (s *Client) GetSourceTemplate(id string) (*SourceTemplate, error) {
9-
urlWithoutParams := "v1/sourceTemplate/%s"
9+
urlWithoutParams := "v1/sourceTemplates/%s"
1010
paramString := ""
1111
sprintfArgs := []interface{}{}
1212
sprintfArgs = append(sprintfArgs, id)
@@ -32,7 +32,7 @@ func (s *Client) GetSourceTemplate(id string) (*SourceTemplate, error) {
3232
}
3333

3434
func (s *Client) UpdateSourceTemplate(sourceTemplate SourceTemplate) error {
35-
urlWithoutParams := "v1/sourceTemplate/%s"
35+
urlWithoutParams := "v1/sourceTemplates/%s"
3636
paramString := ""
3737
sprintfArgs := []interface{}{}
3838
sprintfArgs = append(sprintfArgs, sourceTemplate.ID)
@@ -44,7 +44,7 @@ func (s *Client) UpdateSourceTemplate(sourceTemplate SourceTemplate) error {
4444
}
4545

4646
func (s *Client) DeleteSourceTemplate(id string) error {
47-
urlWithoutParams := "v1/sourceTemplate/%s"
47+
urlWithoutParams := "v1/sourceTemplates/%s"
4848
paramString := ""
4949
sprintfArgs := []interface{}{}
5050
sprintfArgs = append(sprintfArgs, id)
@@ -57,7 +57,7 @@ func (s *Client) DeleteSourceTemplate(id string) error {
5757
}
5858

5959
func (s *Client) CreateSourceTemplate(sourceTemplate SourceTemplate) (string, error) {
60-
urlWithoutParams := "v1/sourceTemplate"
60+
urlWithoutParams := "v1/sourceTemplates"
6161

6262
data, err := s.Post(urlWithoutParams, sourceTemplate)
6363
if err != nil {
@@ -81,6 +81,7 @@ type SourceTemplate struct {
8181
Selector Selector `json:"selector,omitempty"`
8282
Config string `json:"config"`
8383
InputJson json.RawMessage `json:"inputJson"`
84+
IsEnabled *bool `json:"isEnabled,omitempty"`
8485
CreatedAt string `json:"createdAt"`
8586
CreatedBy string `json:"createdBy"`
8687
ModifiedBy string `json:"modifiedBy"`

website/docs/r/ot_collector.html.markdown

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ description: |-
77

88
# sumologic_OT_Collector
99
Provides a [Sumologic OT Collector][1]
10-
**NOTE**: This resource can only be imported and deleted by terraform. Creation/Updation of this resource via terrform is not supported.
11-
Tag Edit functionality will be supported in future.
10+
11+
**NOTE**:
12+
- This resource can only be imported and deleted by terraform. Creation of this resource via terraform is not supported.
13+
- Editing of supported fields (such as name, timezone, and tags) is now supported via Terraform.
1214

1315
## Example Usage
1416
```hcl

0 commit comments

Comments
 (0)