Thanks! I'll try to explain in excruciating detail what I'm doing and why I'm doing it and maybe get to a better solution.
We are implementing "Continuous Deployment" (CD) using SaltStack to provision the minions, with ThoughtWorks GoCD to handle the CD pipeline workflow and Jenkins to do the CI part (build, unit test, SonarQube analysis, GoCD Pipeline trigger). A typical GoCD pipeline looks like the following:
An overview of what each pipeline does is as follows (some detail omitted):
- ratingtool
- Triggered by Jenkins
- Takes as input from Jenkins the following build information (among other things)
- Nexus Artifact URL (WAR, DEB, JAR_With_Deps)
- Nexus Arifact SHA1
- etc.
- Saves build information for downstream pipelines as a properties file
- RatingTool_Prep_Materials
- Retrieves property file from upstream pipeline 'ratingtool'
- Checks out salt_pillars from GIT containing salt state templates
- Generates pillar states from templates containing the new build URL/SHA1
- Commits change back to git
- Generates salt pillar file/folder structure expected by SaltMaster
- Saves pillars for all environments
- RatingTool_DEV (Developer Environment)
- Retrieve pillar from upstream pipeline "RatingTool_Prep_Materials"
- Copy the DEV environment pillars to salt master
- Initiate salt highstate for DEV minions via salt-api
- Run Automated Test Suite
- RatingTool_ACC (Acceptance Test Environment)
- Retrieve pillar from upstream pipeline "RatingTool_Prep_Materials"
- Copy the ACC environment pillars to salt master
- Initiate salt highstate for ACC minions via salt-api
- Run Automated Test Suite
- <Repeat Until Production environment is reached>
Even though our pillars are stored in GIT, the Salt Master does NOT directly use GIT for pillars.
Issue #1 - We initially started with GIT using branches for each environment. This became a nightmare to maintain. With some of our projects, we will eventually have 100+ environments across 40 data centers for our cloud service in production. Each application for each environment requires unique settings/configuration specific to that environment. Some pillars are shared but keeping some files in the master branch and some in environment-specific branches for 100+ environments is PAINFUL to maintain.
Issue #2 - Idempotency in each environment for each application... or how to control the progression/promotion of new builds into each environment in to prevent a HighState from deploying a build that wasn't ready for that environment. For example, if the current state has:
DEV = v1.3.6 | ACC = v1.3.2 | BTA = v1.2.9 | PRD-DC5 = v1.2.2
If DEV validation passes, build v1.3.6 can then be promoted to ACC and the state will become:
DEV = v1.3.6 | ACC = v1.3.6 | BTA = v1.2.9 | PRD-DC5 = v1.2.2
Then a new build might get triggered changing the state to:
DEV = v1.3.7 | ACC = v1.3.6 | BTA = v1.2.9 | PRD-DC5 = v1.2.2
Issue #3 - How to minimize duplication of pillar state files for a given application as well as keep all application-specific files together? For a given application, many of the pillar settings are the same. This also relates to making it so that when a DEV or OPS engineer makes changes to the settings, we minimize the number of files and file locations that get touched.
Current solution:
Our pillars files are in the GIT 'master' branch using the following file structure:
├───ratingtool
│ ├───acc
│ │ custom.sls
│ │ globals.yml
│ │ salt-api_request.json
│ │
│ ├───bta
│ │ custom.sls
│ │ globals.yml
│ │ salt-api_request.json
│ │
│ ├───dev
│ │ custom.sls
│ │ globals.yml
│ │ salt-api_request.json
│ │
│ ├───prd-dc5
│ │ custom.sls
│ │ globals.yml
│ │ salt-api_request.json
│ │
│ └───shared
│ init.sls
│ war_file.sls //from Template by "prep_materials" pipeline
│ war_file.sls.template //Template
This allows all application-specific pillars to be located within one subfolder per-application.
Additionally to minimize duplication of pillar files, common files were moved to a 'shared' folder to be used in each environment. In the example above, the "RatingTool_Prep_Materials" pipeline phase transforms the GIT folder structure to the following structure compatible with the salt-master layout:
...
This structure is NOT saved to GIT, but managed by the GoCD pipeline as an artifact of the pipeline process. The following pillar file structure exists on the salt master:
/srv/pillar
├── acc
│ ├── ratingtool
├── bta
│ └── ratingtool
├── dev
│ └── ratingtool
├── int
│ └── ratingtool
├── prd-dc5
│ └── ratingtool
When the pipeline 'RatingTool_DEV' runs, it copies files from pillar/dev/** for that build to the salt master at /srv/pillar/dev/ratingtool and then calls state.highstate. The salt-master now has the updated pillar containing the new application URL/SHA1.
The file copy process is cumbersome and I'd like to find a pure GIT mechanism.
The first step to a GIT solution (and the reason I started this thread) is to try and get top.sls (for all environments) to be pulled from GIT since I don't have an automated process in place to update top.sls. Since we have different salt masters, I split top.sls into separate files based on the salt master using it. Each master now has an ext_pillar entry for pulling top:
#/etc/salt/master from DEV salt-master
- master git@gitlab:/salt-pillars.git:
#/etc/salt/master from OPS salt-master
- master git@gitlab:/salt-pillars.git:
The salt-master seems to be reading the top file, but not applying it to any environment except 'base' which seems counter-intuitive since the top.sls file in the 'base' pillar_root works for all environments.
I'd also like to use a GIT-only solution for managing build promotion to the various environments, but I don't have a clear picture as to what might work without adding a lot of complexity.
Sorry this was long but I'm open to all input.
-Jeff