├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── SwarmManagement ├── SwarmConfigs.py ├── SwarmManager.py ├── SwarmNetworks.py ├── SwarmSecrets.py ├── SwarmStacks.py ├── SwarmTools.py ├── SwarmVolumes.py ├── __init__.py └── __main__.py ├── example ├── README.md ├── docker-compose.ssl.proxy.yml ├── environment.env ├── site.conf ├── site.crt ├── site.key └── swarm.management.yml ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── TestSwarmHandlers.py ├── TestSwarmTools.py ├── TestTools.py └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | # general things to ignore 2 | build/ 3 | dist/ 4 | *.egg-info/ 5 | *.egg 6 | *.py[cod] 7 | __pycache__/ 8 | *.so 9 | *~ 10 | 11 | # due to using tox and pytest 12 | .tox 13 | .cache 14 | 15 | .vscode/ 16 | .idea/ 17 | venv/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | services: 4 | - docker 5 | 6 | python: 7 | - "2.7" 8 | - "3.6" 9 | 10 | install: pip install -r requirements.txt 11 | 12 | script: python -m unittest discover -p *Test*.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DIPS AS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the README 2 | include README.md 3 | 4 | # Include the license file 5 | include LICENSE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker Swarm Management 2 | 3 | [![PyPI version](https://badge.fury.io/py/SwarmManagement.svg)](https://badge.fury.io/py/SwarmManagement) 4 | [![Build Status](https://travis-ci.com/DIPSAS/SwarmManagement.svg?branch=master)](https://travis-ci.com/DIPSAS/SwarmManagement) 5 | [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) 6 | 7 | # !DEPRECATED! 8 | Please follow new source: 9 | - https://github.com/hansehe/SwarmManagement 10 | 11 | Swarm Management is a python application, installed with pip. 12 | The application makes it easy to manage a Docker Swarm by configuring a single *.yml file describing which stacks to deploy, and which networks, configs or secrets to create. 13 | 14 | ## Install Or Upgrade 15 | - pip install --upgrade SwarmManagement 16 | 17 | ## Verify Installation 18 | - `swm -help` 19 | 20 | ## Example 21 | 1. Create a `swarm.management.yml` file describing all properties of the swarm. 22 | - The `swarm.management.yml` file contains following properties: 23 | ```yaml 24 | stacks: 25 | : 26 | networks: 27 | : 28 | encrypted: false 29 | driver: overlay 30 | attachable: true 31 | options: 32 | - --ipv6 33 | configs: 34 | : 35 | secrets: 36 | : 37 | volumes: 38 | : 39 | driver: local 40 | driverOptions: 41 | - type=tmpfs 42 | - device=tmpfs 43 | - o=size=100m,uid=1000 44 | env_files: 45 | - 46 | ``` 47 | 48 | 2. Manage Swarm: 49 | - Start Swarm with: 50 | - -> swm -start 51 | - Stop Swarm with: 52 | - -> swm -stop 53 | - Restart Swarm with: 54 | - -> swm -restart 55 | - Deploy/Update or Remove a single stack: 56 | - -> swm -stack -deploy `` 57 | - -> swm -stack -remove `` 58 | - Or deploy/remove all stacks with the `all` attribute: 59 | - -> swm -stack -deploy all 60 | - -> swm -stack -remove all 61 | - Create or Remove a single network: 62 | - -> swm -network -create `` 63 | - -> swm -network -remove `` 64 | - Or create/remove all networks with the `all` attribute: 65 | - -> swm -network -create all 66 | - -> swm -network -remove all 67 | - Create or Remove a single config: 68 | - -> swm -config -create `` 69 | - -> swm -config -remove `` 70 | - Or create/remove all configs with the `all` attribute: 71 | - -> swm -stack -create all 72 | - -> swm -stack -remove all 73 | - Create or Remove a single secret: 74 | - -> swm -secret -create `` 75 | - -> swm -secret -remove `` 76 | - Or create/remove all secrets with the `all` attribute: 77 | - -> swm -secret -create all 78 | - -> swm -secret -remove all 79 | - Create or Remove a single volume: 80 | - -> swm -volume -create `` 81 | - -> swm -volume -remove `` 82 | - Or create/remove all volumes with the `all` attribute: 83 | - -> swm -volume -create all 84 | - -> swm -volume -remove all 85 | - SwarmManagement uses the `swarm.management.yml` file by default to configure the swarm. 86 | - Specify a single or multiple *.yml files to use for configuring the swarm using the `-f` attribute: 87 | - -> swm -start -f swarm-stacks.yml -f swarm-networks.yml 88 | - Additional info is found by asking SwarmManagement: 89 | - -> swm -help 90 | - -> swm -stack -help 91 | - -> swm -network -help 92 | - -> swm -config -help 93 | - -> swm -secret -help 94 | - -> swm -volume -help 95 | 96 | Please have a look at an example of use here: 97 | - https://github.com/DIPSAS/SwarmManagement/tree/master/example 98 | 99 | ## Section Features 100 | 101 | ## Start/Stop or Restart Swarm 102 | Deploy the swarm with `swm -start`, and stop the swarm with `swm -stop`. 103 | Restart the swarm with `swm -restart `. The `` argument is optional, and defaults to 10 seconds if not given. 104 | 105 | ### Stacks 106 | The `stacks` section lists all stacks to be deployed as: `: ` 107 | 108 | ### Networks 109 | The `networks` section lists all networks to be created as, and each network is created with the following default properties: 110 | * `encrypted: false` 111 | * `driver: overlay` 112 | * `attachable: true` 113 | * `options:` 114 | - `` 115 | 116 | ### Configs 117 | The `configs` section lists all configs to be created as: `: ` 118 | 119 | ### Secrets 120 | The `secrets` section lists all secrets to be created as: `: ` 121 | 122 | ### Volumes 123 | The `volumes` section lists all volumes to be created as, and each volumes is created with the following default properties: 124 | * `driver: local` 125 | * `driverOptions:` 126 | - `` 127 | 128 | ### Environment variables 129 | The `env_files` section lists all environmnet (`.env`) files with environment variables to expose. 130 | By convention, a present `.env` file will automatically be loaded. 131 | 132 | ## Prerequisites 133 | - Docker: 134 | - https://www.docker.com/get-docker 135 | - Install Dependencies: 136 | - pip install -r requirements.txt 137 | 138 | ## Additional Info 139 | - The pip package may be located at: 140 | - https://pypi.org/project/SwarmManagement 141 | 142 | ## Publish New Version. 143 | 1. Configure setup.py with new version. 144 | 2. Install build tools: `pip install twine wheel` 145 | 3. Build: python setup.py bdist_wheel 146 | 4. Check: twine check dist/* 147 | 5. Publish: twine upload dist/* 148 | 149 | ## Run Unit Tests 150 | - python -m unittest -------------------------------------------------------------------------------- /SwarmManagement/SwarmConfigs.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmTools 2 | from DockerBuildSystem import DockerSwarmTools, YamlTools 3 | import sys 4 | 5 | 6 | def GetInfoMsg(): 7 | infoMsg = "Configs is configured by adding a 'configs' property to the .yaml file.\r\n" 8 | infoMsg += "The 'configs' property is a dictionary of configs.\r\n" 9 | infoMsg += "Each key in the config dictionary is the config name with a value containing the path to the config file, as such: \r\n" 10 | infoMsg += ": \r\n" 11 | infoMsg += "Example: \r\n" 12 | infoMsg += "configs: : \r\n" 13 | infoMsg += "Create or remove a config by adding '-config -c/-create ' or '-config -rm/-remove ' to the arguments\r\n" 14 | infoMsg += "Create or remove all configs by adding '-config -c/-create all' or '-config -rm/-remove all' to the arguments\r\n" 15 | return infoMsg 16 | 17 | 18 | def GetConfigs(arguments): 19 | yamlData = SwarmTools.LoadYamlDataFromFiles(arguments) 20 | return YamlTools.GetProperties('configs', yamlData) 21 | 22 | 23 | def CreateConfigs(configsToCreate, configs): 24 | for configToCreate in configsToCreate: 25 | if configToCreate == 'all': 26 | for config in configs: 27 | CreateConfig(config, configs[config]) 28 | else: 29 | if configToCreate in configs: 30 | CreateConfig(configToCreate, configs[configToCreate]) 31 | 32 | 33 | def CreateConfig(configName, configFile): 34 | DockerSwarmTools.CreateSwarmConfig( 35 | configFile, configName) 36 | 37 | 38 | def RemoveConfigs(configsToRemove, configs): 39 | for configToRemove in configsToRemove: 40 | if configToRemove == 'all': 41 | for config in configs: 42 | RemoveConfig(config) 43 | else: 44 | if configToRemove in configs: 45 | RemoveConfig(configToRemove) 46 | 47 | 48 | def RemoveConfig(configName): 49 | DockerSwarmTools.RemoveSwarmConfig(configName) 50 | 51 | 52 | def HandleConfigs(arguments): 53 | if len(arguments) == 0: 54 | return 55 | if not('-config' in arguments): 56 | return 57 | 58 | if '-help' in arguments: 59 | print(GetInfoMsg()) 60 | return 61 | 62 | configsToCreate = SwarmTools.GetArgumentValues(arguments, '-create') 63 | configsToCreate += SwarmTools.GetArgumentValues(arguments, '-c') 64 | 65 | configsToRemove = SwarmTools.GetArgumentValues(arguments, '-remove') 66 | configsToRemove += SwarmTools.GetArgumentValues(arguments, '-rm') 67 | 68 | configs = GetConfigs(arguments) 69 | 70 | CreateConfigs(configsToCreate, configs) 71 | RemoveConfigs(configsToRemove, configs) 72 | 73 | 74 | if __name__ == "__main__": 75 | arguments = sys.argv[1:] 76 | HandleConfigs(arguments) 77 | -------------------------------------------------------------------------------- /SwarmManagement/SwarmManager.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from SwarmManagement import SwarmStacks, SwarmConfigs, SwarmSecrets, SwarmNetworks, SwarmVolumes, SwarmTools 3 | from DockerBuildSystem import DockerSwarmTools 4 | 5 | 6 | def GetInfoMsg(): 7 | infoMsg = "Manage Docker Swarm\r\n" 8 | infoMsg += "Add '-start' to arguments to start swarm with all stacks deployed and properties created (networks, secrets, configs..).\r\n" 9 | infoMsg += "Add '-stop' to arguments to stop swarm with all stacks and properties removed.\r\n" 10 | infoMsg += "Add '-restart' to arguments to restart swarm.\r\n" 11 | infoMsg += "Otherwise:\r\n\r\n" 12 | infoMsg += "Deploy Or Remove Stacks:\r\n" 13 | infoMsg += SwarmStacks.GetInfoMsg() + "\r\n\r\n" 14 | infoMsg += "Create Or Remove Networks:\r\n" 15 | infoMsg += SwarmNetworks.GetInfoMsg() + "\r\n\r\n" 16 | infoMsg += "Create Or Remove Configs:\r\n" 17 | infoMsg += SwarmConfigs.GetInfoMsg() + "\r\n\r\n" 18 | infoMsg += "Create Or Remove Secrets:\r\n" 19 | infoMsg += SwarmSecrets.GetInfoMsg() + "\r\n\r\n" 20 | infoMsg += "Create Or Remove Volumes:\r\n" 21 | infoMsg += SwarmVolumes.GetInfoMsg() + "\r\n\r\n" 22 | infoMsg += "Additional Info:\r\n" 23 | infoMsg += SwarmTools.GetInfoMsg() + "\r\n\r\n" 24 | infoMsg += "Add '-help' to arguments to print this info again.\r\n\r\n" 25 | return infoMsg 26 | 27 | 28 | def StartSwarm(arguments): 29 | DockerSwarmTools.StartSwarm() 30 | SwarmVolumes.HandleVolumes(['-volume', '-create', 'all'] + arguments) 31 | SwarmConfigs.HandleConfigs(['-config', '-create', 'all'] + arguments) 32 | SwarmSecrets.HandleSecrets(['-secret', '-create', 'all'] + arguments) 33 | SwarmNetworks.HandleNetworks(['-network', '-create', 'all'] + arguments) 34 | SwarmStacks.HandleStacks(['-stack', '-deploy', 'all'] + arguments) 35 | 36 | 37 | def StopSwarm(arguments): 38 | SwarmStacks.HandleStacks(['-stack', '-remove', 'all'] + arguments) 39 | SwarmConfigs.HandleConfigs(['-config', '-remove', 'all'] + arguments) 40 | SwarmSecrets.HandleSecrets(['-secret', '-remove', 'all'] + arguments) 41 | # SwarmNetworks.HandleNetworks(['-network', '-remove', 'all'] + arguments) 42 | 43 | 44 | def RestartSwarm(arguments): 45 | StopSwarm(arguments) 46 | secTimeout = 10 47 | restartArguments = SwarmTools.GetArgumentValues(arguments, '-restart') 48 | if len(restartArguments) > 0 and restartArguments[0].isdigit(): 49 | secTimeout = int(restartArguments[0]) 50 | SwarmTools.TimeoutCounter(secTimeout) 51 | StartSwarm(arguments) 52 | 53 | 54 | def HandleManagement(arguments): 55 | if len(arguments) == 0: 56 | print(GetInfoMsg()) 57 | return 58 | 59 | SwarmTools.LoadEnvironmentVariables(arguments) 60 | SwarmTools.HandleDumpYamlData(arguments) 61 | 62 | if '-help' in arguments \ 63 | and not('-stack' in arguments) \ 64 | and not('-config' in arguments) \ 65 | and not('-secret' in arguments) \ 66 | and not('-network' in arguments): 67 | print(GetInfoMsg()) 68 | elif '-start' in arguments: 69 | StartSwarm(arguments) 70 | elif '-stop' in arguments: 71 | StopSwarm(arguments) 72 | elif '-restart' in arguments: 73 | RestartSwarm(arguments) 74 | else: 75 | SwarmVolumes.HandleVolumes(arguments) 76 | SwarmConfigs.HandleConfigs(arguments) 77 | SwarmSecrets.HandleSecrets(arguments) 78 | SwarmNetworks.HandleNetworks(arguments) 79 | SwarmStacks.HandleStacks(arguments) 80 | 81 | 82 | if __name__ == "__main__": 83 | arguments = sys.argv[1:] 84 | HandleManagement(arguments) 85 | -------------------------------------------------------------------------------- /SwarmManagement/SwarmNetworks.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmTools 2 | from DockerBuildSystem import DockerSwarmTools, YamlTools 3 | import sys 4 | 5 | 6 | def GetInfoMsg(): 7 | infoMsg = "Networks is configured by adding a 'networks' property to the .yaml file.\r\n" 8 | infoMsg += "The 'networks' property is a dictionary of networks.\r\n" 9 | infoMsg += "Each key in the network dictionary is the network name, as such: \r\n" 10 | infoMsg += ":\r\n" 11 | infoMsg += "Example: \r\n" 12 | infoMsg += "networks: :\r\n" 13 | infoMsg += "Create or remove a network by adding '-network -c/-create ' or 'network -rm/-remove ' to the arguments\r\n" 14 | infoMsg += "Create or remove all networks by adding '-network -c/-create all' or 'network -rm/-remove all' to the arguments\r\n" 15 | return infoMsg 16 | 17 | 18 | def GetNetworks(arguments): 19 | yamlData = SwarmTools.LoadYamlDataFromFiles(arguments) 20 | return YamlTools.GetProperties('networks', yamlData) 21 | 22 | 23 | def CreateNetworks(networksToCreate, networks): 24 | for networkToCreate in networksToCreate: 25 | if networkToCreate == 'all': 26 | for network in networks: 27 | CreateNetwork(network, networks[network]) 28 | else: 29 | if networkToCreate in networks: 30 | CreateNetwork(networkToCreate, networks[networkToCreate]) 31 | 32 | 33 | def CreateNetwork(networkName, networkProperties): 34 | if networkProperties == None: 35 | networkProperties = {} 36 | elif isinstance(networkProperties, bool): 37 | networkProperties = {'encrypted': networkProperties} 38 | 39 | encrypted = YamlTools.TryGetFromDictionary(networkProperties, 'encrypted', False) 40 | driver = YamlTools.TryGetFromDictionary(networkProperties, 'driver', 'overlay') 41 | attachable = YamlTools.TryGetFromDictionary(networkProperties, 'attachable', True) 42 | options = YamlTools.TryGetFromDictionary(networkProperties, 'options', []) 43 | 44 | DockerSwarmTools.CreateSwarmNetwork( 45 | networkName, encrypted, driver, attachable, options) 46 | 47 | 48 | def RemoveNetworks(networksToRemove, networks): 49 | for networkToRemove in networksToRemove: 50 | if networkToRemove == 'all': 51 | for network in networks: 52 | RemoveNetwork(network) 53 | else: 54 | if networkToRemove in networks: 55 | RemoveNetwork(networkToRemove) 56 | 57 | 58 | def RemoveNetwork(networkName): 59 | DockerSwarmTools.RemoveSwarmNetwork(networkName) 60 | 61 | 62 | def HandleNetworks(arguments): 63 | if len(arguments) == 0: 64 | return 65 | if not('-network' in arguments): 66 | return 67 | 68 | if '-help' in arguments: 69 | print(GetInfoMsg()) 70 | return 71 | 72 | networksToCreate = SwarmTools.GetArgumentValues(arguments, '-create') 73 | networksToCreate += SwarmTools.GetArgumentValues(arguments, '-c') 74 | 75 | networksToRemove = SwarmTools.GetArgumentValues(arguments, '-remove') 76 | networksToRemove += SwarmTools.GetArgumentValues(arguments, '-rm') 77 | 78 | networks = GetNetworks(arguments) 79 | 80 | CreateNetworks(networksToCreate, networks) 81 | RemoveNetworks(networksToRemove, networks) 82 | 83 | 84 | if __name__ == "__main__": 85 | arguments = sys.argv[1:] 86 | HandleNetworks(arguments) 87 | -------------------------------------------------------------------------------- /SwarmManagement/SwarmSecrets.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmTools 2 | from DockerBuildSystem import DockerSwarmTools, YamlTools 3 | import sys 4 | 5 | 6 | def GetInfoMsg(): 7 | infoMsg = "Secrets is configured by adding a 'secrets' property to the .yaml file.\r\n" 8 | infoMsg += "The 'secrets' property is a dictionary of secrets.\r\n" 9 | infoMsg += "Each key in the secret dictionary is the secret name with a value containing the path to the secret file, as such: \r\n" 10 | infoMsg += ": \r\n" 11 | infoMsg += "Example: \r\n" 12 | infoMsg += "secrets: : \r\n" 13 | infoMsg += "Create or remove a secret by adding '-secret -c/-create ' or 'secret -rm/-remove ' to the arguments\r\n" 14 | infoMsg += "Create or remove all secrets by adding '-secret -c/-create all' or 'secret -rm/-remove all' to the arguments\r\n" 15 | return infoMsg 16 | 17 | 18 | def GetSecrets(arguments): 19 | yamlData = SwarmTools.LoadYamlDataFromFiles(arguments) 20 | return YamlTools.GetProperties('secrets', yamlData) 21 | 22 | 23 | def CreateSecrets(secretsToCreate, secrets): 24 | for secretToCreate in secretsToCreate: 25 | if secretToCreate == 'all': 26 | for secret in secrets: 27 | CreateSecret(secret, secrets[secret]) 28 | else: 29 | if secretToCreate in secrets: 30 | CreateSecret(secretToCreate, secrets[secretToCreate]) 31 | 32 | 33 | def CreateSecret(secretName, secretFile): 34 | DockerSwarmTools.CreateSwarmSecret( 35 | secretFile, secretName) 36 | 37 | 38 | def RemoveSecrets(secretsToRemove, secrets): 39 | for secretToRemove in secretsToRemove: 40 | if secretToRemove == 'all': 41 | for secret in secrets: 42 | RemoveSecret(secret) 43 | else: 44 | if secretToRemove in secrets: 45 | RemoveSecret(secretToRemove) 46 | 47 | 48 | def RemoveSecret(secretName): 49 | DockerSwarmTools.RemoveSwarmSecret(secretName) 50 | 51 | 52 | def HandleSecrets(arguments): 53 | if len(arguments) == 0: 54 | return 55 | if not('-secret' in arguments): 56 | return 57 | 58 | if '-help' in arguments: 59 | print(GetInfoMsg()) 60 | return 61 | 62 | secretsToCreate = SwarmTools.GetArgumentValues(arguments, '-create') 63 | secretsToCreate += SwarmTools.GetArgumentValues(arguments, '-c') 64 | 65 | secretsToRemove = SwarmTools.GetArgumentValues(arguments, '-remove') 66 | secretsToRemove += SwarmTools.GetArgumentValues(arguments, '-rm') 67 | 68 | secrets = GetSecrets(arguments) 69 | 70 | CreateSecrets(secretsToCreate, secrets) 71 | RemoveSecrets(secretsToRemove, secrets) 72 | 73 | 74 | if __name__ == "__main__": 75 | arguments = sys.argv[1:] 76 | HandleSecrets(arguments) 77 | -------------------------------------------------------------------------------- /SwarmManagement/SwarmStacks.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmTools 2 | from DockerBuildSystem import DockerSwarmTools, YamlTools 3 | import sys 4 | 5 | 6 | def GetInfoMsg(): 7 | infoMsg = "Stacks is configured by adding a 'stacks' property to the .yaml file.\r\n" 8 | infoMsg += "The 'stacks' property is a dictionary of stacks.\r\n" 9 | infoMsg += "Each key in the stack dictionary is the stack name with a value containing the path to the compose file, as such: \r\n" 10 | infoMsg += ": \r\n" 11 | infoMsg += "Example: \r\n" 12 | infoMsg += "stacks: : \r\n" 13 | infoMsg += "Deploy or remove a stack by adding '-stack -d/-deploy ' or 'stack -rm/-remove ' to the arguments\r\n" 14 | infoMsg += "Deploy or remove all stacks by adding '-stack -d/-deploy all' or 'stack -rm/-remove all' to the arguments\r\n" 15 | return infoMsg 16 | 17 | 18 | def GetStacks(arguments): 19 | yamlData = SwarmTools.LoadYamlDataFromFiles(arguments) 20 | return YamlTools.GetProperties('stacks', yamlData) 21 | 22 | 23 | def DeployStacks(stacksToDeploy, stacks, environmentFiles): 24 | for stackToDeploy in stacksToDeploy: 25 | if stackToDeploy == 'all': 26 | for stack in stacks: 27 | DeployStack(stack, stacks[stack], environmentFiles) 28 | else: 29 | if stackToDeploy in stacks: 30 | DeployStack(stackToDeploy, stacks[stackToDeploy], environmentFiles) 31 | 32 | 33 | def DeployStack(stackName, composeFile, environmentFiles): 34 | DockerSwarmTools.DeployStack( 35 | composeFile, stackName, environmentFiles, withRegistryAuth=True) 36 | 37 | 38 | def RemoveStacks(stacksToRemove, stacks): 39 | for stackToRemove in stacksToRemove: 40 | if stackToRemove == 'all': 41 | for stack in stacks: 42 | RemoveStack(stack) 43 | else: 44 | if stackToRemove in stacks: 45 | RemoveStack(stackToRemove) 46 | 47 | 48 | def RemoveStack(stack): 49 | DockerSwarmTools.RemoveStack(stack) 50 | 51 | 52 | def HandleStacks(arguments): 53 | if len(arguments) == 0: 54 | return 55 | if not('-stack' in arguments): 56 | return 57 | 58 | if '-help' in arguments: 59 | print(GetInfoMsg()) 60 | return 61 | 62 | stacksToDeploy = SwarmTools.GetArgumentValues(arguments, '-deploy') 63 | stacksToDeploy += SwarmTools.GetArgumentValues(arguments, '-d') 64 | 65 | stacksToRemove = SwarmTools.GetArgumentValues(arguments, '-remove') 66 | stacksToRemove += SwarmTools.GetArgumentValues(arguments, '-rm') 67 | 68 | stacks = GetStacks(arguments) 69 | yamlData = SwarmTools.LoadYamlDataFromFiles(arguments) 70 | environmentFiles = SwarmTools.GetEnvironmnetVariablesFiles(arguments, yamlData) 71 | 72 | DeployStacks(stacksToDeploy, stacks, environmentFiles) 73 | RemoveStacks(stacksToRemove, stacks) 74 | 75 | 76 | if __name__ == "__main__": 77 | arguments = sys.argv[1:] 78 | HandleStacks(arguments) 79 | -------------------------------------------------------------------------------- /SwarmManagement/SwarmTools.py: -------------------------------------------------------------------------------- 1 | import time 2 | import os 3 | import sys 4 | from DockerBuildSystem import TerminalTools, YamlTools 5 | 6 | DEFAULT_ENVIRONMENT_FILE = '.env' 7 | DEFAULT_SWARM_MANAGEMENT_YAML_FILES = [ 8 | 'swarm.management.yml', 9 | 'swarm.management.yaml', 10 | 'swarm-management.yml', 11 | 'swarm-management.yaml'] 12 | 13 | 14 | def GetInfoMsg(): 15 | infoMsg = "One or more yaml files are used to configure the swarm.\r\n" 16 | infoMsg += "The yaml file 'swarm.management.yml' is used by default if no other files are specified.\r\n" 17 | infoMsg += "A yaml file may be specified by adding '-file' or '-f' to the arguments.\r\n" 18 | infoMsg += "Example: -f swarm.management-stacks.yml -f swarm.management-networks.yml\r\n" 19 | infoMsg += "Environment variables may be set with environment files.\r\n" 20 | infoMsg += GetEnvironmentVariablesInfoMsg() 21 | infoMsg += GetYamlDumpInfoMsg() 22 | return infoMsg 23 | 24 | 25 | def GetEnvironmentVariablesInfoMsg(): 26 | infoMsg = "Environment variables may be set with environment files or variables directly.\r\n" 27 | infoMsg += "Multiple environment files or variables may be set set with the 'env_files' property in the yaml file.\r\n" 28 | infoMsg += "Example: \r\n" 29 | infoMsg += "env_files: \r\n" 30 | infoMsg += "\t - 'environment.env'\r\n" 31 | infoMsg += "\t - 'envKey=envValue'\r\n" 32 | infoMsg += "Topmost listed files will override any matching variables from the file listed below.\r\n" 33 | infoMsg += "Directly setting a variable will always override an existing matching variable.\r\n" 34 | infoMsg += "Environment files and variables may also be set set with the -e/-env argument.\r\n" 35 | infoMsg += "Example: -e environment.env envKey=envValue\r\n" 36 | return infoMsg 37 | 38 | 39 | def GetYamlDumpInfoMsg(): 40 | infoMsg = "It is possible to dump current yaml data using the '-dump' argument.\r\n" 41 | infoMsg += "Example: -dump output.yml\r\n" 42 | return infoMsg 43 | 44 | 45 | def GetNoYmlFilesFoundMsg(defaultYamlFiles=DEFAULT_SWARM_MANAGEMENT_YAML_FILES): 46 | infoMsg = "Error\r\n" 47 | infoMsg += "Could not find any supported management files.\r\n" 48 | infoMsg += "Are you in the right directory?\r\n" 49 | infoMsg += "Supported management files: " + str.join(', ', defaultYamlFiles) + "\r\n" 50 | return infoMsg 51 | 52 | 53 | def AssertYamlFilesExists(yamlFiles, defaultYamlFiles=DEFAULT_SWARM_MANAGEMENT_YAML_FILES): 54 | existingYamlFiles = [] 55 | for yamlFile in yamlFiles: 56 | if os.path.isfile(yamlFile): 57 | existingYamlFiles.append(yamlFile) 58 | 59 | if len(existingYamlFiles) == 0: 60 | print(GetNoYmlFilesFoundMsg(defaultYamlFiles)) 61 | exit(-1) 62 | 63 | return existingYamlFiles 64 | 65 | 66 | def HandleDumpYamlData(arguments, defaultYamlFiles=DEFAULT_SWARM_MANAGEMENT_YAML_FILES): 67 | if not('-dump' in arguments): 68 | return 69 | outputFiles = GetArgumentValues(arguments, '-dump') 70 | for outputFile in outputFiles: 71 | yamlData = LoadYamlDataFromFiles(arguments, defaultYamlFiles) 72 | YamlTools.DumpYamlDataToFile(yamlData, outputFile) 73 | 74 | 75 | def GetArgumentValues(arguments, argumentType, ignoreArgumentsWithPrefix="-", stopAtFirstArgumentWithPrefix="-"): 76 | argumentValues = [] 77 | for i in range(len(arguments)): 78 | currentArgumentType = arguments[i] 79 | if currentArgumentType == argumentType: 80 | for j in range(i, len(arguments)): 81 | argumentValue = arguments[j] 82 | if not(argumentValue.startswith(ignoreArgumentsWithPrefix)): 83 | argumentValues.append(argumentValue) 84 | elif argumentValue != argumentType and \ 85 | len(argumentValues) > 0 and \ 86 | argumentValue.startswith(stopAtFirstArgumentWithPrefix): 87 | return argumentValues 88 | return argumentValues 89 | 90 | 91 | def LoadYamlDataFromFiles(arguments, defaultYamlFiles=DEFAULT_SWARM_MANAGEMENT_YAML_FILES, \ 92 | ignoreEmptyYamlData = False): 93 | yamlFiles = GetArgumentValues(arguments, '-file') 94 | yamlFiles += GetArgumentValues(arguments, '-f') 95 | if len(yamlFiles) == 0: 96 | yamlFiles = defaultYamlFiles 97 | yamlFiles = AssertYamlFilesExists(yamlFiles, defaultYamlFiles) 98 | yamlData = YamlTools.GetYamlData(yamlFiles, ignoreEmptyYamlData, infoMsgOnError=GetInfoMsg()) 99 | return yamlData 100 | 101 | 102 | def LoadEnvironmentVariables(arguments, defaultYamlFiles=DEFAULT_SWARM_MANAGEMENT_YAML_FILES): 103 | yamlData = LoadYamlDataFromFiles( 104 | arguments, defaultYamlFiles, True) 105 | environmentFiles = GetEnvironmnetVariablesFiles( 106 | arguments, yamlData) 107 | for environmentFile in environmentFiles: 108 | if os.path.isfile(environmentFile): 109 | TerminalTools.LoadEnvironmentVariables(environmentFile) 110 | elif '=' in environmentFile: 111 | key, value = environmentFile.split('=') 112 | os.environ[key] = value 113 | 114 | 115 | 116 | def GetEnvironmnetVariablesFiles(arguments, yamlData): 117 | envFiles = [] 118 | envFiles += GetArgumentValues(arguments, '-env') 119 | envFiles += GetArgumentValues(arguments, '-e') 120 | if 'env_files' in yamlData: 121 | envFiles += yamlData['env_files'] 122 | if os.path.isfile(DEFAULT_ENVIRONMENT_FILE): 123 | envFiles += [DEFAULT_ENVIRONMENT_FILE] 124 | return envFiles 125 | 126 | 127 | def TimeoutCounter(secTimeout): 128 | startTime = time.time() 129 | elapsedTime = time.time() - startTime 130 | timeLeft = secTimeout - int(elapsedTime) 131 | printedTime = timeLeft 132 | while elapsedTime < secTimeout: 133 | timeLeft = secTimeout - int(elapsedTime) 134 | if timeLeft < printedTime: 135 | printedTime = timeLeft 136 | print("Restarting Swarm in %d seconds" % printedTime) 137 | elapsedTime = time.time() - startTime 138 | 139 | 140 | if __name__ == "__main__": 141 | arguments = sys.argv[1:] 142 | HandleDumpYamlData(arguments) 143 | -------------------------------------------------------------------------------- /SwarmManagement/SwarmVolumes.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmTools 2 | from DockerBuildSystem import DockerSwarmTools, YamlTools 3 | import sys 4 | 5 | 6 | def GetInfoMsg(): 7 | infoMsg = "Volumes is configured by adding a 'volumes' property to the .yaml file.\r\n" 8 | infoMsg += "The 'volumes' property is a dictionary of volumes.\r\n" 9 | infoMsg += "Each key in the volume dictionary is the volume name, as such: \r\n" 10 | infoMsg += ":\r\n" 11 | infoMsg += "Example: \r\n" 12 | infoMsg += "volumes: :\r\n" 13 | infoMsg += "Create or remove a volume by adding '-volume -c/-create ' or '-volume -rm/-remove ' to the arguments\r\n" 14 | infoMsg += "Create or remove all volumes by adding '-volume -c/-create all' or '-volume -rm/-remove all' to the arguments\r\n" 15 | return infoMsg 16 | 17 | 18 | def GetVolumes(arguments): 19 | yamlData = SwarmTools.LoadYamlDataFromFiles(arguments) 20 | return YamlTools.GetProperties('volumes', yamlData) 21 | 22 | 23 | def CreateVolumes(volumesToCreate, volumes): 24 | for volumeToCreate in volumesToCreate: 25 | if volumeToCreate == 'all': 26 | for volume in volumes: 27 | CreateVolume(volume, volumes[volume]) 28 | else: 29 | if volumeToCreate in volumes: 30 | CreateVolume(volumeToCreate, volumes[volumeToCreate]) 31 | 32 | 33 | def CreateVolume(volumeName, volumeProperties): 34 | if volumeProperties == None: 35 | volumeProperties = {} 36 | driver = YamlTools.TryGetFromDictionary(volumeProperties, 'driver', 'local') 37 | driverOptions = YamlTools.TryGetFromDictionary(volumeProperties, 'driverOptions', []) 38 | 39 | DockerSwarmTools.CreateSwarmVolume(volumeName, driver, driverOptions) 40 | 41 | 42 | def RemoveVolumes(volumesToRemove, volumes): 43 | for volumeToRemove in volumesToRemove: 44 | if volumeToRemove == 'all': 45 | for volume in volumes: 46 | RemoveVolume(volume) 47 | else: 48 | if volumeToRemove in volumes: 49 | RemoveVolume(volumeToRemove) 50 | 51 | 52 | def RemoveVolume(volumeName): 53 | DockerSwarmTools.RemoveSwarmVolume(volumeName) 54 | 55 | 56 | def HandleVolumes(arguments): 57 | if len(arguments) == 0: 58 | return 59 | if not('-volume' in arguments): 60 | return 61 | 62 | if '-help' in arguments: 63 | print(GetInfoMsg()) 64 | return 65 | 66 | volumesToCreate = SwarmTools.GetArgumentValues(arguments, '-create') 67 | volumesToCreate += SwarmTools.GetArgumentValues(arguments, '-c') 68 | 69 | volumesToRemove = SwarmTools.GetArgumentValues(arguments, '-remove') 70 | volumesToRemove += SwarmTools.GetArgumentValues(arguments, '-rm') 71 | 72 | volumes = GetVolumes(arguments) 73 | 74 | CreateVolumes(volumesToCreate, volumes) 75 | RemoveVolumes(volumesToRemove, volumes) 76 | 77 | 78 | if __name__ == "__main__": 79 | arguments = sys.argv[1:] 80 | HandleVolumes(arguments) 81 | -------------------------------------------------------------------------------- /SwarmManagement/__init__.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmManager 2 | import sys 3 | 4 | def main(): 5 | """Entry point for the application script""" 6 | arguments = sys.argv[1:] 7 | SwarmManager.HandleManagement(arguments) 8 | 9 | if __name__ == "__main__": 10 | main() -------------------------------------------------------------------------------- /SwarmManagement/__main__.py: -------------------------------------------------------------------------------- 1 | from SwarmManagement import SwarmManager 2 | import sys 3 | 4 | def main(): 5 | """Entry point for the application script""" 6 | arguments = sys.argv[1:] 7 | SwarmManager.HandleManagement(arguments) 8 | 9 | if __name__ == "__main__": 10 | main() -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example Of Use With Swarm Management 2 | 1. Install SwarmManagement with pip: 3 | - pip install SwarmManagement 4 | 2. Manage Swarm 5 | - Start Swarm with: 6 | - -> SwarmManagement -start 7 | - Stop Swarm with: 8 | - -> SwarmManagement -stop 9 | - SwarmManagement uses the `swarm.management.yml` file by default to configure the swarm. 10 | - Additional info is found by asking SwarmManagement: 11 | - -> SwarmManagement -help -------------------------------------------------------------------------------- /example/docker-compose.ssl.proxy.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | services: 4 | ssl-proxy-web: 5 | image: nginx:latest 6 | deploy: 7 | replicas: 1 8 | restart_policy: 9 | condition: on-failure 10 | secrets: 11 | - site.key 12 | - site.crt 13 | configs: 14 | - source: site.conf 15 | target: /etc/nginx/conf.d/site.conf 16 | networks: 17 | - frontend_network 18 | ports: 19 | - ${SSL_FRONTEND_PORT}:443 20 | 21 | networks: 22 | frontend_network: 23 | external: true 24 | 25 | configs: 26 | site.conf: 27 | external: true 28 | 29 | secrets: 30 | site.key: 31 | external: true 32 | site.crt: 33 | external: true -------------------------------------------------------------------------------- /example/environment.env: -------------------------------------------------------------------------------- 1 | # SSL Proxy Frontend 2 | SSL_FRONTEND_PORT=443 -------------------------------------------------------------------------------- /example/site.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | server_name localhost; 4 | ssl_certificate /run/secrets/site.crt; 5 | ssl_certificate_key /run/secrets/site.key; 6 | 7 | location / { 8 | root /usr/share/nginx/html; 9 | index index.html index.htm; 10 | } 11 | } -------------------------------------------------------------------------------- /example/site.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICFjCCAX8CAgPoMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNVBAYTAk5PMQ8wDQYD 3 | VQQIDAZOb3J3YXkxDzANBgNVBAcMBk5vcndheTESMBAGA1UECgwJRFVNTVkgQVNB 4 | MQ4wDAYDVQQLDAVEVU1NWTAeFw0xODEyMTMxNTM2MTJaFw0yODEyMTAxNTM2MTJa 5 | MFMxCzAJBgNVBAYTAk5PMQ8wDQYDVQQIDAZOb3J3YXkxDzANBgNVBAcMBk5vcndh 6 | eTESMBAGA1UECgwJRFVNTVkgQVNBMQ4wDAYDVQQLDAVEVU1NWTCBnzANBgkqhkiG 7 | 9w0BAQEFAAOBjQAwgYkCgYEAy54o+7pC1i3zGYZoMIhp+nI/GjYKTQYOrNglcpqc 8 | X96KkiJryS5U0xRkC1bha87V58zWgmqijhGq1kdXSTxDckuDPjbgWNAlJ4DFfHTj 9 | ZyoPV5rseytvHkdPigk2gUAXdmKtCmNR5UvsMtzg0VIpKsAG6QdTirYm4OzLnSrC 10 | iFkCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAyDqtk9/YomMlNyFNG38lzkl517Xmf 11 | 7oi44MuH59Q7ttLWQGch7LzYrep8YzmJwlByB9I8bBF7pp2v/L5MURsgItlLPdRt 12 | 7/3DL7IfQ1AUsaKoQOQnBHJV+7iIP4eqniAQ4uj0aqjXYDMbqv2pAPbdnN0xL7s6 13 | eINJcgIDEMCgaQ== 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /example/site.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMueKPu6QtYt8xmG 3 | aDCIafpyPxo2Ck0GDqzYJXKanF/eipIia8kuVNMUZAtW4WvO1efM1oJqoo4RqtZH 4 | V0k8Q3JLgz424FjQJSeAxXx042cqD1ea7Hsrbx5HT4oJNoFAF3ZirQpjUeVL7DLc 5 | 4NFSKSrABukHU4q2JuDsy50qwohZAgMBAAECgYBl5b+CwiLChno60t1/NDU9BUF4 6 | /4RGKXrcmsqawK7y9y6HlTw1kL8YZwLt/cuPpRG9D/BlkbHJwSOqWkdfOg5R1r/f 7 | yNj1CoP1TmZ75t6hKHdqZbjH3k6Tc5Y0c+lqdNVUczgfpAHY2Z8NSugRiOE8SsIi 8 | hX5XHPqafqjHoEjg5QJBAOvdKEinScjkStElT/eaaj/fD3KnONBifukuViNsex9x 9 | MP+G5F2l7k7KJDh73sO1ODd2VlbryFA21+tUBGUAdQ8CQQDdAEJusne+lVPn5dgZ 10 | snz0/LAecSE8YK7OTnmPxcZamZGywvbGHWOTmCoORD0Vq8WKxTgzpk0D5NCZuI18 11 | L7wXAkAKKdV6i/rZ9TxkFr+DY9wANJRt1FbWY+gkFCMSE7KagD4kRKPChUvniT5W 12 | UMAgnsZ1XVwLEs3m5vYW5d/qmkJxAkAyb/W5Z9e2UkRE/4rTSo4EJ4tWV0Fbk0Ex 13 | 6m0J5/w9/yBmaOKDEoAAVlD60o4SXRKHej06ZHDdO6J0As+fkRFfAkBccBw8tBMe 14 | zq6cBd5h0ZDawweMq4Tmqro4WIENHmIGBAKfeQ17Uwm+n40nAcfRMXRobneea4KX 15 | hSdUcGwjxOJp 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /example/swarm.management.yml: -------------------------------------------------------------------------------- 1 | 2 | stacks: 3 | ssl_proxy: docker-compose.ssl.proxy.yml 4 | 5 | networks: 6 | frontend_network: 7 | encrypted: false 8 | driver: overlay 9 | attachable: true 10 | options: 11 | - --ipv6 12 | backend_network: 13 | 14 | configs: 15 | site.conf: site.conf 16 | 17 | secrets: 18 | site.key: site.key 19 | site.crt: site.crt 20 | 21 | volumes: 22 | first_volume: 23 | driver: local 24 | driverOptions: 25 | - type=tmpfs 26 | - device=tmpfs 27 | - o=size=100m,uid=1000 28 | second_volume: 29 | 30 | env_files: 31 | - environment.env -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | DockerBuildSystem>=1.1.52 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | # This includes the license file in the wheel. 3 | license_file = LICENSE 4 | 5 | [bdist_wheel] 6 | # This flag says to generate wheels that support both Python 2 and Python 7 | # 3. If your code will not run unchanged on both Python 2 and 3, you will 8 | # need to generate separate wheels for each Python version that you 9 | # support. Removing this line (or setting universal to 0) will prevent 10 | # bdist_wheel from trying to make a universal wheel. For more see: 11 | # https://packaging.python.org/tutorials/distributing-packages/#wheels 12 | universal=1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | 3 | See: 4 | https://packaging.python.org/en/latest/distributing.html 5 | https://github.com/pypa/sampleproject 6 | """ 7 | 8 | # Always prefer setuptools over distutils 9 | from setuptools import setup, find_packages 10 | # To use a consistent encoding 11 | from codecs import open 12 | from os import path 13 | 14 | here = path.abspath(path.dirname(__file__)) 15 | 16 | # Get the long description from the README file 17 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 18 | long_description = f.read() 19 | 20 | with open('requirements.txt') as f: 21 | reqLines = f.readlines() 22 | REQUIREMENTS = [reqLine.replace('\r', '').replace('\n', '') for reqLine in reqLines] 23 | 24 | # Arguments marked as "Required" below must be included for upload to PyPI. 25 | # Fields marked as "Optional" may be commented out. 26 | 27 | setup( 28 | # This is the name of your project. The first time you publish this 29 | # package, this name will be registered for you. It will determine how 30 | # users can install this project, e.g.: 31 | # 32 | # $ pip install sampleproject 33 | # 34 | # And where it will live on PyPI: https://pypi.org/project/sampleproject/ 35 | # 36 | # There are some restrictions on what makes a valid project name 37 | # specification here: 38 | # https://packaging.python.org/specifications/core-metadata/#name 39 | name='SwarmManagement', # Required 40 | 41 | # Versions should comply with PEP 440: 42 | # https://www.python.org/dev/peps/pep-0440/ 43 | # 44 | # For a discussion on single-sourcing the version across setup.py and the 45 | # project code, see 46 | # https://packaging.python.org/en/latest/single_source_version.html 47 | version='1.1.73', # Required 48 | 49 | # This is a one-line description or tagline of what your project does. This 50 | # corresponds to the "Summary" metadata field: 51 | # https://packaging.python.org/specifications/core-metadata/#summary 52 | description='A library for managing Docker Swarm.', # Required 53 | 54 | # This is an optional longer description of your project that represents 55 | # the body of text which users will see when they visit PyPI. 56 | # 57 | # Often, this is the same as your README, so you can just read it in from 58 | # that file directly (as we have already done above) 59 | # 60 | # This field corresponds to the "Description" metadata field: 61 | # https://packaging.python.org/specifications/core-metadata/#description-optional 62 | long_description=long_description, # Optional 63 | 64 | # Denotes that our long_description is in Markdown; valid values are 65 | # text/plain, text/x-rst, and text/markdown 66 | # 67 | # Optional if long_description is written in reStructuredText (rst) but 68 | # required for plain-text or Markdown; if unspecified, "applications should 69 | # attempt to render [the long_description] as text/x-rst; charset=UTF-8 and 70 | # fall back to text/plain if it is not valid rst" (see link below) 71 | # 72 | # This field corresponds to the "Description-Content-Type" metadata field: 73 | # https://packaging.python.org/specifications/core-metadata/#description-content-type-optional 74 | long_description_content_type='text/markdown', # Optional (see note above) 75 | 76 | # This should be a valid link to your project's main homepage. 77 | # 78 | # This field corresponds to the "Home-Page" metadata field: 79 | # https://packaging.python.org/specifications/core-metadata/#home-page-optional 80 | url='https://github.com/DIPSAS/SwarmManagement', # Optional 81 | 82 | # This should be your name or the name of the organization which owns the 83 | # project. 84 | author='DIPS AS', # Optional 85 | 86 | # This should be a valid email address corresponding to the author listed 87 | # above. 88 | author_email='heh@dips.no', # Optional 89 | 90 | # Classifiers help users find your project by categorizing it. 91 | # 92 | # For a list of valid classifiers, see https://pypi.org/classifiers/ 93 | classifiers=[ # Optional 94 | # How mature is this project? Common values are 95 | # 3 - Alpha 96 | # 4 - Beta 97 | # 5 - Production/Stable 98 | 'Development Status :: 5 - Production/Stable', 99 | 100 | # Indicate who your project is intended for 101 | 'Intended Audience :: Developers', 102 | 'Topic :: Software Development :: Build Tools', 103 | 104 | # Pick your license as you wish 105 | 'License :: OSI Approved :: MIT License', 106 | 107 | # Specify the Python versions you support here. In particular, ensure 108 | # that you indicate whether you support Python 2, Python 3 or both. 109 | 'Programming Language :: Python :: 2', 110 | 'Programming Language :: Python :: 2.7', 111 | 'Programming Language :: Python :: 3', 112 | 'Programming Language :: Python :: 3.4', 113 | 'Programming Language :: Python :: 3.5', 114 | 'Programming Language :: Python :: 3.6', 115 | ], 116 | 117 | # This field adds keywords for your project which will appear on the 118 | # project page. What does your project relate to? 119 | # 120 | # Note that this is a string of words separated by whitespace, not a list. 121 | keywords='Docker Swarm Management', # Optional 122 | 123 | # You can just specify package directories manually here if your project is 124 | # simple. Or you can use find_packages(). 125 | # 126 | # Alternatively, if you just want to distribute a single Python file, use 127 | # the `py_modules` argument instead as follows, which will expect a file 128 | # called `my_module.py` to exist: 129 | # 130 | # py_modules=["my_module"], 131 | # 132 | packages=find_packages(exclude=['contrib', 'docs', '*tests']), # Required 133 | 134 | # This field lists other packages that your project depends on to run. 135 | # Any package you put here will be installed by pip when your project is 136 | # installed, so they must be valid existing projects. 137 | # 138 | # For an analysis of "install_requires" vs pip's requirements files see: 139 | # https://packaging.python.org/en/latest/requirements.html 140 | install_requires=REQUIREMENTS, # Optional 141 | 142 | # List additional groups of dependencies here (e.g. development 143 | # dependencies). Users will be able to install these using the "extras" 144 | # syntax, for example: 145 | # 146 | # $ pip install sampleproject[dev] 147 | # 148 | # Similar to `install_requires` above, these must be valid existing 149 | # projects. 150 | extras_require={ # Optional 151 | 'dev': ['check-manifest'], 152 | 'test': ['coverage'], 153 | }, 154 | 155 | # If there are data files included in your packages that need to be 156 | # installed, specify them here. 157 | # 158 | # If using Python 2.6 or earlier, then these have to be included in 159 | # MANIFEST.in as well. 160 | # package_data={ # Optional 161 | # 'sample': ['package_data.dat'], 162 | # }, 163 | 164 | # Although 'package_data' is the preferred approach, in some case you may 165 | # need to place data files outside of your packages. See: 166 | # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files 167 | # 168 | # In this case, 'data_file' will be installed into '/my_data' 169 | # data_files=[('my_data', ['data/data_file'])], # Optional 170 | 171 | # To provide executable scripts, use entry points in preference to the 172 | # "scripts" keyword. Entry points provide cross-platform support and allow 173 | # `pip` to create the appropriate form of executable for the target 174 | # platform. 175 | # 176 | # For example, the following would provide a command called `sample` which 177 | # executes the function `main` from this package when invoked: 178 | # entry_points={ # Optional 179 | # 'console_scripts': [ 180 | # 'sample=sample:main', 181 | # ], 182 | # }, 183 | entry_points={ # Optional 184 | 'console_scripts': [ 185 | 'SwarmManagement=SwarmManagement:main', 186 | 'swm=SwarmManagement:main', 187 | ], 188 | }, 189 | 190 | # List additional URLs that are relevant to your project as a dict. 191 | # 192 | # This field corresponds to the "Project-URL" metadata fields: 193 | # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use 194 | # 195 | # Examples listed include a pattern for specifying where the package tracks 196 | # issues, where the source is hosted, where to say thanks to the package 197 | # maintainers, and where to support the project financially. The key is 198 | # what's used to render the link text on PyPI. 199 | project_urls={ # Optional 200 | # 'Bug Reports': 'https://github.com/pypa/sampleproject/issues', 201 | 'Funding': 'https://donate.pypi.org', 202 | 'Say Thanks!': 'http://saythanks.io/to/example', 203 | 'Source': 'https://github.com/DIPSAS/SwarmManagement', 204 | }, 205 | ) 206 | -------------------------------------------------------------------------------- /tests/TestSwarmHandlers.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from tests import TestTools 4 | from SwarmManagement import SwarmConfigs 5 | from SwarmManagement import SwarmSecrets 6 | from SwarmManagement import SwarmVolumes 7 | from SwarmManagement import SwarmNetworks 8 | from SwarmManagement import SwarmStacks 9 | from SwarmManagement import SwarmManager 10 | 11 | class TestSwarmHandlers(unittest.TestCase): 12 | 13 | def test_a_config(self): 14 | print('EXECUTING SWARM CONFIG TEST') 15 | cwd = TestTools.ChangeToSampleFolderAndGetCwd() 16 | arguments = ['-config', '-create', 'all'] 17 | SwarmConfigs.HandleConfigs(arguments) 18 | arguments = ['-config', '-rm', 'all'] 19 | SwarmConfigs.HandleConfigs(arguments) 20 | arguments = ['-config', '-create', 'site.conf'] 21 | SwarmConfigs.HandleConfigs(arguments) 22 | arguments = ['-config', '-rm', 'site.conf'] 23 | SwarmConfigs.HandleConfigs(arguments) 24 | os.chdir(cwd) 25 | print('DONE EXECUTING SWARM CONFIG TEST') 26 | 27 | def test_b_secret(self): 28 | print('EXECUTING SWARM SECRET TEST') 29 | cwd = TestTools.ChangeToSampleFolderAndGetCwd() 30 | arguments = ['-secret', '-create', 'all'] 31 | SwarmSecrets.HandleSecrets(arguments) 32 | arguments = ['-secret', '-rm', 'all'] 33 | SwarmSecrets.HandleSecrets(arguments) 34 | arguments = ['-secret', '-create', 'site.key'] 35 | SwarmSecrets.HandleSecrets(arguments) 36 | arguments = ['-secret', '-rm', 'site.key'] 37 | SwarmSecrets.HandleSecrets(arguments) 38 | os.chdir(cwd) 39 | print('DONE EXECUTING SWARM SECRET TEST') 40 | 41 | def test_c_networks(self): 42 | print('EXECUTING SWARM NETWORK TEST') 43 | cwd = TestTools.ChangeToSampleFolderAndGetCwd() 44 | arguments = ['-network', '-create', 'all'] 45 | SwarmNetworks.HandleNetworks(arguments) 46 | arguments = ['-network', '-rm', 'all'] 47 | SwarmNetworks.HandleNetworks(arguments) 48 | arguments = ['-network', '-create', 'frontend_network'] 49 | SwarmNetworks.HandleNetworks(arguments) 50 | arguments = ['-network', '-rm', 'frontend_network'] 51 | SwarmNetworks.HandleNetworks(arguments) 52 | os.chdir(cwd) 53 | print('DONE EXECUTING SWARM NETWORK TEST') 54 | 55 | def test_d_volumes(self): 56 | print('EXECUTING SWARM VOLUMES TEST') 57 | cwd = TestTools.ChangeToSampleFolderAndGetCwd() 58 | arguments = ['-volume', '-create', 'all'] 59 | SwarmVolumes.HandleVolumes(arguments) 60 | arguments = ['-volume', '-rm', 'all'] 61 | SwarmVolumes.HandleVolumes(arguments) 62 | arguments = ['-volume', '-create', 'first_volume'] 63 | SwarmVolumes.HandleVolumes(arguments) 64 | arguments = ['-volume', '-rm', 'first_volume'] 65 | SwarmVolumes.HandleVolumes(arguments) 66 | os.chdir(cwd) 67 | print('DONE EXECUTING SWARM VOLUMES TEST') 68 | 69 | def test_e_manager(self): 70 | print('EXECUTING SWARM MANAGER TEST') 71 | cwd = TestTools.ChangeToSampleFolderAndGetCwd() 72 | arguments = ['-start'] 73 | SwarmManager.HandleManagement(arguments) 74 | arguments = ['-restart', '2'] 75 | SwarmManager.HandleManagement(arguments) 76 | arguments = ['-restart'] 77 | SwarmManager.HandleManagement(arguments) 78 | arguments = ['-stop'] 79 | SwarmManager.HandleManagement(arguments) 80 | os.chdir(cwd) 81 | print('DONE EXECUTING SWARM MANAGER TEST') 82 | 83 | def test_f_stacks(self): 84 | print('EXECUTING SWARM STACKS TEST') 85 | cwd = TestTools.ChangeToSampleFolderAndGetCwd() 86 | arguments = ['-start'] 87 | SwarmManager.HandleManagement(arguments) 88 | arguments = ['-stack', '-deploy', 'all'] 89 | SwarmStacks.HandleStacks(arguments) 90 | arguments = ['-stack', '-rm', 'all'] 91 | SwarmStacks.HandleStacks(arguments) 92 | arguments = ['-stack', '-deploy', 'ssl_proxy'] 93 | SwarmStacks.HandleStacks(arguments) 94 | arguments = ['-stack', '-rm', 'ssl_proxy'] 95 | SwarmStacks.HandleStacks(arguments) 96 | arguments = ['-stop'] 97 | SwarmManager.HandleManagement(arguments) 98 | os.chdir(cwd) 99 | print('DONE EXECUTING SWARM STACKS TEST') 100 | 101 | if __name__ == '__main__': 102 | unittest.main() -------------------------------------------------------------------------------- /tests/TestSwarmTools.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from SwarmManagement import SwarmTools 3 | 4 | class TestSwarmTools(unittest.TestCase): 5 | 6 | def test_getInforMsg_success(self): 7 | self.assertIsNotNone(SwarmTools.GetInfoMsg()) 8 | 9 | def test_getArgumentValues_specific_selections(self): 10 | runSelections = ['run1', 'run2', 'run3'] 11 | buildSelections = ['build1', 'build2'] 12 | arguments = ['-run'] + runSelections + ['-build'] + buildSelections 13 | print(arguments) 14 | selectedRuns = SwarmTools.GetArgumentValues(arguments, '-run') 15 | selectedBuilds = SwarmTools.GetArgumentValues(arguments, '-build') 16 | print(selectedRuns) 17 | print(selectedBuilds) 18 | self.assertEqual(len(runSelections), len(selectedRuns)) 19 | self.assertEqual(len(buildSelections), len(selectedBuilds)) 20 | self.assertListEqual(runSelections, selectedRuns) 21 | self.assertListEqual(buildSelections, selectedBuilds) 22 | 23 | def test_getArgumentValues_common_selections(self): 24 | selections = ['selection1', 'selection2', 'selection3'] 25 | arguments = ['-run'] + ['-build'] + selections 26 | print(arguments) 27 | selectedRuns = SwarmTools.GetArgumentValues(arguments, '-run') 28 | selectedBuilds = SwarmTools.GetArgumentValues(arguments, '-build') 29 | print(selectedRuns) 30 | print(selectedBuilds) 31 | self.assertEqual(len(selections), len(selectedRuns)) 32 | self.assertListEqual(selections, selectedRuns) 33 | self.assertEqual(len(selections), len(selectedBuilds)) 34 | self.assertListEqual(selections, selectedBuilds) 35 | 36 | 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /tests/TestTools.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | TEST_SAMPLE_FOLDER = 'example' 5 | 6 | 7 | def ChangeToSampleFolderAndGetCwd(): 8 | cwd = os.getcwd() 9 | os.chdir(TEST_SAMPLE_FOLDER) 10 | return cwd -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------