Skip to content

Commit

Permalink
fix: json casing (#177)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
achandras authored Jul 8, 2022
1 parent e385da1 commit 5adbfc4
Show file tree
Hide file tree
Showing 13 changed files with 599 additions and 11 deletions.
121 changes: 112 additions & 9 deletions sysdig/cfn_preprocess_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package sysdig
import (
"context"
"fmt"
"unicode"

"github.com/Jeffail/gabs/v2"
"github.com/rs/zerolog/log"
Expand All @@ -27,11 +28,50 @@ func GetValueFromTemplate(what *gabs.Container) (string, *gabs.Container) {
fallback = nil
default:
fallback = what
result = "placeholder: " + what.String()
result = what.String()
}
return result, fallback
}

func capitalize(key string) string {
r := []rune(key)
r[0] = unicode.ToUpper(r[0])
return string(r)
}

// updateKey recursively capitalizes the first letter of each key in the input object
func updateKeys(inputMap gabs.Container) error {
// in this case, the object is probably an array, so update each child
if len(inputMap.ChildrenMap()) == 0 {
for _, child := range inputMap.Children() {
err := updateKeys(*child)
if err != nil {
return err
}
}
} else {
for key, child := range inputMap.ChildrenMap() {
_, err := inputMap.Set(child.Data(), capitalize(key))
if err != nil {
return fmt.Errorf("failed to update new key %s", capitalize(key))
}

err = inputMap.Delete(key)
if err != nil {
return fmt.Errorf("failed to delete old key %s" + key)
}

// recurse to update child's keys
err = updateKeys(*child)
if err != nil {
return err
}
}
}

return nil
}

func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte, error) {
l := log.Ctx(ctx)
template, err := gabs.ParseJSON(patchedStack)
Expand All @@ -56,8 +96,8 @@ func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte
}
}

if container.Exists("Environment") {
for _, env := range container.S("Environment").Children() {
if container.Exists("environment") {
for _, env := range container.S("environment").Children() {
if env.Exists("name") {
name, _ := env.S("name").Data().(string)
err = env.Delete("name")
Expand All @@ -81,22 +121,85 @@ func terraformPreModifications(ctx context.Context, patchedStack []byte) ([]byte
}
}
}

passthrough, _ := GetValueFromTemplate(container.S("environment"))
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
_, err = container.Set(parsedPassthrough, "Environment")
if err != nil {
return nil, fmt.Errorf("Could not update Environment field: %v", err)
}

err = container.Delete("environment")
if err != nil {
return nil, fmt.Errorf("could not delete environment in the Container definition: %w", err)
}
}

if container.Exists("entryPoint") {
for _, arg := range container.S("entryPoint").Children() {
passthrough, _ := GetValueFromTemplate(arg)
err = container.ArrayAppend(passthrough, "EntryPoint")
if err != nil {
return nil, fmt.Errorf("Could not append entrypoint values to EntryPoint: %v", err)
}
passthrough, _ := GetValueFromTemplate(container.S("entryPoint"))
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
_, err = container.Set(parsedPassthrough, "EntryPoint")
if err != nil {
return nil, fmt.Errorf("Could not update EntryPoint field: %v", err)
}

err = container.Delete("entryPoint")
if err != nil {
return nil, fmt.Errorf("could not delete entryPoint in the Container definition: %w", err)
}
}

if container.Exists("command") {
passthrough, _ := GetValueFromTemplate(container.S("command"))
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
_, err = container.Set(parsedPassthrough, "Command")
if err != nil {
return nil, fmt.Errorf("Could not update Command field: %v", err)
}

err = container.Delete("command")
if err != nil {
return nil, fmt.Errorf("could not delete command in the Container definition: %w", err)
}
}

if container.Exists("volumesFrom") {
err = updateKeys(*container.S("volumesFrom"))
if err != nil {
return nil, fmt.Errorf("could not update volumesFrom items: %v", err)
}

passthrough, _ := GetValueFromTemplate(container.S("volumesFrom"))
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
_, err = container.Set(parsedPassthrough, "VolumesFrom")
if err != nil {
return nil, fmt.Errorf("could not update VolumesFrom field: %v", err)
}

err = container.Delete("volumesFrom")
if err != nil {
return nil, fmt.Errorf("could not delete volumesFrom in the container definition: %w", err)
}
}

if container.Exists("linuxParameters") {
err = updateKeys(*container.S("linuxParameters"))
if err != nil {
return nil, fmt.Errorf("could not update linuxParameters items: %v", err)
}

passthrough, _ := GetValueFromTemplate(container.S("linuxParameters"))
parsedPassthrough, _ := gabs.ParseJSON([]byte(passthrough))
_, err = container.Set(parsedPassthrough, "LinuxParameters")
if err != nil {
return nil, fmt.Errorf("could not update LinuxParameters field: %v", err)
}

err = container.Delete("linuxParameters")
if err != nil {
return nil, fmt.Errorf("could not delete linuxParameters in the COntainer definition: %w", err)
}
}
}
}

Expand Down
63 changes: 63 additions & 0 deletions sysdig/data_source_sysdig_fargate_ECS_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,54 @@ import (
"context"
"encoding/json"
"io/ioutil"
"sort"
"testing"

"github.com/Jeffail/gabs/v2"
"github.com/falcosecurity/kilt/runtimes/cloudformation/cfnpatcher"
"github.com/stretchr/testify/assert"
)

var (
testKiltDefinition = KiltRecipeConfig{
SysdigAccessKey: "sysdig_access_key",
AgentImage: "workload_agent_image",
OrchestratorHost: "orchestrator_host",
OrchestratorPort: "orchestrator_port",
CollectorHost: "collector_host",
CollectorPort: "collector_port",
}

testContainerDefinitionFiles = []string{
"fargate_entrypoint_test",
"fargate_env_test",
"fargate_cmd_test",
"fargate_linuxparameters_test",
"fargate_combined_test",
}
)

// sortContainerEnv goes into a container definition and sorts the environment variables
func sortContainerEnv(json []byte) string {
jsonObject, _ := gabs.ParseJSON(json)
containers, _ := jsonObject.Data().([]interface{})
for _, container := range containers {
if env, ok := container.(map[string]interface{})["Environment"]; ok {
envSort := env.([]interface{})
sort.Slice(envSort, func(i, j int) bool {
return gabs.Wrap(envSort[i]).S("Name").Data().(string) < gabs.Wrap(envSort[j]).S("Name").Data().(string)
})
}
}
return jsonObject.String()
}

func sortAndCompare(t *testing.T, expected []byte, actual []byte) {
expectedJSON := sortContainerEnv(expected)
actualJSON := sortContainerEnv(actual)
assert.JSONEq(t, expectedJSON, actualJSON)
}

func TestECStransformation(t *testing.T) {
inputfile, err := ioutil.ReadFile("testfiles/ECSinput.json")

Expand Down Expand Up @@ -87,3 +129,24 @@ func TestECStransformation(t *testing.T) {
assert.Equal(t, expectedContainerDefinitions[0].EntryPoint, patchedContainerDefinitions[0].EntryPoint)
assert.Equal(t, patchedContainerDefinitions[0].EntryPoint2, "")
}

func TestTransform(t *testing.T) {
for _, testName := range testContainerDefinitionFiles {
t.Run(testName, func(t *testing.T) {
jsonConfig, _ := json.Marshal(testKiltDefinition)
kiltConfig := &cfnpatcher.Configuration{
Kilt: agentinoKiltDefinition,
ImageAuthSecret: "image_auth_secret",
OptIn: false,
UseRepositoryHints: true,
RecipeConfig: string(jsonConfig),
}

inputContainerDefinition, _ := ioutil.ReadFile("testfiles/" + testName + ".json")
patched, _ := patchFargateTaskDefinition(context.Background(), string(inputContainerDefinition), kiltConfig)
expectedContainerDefinition, _ := ioutil.ReadFile("testfiles/" + testName + "_expected.json")

sortAndCompare(t, expectedContainerDefinition, []byte(*patched))
})
}
}
4 changes: 2 additions & 2 deletions sysdig/testfiles/ECSinput.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
{
"Environment": [
{
"name": "pmet",
"value": "temp"
"Name": "pmet",
"Value": "temp"
},
{
"Name": "SYSDIG_ENDPOINT",
Expand Down
14 changes: 14 additions & 0 deletions sysdig/testfiles/fargate_cmd_test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"name": "test",
"image": "test_image:latest",
"entryPoint": [
"/bin/test"
],
"command": [
"test",
"--test-arg",
"test-arg-value"
]
}
]
64 changes: 64 additions & 0 deletions sysdig/testfiles/fargate_cmd_test_expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[
{
"name": "test",
"Image": "test_image:latest",
"EntryPoint": [
"/opt/draios/bin/instrument"
],
"Command": [
"/bin/test",
"test",
"--test-arg",
"test-arg-value"
],
"Environment": [
{
"Name": "SYSDIG_ORCHESTRATOR_PORT",
"Value": "orchestrator_port"
},
{
"Name": "SYSDIG_COLLECTOR",
"Value": "collector_host"
},
{
"Name": "SYSDIG_COLLECTOR_PORT",
"Value": "collector_port"
},
{
"Name": "SYSDIG_ACCESS_KEY",
"Value": "sysdig_access_key"
},
{
"Name": "SYSDIG_LOGGING",
"Value": ""
},
{
"Name": "SYSDIG_ORCHESTRATOR",
"Value": "orchestrator_host"
}
],
"LinuxParameters": {
"Capabilities": {
"Add": [
"SYS_PTRACE"
]
}
},
"VolumesFrom": [
{
"ReadOnly": true,
"SourceContainer": "SysdigInstrumentation"
}
]
},
{
"EntryPoint": [
"/opt/draios/bin/logwriter"
],
"Image": "workload_agent_image",
"Name": "SysdigInstrumentation",
"RepositoryCredentials": {
"CredentialsParameter": "image_auth_secret"
}
}
]
30 changes: 30 additions & 0 deletions sysdig/testfiles/fargate_combined_test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"name": "test",
"image": "test_image:latest",
"entryPoint": [
"/bin/test"
],
"command": [
"test",
"--test-arg",
"test-arg-value"
],
"environment": [
{
"name": "TMP",
"value": "temporary"
},
{
"name": "SYSDIG_CUSTOM",
"value": "custom"
}
],
"volumesFrom": [
{
"sourceContainer": "test_container",
"readOnly": true
}
]
}
]
Loading

0 comments on commit 5adbfc4

Please sign in to comment.