Skip to content

Commit

Permalink
Check type casts to void crashing when casting explicit null values (#…
Browse files Browse the repository at this point in the history
…534)

* Ignore fields explicitly set to null in Kibana resources

* Ignore fields explicitly set to null in APM resources

* Ignore fields explicitly set to null in ES resources

* Ignore fields explicitly set to null in Enterprise Search resources

* Ignore fields explicitly set to null in Integrations Server  resources

* Ignore fields explicitly set to null in observability resources

* Ignore fields explicitly set to null in azure private endpoint resources

* Changelog
  • Loading branch information
tobio authored Oct 5, 2022
1 parent e8b5ad2 commit 00075aa
Show file tree
Hide file tree
Showing 16 changed files with 641 additions and 344 deletions.
3 changes: 3 additions & 0 deletions .changelog/534.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource: Updates all nested field accesses to validate type casts. This prevents a provider crash when a field is explicitly set to `null`.
```
79 changes: 38 additions & 41 deletions ec/ecresource/deploymentresource/apm_expanders.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,27 +53,25 @@ func expandApmResources(apms []interface{}, tpl *models.ApmPayload) ([]*models.A
func expandApmResource(raw interface{}, res *models.ApmPayload) (*models.ApmPayload, error) {
var apm = raw.(map[string]interface{})

if esRefID, ok := apm["elasticsearch_cluster_ref_id"]; ok {
res.ElasticsearchClusterRefID = ec.String(esRefID.(string))
if esRefID, ok := apm["elasticsearch_cluster_ref_id"].(string); ok {
res.ElasticsearchClusterRefID = ec.String(esRefID)
}

if refID, ok := apm["ref_id"]; ok {
res.RefID = ec.String(refID.(string))
if refID, ok := apm["ref_id"].(string); ok {
res.RefID = ec.String(refID)
}

if region, ok := apm["region"]; ok {
if r := region.(string); r != "" {
res.Region = ec.String(r)
}
if region, ok := apm["region"].(string); ok && region != "" {
res.Region = ec.String(region)
}

if cfg, ok := apm["config"]; ok {
if cfg, ok := apm["config"].([]interface{}); ok {
if err := expandApmConfig(cfg, res.Plan.Apm); err != nil {
return nil, err
}
}

if rt, ok := apm["topology"]; ok && len(rt.([]interface{})) > 0 {
if rt, ok := apm["topology"].([]interface{}); ok && len(rt) > 0 {
topology, err := expandApmTopology(rt, res.Plan.ClusterTopology)
if err != nil {
return nil, err
Expand All @@ -86,15 +84,18 @@ func expandApmResource(raw interface{}, res *models.ApmPayload) (*models.ApmPayl
return res, nil
}

func expandApmTopology(raw interface{}, topologies []*models.ApmTopologyElement) ([]*models.ApmTopologyElement, error) {
rawTopologies := raw.([]interface{})
func expandApmTopology(rawTopologies []interface{}, topologies []*models.ApmTopologyElement) ([]*models.ApmTopologyElement, error) {
res := make([]*models.ApmTopologyElement, 0, len(rawTopologies))

for i, rawTop := range rawTopologies {
topology := rawTop.(map[string]interface{})
topology, ok := rawTop.(map[string]interface{})
if !ok {
continue
}

var icID string
if id, ok := topology["instance_configuration_id"]; ok {
icID = id.(string)
if id, ok := topology["instance_configuration_id"].(string); ok {
icID = id
}
// When a topology element is set but no instance_configuration_id
// is set, then obtain the instance_configuration_id from the topology
Expand All @@ -116,11 +117,8 @@ func expandApmTopology(raw interface{}, topologies []*models.ApmTopologyElement)
elem.Size = size
}

if zones, ok := topology["zone_count"]; ok {
if z := zones.(int); z > 0 {
elem.ZoneCount = int32(z)
}

if zones, ok := topology["zone_count"].(int); ok && zones > 0 {
elem.ZoneCount = int32(zones)
}

res = append(res, elem)
Expand All @@ -129,40 +127,39 @@ func expandApmTopology(raw interface{}, topologies []*models.ApmTopologyElement)
return res, nil
}

func expandApmConfig(raw interface{}, res *models.ApmConfiguration) error {
for _, rawCfg := range raw.([]interface{}) {
var cfg = rawCfg.(map[string]interface{})
func expandApmConfig(raw []interface{}, res *models.ApmConfiguration) error {
for _, rawCfg := range raw {
cfg, ok := rawCfg.(map[string]interface{})
if !ok {
continue
}

if debugEnabled, ok := cfg["debug_enabled"]; ok {
if debugEnabled, ok := cfg["debug_enabled"].(bool); ok {
if res.SystemSettings == nil {
res.SystemSettings = &models.ApmSystemSettings{}
}
res.SystemSettings.DebugEnabled = ec.Bool(debugEnabled.(bool))
res.SystemSettings.DebugEnabled = ec.Bool(debugEnabled)
}

if settings, ok := cfg["user_settings_json"]; ok && settings != nil {
if s, ok := settings.(string); ok && s != "" {
if err := json.Unmarshal([]byte(s), &res.UserSettingsJSON); err != nil {
return fmt.Errorf("failed expanding apm user_settings_json: %w", err)
}
if settings, ok := cfg["user_settings_json"].(string); ok && settings != "" {
if err := json.Unmarshal([]byte(settings), &res.UserSettingsJSON); err != nil {
return fmt.Errorf("failed expanding apm user_settings_json: %w", err)
}
}
if settings, ok := cfg["user_settings_override_json"]; ok && settings != nil {
if s, ok := settings.(string); ok && s != "" {
if err := json.Unmarshal([]byte(s), &res.UserSettingsOverrideJSON); err != nil {
return fmt.Errorf("failed expanding apm user_settings_override_json: %w", err)
}
if settings, ok := cfg["user_settings_override_json"].(string); ok && settings != "" {
if err := json.Unmarshal([]byte(settings), &res.UserSettingsOverrideJSON); err != nil {
return fmt.Errorf("failed expanding apm user_settings_override_json: %w", err)
}
}
if settings, ok := cfg["user_settings_yaml"]; ok {
res.UserSettingsYaml = settings.(string)
if settings, ok := cfg["user_settings_yaml"].(string); ok && settings != "" {
res.UserSettingsYaml = settings
}
if settings, ok := cfg["user_settings_override_yaml"]; ok {
res.UserSettingsOverrideYaml = settings.(string)
if settings, ok := cfg["user_settings_override_yaml"].(string); ok && settings != "" {
res.UserSettingsOverrideYaml = settings
}

if v, ok := cfg["docker_image"]; ok {
res.DockerImage = v.(string)
if v, ok := cfg["docker_image"].(string); ok {
res.DockerImage = v
}
}

Expand Down
41 changes: 41 additions & 0 deletions ec/ecresource/deploymentresource/apm_expanders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,47 @@ func Test_expandApmResources(t *testing.T) {
},
}},
},
{
name: "parses an APM resource with explicit nils",
args: args{
tpl: tpl(),
ess: []interface{}{map[string]interface{}{
"ref_id": "tertiary-apm",
"elasticsearch_cluster_ref_id": "somerefid",
"resource_id": mock.ValidClusterID,
"region": nil,
"config": []interface{}{map[string]interface{}{
"user_settings_yaml": nil,
"user_settings_override_yaml": nil,
"user_settings_json": nil,
"user_settings_override_json": nil,
"debug_enabled": nil,
}},
"topology": []interface{}{map[string]interface{}{
"instance_configuration_id": "aws.apm.r5d",
"size": "4g",
"size_resource": "memory",
"zone_count": 1,
}},
}},
},
want: []*models.ApmPayload{{
ElasticsearchClusterRefID: ec.String("somerefid"),
Region: ec.String("us-east-1"),
RefID: ec.String("tertiary-apm"),
Plan: &models.ApmPlan{
Apm: &models.ApmConfiguration{},
ClusterTopology: []*models.ApmTopologyElement{{
ZoneCount: 1,
InstanceConfigurationID: "aws.apm.r5d",
Size: &models.TopologySize{
Resource: ec.String("memory"),
Value: ec.Int32(4096),
},
}},
},
}},
},
{
name: "tries to parse an apm resource when the template doesn't have an APM instance set.",
args: args{
Expand Down
Loading

0 comments on commit 00075aa

Please sign in to comment.