diff --git a/example/example2/mutators/hello_world_meta.rego b/example/example2/mutators/hello_world_meta.rego index aa09573..e16d141 100644 --- a/example/example2/mutators/hello_world_meta.rego +++ b/example/example2/mutators/hello_world_meta.rego @@ -1,30 +1,28 @@ package hello_world_meta +import rego.v1 -patch[operation] { +add_meta_ops contains operation if { + object.get(input, "Meta", null) == null - not input.Meta - operation := { - "op": "add", - "path": "/Meta", - "value": {} - } + operation := { + "op": "add", + "path": "/Meta", + "value": {}, + } } -patch[operation] { - is_null(input.Meta) - operation := { - "op": "add", - "path": "/Meta", - "value": {} - } -} -patch[operation] { +add_hello_to_meta_ops contains operation if { + object.get(input, ["Meta", "hello"], null) == null - not input.Meta.hello - operation := { - "op": "add", - "path": "/Meta/hello", - "value": "world" - } + operation := { + "op": "add", + "path": "/Meta/hello", + "value": "world", + } } + +patch := [ operation | + some ops in [add_meta_ops, add_hello_to_meta_ops] + operation := ops[_] +] diff --git a/example/example2/mutators/hello_world_meta_test.rego b/example/example2/mutators/hello_world_meta_test.rego index f269e45..e72aefd 100644 --- a/example/example2/mutators/hello_world_meta_test.rego +++ b/example/example2/mutators/hello_world_meta_test.rego @@ -1,68 +1,64 @@ package hello_world_meta_test -import data.hello_world_meta.patch +import data.hello_world_meta -import future.keywords +import rego.v1 test_hello_world if { - e := patch with input as { + e := hello_world_meta.patch with input as { "ID": "my-job", "Meta": {}, } - e[{ - "op": "add", - "path": "/Meta/hello", - "value": "world" - }] - + count(e) == 1 + e == [{ + "op": "add", + "path": "/Meta/hello", + "value": "world", + }] } test_hello_world_add_meta if { - e := patch with input as { - "ID": "my-job" - } - count(e) == 2 - trace(sprintf("patch: %v", [e])) + e := hello_world_meta.patch with input as {"ID": "my-job"} - e == { - { - "op": "add", - "path": "/Meta", - "value": {} - }, - { - "op": "add", - "path": "/Meta/hello", - "value": "world" - } - } + e == [ + { + "op": "add", + "path": "/Meta", + "value": {}, + }, + { + "op": "add", + "path": "/Meta/hello", + "value": "world", + }, + ] } + test_hello_world_add_meta_if_meta_null if { - e := patch with input as { + e := hello_world_meta.patch with input as { "ID": "my-job", - "Meta": null + "Meta": null, } - count(e) == 2 - trace(sprintf("patch: %v", [e])) + count(e) == 2 - e == { - { - "op": "add", - "path": "/Meta", - "value": {} - }, - { - "op": "add", - "path": "/Meta/hello", - "value": "world" - } - } + e == [ + { + "op": "add", + "path": "/Meta", + "value": {}, + }, + { + "op": "add", + "path": "/Meta/hello", + "value": "world", + }, + ] } + test_hello_world_no_code_if_exists if { - e := patch with input as { + e := hello_world_meta.patch with input as { "ID": "my-job", - "Meta": {"hello": "world"} + "Meta": {"hello": "world"}, } - count(e) == 0 - + count(e) == 0 } diff --git a/example/example3/mutators/pg.rego b/example/example3/mutators/pg.rego index cdbb054..07ea5b3 100644 --- a/example/example3/mutators/pg.rego +++ b/example/example3/mutators/pg.rego @@ -1,14 +1,13 @@ package pginject -import future.keywords +import rego.v1 vault_policy := "db-access" -patch contains operation if { +add_vault_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres - input.TaskGroups[g].Tasks[t].Vault == null - trace(sprintf("injecting postgres task %d into group %d", [t, g])) + object.get(input.TaskGroups[g].Tasks[t], "Vault", null) == null operation := { "op": "add", @@ -23,7 +22,7 @@ patch contains operation if { } } -patch contains operation if { +add_vault_policy_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres operation := { @@ -33,10 +32,10 @@ patch contains operation if { } } -patch contains operation if { +add_env_template_block_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres - input.TaskGroups[g].Tasks[t].Templates == null + object.get(input.TaskGroups[g].Tasks[t], "Templates", null) == null operation := { "op": "add", @@ -45,10 +44,9 @@ patch contains operation if { } } -patch contains operation if { +add_env_template_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres - operation := { "op": "add", "path": sprintf("/TaskGroups/%d/Tasks/%d/Templates/-", [g, t]), @@ -72,11 +70,11 @@ patch contains operation if { }, } } + env_tmpl := native_tmpl if { input.TaskGroups[g].Tasks[t].Meta.postgres == "native" - - native_tmpl:= concat("\n", [ + native_tmpl := concat("\n", [ "{{ range nomadService \"postgres\" }}", "PGHOSTADDR={{ .Address }}", "PGPORT={{ .Port }}", @@ -87,13 +85,12 @@ env_tmpl := native_tmpl if { "PGPASSWORD={{ .Data.password }}", "{{ end }}", ]) - } + env_tmpl := spring_boot_tmpl if { input.TaskGroups[g].Tasks[t].Meta.postgres == "springboot" - - spring_boot_tmpl:= concat("\n", [ + spring_boot_tmpl := concat("\n", [ "{{ range nomadService \"postgres\" }}", "SPRING_DATASOURCE_URL=jdbc:postgresql://{{ .Address }}:{{ .Port }}/postgres", "{{ end }}", @@ -102,11 +99,12 @@ env_tmpl := spring_boot_tmpl if { "SPRING_DATASOURCE_PASSWORD={{ .Data.password }}", "{{ end }}", ]) - } -patch contains operation if { + +add_constaint_block_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres - input.TaskGroups[g].Constraints == null + + object.get(input.TaskGroups[g].Tasks[t], "Constraints", null) == null operation := { "op": "add", @@ -115,13 +113,18 @@ patch contains operation if { } } -patch contains operation if { +add_vault_constraint_block_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres - not input.TaskGroups[g].Constraints[{ - "LTarget": "${attr.vault.version}", - "Operand": "semver", - "RTarget": ">= 0.6.1" - }] + + constraints := object.get(input.TaskGroups[g].Tasks[t], "Constraints", []) + every constraint in constraints { + constraint != { + "LTarget": "${attr.vault.version}", + "Operand": "semver", + "RTarget": ">= 0.6.1", + } + } + operation := { "op": "add", "path": sprintf("/TaskGroups/%d/Constraints/-", [g]), @@ -132,3 +135,15 @@ patch contains operation if { }, } } + +patch := [operation | + some ops in [ + add_vault_ops, + add_vault_policy_ops, + add_env_template_block_ops, + add_env_template_ops, + add_constaint_block_ops, + add_vault_constraint_block_ops, + ] + operation := ops[_] +] diff --git a/example/example3/mutators/pg_test.rego b/example/example3/mutators/pg_test.rego index f6b2931..0df14ae 100644 --- a/example/example3/mutators/pg_test.rego +++ b/example/example3/mutators/pg_test.rego @@ -3,205 +3,88 @@ package pginject_test import data.pginject.error import data.pginject.patch -import future.keywords +import rego.v1 test_pginject if { patch_ops := patch with input as { - "Affinities": null, - "AllAtOnce": false, - "Constraints": null, - "ConsulNamespace": "", - "ConsulToken": "", - "CreateIndex": 289, - "Datacenters": ["*"], - "DispatchIdempotencyToken": "", - "Dispatched": false, - "ID": "app", - "JobModifyIndex": 459, - "Meta": null, - "ModifyIndex": 468, - "Multiregion": null, - "Name": "app", - "Namespace": "default", - "NomadTokenID": "", - "ParameterizedJob": null, - "ParentID": "", - "Payload": null, - "Periodic": null, - "Priority": 50, - "Region": "global", - "Spreads": null, - "Stable": true, - "Status": "running", - "StatusDescription": "", - "Stop": false, - "SubmitTime": 1679091610901210000, "TaskGroups": [{ - "Affinities": null, - "Constraints": null, - "Consul": {"Namespace": ""}, - "Count": 1, - "EphemeralDisk": { - "Migrate": false, - "SizeMB": 300, - "Sticky": false, - }, - "MaxClientDisconnect": null, - "Meta": null, - "Migrate": { - "HealthCheck": "checks", - "HealthyDeadline": 300000000000, - "MaxParallel": 1, - "MinHealthyTime": 10000000000, - }, "Name": "app", - "Networks": null, - "ReschedulePolicy": { - "Attempts": 0, - "Delay": 30000000000, - "DelayFunction": "exponential", - "Interval": 0, - "MaxDelay": 3600000000000, - "Unlimited": true, - }, - "RestartPolicy": { - "Attempts": 2, - "Delay": 15000000000, - "Interval": 1800000000000, - "Mode": "fail", - }, - "Scaling": null, - "Services": null, - "ShutdownDelay": null, - "Spreads": null, - "StopAfterClientDisconnect": null, "Tasks": [{ - "Affinities": null, - "Artifacts": null, - "CSIPluginConfig": null, - "Config": { - "args": [ - "-c", - "while true; do echo 'hello $(date)'; sleep 5; done", - ], - "command": "sh", - "image": "busybox:1.34.1", - }, - "Constraints": null, - "DispatchPayload": null, - "Driver": "docker", - "Env": null, - "Identity": null, - "KillSignal": "", - "KillTimeout": 5000000000, - "Kind": "", - "Leader": false, - "Lifecycle": null, - "LogConfig": { - "MaxFileSizeMB": 10, - "MaxFiles": 10, - }, "Meta": {"postgres": "native"}, "Name": "app", - "Resources": { - "CPU": 100, - "Cores": 0, - "Devices": null, - "DiskMB": 0, - "IOPS": 0, - "MemoryMB": 300, - "MemoryMaxMB": 0, - "Networks": null, - }, - "RestartPolicy": { - "Attempts": 2, - "Delay": 15000000000, - "Interval": 1800000000000, - "Mode": "fail", - }, - "ScalingPolicies": null, - "Services": null, - "ShutdownDelay": 0, - "Templates": null, - "User": "", - "Vault": null, - "VolumeMounts": null, }], - "Update": { - "AutoPromote": false, - "AutoRevert": false, - "Canary": 0, - "HealthCheck": "checks", - "HealthyDeadline": 300000000000, - "MaxParallel": 1, - "MinHealthyTime": 10000000000, - "ProgressDeadline": 600000000000, - "Stagger": 30000000000, - }, - "Volumes": null, }], "Type": "service", - "Update": { - "AutoPromote": false, - "AutoRevert": false, - "Canary": 0, - "HealthCheck": "", - "HealthyDeadline": 0, - "MaxParallel": 1, - "MinHealthyTime": 0, - "ProgressDeadline": 0, - "Stagger": 30000000000, - }, - "VaultNamespace": "", - "VaultToken": "", - "Version": 6, } - count(patch_ops) == 4 - patch_ops == { - { - "op": "add", - "path": "/TaskGroups/0/Tasks/0/Vault", - "value": { - "ChangeMode": "restart", - "ChangeSignal": "SIGHUP", - "Env": true, - "Namespace": "", - "Policies": [], - }, - }, - { - "op": "add", - "path": "/TaskGroups/0/Tasks/0/Vault/Policies/-", - "value": "db-access", + + count(patch_ops) == 6 + + # test each entry of patchops + + print("checking patch_ops[0]") + patch_ops[0] == { + "op": "add", + "path": "/TaskGroups/0/Tasks/0/Vault", + "value": { + "ChangeMode": "restart", + "ChangeSignal": "SIGHUP", + "Env": true, + "Namespace": "", + "Policies": [], }, + } + print("checking patch_ops[1]") + patch_ops[1] == { + "op": "add", + "path": "/TaskGroups/0/Tasks/0/Vault/Policies/-", + "value": "db-access", + } - { - "op": "add", - "path": "/TaskGroups/0/Tasks/0/Templates", - "value": [], + print("checking patch_ops[2]") + patch_ops[2] == { + "op": "add", + "path": "/TaskGroups/0/Tasks/0/Templates", + "value": [], + } + print("checking patch_ops[3]") + print(patch_ops[3]) + patch_ops[3] == { + "op": "add", + "path": "/TaskGroups/0/Tasks/0/Templates/-", + "value": { + "ChangeMode": "restart", + "ChangeScript": null, + "ChangeSignal": "", + "DestPath": "${NOMAD_SECRETS_DIR}/postgres.env", + "EmbeddedTmpl": "{{ range nomadService \"postgres\" }}\nPGHOSTADDR={{ .Address }}\nPGPORT={{ .Port }}\n{{ end }}\nPGDATABASE=postgres\n{{ with secret \"postgres/creds/dev\" }}\nPGUSER={{ .Data.username }}\nPGPASSWORD={{ .Data.password }}\n{{ end }}", + "Envvars": true, + "ErrMissingKey": false, + "Gid": null, + "LeftDelim": "{{", + "Perms": "0644", + "RightDelim": "}}", + "SourcePath": "", + "Splay": 5000000000, + "Uid": null, + "VaultGrace": 0, + "Wait": null, }, - { - "op": "replace", - "path": "/TaskGroups/0/Tasks/0/Templates/-", - "value": [{ - "ChangeMode": "restart", - "ChangeScript": null, - "ChangeSignal": "", - "DestPath": "${NOMAD_SECRETS_DIR}/postgres.env", - "EmbeddedTmpl": "{{ range nomadService \"postgres\" }}\n PGHOSTADDR={{ .Address }}\n PGPORT={{ .Port }}\n{{ end }}\nPGDATABASE=postgres\n{{ with secret \"postgres/creds/dev\" }}\n PGUSER={{ .Data.username }}\n PGPASSWORD={{ .Data.password }}\n{{ end }}\n\n", - "Envvars": true, - "ErrMissingKey": false, - "Gid": null, - "LeftDelim": "{{", - "Perms": "0644", - "RightDelim": "}}", - "SourcePath": "", - "Splay": 5000000000, - "Uid": null, - "VaultGrace": 0, - "Wait": null, - }], + } + print("checking patch_ops[4]") + patch_ops[4] == { + "op": "add", + "path": "/TaskGroups/0/Constraints", + "value": [], + } + print("checking patch_ops[5]") + patch_ops[5] == { + "op": "add", + "path": "/TaskGroups/0/Constraints/-", + "value": { + "LTarget": "${attr.vault.version}", + "Operand": "semver", + "RTarget": ">= 0.6.1", }, } } diff --git a/misc/hashitalk_deploy2023/demos/demo2/postgres.rego b/misc/hashitalk_deploy2023/demos/demo2/postgres.rego index 98012aa..546c467 100644 --- a/misc/hashitalk_deploy2023/demos/demo2/postgres.rego +++ b/misc/hashitalk_deploy2023/demos/demo2/postgres.rego @@ -1,14 +1,14 @@ package postgres + import future.keywords jobName := input.Name -patch contains operation if { +vault_block_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres object.get(input.TaskGroups[g].Tasks[t], "Vault", null) == null - operation := { "op": "add", "path": sprintf("/TaskGroups/%d/Tasks/%d/Vault", [g, t]), @@ -22,17 +22,17 @@ patch contains operation if { } } -patch contains operation if { +vault_policies_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres operation := { "op": "add", "path": sprintf("/TaskGroups/%d/Tasks/%d/Vault/Policies/-", [g, t]), - "value": sprintf("%s-db-access", [jobName]) + "value": sprintf("%s-db-access", [jobName]), } } -patch contains operation if { +task_templates_block_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres object.get(input.TaskGroups[g].Tasks[t], "Templates", null) == null @@ -44,10 +44,9 @@ patch contains operation if { } } -patch contains operation if { +task_postgres_env_template_ops contains operation if { input.TaskGroups[g].Tasks[t].Meta.postgres - operation := { "op": "add", "path": sprintf("/TaskGroups/%d/Tasks/%d/Templates/-", [g, t]), @@ -59,40 +58,50 @@ patch contains operation if { "LeftDelim": "{{", "Perms": "0644", "RightDelim": "}}", - "Splay": 5000000000 + "Splay": 5000000000, }, } } + env_tmpl := libpq_tmpl if { input.TaskGroups[g].Tasks[t].Meta.postgres == "libpq" - db_name := replace(jobName, "-", "_") - libpq_tmpl:= concat("\n", [ - sprintf("PGDATABASE=%s",[db_name]), + db_name := replace(jobName, "-", "_") + libpq_tmpl := concat("\n", [ + sprintf("PGDATABASE=%s", [db_name]), sprintf("{{ with secret \"db/%s/creds/admin\" }}", [db_name]), "PGUSER={{ .Data.username }}", "PGPASSWORD={{ .Data.password }}", "{{ end }}", - "{{ range nomadService \"postgres\" }}", + "{{ range nomadService \"postgres\" }}", "PGHOSTADDR={{ .Address }}", "PGPORT={{ .Port }}", "{{ end }}", ]) - } + env_tmpl := spring_boot_tmpl if { input.TaskGroups[g].Tasks[t].Meta.postgres == "springboot" - db_name := replace(jobName, "-", "_") + db_name := replace(jobName, "-", "_") - spring_boot_tmpl:= concat("\n", [ + spring_boot_tmpl := concat("\n", [ "{{ range nomadService \"postgres\" }}", - sprintf("SPRING_DATASOURCE_URL=jdbc:postgresql://{{ .Address }}:{{ .Port }}/%s",[db_name]), + sprintf("SPRING_DATASOURCE_URL=jdbc:postgresql://{{ .Address }}:{{ .Port }}/%s", [db_name]), "{{ end }}", sprintf("{{ with secret \"db/%s/creds/admin\" }}", [db_name]), "SPRING_DATASOURCE_USERNAME={{ .Data.username }}", "SPRING_DATASOURCE_PASSWORD={{ .Data.password }}", "{{ end }}", ]) - } + +patch := [op | + some ops in [ + vault_block_ops, + vault_policies_ops, + task_templates_block_ops, + task_postgres_env_template_ops, + ] + op := ops[_] +] diff --git a/misc/hashitalk_deploy2023/demos/demo2/postgres_test.rego b/misc/hashitalk_deploy2023/demos/demo2/postgres_test.rego index 9f4d502..e03f0da 100644 --- a/misc/hashitalk_deploy2023/demos/demo2/postgres_test.rego +++ b/misc/hashitalk_deploy2023/demos/demo2/postgres_test.rego @@ -14,7 +14,7 @@ test_patch_vault_policy if { }], }], } - patch_ops == { + patch_ops == [ { "op": "add", "path": "/TaskGroups/0/Tasks/0/Vault", @@ -50,5 +50,5 @@ test_patch_vault_policy if { "Splay": 5000000000, }, }, - } + ] }