Skip to content

Commit 5adbfc4

Browse files
authored
fix: json casing (#177)
* fix: update container definition casing to match Kilt's expected format * Add a couple of unit tests for entrypoint and env (fails due to env order) * Update tests to sort env before comparing * Check for correct capitalization on volumesFrom * Add conversion of linuxParameters to CFN capitalized keys * lint: add missing err checks * fix: remove debug * Add error checking for updateKeys * Update test to use consistent capitalization
1 parent e385da1 commit 5adbfc4

13 files changed

+599
-11
lines changed

sysdig/cfn_preprocess_template.go

Lines changed: 112 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package sysdig
1111
import (
1212
"context"
1313
"fmt"
14+
"unicode"
1415

1516
"github.com/Jeffail/gabs/v2"
1617
"github.com/rs/zerolog/log"
@@ -27,11 +28,50 @@ func GetValueFromTemplate(what *gabs.Container) (string, *gabs.Container) {
2728
fallback = nil
2829
default:
2930
fallback = what
30-
result = "placeholder: " + what.String()
31+
result = what.String()
3132
}
3233
return result, fallback
3334
}
3435

36+
func capitalize(key string) string {
37+
r := []rune(key)
38+
r[0] = unicode.ToUpper(r[0])
39+
return string(r)
40+
}
41+
42+
// updateKey recursively capitalizes the first letter of each key in the input object
43+
func updateKeys(inputMap gabs.Container) error {
44+
// in this case, the object is probably an array, so update each child
45+
if len(inputMap.ChildrenMap()) == 0 {
46+
for _, child := range inputMap.Children() {
47+
err := updateKeys(*child)
48+
if err != nil {
49+
return err
50+
}
51+
}
52+
} else {
53+
for key, child := range inputMap.ChildrenMap() {
54+
_, err := inputMap.Set(child.Data(), capitalize(key))
55+
if err != nil {
56+
return fmt.Errorf("failed to update new key %s", capitalize(key))
57+
}
58+
59+
err = inputMap.Delete(key)
60+
if err != nil {
61+
return fmt.Errorf("failed to delete old key %s" + key)
62+
}
63+
64+
// recurse to update child's keys
65+
err = updateKeys(*child)
66+
if err != nil {
67+
return err
68+
}
69+
}
70+
}
71+
72+
return nil
73+
}
74+
3575
func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte, error) {
3676
l := log.Ctx(ctx)
3777
template, err := gabs.ParseJSON(patchedStack)
@@ -56,8 +96,8 @@ func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte
5696
}
5797
}
5898

59-
if container.Exists("Environment") {
60-
for _, env := range container.S("Environment").Children() {
99+
if container.Exists("environment") {
100+
for _, env := range container.S("environment").Children() {
61101
if env.Exists("name") {
62102
name, _ := env.S("name").Data().(string)
63103
err = env.Delete("name")
@@ -81,22 +121,85 @@ func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte
81121
}
82122
}
83123
}
124+
125+
passthrough, _ := GetValueFromTemplate(container.S("environment"))
126+
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
127+
_, err = container.Set(parsedPassthrough, "Environment")
128+
if err != nil {
129+
return nil, fmt.Errorf("Could not update Environment field: %v", err)
130+
}
131+
132+
err = container.Delete("environment")
133+
if err != nil {
134+
return nil, fmt.Errorf("could not delete environment in the Container definition: %w", err)
135+
}
84136
}
85137

86138
if container.Exists("entryPoint") {
87-
for _, arg := range container.S("entryPoint").Children() {
88-
passthrough, _ := GetValueFromTemplate(arg)
89-
err = container.ArrayAppend(passthrough, "EntryPoint")
90-
if err != nil {
91-
return nil, fmt.Errorf("Could not append entrypoint values to EntryPoint: %v", err)
92-
}
139+
passthrough, _ := GetValueFromTemplate(container.S("entryPoint"))
140+
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
141+
_, err = container.Set(parsedPassthrough, "EntryPoint")
142+
if err != nil {
143+
return nil, fmt.Errorf("Could not update EntryPoint field: %v", err)
93144
}
94145

95146
err = container.Delete("entryPoint")
96147
if err != nil {
97148
return nil, fmt.Errorf("could not delete entryPoint in the Container definition: %w", err)
98149
}
99150
}
151+
152+
if container.Exists("command") {
153+
passthrough, _ := GetValueFromTemplate(container.S("command"))
154+
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
155+
_, err = container.Set(parsedPassthrough, "Command")
156+
if err != nil {
157+
return nil, fmt.Errorf("Could not update Command field: %v", err)
158+
}
159+
160+
err = container.Delete("command")
161+
if err != nil {
162+
return nil, fmt.Errorf("could not delete command in the Container definition: %w", err)
163+
}
164+
}
165+
166+
if container.Exists("volumesFrom") {
167+
err = updateKeys(*container.S("volumesFrom"))
168+
if err != nil {
169+
return nil, fmt.Errorf("could not update volumesFrom items: %v", err)
170+
}
171+
172+
passthrough, _ := GetValueFromTemplate(container.S("volumesFrom"))
173+
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
174+
_, err = container.Set(parsedPassthrough, "VolumesFrom")
175+
if err != nil {
176+
return nil, fmt.Errorf("could not update VolumesFrom field: %v", err)
177+
}
178+
179+
err = container.Delete("volumesFrom")
180+
if err != nil {
181+
return nil, fmt.Errorf("could not delete volumesFrom in the container definition: %w", err)
182+
}
183+
}
184+
185+
if container.Exists("linuxParameters") {
186+
err = updateKeys(*container.S("linuxParameters"))
187+
if err != nil {
188+
return nil, fmt.Errorf("could not update linuxParameters items: %v", err)
189+
}
190+
191+
passthrough, _ := GetValueFromTemplate(container.S("linuxParameters"))
192+
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
193+
_, err = container.Set(parsedPassthrough, "LinuxParameters")
194+
if err != nil {
195+
return nil, fmt.Errorf("could not update LinuxParameters field: %v", err)
196+
}
197+
198+
err = container.Delete("linuxParameters")
199+
if err != nil {
200+
return nil, fmt.Errorf("could not delete linuxParameters in the COntainer definition: %w", err)
201+
}
202+
}
100203
}
101204
}
102205

sysdig/data_source_sysdig_fargate_ECS_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,54 @@ import (
44
"context"
55
"encoding/json"
66
"io/ioutil"
7+
"sort"
78
"testing"
89

10+
"github.com/Jeffail/gabs/v2"
911
"github.com/falcosecurity/kilt/runtimes/cloudformation/cfnpatcher"
1012
"github.com/stretchr/testify/assert"
1113
)
1214

15+
var (
16+
testKiltDefinition = KiltRecipeConfig{
17+
SysdigAccessKey: "sysdig_access_key",
18+
AgentImage: "workload_agent_image",
19+
OrchestratorHost: "orchestrator_host",
20+
OrchestratorPort: "orchestrator_port",
21+
CollectorHost: "collector_host",
22+
CollectorPort: "collector_port",
23+
}
24+
25+
testContainerDefinitionFiles = []string{
26+
"fargate_entrypoint_test",
27+
"fargate_env_test",
28+
"fargate_cmd_test",
29+
"fargate_linuxparameters_test",
30+
"fargate_combined_test",
31+
}
32+
)
33+
34+
// sortContainerEnv goes into a container definition and sorts the environment variables
35+
func sortContainerEnv(json []byte) string {
36+
jsonObject, _ := gabs.ParseJSON(json)
37+
containers, _ := jsonObject.Data().([]interface{})
38+
for _, container := range containers {
39+
if env, ok := container.(map[string]interface{})["Environment"]; ok {
40+
envSort := env.([]interface{})
41+
sort.Slice(envSort, func(i, j int) bool {
42+
return gabs.Wrap(envSort[i]).S("Name").Data().(string) < gabs.Wrap(envSort[j]).S("Name").Data().(string)
43+
})
44+
}
45+
}
46+
return jsonObject.String()
47+
}
48+
49+
func sortAndCompare(t *testing.T, expected []byte, actual []byte) {
50+
expectedJSON := sortContainerEnv(expected)
51+
actualJSON := sortContainerEnv(actual)
52+
assert.JSONEq(t, expectedJSON, actualJSON)
53+
}
54+
1355
func TestECStransformation(t *testing.T) {
1456
inputfile, err := ioutil.ReadFile("testfiles/ECSinput.json")
1557

@@ -87,3 +129,24 @@ func TestECStransformation(t *testing.T) {
87129
assert.Equal(t, expectedContainerDefinitions[0].EntryPoint, patchedContainerDefinitions[0].EntryPoint)
88130
assert.Equal(t, patchedContainerDefinitions[0].EntryPoint2, "")
89131
}
132+
133+
func TestTransform(t *testing.T) {
134+
for _, testName := range testContainerDefinitionFiles {
135+
t.Run(testName, func(t *testing.T) {
136+
jsonConfig, _ := json.Marshal(testKiltDefinition)
137+
kiltConfig := &cfnpatcher.Configuration{
138+
Kilt: agentinoKiltDefinition,
139+
ImageAuthSecret: "image_auth_secret",
140+
OptIn: false,
141+
UseRepositoryHints: true,
142+
RecipeConfig: string(jsonConfig),
143+
}
144+
145+
inputContainerDefinition, _ := ioutil.ReadFile("testfiles/" + testName + ".json")
146+
patched, _ := patchFargateTaskDefinition(context.Background(), string(inputContainerDefinition), kiltConfig)
147+
expectedContainerDefinition, _ := ioutil.ReadFile("testfiles/" + testName + "_expected.json")
148+
149+
sortAndCompare(t, expectedContainerDefinition, []byte(*patched))
150+
})
151+
}
152+
}

sysdig/testfiles/ECSinput.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
{
33
"Environment": [
44
{
5-
"name": "pmet",
6-
"value": "temp"
5+
"Name": "pmet",
6+
"Value": "temp"
77
},
88
{
99
"Name": "SYSDIG_ENDPOINT",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"name": "test",
4+
"image": "test_image:latest",
5+
"entryPoint": [
6+
"/bin/test"
7+
],
8+
"command": [
9+
"test",
10+
"--test-arg",
11+
"test-arg-value"
12+
]
13+
}
14+
]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
[
2+
{
3+
"name": "test",
4+
"Image": "test_image:latest",
5+
"EntryPoint": [
6+
"/opt/draios/bin/instrument"
7+
],
8+
"Command": [
9+
"/bin/test",
10+
"test",
11+
"--test-arg",
12+
"test-arg-value"
13+
],
14+
"Environment": [
15+
{
16+
"Name": "SYSDIG_ORCHESTRATOR_PORT",
17+
"Value": "orchestrator_port"
18+
},
19+
{
20+
"Name": "SYSDIG_COLLECTOR",
21+
"Value": "collector_host"
22+
},
23+
{
24+
"Name": "SYSDIG_COLLECTOR_PORT",
25+
"Value": "collector_port"
26+
},
27+
{
28+
"Name": "SYSDIG_ACCESS_KEY",
29+
"Value": "sysdig_access_key"
30+
},
31+
{
32+
"Name": "SYSDIG_LOGGING",
33+
"Value": ""
34+
},
35+
{
36+
"Name": "SYSDIG_ORCHESTRATOR",
37+
"Value": "orchestrator_host"
38+
}
39+
],
40+
"LinuxParameters": {
41+
"Capabilities": {
42+
"Add": [
43+
"SYS_PTRACE"
44+
]
45+
}
46+
},
47+
"VolumesFrom": [
48+
{
49+
"ReadOnly": true,
50+
"SourceContainer": "SysdigInstrumentation"
51+
}
52+
]
53+
},
54+
{
55+
"EntryPoint": [
56+
"/opt/draios/bin/logwriter"
57+
],
58+
"Image": "workload_agent_image",
59+
"Name": "SysdigInstrumentation",
60+
"RepositoryCredentials": {
61+
"CredentialsParameter": "image_auth_secret"
62+
}
63+
}
64+
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
{
3+
"name": "test",
4+
"image": "test_image:latest",
5+
"entryPoint": [
6+
"/bin/test"
7+
],
8+
"command": [
9+
"test",
10+
"--test-arg",
11+
"test-arg-value"
12+
],
13+
"environment": [
14+
{
15+
"name": "TMP",
16+
"value": "temporary"
17+
},
18+
{
19+
"name": "SYSDIG_CUSTOM",
20+
"value": "custom"
21+
}
22+
],
23+
"volumesFrom": [
24+
{
25+
"sourceContainer": "test_container",
26+
"readOnly": true
27+
}
28+
]
29+
}
30+
]

0 commit comments

Comments
 (0)