{"id":19591943,"url":"https://github.com/catalyst/gotiller","last_synced_at":"2025-06-15T02:37:18.385Z","repository":{"id":66891468,"uuid":"335481002","full_name":"catalyst/gotiller","owner":"catalyst","description":null,"archived":false,"fork":false,"pushed_at":"2021-02-05T03:46:09.000Z","size":440,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-01-09T06:47:05.907Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/catalyst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-03T02:11:55.000Z","updated_at":"2021-02-05T03:46:12.000Z","dependencies_parsed_at":"2023-05-13T01:15:22.561Z","dependency_job_id":null,"html_url":"https://github.com/catalyst/gotiller","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fgotiller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fgotiller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fgotiller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/catalyst%2Fgotiller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/catalyst","download_url":"https://codeload.github.com/catalyst/gotiller/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240867428,"owners_count":19870405,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T08:32:07.731Z","updated_at":"2025-02-26T14:14:24.462Z","avatar_url":"https://github.com/catalyst.png","language":"Go","readme":"GoTiller\n========\n\nRuby Tiller replacement implemented in Go.\n\nIt is not a drop-in replacement, but it is similar enough. Some basic\nconversion is provided.\n\n**An important convention:**\n\nGoTiller has a concept of **environment** inherited from Tiller to\nseparate sets of rules for different scenarios/purposes, eg *prod*,\n*uat*, *test* etc.\n\nAt the same time we have the (shell-ish) *environment* in which\n`gotiller` runs, and sources variables from. In order to minimise\nthe confusion we will refer to that *environment* as **env**.\n\nIf You have used Tiller\n-----------------------\n\n### What's the same\n\n-   Config files are Yaml\n-   Config directory structure/file naming\n-   The intention is to keep the YAML structure as similar as possible\n\n### What's not\n\n#### Sources and variables\n\n-   `data_sources:` and `template_sources:` do not exist - plugins are\n    implied with inclusion of the corresponding sections; \"File\" plugin\n    is assumed\n-   `config:` is replaced with `vars:`\n-   `global(_values):` are replaced with `_vars:`\n\nRationale: `global(_values):` is equivalent to `\u003cany_template\u003e: config:`,\nthus breaking the structure of `\u003ctemplate\u003e:`. Besides, it means that you\ncannot have templates named `global` or `global_values`, which is not\na big deal but still. So it had to be renamed. I find `config:` to be\nconfusing, so I went with `_vars:`.\n\nOnce `_vars:` was there, `config:` had to become `vars:`.\n\n#### Environment plugin\n\nThis is a big change.\n\n-   The `environment:` section is replaced with a single entry\n    `env_vars_prefix:`\n-   `env_vars_prefix` is not implied, ie if it is missing it is not\n    assumed that we want env vars staring with `env_`\n-   \"\" is not a valid value, ie you cannot have `env_vars_prefix: \"\"`\n    to slurp all environment - it is an equivalent of not having\n    `env_vars_prefix:`\n-   `lowercase:` is not supported, ie env vars will not be lowercased\n    before checking the prefix\n-   `env_vars_prefix` is stripped down from matching env vars - an\n    example:\n\n\u003c!-- --\u003e\n\n    in your template:    \"host={{ .db_host }}\"\n    in your config:      env_vars_prefix: env_\n    in your running env: env_db_host=myhost\n\nConversion will strip `env_vars_prefix` from vars in templates.\n\n#### Plugins\n\nAt the moment, only `filesystem` and `environment` sources are implemented.\n\n#### ERB vs Go templates\n\nApart from the trivial embedding tag difference (`\u003c% %\u003e` pair in ERB vs\n`{{ }}` in Go templates) there are notable differences. Let's see what\nis missing. For what new is availabe have a look at the [Go text\ntemplate package docs](https://golang.org/pkg/text/template/).\n\n##### No logical operators\n\nGo template control structures (\"actions\") only do functions. So if you\nhad `\"if a \u0026\u0026 b\"` that turns into `\"if and a b\"`\n\n##### No code embedding\n\nYou cannot just run (evaluate) any random code. You can only do:\n\n-   acions (`if`, `range` (for loops) etc)\n-   function calls: `fn_name arg1 arg2...`\n\nTemplating processor is preloaded with a small set of functions, mainly\nones that do logical operations and text formatting. However it is\npossible to make functions available to the processor. We will make some\npopular functions available in our `gotiller` executable, and there's\nalways an option to build a custom one.\n\n### Conversion\n\nTBD\n\nIf you haven't used Tiller\n--------------------------\n\n[Tiller](https://tiller.readthedocs.io) is an one-off puppet. It was\ncreated to alleviate pain of configuring containers that come fom the\nsame image but run in different environments.\n\nConfig directory and YAML structure\n-----------------------------------\n\n    |- common.yaml\n    |- config.d\n    |   |- conf1.yaml\n    |   |- conf2.yaml\n    |   └- ...\n    |\n    |- environments\n    |   |- prod.yaml\n    |   |- uat.yaml\n    |   |- test.taml\n    |   └- ...\n    |\n    └- templates\n        |- some.conf\n        |- another.ini\n        └- ...\n\n### Config files\n\nBase config file is `common.yaml`. Files from `config.d` overlay config\noptions/rules on top, and are taken in alphabetical order.\n\nIn the following structures, any of the entries/stanzas could be\nmissing.\n\n#### Config structure\n\nConfig files understand following keys:\n\n-   defaults: - base templates structure (see below) applicable to all\n    environments\n-   environments - environments structure (see below)\n-   default_environment - environment to assume if no environment is\n    specified\n-   env_vars_prefix - prefix of the env vars (see convention at the\n    top) to apply; if missing or empty no vars are taken from env\n\n    defaults: {Templates structure}\n\n    environments:\n      prod: {Templates structure}\n      test: {Templates structure}\n      environmentX: {Templates structure}\n      ...\n\n    env_vars_prefix: env_\n\n#### Templates structure\n\nTemplates are keyed on template filenames, as stored in `templates`\ndirectory:\n\n    _vars\n      var1: val1\n      ...\n    tpl1: {Target structure}\n    tpl2: {Target structure}\n    ...\n\n#### Target structure\n\n`user:` and `group:` entries are optional, default to running process\nusername/group.\n\n    target: /path/on/the/filesystem/where/to/write/processed/template\n\n    user: os-username\n    group: os-group\n\n    vars:\n      var1: val1\n      ...\n\n### Environment files\n\nFile`some_enironment.yaml` in `environments` directory hold *Template*\nstructure, an equivalent of `environment: -\u003e some_environment:`\n\nTarget parameter rules\n----------------------\n\nWhen `gotiller` runs it\n\n-   forms a working *Templates* structure\n-   for each template from the working structure forms a set of vars to\n    apply and processes the template\n\n### Working Config structure formation\n\n-   Take `common.yaml`, otherwise an empty structure\n-   Overlay files from `config.d` in alphabetical order\n\n### Working Templates structure formation for environment\n\nLet's name the specified environment *eX*\n\n-   From the working Config structure:\n    -   Take `defaults:`\n    -   Overlay with `environments: -\u003e eX:`\n-   Overlay with `environments/eX.yaml`\n\n`environments/eX.yaml` trumps `environments: -\u003e eX:` from the working\nconfig, trumps `defaults:` from the working config\n\n### Variable set formation for template\n\nWorking *Target* is the Target structure for the working *template*\ntaken from the Working Templates\n\n-   From the working Config structure take `defaults: _vars:`\n-   Overlay with the `defaults:` Target `vars:`\n-   Overlay with the working environment structure `_vars:`\n-   Overlay with the working environment Target `vars:`\n-   Overlay with the *env vars*\n\n*env vars* trump Target `vars:` trump environment `_vars` trump `defaults:`\n\n### Utility functions\n\nFunctions that are available in templates to make things possible.\n\nAll functions that take strings as arguments are \"safe\" - see\n`safe` function below. Functions that take `int`arguments are not.\n\n#### `safe string`\n\nRefering to an undefined variable may throw panic because of the strong\ntyping, or output `\u003cno value\u003e`. `safe` will turn non-existing variable\nto an empty string.\n\n#### `tostr a` - anything to string\n#### `strtoi s` - string to int\n#### `coalesce v1 v2 ...` - returns first value that is not undefined\n\n#### `tolower s` - convert string to lowercase\n#### `match s regex` - returns boolean\n#### `regexrepl s regex replacement`\n#### `decode64 s`\n\n#### `quotedlist string delimiter`\n\nThis one will return a comma separated list of quoted entries. An example:\n\n    quotedlist \"one,two,three\" \",\"\n\nwill yield\n\n    `\"one\", \"two\", \"three\"`\n\n#### `iadd x y` - int +\n#### `imul x y` - int *\n#### `idiv x y` - int /\n#### `imod x m` - x % m\n\n#### `sequence start length`\n\nTo be used with `range` to create incremented loops. See below.\n\n#### `val var_name`\n\nReturns value of the variable `var_name`. This is the only way to get values\ndynamically. For example\n\n    {{ range sequence 0 3 }}\n    {{ $var_name := printf \"var%d\" . }}\n    something={{ val $var_name }}\n    {{ end }}\n\nwill give\n\n    something0=\u003cvalue of var0\u003e\n    something1=\u003cvalue of var1\u003e\n    something2=\u003cvalue of var2\u003e\n\nBonus: if `.var` makes you unhappy, you can always use `val \"var\"` instead.\n\n#### `timeoffset string`\n\nGives an int in the 0 - 60 range based on the CRC32 hashed value of a string.\n\n#### `isfile path`\n\nReturns boolean whether the file specified with path exists. In case of a dir throws an exception.\n\n### A full blown example\n#### Config\n\n**`common.conf`**\n```\ndefaults:\n    _vars:\n        v1: v_default_1\n        v2: v_default_2\n        v3: v_default_3\n\n    tpl.conf:\n        target: /app/a.conf\n        vars:\n            v_true_1: \"A string\"\n            v_true_2: 1\n            v_false_1: \"\"\n            v1: v_template_default_1\n            v2: v_template_default_2\n\nenv_vars_prefix: env_\n\ndefault_environment: e1\n\nenvironments:\n    e1:\n        tpl.conf:\n            vars:\n                v2: v_common_e1_2\n                x: v_common_e1_x\n                y: v_common_e1_y\n    e2:\n        tpl.conf:\n            vars:\n                v1: v_common_e2_1\n                v3: v_common_e2_3\n                x: v_common_e2_x\n                z: v_common_e2_z\n\n```\n\n**`config.d/xyz.yaml`**\n```\ndefaults:\n    _vars:\n        x: v_default_x\n        y: v_default_y\n        z: v_default_z\n\n```\n\n**`environments/e1.yaml`**\n```\ntpl.conf:\n    vars:\n        x: v_env1_x\n\n```\n\n**Running env**\n```\nenv_y=v_env_y\n\n```\n\n#### Template\n```\n# Config for app\n\n{{if and .v_true_1 (not .v_false_1) -}}\n\nparam_1_that_shows=\"{{.v1}} from common.yaml.defaults\"\n\n  {{- if and .v_true_1 .v_false_1}}\n\nsome_param=\"This won't show\"\n\n  {{- end}}\n  {{- if or (and .v_true_1 .v_false_1) (not (and .v_true_2 .v_false_2))}}\n\nparam_2_that_shows=\"{{.v2}} from common.yaml environments e1\"\n\n  {{- end}}\n\n{{- /* This is a comment.\n       On multiple lines. */ -}}\n{{- end}}\n\nparam_3=\"{{.v3}} from common.yaml defaults _vars\"\n\nparam_x=\"{{.x}} from environments/e1\"\n\nparam_y=\"{{.y}} from env\"\n\nparam_z=\"{{.z}} from config.d/xyz.yaml defaults\"\n\n{{/* This demonstrates saving current level when changing \".\".  If there's no \"-\" before comment, there can be no space before \"/*\" */ -}}\n{{- $save := . -}}\n{{- /* range is loop */ -}}\n{{- range sequence 0 3 -}}\n{{$save.z}}\n{{end -}}\n\n```\n\n#### Result\n```\n# Config for app\n\nparam_1_that_shows=\"v_template_default_1 from common.yaml.defaults\"\n\nparam_2_that_shows=\"v_common_e1_2 from common.yaml environments e1\"\n\nparam_3=\"v_default_3 from common.yaml defaults _vars\"\n\nparam_x=\"v_env1_x from environments/e1\"\n\nparam_y=\"v_common_e1_y from env\"\n\nparam_z=\"v_default_z from config.d/xyz.yaml defaults\"\n\nv_default_z0\nv_default_z1\nv_default_z2\n\n```\n\nMakefile\n--------\n\n`make` builds statically compiled executables for specified arhitectures. If no\narchitecture is specified it will build for default architecture (`amd64`). For example:\n\n    make ARCH=\"amd64 arm\"\n\nBinaries are created in the `bin` directory.\n\nCLI\n---\n\nTBD\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatalyst%2Fgotiller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcatalyst%2Fgotiller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcatalyst%2Fgotiller/lists"}