Skip to content

Commit

Permalink
feat(apply): adds option show dependency graph
Browse files Browse the repository at this point in the history
This commit exposes the hidden feature of rio apply where you can
visualise the dependency between various rapyuta.io resources in your
manifests. It uses `mermaid.live` to show the visualization.
  • Loading branch information
pallabpain committed May 31, 2023
1 parent 1a8d207 commit 70f8950
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 45 deletions.
41 changes: 33 additions & 8 deletions riocli/apply/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,34 @@
help_headers_color='yellow',
help_options_color='green',
)
@click.option('--dryrun', '-d', is_flag=True, default=False, help='dry run the yaml files without applying any change')
@click.option('--dryrun', '-d', is_flag=True, default=False,
help='dry run the yaml files without applying any change')
@click.option('--show-graph', '-g', is_flag=True, default=False,
help='Opens a mermaid.live dependency graph')
@click.option('--values', '-v',
help="path to values yaml file. key/values specified in the values file can be used as variables in template yamls")
help="path to values yaml file. key/values "
"specified in the values file can be "
"used as variables in template YAMLs")
@click.option('--secrets', '-s',
help="secret files are sops encoded value files. rio-cli expects sops to be authorized for decoding files on this computer")
@click.option('--workers', '-w', help="number of parallel workers while running apply command. defaults to 6.",
type=int)
@click.option('-f', '--force', '--silent', 'silent', is_flag=True, type=click.BOOL, default=False,
help="secret files are sops encoded value files. "
"rio-cli expects sops to be authorized for "
"decoding files on this computer")
@click.option('--workers', '-w',
help="number of parallel workers while running apply "
"command. defaults to 6.", type=int)
@click.option('-f', '--force', '--silent', 'silent', is_flag=True,
type=click.BOOL, default=False,
help="Skip confirmation")
@click.argument('files', nargs=-1)
def apply(values: str, secrets: str, files: Iterable[str], dryrun: bool = False, workers: int = 6,
silent: bool = False) -> None:
def apply(
values: str,
secrets: str,
files: Iterable[str],
dryrun: bool = False,
workers: int = 6,
silent: bool = False,
show_graph: bool = False,
) -> None:
"""
Apply resource manifests
"""
Expand All @@ -58,6 +74,15 @@ def apply(values: str, secrets: str, files: Iterable[str], dryrun: bool = False,
rc = Applier(glob_files, abs_values, abs_secrets)
rc.parse_dependencies()

if show_graph and dryrun:
click.secho('You cannot dry run and launch the graph together.',
fg='yellow')
return

if show_graph:
rc.show_dependency_graph()
return

if not silent and not dryrun:
click.confirm("Do you want to proceed?", default=True, abort=True)

Expand Down
33 changes: 23 additions & 10 deletions riocli/apply/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# limitations under the License.
import copy
import json
import os
import queue
import threading
import typing
Expand Down Expand Up @@ -138,14 +137,20 @@ def delete(self, *args, **kwargs):
delete_order = list(self.graph.static_order())
delete_order.reverse()
for obj in delete_order:
if obj in self.resolved_objects and 'manifest' in self.resolved_objects[obj]:
if obj in self.resolved_objects and 'manifest' in \
self.resolved_objects[obj]:
self._delete_manifest(obj, *args, **kwargs)

def print_resolved_manifests(self):
manifests = [o for _, o in self.objects.items()]
dump_all_yaml(manifests)

def parse_dependencies(self, check_missing=True, delete=False, template=False):
def parse_dependencies(
self,
check_missing=True,
delete=False,
template=False,
):
number_of_objects = 0
for f, data in self.files.items():
for model in data:
Expand All @@ -170,7 +175,10 @@ def parse_dependencies(self, check_missing=True, delete=False, template=False):

if not template:
self._display_context(
total_time=total_time, total_objects=number_of_objects, resource_list=resource_list)
total_time=total_time,
total_objects=number_of_objects,
resource_list=resource_list,
)

if check_missing:
missing_resources = []
Expand Down Expand Up @@ -365,14 +373,19 @@ def _initialize_kind_dependency(self, kind):
if not self.dependencies.get(kind):
self.dependencies[kind] = {}

def show_dependency_graph(self):
"""Lauches mermaid.live dependency graph"""
link = mermaid_link("\n".join(self.diagram))
click.launch(link)

# Utils
def _display_context(self, total_time: int, total_objects: int, resource_list: typing.List) -> None:
def _display_context(
self,
total_time: int,
total_objects: int,
resource_list: typing.List
) -> None:
# Display context

if os.environ.get('MERMAID'):
diagram_link = mermaid_link("\n".join(self.diagram))
click.launch(diagram_link)

headers = [click.style('Resource Context', bold=True, fg='yellow')]
context = [
['Expected Time (mins)', round(total_time, 2)],
Expand Down
46 changes: 19 additions & 27 deletions riocli/utils/mermaid.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Copyright 2023 Rapyuta Robotics
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import json

Expand All @@ -6,41 +20,18 @@ def mermaid_safe(s: str):
return s.replace(" ", "_")


def js_string_to_byte(data):
def js_string_to_byte(data: str):
return bytes(data, 'iso-8859-1')


def js_bytes_to_string(data):
def js_bytes_to_string(data: bytes):
return data.decode('iso-8859-1')


def js_btoa(data):
def js_btoa(data: bytes):
return base64.b64encode(data)


# def js_encode_uri_component(data):
# return quote(data)


# def js_atob(data):
# return base64.b64decode(data)

# def pako_inflate_raw(data):
# decompress = zlib.decompressobj(-15)
# decompressed_data = decompress.decompress(data)
# decompressed_data += decompress.flush()
# return decompressed_data


# def pako_deflate_raw(data):
# compress = zlib.compressobj(
# zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15, 8,
# zlib.Z_DEFAULT_STRATEGY)
# compressed_data = compress.compress(js_string_to_byte(js_encode_uri_component(data)))
# compressed_data += compress.flush()
# return compressed_data


def mermaid_link(diagram):
obj = {
"code": diagram,
Expand All @@ -54,4 +45,5 @@ def mermaid_link(diagram):
json_str = json.dumps(obj)
json_bytes = js_string_to_byte(json_str)
encoded_uri = js_btoa(json_bytes)
return "https://mermaid.live/view#base64:{}".format(js_bytes_to_string(encoded_uri))
return 'https://mermaid.live/view#base64:{}'.format(
js_bytes_to_string(encoded_uri))

0 comments on commit 70f8950

Please sign in to comment.