Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/models/http_event_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ type HttpEventCollectorObject struct {
SourceType string `json:"sourcetype,omitempty" url:"sourcetype,omitempty"`
Token string `json:"token,omitempty" url:"token,omitempty"`
Disabled bool `json:"disabled,omitempty" url:"disabled"`
UseACK int `json:"useACK,string,omitempty" url:"useACK"`
UseACK SplunkBoolInt `json:"useACK,omitempty" url:"useACK"`
}
38 changes: 38 additions & 0 deletions client/models/splunk_bool_int.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package models

import (
"encoding/json"
"fmt"
"strconv"
"strings"
)

// SplunkBoolInt handles Splunk Cloud API responses that return boolean strings
// ("true"/"false") for numeric fields instead of integer strings ("0"/"1").
// This inconsistency exists between Splunk Enterprise and Splunk Cloud APIs.
type SplunkBoolInt int

func (b *SplunkBoolInt) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err == nil {
switch strings.ToLower(strings.TrimSpace(s)) {
case "true", "1":
*b = 1
case "false", "0":
*b = 0
default:
return fmt.Errorf("cannot parse %q as SplunkBoolInt", s)
}
return nil
}
var i int
if err := json.Unmarshal(data, &i); err != nil {
return fmt.Errorf("cannot parse %s as SplunkBoolInt: %w", data, err)
}
*b = SplunkBoolInt(i)
return nil
}

func (b SplunkBoolInt) MarshalJSON() ([]byte, error) {
return json.Marshal(strconv.Itoa(int(b)))
}
75 changes: 75 additions & 0 deletions client/models/splunk_bool_int_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package models

import (
"encoding/json"
"testing"
)

func TestSplunkBoolInt_UnmarshalJSON(t *testing.T) {
tests := []struct {
name string
input string
expected SplunkBoolInt
wantErr bool
}{
// Splunk Cloud returns boolean strings
{name: "string false", input: `"false"`, expected: 0},
{name: "string true", input: `"true"`, expected: 1},
// Splunk Enterprise returns integer strings
{name: "string 0", input: `"0"`, expected: 0},
{name: "string 1", input: `"1"`, expected: 1},
// Direct integer values
{name: "int 0", input: `0`, expected: 0},
{name: "int 1", input: `1`, expected: 1},
// Invalid
{name: "invalid string", input: `"foo"`, wantErr: true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var b SplunkBoolInt
err := json.Unmarshal([]byte(tt.input), &b)
if (err != nil) != tt.wantErr {
t.Errorf("UnmarshalJSON(%s) error = %v, wantErr %v", tt.input, err, tt.wantErr)
return
}
if !tt.wantErr && b != tt.expected {
t.Errorf("UnmarshalJSON(%s) = %d, want %d", tt.input, b, tt.expected)
}
})
}
}

func TestSplunkBoolInt_MarshalJSON(t *testing.T) {
tests := []struct {
input SplunkBoolInt
expected string
}{
{input: 0, expected: `"0"`},
{input: 1, expected: `"1"`},
}

for _, tt := range tests {
data, err := json.Marshal(tt.input)
if err != nil {
t.Errorf("MarshalJSON(%d) error = %v", tt.input, err)
continue
}
if string(data) != tt.expected {
t.Errorf("MarshalJSON(%d) = %s, want %s", tt.input, data, tt.expected)
}
}
}

func TestHttpEventCollectorObject_UnmarshalUseACK(t *testing.T) {
// Simulates the Splunk Cloud API response that was causing the bug
splunkCloudResponse := `{"useACK": "false", "index": "main"}`

var obj HttpEventCollectorObject
if err := json.Unmarshal([]byte(splunkCloudResponse), &obj); err != nil {
t.Fatalf("failed to unmarshal Splunk Cloud response: %v", err)
}
if obj.UseACK != 0 {
t.Errorf("UseACK = %d, want 0", obj.UseACK)
}
}
4 changes: 2 additions & 2 deletions splunk/resource_splunk_inputs_http_event_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func httpEventCollectorInputRead(d *schema.ResourceData, meta interface{}) error
return err
}

if err = d.Set("use_ack", entry.Content.UseACK); err != nil {
if err = d.Set("use_ack", int(entry.Content.UseACK)); err != nil {
return err
}

Expand Down Expand Up @@ -238,7 +238,7 @@ func getHttpEventCollectorConfig(d *schema.ResourceData) (httpInputConfigObject
httpInputConfigObject.Indexes = d.Get("indexes").([]interface{})
httpInputConfigObject.Source = d.Get("source").(string)
httpInputConfigObject.SourceType = d.Get("sourcetype").(string)
httpInputConfigObject.UseACK = d.Get("use_ack").(int)
httpInputConfigObject.UseACK = models.SplunkBoolInt(d.Get("use_ack").(int))
httpInputConfigObject.Disabled = d.Get("disabled").(bool)
return httpInputConfigObject
}
Expand Down