Skip to content
Merged
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
18 changes: 6 additions & 12 deletions pkg/bcel/bcel.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,12 @@ func resourceToCELMap(resource *v2.Resource) map[string]any {
"DisplayName": resource.DisplayName,
}

if t, err := sdkResource.GetGroupTrait(resource); err == nil && t.GetProfile() != nil {
out["profile"] = t.GetProfile().AsMap()
} else if t, err := sdkResource.GetUserTrait(resource); err == nil && t.GetProfile() != nil {
out["profile"] = t.GetProfile().AsMap()
} else if t, err := sdkResource.GetRoleTrait(resource); err == nil && t.GetProfile() != nil {
out["profile"] = t.GetProfile().AsMap()
} else if t, err := sdkResource.GetAppTrait(resource); err == nil && t.GetProfile() != nil {
out["profile"] = t.GetProfile().AsMap()
}

// Empty default so `has(resource.profile.X)` is well-defined for optional fields.
if _, exists := out["profile"]; !exists {
// Profile lives on Resource (trait-level profile is deprecated SA1019).
// GetProfile reads resource-level first and falls back to legacy trait fields.
if profile := sdkResource.GetProfile(resource); profile != nil {
out["profile"] = profile.AsMap()
} else {
// Empty default so `has(resource.profile.X)` is well-defined for optional fields.
out["profile"] = map[string]any{}
}

Expand Down
10 changes: 8 additions & 2 deletions pkg/bsql/nhi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,17 @@ func TestMapResource_AgentTrait(t *testing.T) {

at, err := sdkResource.GetAgentTrait(r)
require.NoError(t, err)
require.Equal(t, v2.AgentTrait_AGENT_STATUS_READY, at.GetStatus())
// Status and profile live on Resource (trait-level getters are deprecated SA1019).
// Agent READY maps to RESOURCE_STATUS_ENABLED (identical enum values).
st := sdkResource.GetStatus(r)
require.NotNil(t, st)
require.Equal(t, v2.Status_RESOURCE_STATUS_ENABLED, st.GetStatus())
require.NotNil(t, at.GetIdentityResourceId())
require.Equal(t, "user", at.GetIdentityResourceId().GetResourceType())
require.Equal(t, "svc-acct-1", at.GetIdentityResourceId().GetResource())
require.Equal(t, "claude-opus", at.GetProfile().GetFields()["model"].GetStringValue())
profile := sdkResource.GetProfile(r)
require.NotNil(t, profile)
require.Equal(t, "claude-opus", profile.GetFields()["model"].GetStringValue())
}

// Graceful degradation — a plain user resource emits only a UserTrait, no
Expand Down
113 changes: 71 additions & 42 deletions pkg/bsql/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,45 +101,47 @@ func (s *SQLSyncer) mapUserTrait(ctx context.Context, r *v2.Resource, rowMap map
opts = append(opts, sdkResource.WithEmail(v, primary))
}

// Status
// Status lives on Resource (trait-level status options are deprecated SA1019).
var resourceStatus *v2.Status_ResourceStatus
var statusDetails string
if mappings.Status != "" {
statusValue, err := s.env.EvaluateString(ctx, mappings.Status, inputs)
if err != nil {
return err
}

var status v2.UserTrait_Status_Status
var status v2.Status_ResourceStatus
switch strings.ToLower(statusValue) {
case "active":
status = v2.UserTrait_Status_STATUS_ENABLED
status = v2.Status_RESOURCE_STATUS_ENABLED
case "enabled":
status = v2.UserTrait_Status_STATUS_ENABLED
status = v2.Status_RESOURCE_STATUS_ENABLED
case "disabled":
status = v2.UserTrait_Status_STATUS_DISABLED
status = v2.Status_RESOURCE_STATUS_DISABLED
case "inactive":
status = v2.UserTrait_Status_STATUS_DISABLED
status = v2.Status_RESOURCE_STATUS_DISABLED
case "suspended":
status = v2.UserTrait_Status_STATUS_DISABLED
status = v2.Status_RESOURCE_STATUS_DISABLED
case "locked":
status = v2.UserTrait_Status_STATUS_DISABLED
status = v2.Status_RESOURCE_STATUS_DISABLED
case "deleted":
status = v2.UserTrait_Status_STATUS_DELETED
status = v2.Status_RESOURCE_STATUS_DELETED
default:
l.Warn("unexpected status value in mapping", zap.String("status", statusValue))
status = v2.UserTrait_Status_STATUS_UNSPECIFIED
status = v2.Status_RESOURCE_STATUS_UNSPECIFIED
}
resourceStatus = &status

if mappings.StatusDetails != "" {
v, err := s.env.EvaluateString(ctx, mappings.StatusDetails, inputs)
if err != nil {
return err
}
opts = append(opts, sdkResource.WithDetailedStatus(status, v))
} else {
opts = append(opts, sdkResource.WithStatus(status))
statusDetails = v
}
}

// Profile lives on Resource (trait-level profile options are deprecated SA1019).
profile := make(map[string]interface{})
for profileKey, profileValue := range mappings.Profile {
v, err := s.env.EvaluateString(ctx, profileValue, inputs)
Expand All @@ -149,10 +151,6 @@ func (s *SQLSyncer) mapUserTrait(ctx context.Context, r *v2.Resource, rowMap map
profile[profileKey] = v
}

if len(profile) > 0 {
opts = append(opts, sdkResource.WithUserProfile(profile))
}

// Last Login
if mappings.LastLogin != "" {
lastLoginValue, err := s.env.EvaluateString(ctx, mappings.LastLogin, inputs)
Expand Down Expand Up @@ -271,6 +269,17 @@ func (s *SQLSyncer) mapUserTrait(ctx context.Context, r *v2.Resource, rowMap map
annos.Update(t)
r.Annotations = annos

if resourceStatus != nil {
if err := sdkResource.WithResourceStatus(*resourceStatus, statusDetails)(r); err != nil {
return err
}
}
if len(profile) > 0 {
if err := sdkResource.WithResourceProfile(profile)(r); err != nil {
return err
}
}

// Annotation applied

return nil
Expand All @@ -291,6 +300,7 @@ func (s *SQLSyncer) mapAppTrait(ctx context.Context, r *v2.Resource, rowMap map[
opts = append(opts, sdkResource.WithAppHelpURL(v))
}

// Profile lives on Resource (trait-level profile options are deprecated SA1019).
profile := make(map[string]interface{})
for profileKey, profileValue := range mappings.Profile {
v, err := s.env.EvaluateString(ctx, profileValue, inputs)
Expand All @@ -300,10 +310,6 @@ func (s *SQLSyncer) mapAppTrait(ctx context.Context, r *v2.Resource, rowMap map[
profile[profileKey] = v
}

if len(profile) > 0 {
opts = append(opts, sdkResource.WithAppProfile(profile))
}

t, err := sdkResource.NewAppTrait(opts...)
if err != nil {
return err
Expand All @@ -313,6 +319,12 @@ func (s *SQLSyncer) mapAppTrait(ctx context.Context, r *v2.Resource, rowMap map[
annos.Update(t)
r.Annotations = annos

if len(profile) > 0 {
if err := sdkResource.WithResourceProfile(profile)(r); err != nil {
return err
}
}

return nil
}

Expand All @@ -321,8 +333,7 @@ func (s *SQLSyncer) mapGroupTrait(ctx context.Context, r *v2.Resource, rowMap ma

mappings := s.config.List.Map.Traits.Group

var opts []sdkResource.GroupTraitOption

// Profile lives on Resource (trait-level profile options are deprecated SA1019).
profile := make(map[string]interface{})
for profileKey, profileValue := range mappings.Profile {
v, err := s.env.EvaluateString(ctx, profileValue, inputs)
Expand All @@ -331,11 +342,8 @@ func (s *SQLSyncer) mapGroupTrait(ctx context.Context, r *v2.Resource, rowMap ma
}
profile[profileKey] = v
}
if len(profile) > 0 {
opts = append(opts, sdkResource.WithGroupProfile(profile))
}

t, err := sdkResource.NewGroupTrait(opts...)
t, err := sdkResource.NewGroupTrait()
if err != nil {
return err
}
Expand All @@ -344,6 +352,12 @@ func (s *SQLSyncer) mapGroupTrait(ctx context.Context, r *v2.Resource, rowMap ma
annos.Update(t)
r.Annotations = annos

if len(profile) > 0 {
if err := sdkResource.WithResourceProfile(profile)(r); err != nil {
return err
}
}

return nil
}

Expand All @@ -352,8 +366,7 @@ func (s *SQLSyncer) mapRoleTrait(ctx context.Context, r *v2.Resource, rowMap map

mappings := s.config.List.Map.Traits.Role

var opts []sdkResource.RoleTraitOption

// Profile lives on Resource (trait-level profile options are deprecated SA1019).
profile := make(map[string]interface{})
for profileKey, profileValue := range mappings.Profile {
v, err := s.env.EvaluateString(ctx, profileValue, inputs)
Expand All @@ -362,11 +375,8 @@ func (s *SQLSyncer) mapRoleTrait(ctx context.Context, r *v2.Resource, rowMap map
}
profile[profileKey] = v
}
if len(profile) > 0 {
opts = append(opts, sdkResource.WithRoleProfile(profile))
}

t, err := sdkResource.NewRoleTrait(opts...)
t, err := sdkResource.NewRoleTrait()
if err != nil {
return err
}
Expand All @@ -375,6 +385,12 @@ func (s *SQLSyncer) mapRoleTrait(ctx context.Context, r *v2.Resource, rowMap map
annos.Update(t)
r.Annotations = annos

if len(profile) > 0 {
if err := sdkResource.WithResourceProfile(profile)(r); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -467,25 +483,29 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma

var opts []sdkResource.AgentTraitOption

// Status lives on Resource (trait-level status options are deprecated SA1019).
// AgentTrait_AgentStatus and Status_ResourceStatus share the same numeric values
// (READY maps to ENABLED).
var resourceStatus *v2.Status_ResourceStatus
if mappings.Status != "" {
v, err := s.env.EvaluateString(ctx, mappings.Status, inputs)
if err != nil {
return err
}

var status v2.AgentTrait_AgentStatus
var status v2.Status_ResourceStatus
switch strings.ToLower(v) {
case "ready", "active", "enabled":
status = v2.AgentTrait_AGENT_STATUS_READY
status = v2.Status_RESOURCE_STATUS_ENABLED
case "disabled", "inactive":
status = v2.AgentTrait_AGENT_STATUS_DISABLED
status = v2.Status_RESOURCE_STATUS_DISABLED
case "deleted":
status = v2.AgentTrait_AGENT_STATUS_DELETED
status = v2.Status_RESOURCE_STATUS_DELETED
default:
l.Warn("unexpected agent status value in mapping", zap.String("status", v))
status = v2.AgentTrait_AGENT_STATUS_UNSPECIFIED
status = v2.Status_RESOURCE_STATUS_UNSPECIFIED
}
opts = append(opts, sdkResource.WithAgentStatus(status))
resourceStatus = &status
}

if mappings.IdentityResourceID != "" {
Expand All @@ -512,6 +532,7 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma
}
}

// Profile lives on Resource (trait-level profile options are deprecated SA1019).
profile := make(map[string]interface{})
for profileKey, profileValue := range mappings.Profile {
v, err := s.env.EvaluateString(ctx, profileValue, inputs)
Expand All @@ -520,9 +541,6 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma
}
profile[profileKey] = v
}
if len(profile) > 0 {
opts = append(opts, sdkResource.WithAgentProfile(profile))
}

t, err := sdkResource.NewAgentTrait(opts...)
if err != nil {
Expand All @@ -533,6 +551,17 @@ func (s *SQLSyncer) mapAgentTrait(ctx context.Context, r *v2.Resource, rowMap ma
annos.Update(t)
r.Annotations = annos

if resourceStatus != nil {
if err := sdkResource.WithResourceStatus(*resourceStatus, "")(r); err != nil {
return err
}
}
if len(profile) > 0 {
if err := sdkResource.WithResourceProfile(profile)(r); err != nil {
return err
}
}

return nil
}

Expand Down
Loading