feat: allow template vars in op

This commit is contained in:
franklin 2021-10-06 15:35:31 +02:00
parent 020c705648
commit 06e12e0058
12 changed files with 85 additions and 35 deletions

View File

@ -8,6 +8,10 @@ linters-settings:
min-confidence: 0
goimports:
local-prefixes: github.com/foomo/squadron
revive:
rules:
- name: indent-error-flow
disabled: true
gocritic:
enabled-tags:
- diagnostic

View File

@ -130,17 +130,32 @@ func (sq *Squadron) FilterConfig(units []string) error {
}
func (sq *Squadron) RenderConfig() error {
tv := TemplateVars{}
var tv TemplateVars
var vars map[string]interface{}
if err := yaml.Unmarshal([]byte(sq.config), &vars); err != nil {
return err
}
// execute again with loaded template vars
tv = TemplateVars{}
if value, ok := vars["global"]; ok {
replace(value)
tv.add("Global", value)
}
if value, ok := vars["squadron"]; ok {
replace(value)
tv.add("Squadron", value)
}
// execute without errors to get existing values
out, err := executeFileTemplate(sq.config, tv, false)
if err != nil {
return errors.Wrap(err, "failed to execute initial file template")
}
var vars map[string]interface{}
if err := yaml.Unmarshal(out, &vars); err != nil {
return err
}
// execute again with loaded template vars
tv = TemplateVars{}
if value, ok := vars["global"]; ok {
replace(value)
tv.add("Global", value)

View File

@ -32,7 +32,7 @@ func (tv *TemplateVars) add(name string, value interface{}) {
func executeFileTemplate(text string, templateVars interface{}, errorOnMissing bool) ([]byte, error) {
templateFunctions := template.FuncMap{}
templateFunctions["env"] = env
templateFunctions["op"] = onePassword
templateFunctions["op"] = onePassword(templateVars, errorOnMissing)
templateFunctions["base64"] = base64
templateFunctions["default"] = defaultIndex
templateFunctions["indent"] = indent
@ -123,33 +123,60 @@ func indent(spaces int, v string) string {
return strings.ReplaceAll(v, "\n", "\n"+pad)
}
func onePassword(account, uuid, field string) (string, error) {
// validate command
if _, err := exec.LookPath("op"); err != nil {
fmt.Println("Your templates includes a call to 1Password, please install it:")
fmt.Println("https://support.1password.com/command-line-getting-started/#set-up-the-command-line-tool")
func render(name, text string, data interface{}, errorOnMissing bool) (string, error) {
var opts []string
if !errorOnMissing {
opts = append(opts, "missingkey=error")
}
out := bytes.NewBuffer([]byte{})
if uuidTpl, err := template.New(name).Option(opts...).Parse(text); err != nil {
return "", err
} else if err := uuidTpl.Execute(out, data); err != nil {
return "", err
}
return out.String(), nil
}
// validate session
if os.Getenv(fmt.Sprintf("OP_SESSION_%s", account)) == "" {
if err := onePasswordSignIn(account); err != nil {
func onePassword(templateVars interface{}, errorOnMissing bool) func(account, uuid, field string) (string, error) {
return func(account, uuid, field string) (string, error) {
if value, err := render("op", uuid, templateVars, errorOnMissing); err != nil {
return "", err
} else {
uuid = value
}
if value, err := render("op", field, templateVars, errorOnMissing); err != nil {
return "", err
} else {
field = value
}
// validate command
if _, err := exec.LookPath("op"); err != nil {
fmt.Println("Your templates includes a call to 1Password, please install it:")
fmt.Println("https://support.1password.com/command-line-getting-started/#set-up-the-command-line-tool")
return "", err
}
}
res, err := onePasswordGet(uuid, field)
if err != nil && strings.Contains(res, "You are not currently signed in") {
// retry with login
if err := onePasswordSignIn(account); err != nil {
return "", err
} else if res, err = onePasswordGet(uuid, field); err != nil {
// validate session
if os.Getenv(fmt.Sprintf("OP_SESSION_%s", account)) == "" {
if err := onePasswordSignIn(account); err != nil {
return "", err
}
}
res, err := onePasswordGet(uuid, field)
if err != nil && strings.Contains(res, "You are not currently signed in") {
// retry with login
if err := onePasswordSignIn(account); err != nil {
return "", err
} else if res, err = onePasswordGet(uuid, field); err != nil {
return "", err
}
} else if err != nil {
return "", err
}
} else if err != nil {
return "", err
return res, nil
}
return res, nil
}
func onePasswordGet(uuid, field string) (string, error) {

View File

@ -17,7 +17,7 @@ squadron:
image:
tag: <% .Squadron.frontend.builds.default.tag %>
repository: <% .Squadron.frontend.builds.default.image %>
env: <% env "LC_CTYPE" %>
env: <% env "SHELL" %>
global: <% .Global.host %>
base64: <% base64 "1234567890" %>
values: |

View File

@ -12,7 +12,7 @@ squadron:
version: 0.1.0
values:
base64: <% base64 "1234567890" %>
env: <% env "LC_CTYPE" %>
env: <% env "SHELL" %>
global: <% .Global.host %>
image:
repository: <% .Squadron.frontend.builds.default.image %>

View File

@ -2,13 +2,13 @@ squadron:
backend:
chart:
name: mychart
version: 0.1.0
repository: http://helm.mycompany.com/repository
version: 0.1.0
values:
ingress:
hosts:
- name: mycompany.com
path: /
port: 80
frontend: ~
- name: mycompany.com
path: /
port: 80
frontend: null
version: "1.0"

View File

@ -1,4 +1,5 @@
squadron:
backend: null
frontend:
builds:
service:

View File

@ -19,7 +19,7 @@ squadron:
tag: <% .Squadron.frontend.builds.default.tag %>
repository: <% .Squadron.frontend.builds.default.image %>
env:
ENV: <% env "LC_CTYPE" %>
ENV: <% env "SHELL" %>
GLOBAL: <% .Global.host %>
BASE64: <% base64 "1234567890" %>
frontend-admin:
@ -36,4 +36,4 @@ squadron:
tag: <% .Squadron.frontend_admin.builds.default.tag %>
repository: <% .Squadron.frontend_admin.builds.default.image %>
env:
ENV: <% env "LC_CTYPE" %>
ENV: <% env "SHELL" %>

View File

@ -14,7 +14,7 @@ squadron:
values:
env:
BASE64: MTIzNDU2Nzg5MA==
ENV: UTF-8
ENV: /bin/zsh
GLOBAL: mycompany.com
image:
repository: docker.mycompany.com/mycomapny/frontend

View File

@ -18,9 +18,12 @@ squadron:
tag: <% .Squadron.frontend.builds.default.tag %>
repository: <% .Squadron.frontend.builds.default.image %>
env:
ENV: <% env "LC_CTYPE" %>
ENV: <% env "SHELL" %>
GLOBAL: <% .Global.host %>
BASE64: <% base64 "1234567890" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "UUID" "FIELD" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name" "FIELD" %>
# ONE_PASSWORD: <% op "ACCOUNT_NAME" "Secret name wit global {{ .Global.host }}" "FIELD" %>
values: |
<% file "testdata/config-template/values.yaml" | indent 8 %>
frontend-admin:

View File

@ -13,7 +13,7 @@ squadron:
values:
env:
BASE64: MTIzNDU2Nzg5MA==
ENV: UTF-8
ENV: /bin/zsh
GLOBAL: mycompany.com
image:
repository: docker.mycompany.com/mycomapny/frontend
@ -23,7 +23,7 @@ squadron:
bar:
- foo
- bar
env: UTF-8
env: /bin/zsh
global: mycompany.com
base64: MTIzNDU2Nzg5MA==
frontend-admin:

View File

@ -2,6 +2,6 @@ foo: bar
bar:
- foo
- bar
env: <% env "LC_CTYPE" %>
env: <% env "SHELL" %>
global: <% .Global.host %>
base64: <% base64 "1234567890" %>