From 03a9dfcafb520900cad24483d6422148db0d4ee1 Mon Sep 17 00:00:00 2001 From: stefan Date: Sun, 20 Mar 2016 11:32:53 +0100 Subject: [PATCH] added possibility to print system boundaries --- README.md | 31 +++++++++++++++++++++++-- bin/compose_plantuml | 37 +++++++++++++++++------------- compose_plantuml/__init__.py | 44 ++++++++++++++++++++++++++++++------ features/boundaries.feature | 44 ++++++++++++++++++++++++++++++++++++ features/link_graph.feature | 36 ++++++----------------------- features/validation.feature | 28 +++++++++++++++++++++++ img/boundaries.svg | 2 ++ img/link_graph.svg | 2 +- 8 files changed, 169 insertions(+), 55 deletions(-) create mode 100644 features/boundaries.feature create mode 100644 features/validation.feature create mode 100644 img/boundaries.svg diff --git a/README.md b/README.md index e5267c2..ae7e9a0 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ Install it via: `pip3 install compose_plantuml` After that use it like: -`compose_plantuml docker-compose.yml` +`compose_plantuml --link-graph docker-compose.yml` ### Via Docker Use it like: -`cat docker-compose.yml | docker run -i funkwerk/compose_plantuml` +`cat docker-compose.yml | docker run -i funkwerk/compose_plantuml --link-graph` ## Link Graph @@ -52,6 +52,33 @@ Rendered it looks like: +## Boundaries + +Boundaries visualize the external boundaries a system has. + +Consider the following docker-compose.yml + +``` +version: '2' +services: + service: + ports: + - 8080:80 +``` + +When calling 'compose_plantuml --boundaries docker-compose.yml' it will generate the following plantuml: + +``` +rectangle system { + [service] +} +[service] --> 8080 : 80 +``` + +Rendered it looks like: + + + ## Related Links - draw compose diff --git a/bin/compose_plantuml b/bin/compose_plantuml index 3de7b8a..7c15937 100755 --- a/bin/compose_plantuml +++ b/bin/compose_plantuml @@ -9,31 +9,36 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument( - '--replace', action='store_const', const=True, - help='should the original file be replaced?', default=False, ) + '--link-graph', action='store_const', const=True, + help='prints a link graph', default=False, ) parser.add_argument( - '--ignore_changes', action='store_const', const=False, - help='ignore changes for return code', - default=True, - ) - parser.add_argument( - '--non_strict', action='store_const', const=True, - help='if this is provided, unknown keys errors are ignored', + '--boundaries', action='store_const', const=True, + help='prints the system boundaries', default=False, ) parser.add_argument('files', nargs=argparse.REMAINDER) args = parser.parse_args() plantuml = ComposePlantuml() - if len(args.files) == 0: - assert args.replace is False, 'replace makes no sense when reading from stdin' + if not args.link_graph and not args.boundaries: + print('specify an output option. link_graph or boundaries') + sys.exit(1) - data = sys.stdin.read() - formatted = plantuml.print_links(data) - - for path in args.files: + def execute(data): try: - plantuml.print_links_from_file(path) + parsed = plantuml.parse(data) except VersionException as exception: print('docker-compose version exception: {0}'.format(exception.message)) sys.exit(1) + + if args.link_graph: + print(plantuml.link_graph(parsed)) + if args.boundaries: + print(plantuml.boundaries(parsed)) + + if len(args.files) == 0: + execute(sys.stdin.read()) + + for path in args.files: + with open(path, 'r') as file: + execute(file.read()) diff --git a/compose_plantuml/__init__.py b/compose_plantuml/__init__.py index a183502..8e3dda4 100755 --- a/compose_plantuml/__init__.py +++ b/compose_plantuml/__init__.py @@ -7,19 +7,34 @@ class ComposePlantuml: def __init__(self): pass - def print_links_from_file(self, path): - with open(path, 'r') as file: - self.print_links(file.read()) - - def print_links(self, data): + def parse(self, data): compose = load(data) self.require_version_2(compose) + return compose + + def link_graph(self, compose): + result = 'skinparam componentStyle uml2\n' for component in sorted(self.components(compose)): - print('[{0}]'.format(component)) + result += '[{0}]\n'.format(component) for source, destination in sorted(self.links(compose)): - print('[{0}] --> [{1}]'.format(source, destination)) + result += '[{0}] --> [{1}]\n'.format(source, destination) + return result.strip() + + def boundaries(self, compose): + result = 'skinparam componentStyle uml2\n' + + result += 'rectangle system {\n' + for component in sorted(self.components(compose)): + result += ' [{0}]\n'.format(component) + result += '}\n' + for service, host, container in sorted(self.ports(compose)): + port = host + if container is not None: + port = '{0} : {1}'.format(host, container) + result += '[{0}] --> {1}\n'.format(service, port) + return result.strip() @staticmethod def components(compose): @@ -37,6 +52,21 @@ def links(compose): result.append((component_name, link)) return result + @staticmethod + def ports(compose): + result = [] + + for component_name in compose.get('services', []): + component = compose['services'][component_name] + + for port in component.get('ports', []): + port = str(port) + host, container = (port, None) + if ':' in port: + host, container = port.split(':') + result.append((component_name, host, container)) + return result + @staticmethod def require_version_2(compose): if 'version' not in compose: diff --git a/features/boundaries.feature b/features/boundaries.feature new file mode 100644 index 0000000..ace24b1 --- /dev/null +++ b/features/boundaries.feature @@ -0,0 +1,44 @@ +Feature: Boundaries + As a DevOps, + I want to see the boundaries of a system + so that I know how to interact with it. + + Scenario: Exposed ports + Given a file named "compose.yml" with: + """ + version: "2" + services: + service: + ports: + - 8080 + """ + When I run `bin/compose_plantuml --boundaries compose.yml` + Then it should pass with exactly: + """ + skinparam componentStyle uml2 + rectangle system { + [service] + } + [service] --> 8080 + + """ + + Scenario: Alias Ports + Given a file named "compose.yml" with: + """ + version: "2" + services: + service: + ports: + - 8080:80 + """ + When I run `bin/compose_plantuml --boundaries compose.yml` + Then it should pass with exactly: + """ + skinparam componentStyle uml2 + rectangle system { + [service] + } + [service] --> 8080 : 80 + + """ diff --git a/features/link_graph.feature b/features/link_graph.feature index 38a9bfe..f536976 100644 --- a/features/link_graph.feature +++ b/features/link_graph.feature @@ -1,31 +1,7 @@ Feature: Link Graph - As a DevOps - I want to have readable, formatted docker-compose files - so that I see errors soon - - Scenario: Requires Version - Given a file named "compose.yml" with: - """ - foo: - image: bar - """ - When I run `bin/compose_plantuml compose.yml` - Then it should fail with: - """ - docker-compose version exception: version not present - """ - - Scenario: Requires Version 2 - Given a file named "compose.yml" with: - """ - version: 1 - """ - When I run `bin/compose_plantuml compose.yml` - Then it should fail with exactly: - """ - docker-compose version exception: need version 2, but got 1 - - """ + As a DevOps, + I want to get a link graph of my system + so that I know where the data flows. Scenario: Basic Link Graph Given a file named "compose.yml" with: @@ -37,9 +13,10 @@ Feature: Link Graph - second second: {} """ - When I run `bin/compose_plantuml compose.yml` + When I run `bin/compose_plantuml --link-graph compose.yml` Then it should pass with exactly: """ + skinparam componentStyle uml2 [first] [second] [first] --> [second] @@ -56,9 +33,10 @@ Feature: Link Graph - second:second_alias second: {} """ - When I run `bin/compose_plantuml compose.yml` + When I run `bin/compose_plantuml --link-graph compose.yml` Then it should pass with exactly: """ + skinparam componentStyle uml2 [first] [second] [first] --> [second] diff --git a/features/validation.feature b/features/validation.feature new file mode 100644 index 0000000..18c9da4 --- /dev/null +++ b/features/validation.feature @@ -0,0 +1,28 @@ +Feature: Validation + As a DevOps, + I want to have error messages for invalid compose input + so that I'm sure it works if it does not return an error. + + Scenario: Requires Version + Given a file named "compose.yml" with: + """ + foo: + image: bar + """ + When I run `bin/compose_plantuml --link-graph compose.yml` + Then it should fail with: + """ + docker-compose version exception: version not present + """ + + Scenario: Requires Version 2 + Given a file named "compose.yml" with: + """ + version: 1 + """ + When I run `bin/compose_plantuml --link-graph compose.yml` + Then it should fail with exactly: + """ + docker-compose version exception: need version 2, but got 1 + + """ diff --git a/img/boundaries.svg b/img/boundaries.svg new file mode 100644 index 0000000..ec80b31 --- /dev/null +++ b/img/boundaries.svg @@ -0,0 +1,2 @@ + +systemservice808080 \ No newline at end of file diff --git a/img/link_graph.svg b/img/link_graph.svg index 7276b5c..337c55f 100644 --- a/img/link_graph.svg +++ b/img/link_graph.svg @@ -1,2 +1,2 @@ -firstsecond \ No newline at end of file +firstsecond \ No newline at end of file