Skip to content
Closed
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
17 changes: 15 additions & 2 deletions internal/fleet/integration_policy/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data *
model.Enabled = types.BoolValue(data.Enabled)
model.IntegrationName = types.StringValue(data.Package.Name)
model.IntegrationVersion = types.StringValue(data.Package.Version)
model.VarsJson = utils.MapToNormalizedType(utils.Deref(data.Vars), path.Root("vars_json"), &diags)
model.VarsJson = normalizeVarsJson(model.VarsJson, utils.MapToNormalizedType(utils.Deref(data.Vars), path.Root("vars_json"), &diags))

model.populateInputFromAPI(ctx, data.Inputs, &diags)

Expand All @@ -64,7 +64,7 @@ func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, i
InputID: types.StringValue(meta.Key),
Enabled: types.BoolPointerValue(inputData.Enabled),
StreamsJson: utils.MapToNormalizedType(utils.Deref(inputData.Streams), meta.Path.AtName("streams_json"), diags),
VarsJson: utils.MapToNormalizedType(utils.Deref(inputData.Vars), meta.Path.AtName("vars_json"), diags),
VarsJson: normalizeVarsJson(model.VarsJson, utils.MapToNormalizedType(utils.Deref(inputData.Vars), meta.Path.AtName("vars_json"), diags)),
}
})
if newInputs == nil {
Expand Down Expand Up @@ -147,3 +147,16 @@ func sortInputs(incoming []integrationPolicyInputModel, existing []integrationPo
return iIdx < jIdx
})
}

// normalizeVarsJson handles the case where both nil (from API) and "{}" (from config)
// represent an empty JSON object. If the existing value is "{}" and the incoming
// value is null (from API returning nil), preserve the "{}" to avoid unwanted diffs.
func normalizeVarsJson(existing jsontypes.Normalized, incoming jsontypes.Normalized) jsontypes.Normalized {
if incoming.IsNull() && !existing.IsNull() && !existing.IsUnknown() {
existingValue := existing.ValueString()
if existingValue == "{}" {
return jsontypes.NewNormalizedValue("{}")
}
}
return incoming
}
39 changes: 39 additions & 0 deletions internal/fleet/integration_policy/models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package integration_policy
import (
"testing"

"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -62,3 +63,41 @@ func Test_SortInputs(t *testing.T) {
require.Equal(t, want, incoming)
})
}

func TestNormalizeVarsJson(t *testing.T) {
t.Parallel()

tests := []struct {
name string
existing jsontypes.Normalized
api jsontypes.Normalized
want jsontypes.Normalized
}{
{
name: "plan defines empty object, but api returns null -> preserve empty object",
existing: jsontypes.NewNormalizedValue("{}"),
api: jsontypes.NewNormalizedNull(),
want: jsontypes.NewNormalizedValue("{}"),
},
{
name: "plan does not define value, api returns null -> preserve null",
existing: jsontypes.NewNormalizedUnknown(),
api: jsontypes.NewNormalizedNull(),
want: jsontypes.NewNormalizedNull(),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := normalizeVarsJson(tt.existing, tt.api)

// Compare the states and values
require.Equal(t, tt.want.IsNull(), result.IsNull(), "IsNull() should match")
require.Equal(t, tt.want.IsUnknown(), result.IsUnknown(), "IsUnknown() should match")

if !tt.want.IsNull() && !tt.want.IsUnknown() {
require.Equal(t, tt.want.ValueString(), result.ValueString(), "ValueString() should match")
}
})
}
}
41 changes: 36 additions & 5 deletions internal/fleet/integration_policy/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,25 @@ func HandleRespSecrets(ctx context.Context, resp *kbapi.PackagePolicy, private p
}

handleVar := func(key string, mval map[string]any, vars map[string]any) {
refID := mval["id"].(string)
if original, ok := secrets[refID]; ok {
vars[key] = original
// Handle single secret reference: {"id": "refID", "isSecretRef": true}
if refID, ok := mval["id"].(string); ok {
if original, ok := secrets[refID]; ok {
vars[key] = original
}
return
}

// Handle list secret reference: {"ids": ["a", "b"], "isSecretRef": true}
if refIDs, ok := mval["ids"].([]any); ok {
resolvedValues := make([]any, 0, len(refIDs))
for _, refIDInterface := range refIDs {
if refID, ok := refIDInterface.(string); ok {
if original, ok := secrets[refID]; ok {
resolvedValues = append(resolvedValues, original)
}
}
}
vars[key] = resolvedValues
}
}

Expand Down Expand Up @@ -136,8 +152,23 @@ func HandleReqRespSecrets(ctx context.Context, req kbapi.PackagePolicyRequest, r
}
}

refID := mval["id"].(string)
secrets[refID] = original
// Handle single secret reference: {"id": "refID", "isSecretRef": true}
if refID, ok := mval["id"].(string); ok {
secrets[refID] = original
return
}

// Handle list secret reference: {"ids": ["a", "b"], "isSecretRef": true}
if refIDs, ok := mval["ids"].([]any); ok {
// For list secrets, the original should be an array
if originalArray, ok := original.([]any); ok {
for i, refIDInterface := range refIDs {
if refID, ok := refIDInterface.(string); ok && i < len(originalArray) {
secrets[refID] = originalArray[i]
}
}
}
}
}
}

Expand Down
Loading
Loading