feat: keep the tenant's configuration in Git with export --tenant - #78
Merged
Merged
Conversation
Admins want templates, environments, teams, property definitions, hubs and integrations under version control the way a team keeps its experiments. `export --tenant -d dir` writes them one directory per kind, and `diff -d` and `apply -d [--dry-run]` recognise those directories next to a team's. Apply follows the references between kinds: property definitions, environments (teams name them), teams (integrations and experiments name them), hubs, templates (service profiles name them), integrations, then service profiles, services, experiments and schedules. What every platform provides is left out: the two hubs the platform connects itself (fixed ids), templates imported from a hub (they keep the hub's id, and `template import` brings them back) and Steadybit's service profiles. The platform masks webhook secrets on read, refuses the mask on write and drops the secret when it is left out, so neither keeping nor removing it in the file round-trips. Exported files hold a mask for every credential, including header values and Slack webhook URLs, which the platform returns in the clear. A mask matches whatever the platform holds, so an export shows no drift; the project apply skips integrations that match, and `integration apply` sends the stored header values and URLs in place of masks. A secret has to be put in to change its integration. Diffs never print credentials. The platform turns a team's empty allowedActions into [wait, service-validation] and a webhook's empty targetAttributeIncludes into ['*']; Kind.Defaults now also covers a field the file leaves empty, so a hand-written file is not drift right after it is applied. Team members' read-only fields are stripped by path, as `team get` does. Each new kind also gets its own `diff` and `apply --dry-run`.
The platform lists hidden templates, and those whose actions, target types or property definitions are not available right now, only when asked, so the tenant export silently left them out and the Git copy was incomplete. It now asks for both. An id that is no UUID was sent as the zero UUID. The export now fails on one, and a hub, template, environment or integration file holding one is reported with its name instead of being taken for a new one.
Diffs masked only string values in secret fields, so a header value written as an unquoted YAML number was printed in the clear. Any non-empty value in a secret field is now masked, whatever its type.
Teams were looked up by key but compared on their id, so an export diffed or applied on another platform, where the team has another id, showed an id difference forever and sent that platform a foreign id. The platform upserts a team by its key and ignores the id: on dev, a file with a foreign id and an existing key updated that team, and one with the team's id and a new key created a second team. The id is therefore neither exported nor compared, and `team apply` no longer sends it, so it cannot contradict the key should the platform ever start reading it.
Hubs were saved without being synchronized, so a restored tenant had the hub but none of its templates by the time the service profiles were applied, and profiles naming those templates failed. `apply -d` now synchronizes each hub as `hub apply --synchronize` does, and a hub that cannot be synchronized ends the apply with the platform's reason, before anything that depends on it.
`integration <kind> apply` refused an untouched exported file for its masked secret, while `apply -d` skipped the same file as matching the platform. A file holding a masked secret that otherwise matches what the platform holds now has nothing to apply and is reported as unchanged; a changed one still needs the secret put in. The platform's version is read once for both the masked header values and this check.
# Conflicts: # CHANGELOG.md # internal/cli/experiment.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
export/diff/applykept one team in Git (experiments, schedules, services, profiles). Admins want the tenant's configuration there too. This is the use case on the Kanbanize card: automating templates and experiments.Layout and order
property-definitions/,environments/,teams/,hubs/,templates/,integrations/{webhook,slack,preflight,preflight-action}/,service-profiles/.--teamand--tenantare mutually exclusive.getwrites; each package's read-only list is reused.diff -d/apply -dhandle whichever directories exist, in one dependency order:diffandapply --dry-run.template importbrings them back);Secrets. Verified on dev and in the platform source:
secretwith one*per character. Sending that mask back is refused with 422. Leaving the secret out deletes it.So:
'********'for every credential: the secret, every header value, and the Slack URL.diff, a mask matches whatever the platform holds, so a fresh export shows no drift. A credential is never printed: a real one from the file shows as'******** (from the file)'.apply -dskips integrations that match the platform. For a changed one, masked headers and the Slack URL are restored from what the platform holds. A maskedsecretis refused, with a message pointing atget/export.Defaults (see #70):
allowedActions: [wait, service-validation]andmanagedBy: MANUAL, webhooks totargetAttributeIncludes: ['*'].name,pictureUrlandmanagedByare read-only.Testing
go test -race ./...andgo vet(alsoGOOS=windows) pass.export --team Wwrites byte-identical files withmainand with this branch;export --tenantwrote 272 templates, 45 environments, 31 teams, 23 property definitions, 1 hub, 11 integrations and 12 service profiles;diff -don that export: every file matches, exit 0.cli-e2e-export-*, teamCLIX, URLs on example.com, all deleted afterwards):Not verified: applying an export to a different platform, where the ids don't exist. It's unknown whether the platform creates templates, hubs and integrations under the id it is sent. Also untested: OIDC-managed teams with members.
Open questions
The CHANGELOG
v6.1.0section will conflict trivially with the other open feature PRs.