Ejemplo n.º 1
0
def handler(system: flow_api.System, this: flow_api.Execution):
    """
    Clone a git repository, create Cloudomation objects from the files.
    All .py files in the flows/ subdirectory will be stored as Flows.
    All .yaml files in the settings/ subdirectory will be stored as Settings.
    All files in the files/ subdirectory will be stored as Files.
    """
    inputs = this.get('input_value')
    # this flow is registered as webhook, triggered by a commit to the
    # repository. The commit sha is passed in .json.commit_sha
    # when started manually, it will sync from master
    try:
        ref = inputs['json']['commit_sha']
    except (KeyError, TypeError):
        ref = 'master'
    # read the connection information of the private repository
    repo_info = system.setting('private git repo').get('value')
    # the git 'get' command fetches the content of the repository.
    # since no files_path is passed, the files will be returned in the
    # output_value of the task
    files = this.connect(
        connector_type='GIT',
        command='get',
        repository_url=repo_info['repository_url'],
        httpCookie=repo_info['httpCookie'],
        ref=ref,
    ).get('output_value')['files']
    # iterate over all files
    for file_ in files:
        # split the path and filename
        path, filename = os.path.split(file_['name'])
        # split the filename and file extension
        name, ext = os.path.splitext(filename)
        if path == 'flows' and ext == '.py':
            # decode the base64 file content to text
            text_content = base64.b64decode(file_['content']).decode()
            # create or update Flow object
            system.flow(name).save(script=text_content)
        elif path == 'settings' and ext == '.yaml':
            # decode the base64 file content to text
            text_content = base64.b64decode(file_['content']).decode()
            # load the yaml string in the file content
            value = yaml.safe_load(text_content)
            # create or update Setting object
            system.setting(name).save(value=value)
        elif path == 'files':
            # create or update File object
            # we use the name with the extension
            # pass the base64 encoded binary file content directly
            system.file(filename).save(content=file_['content'],
                                       convert_binary=False)

    return this.success('all done')
Ejemplo n.º 2
0
def handler(system: flow_api.System, this: flow_api.Execution):
    # this flow script will sync from the master branch.
    ref = 'master'

    # get the connection information for the github repo
    repo_info = system.setting('github_info').get('value')
    github_username = repo_info['github_username']
    github_repo_name = repo_info['github_repo_name']
    github_token = repo_info['github_token']

    repo_url = (f'https://{github_username}:{github_token}@github.com/'
                f'{github_username}/{github_repo_name}.git')

    # the git 'get' command fetches the content of the repository.
    files = this.connect(
        connector_type='GIT',
        command='get',
        repository_url=repo_url,
        ref=ref,
    ).get('output_value')['files']

    # iterate over all files
    for file_ in files:
        # split the path and filename
        path, filename = os.path.split(file_['name'])
        # split the filename and file extension
        name, ext = os.path.splitext(filename)

        # Create or update flow scripts from all .py files.
        if ext == '.py':
            # decode the base64 file content to text
            text_content = base64.b64decode(file_['content']).decode()
            # create or update Flow object
            system.flow(name).save(script=text_content)

        # Create or update settings from all .yaml files.
        elif ext == '.yaml':
            # decode the base64 file content to text
            text_content = base64.b64decode(file_['content']).decode()
            # load the yaml string in the file content
            value = yaml.safe_load(text_content)
            # create or update Setting object
            system.setting(name).save(value=value)

        # All files that have a file extension other than .py or .yaml are ignored.

    return this.success('Github sync complete')
Ejemplo n.º 3
0
def handler(system: flow_api.System, this: flow_api.Execution):
    """
    Clone a git repository, create Cloudomation objects from the files.
    All .py files in the flows/ subdirectory will be stored as Flows.
    All .yaml files in the settings/ subdirectory will be stored as Settings.
    All files in the files/ subdirectory will be stored as Files.
    
    A pop-up message form will request all necessary user information for the repository.
    
    The inputs are not checked for validity!
    """

    # This is a pop-up message form, it will ask for some information
    # The last element is an OK-button, after clicking it the
    # values are passed further and the pop-up message closes.
    git_login_form_response = system.message(
        subject='Input Git repository information to connect',
        message_type='POPUP',
        body={
            'type': 'object',
            'properties': {
                'username': {
                    'element': 'string',
                    'type': 'string',
                    'label': 'Enter your username for the GIT repository:',
                    'order': 1,
                },
                'password': {
                    'element': 'password',
                    'type': 'string',
                    'label': 'Enter your password for the GIT repository:',
                    'order': 2,
                },
                # Here an example input is shown in the field, but its value is empty:
                'repository url': {
                    'element': 'string',
                    'type': 'string',
                    'label':
                    'Enter the URL for the repository you want to clone into Cloudomation:',
                    'example': 'https://repository.url/user/repository.git',
                    'order': 3,
                },
                # Here a default value is provided with the field:
                'reference': {
                    'element': 'string',
                    'type': 'string',
                    'label':
                    'Enter the "branch" you want to clone from or a "commit sha":',
                    'default': 'develop',
                    'order': 4,
                },
                'Ok': {
                    'element': 'submit',
                    'label': 'OK',
                    'type': 'boolean',
                    'order': 5,
                },
            },
            'required': [
                'username',
                'password',
                'repository url',
                'reference',
            ],
        },
    ).wait().get('response')
    git_username = git_login_form_response['username']
    git_password = git_login_form_response['password']
    git_repo_url = git_login_form_response['repository url']
    git_reference = git_login_form_response['reference']

    # Now we use a "GIT" task.
    # The git 'get' command fetches the content of the repository.
    # Since no files_path is passed, the files will be returned in the
    # output_value of the task
    files = None
    try:
        files = this.connect(
            connector_type='GIT',
            command='get',
            repository_url=git_repo_url,
            # httpCookie=repo_info.get('httpCookie'),
            ref=git_reference,
            username=git_username,
            password=git_password,
        ).get('output_value')['files']
    except flow_api.exceptions.DependencyFailedError as err:
        # send the error message to the output of this execution:
        return this.error(repr(err))

    # iterate over all files and save them on Cloudomation:
    for file_ in files:
        # split the path and filename
        path, filename = os.path.split(file_['name'])
        # split the filename and file extension
        name, ext = os.path.splitext(filename)
        if 'flows' in path and ext == '.py':
            # decode the base64 file content to text
            text_content = base64.b64decode(file_['content']).decode()
            # create or update Flow object
            system.flow(name).save(script=text_content)
        elif 'settings' in path and ext == '.yaml':
            # decode the base64 file content to text
            text_content = base64.b64decode(file_['content']).decode()
            # load the yaml string in the file content
            value = yaml.safe_load(text_content)
            # create or update Setting object
            system.setting(name).save(value=value)
        elif 'files' in path:
            # create or update File object
            # we use the name with the extension
            # pass the base64 encoded binary file content directly
            system.file(filename).save(content=file_['content'],
                                       convert_binary=False)

    return this.success('all done')
Ejemplo n.º 4
0
def handler(system: flow_api.System, this: flow_api.Execution):
    inputs = this.get('input_value') or {}
    message_id = inputs.get('message_id')

    if message_id is None:
        defaults = {
            'interval': 60,
            'wait': True,
        }
        if 'flow_name' in inputs:
            defaults['flow_name'] = inputs['flow_name']
        if 'flow_id' in inputs:
            defaults['flow_name'] = system.flow(inputs['flow_id'],
                                                by='id').get('name')

        message = system.message(
            subject='Recurring execution',
            body={
                'type': 'object',
                'properties': {
                    'flow_name': {
                        'label':
                        'Name of the flow which should be started recurring',
                        'element': 'string',
                        'type': 'string',
                        'example': defaults.get('flow_name'),
                        'default': defaults.get('flow_name'),
                        'order': 1,
                    },
                    'interval': {
                        'label': 'Interval of recurring execution in seconds',
                        'element': 'number',
                        'type': 'number',
                        'example': defaults['interval'],
                        'default': defaults['interval'],
                        'order': 2,
                    },
                    'wait': {
                        'label': 'Wait for child executions to finish',
                        'element': 'toggle',
                        'type': 'boolean',
                        'default': defaults['wait'],
                        'order': 3,
                    },
                    'max_iterations': {
                        'label':
                        'Maximum number of iterations (unlimited if omitted)',
                        'element': 'number',
                        'type': 'number',
                        'order': 4,
                    },
                    'start': {
                        'label': 'Start recurring',
                        'element': 'submit',
                        'type': 'boolean',
                        'order': 5,
                    },
                },
                'required': [
                    'flow_name',
                    'interval',
                ],
            },
        )
        message_id = message.get('id')
        this.save(output_value={
            'message_id': message_id,
        })
        this.flow(
            'Recurring',
            name='Recurring execution',
            message_id=message_id,
            wait=False,
        )
        return this.success('requested details')

    message = system.message(message_id)
    response = message.wait().get('response')
    this.log(response=response)
    flow_name = response['flow_name']
    interval = response['interval']
    wait = response['wait']
    max_iterations = response.get('max_iterations')
    this.save(name=f'Recurring {flow_name}')

    # Loop
    iterations = 0
    start = time.time()
    while max_iterations is None or iterations < max_iterations:
        iterations += 1
        if max_iterations:
            this.save(message=f'iteration {iterations}/{max_iterations}')
        else:
            this.save(message=f'iteration {iterations}')
        # Start child execution
        inputs = {
            'start': start,
            'iterations': iterations,
            'max_iterations': max_iterations,
        }
        child = this.flow(flow_name,
                          inputs=inputs,
                          name=f'{flow_name} iteration #{iterations}',
                          run=False)
        if wait:
            try:
                child.run()
            except Exception:
                this.log(f'iteration #{iterations} failed')
        else:
            child.run_async()
        if max_iterations is not None and iterations >= max_iterations:
            break
        if wait:
            now = time.time()
            scheduled = datetime.fromtimestamp(now + interval, timezone.utc)
        else:
            scheduled = datetime.fromtimestamp(start + (iterations * interval),
                                               timezone.utc)
        scheduled_ts = scheduled.isoformat(sep=' ', timespec='minutes')
        this.save(message=scheduled_ts)
        if wait:
            this.sleep(interval)
        else:
            this.sleep_until(start + (iterations * interval))

    return this.success(f'started {iterations} iterations')
Ejemplo n.º 5
0
def handler(system: flow_api.System, this: flow_api.Execution):
    inputs = this.get('input_value') or {}
    message_id = inputs.get('message_id')

    if message_id is None:
        defaults = {
            'scheduled_at': '08:30',
        }
        if 'flow_name' in inputs:
            defaults['flow_name'] = inputs['flow_name']
        if 'flow_id' in inputs:
            defaults['flow_name'] = system.flow(inputs['flow_id'], by='id').get('name')
        message = system.message(
            subject='Scheduled execution',
            body={
                'type': 'object',
                'properties': {
                    'flow_name': {
                        'label': 'Name of the flow which should be scheduled',
                        'element': 'string',
                        'type': 'string',
                        'example': defaults.get('flow_name'),
                        'default': defaults.get('flow_name'),
                        'order': 1,
                    },
                    'scheduled_at': {
                        'label': 'Time when the child execution should be started',
                        'element': 'time',
                        'type': 'string',
                        'format': 'time',
                        'default': defaults['scheduled_at'],
                        'order': 2,
                    },
                    'max_iterations': {
                        'label': 'Maximum number of iterations (unlimited if omitted)',
                        'element': 'number',
                        'type': 'number',
                        'order': 3,
                    },
                    'start': {
                        'label': 'Start schedule',
                        'element': 'submit',
                        'type': 'boolean',
                        'order': 4,
                    },
                },
                'required': [
                    'flow_name',
                    'scheduled_at',
                ],
            },
        )
        message_id = message.get('id')
        this.save(output_value={
            'message_id': message_id,
        })
        this.flow(
            'Scheduled',
            name='Scheduled execution',
            message_id=message_id,
            wait=False,
        )
        return this.success('requested details')

    message = system.message(message_id)
    response = message.wait().get('response')
    this.log(response=response)
    flow_name = response['flow_name']
    scheduled_at = response['scheduled_at']
    max_iterations = response.get('max_iterations')
    local_tz = response.get('timezone', 'Europe/Vienna')
    this.save(name=f'Scheduled {flow_name}')

    try:
        scheduled_at_t = datetime.datetime.strptime(scheduled_at, '%H:%M:%S%z').timetz()
    except ValueError:
        scheduled_at_t = datetime.datetime.strptime(scheduled_at, '%H:%M:%S').timetz()
    this.log(scheduled_at_t=scheduled_at_t)
    iterations = 0
    start = datetime.datetime.now(datetime.timezone.utc).timestamp()
    while max_iterations is None or iterations < max_iterations:
        now = datetime.datetime.now(datetime.timezone.utc)
        if scheduled_at_t.tzinfo is None:
            now = now.astimezone(pytz.timezone(local_tz))
        this.log(now=now)
        today = now.date()
        this.log(today=today)
        scheduled = datetime.datetime.combine(today, scheduled_at_t)
        if scheduled.tzinfo is None or scheduled.tzinfo.utcoffset(scheduled) is None:
            scheduled = pytz.timezone(local_tz).localize(scheduled)
        if scheduled < now:  # next iteration tomorrow
            scheduled += datetime.timedelta(days=1)
        this.log(scheduled=scheduled)
        scheduled_ts = scheduled.isoformat(sep=' ', timespec='minutes')
        this.log(scheduled_ts=scheduled_ts)
        delta_sec = (scheduled - now).total_seconds()
        this.log(delta_sec=delta_sec)
        this.save(message=scheduled_ts)
        this.sleep(delta_sec)
        iterations += 1
        this.save(message=f'iteration {iterations}/{max_iterations}')
        # Start child execution
        inputs = {
            'start': start,
            'iterations': iterations,
            'max_iterations': max_iterations,
        }
        this.flow(
            flow_name,
            inputs=inputs,
            name=f'{flow_name} iteration #{iterations}',
            wait=False,
        )
        if max_iterations is not None and iterations >= max_iterations:
            break

    return this.success(f'started {iterations} iterations')
Ejemplo n.º 6
0
def handler(system: flow_api.System, this: flow_api.Execution):
    inputs = this.get('input_value') or {}
    message_id = inputs.get('message_id')

    if message_id is None:
        defaults = {
            'delay': '60',
        }
        if 'flow_name' in inputs:
            defaults['flow_name'] = inputs['flow_name']
        if 'flow_id' in inputs:
            defaults['flow_name'] = system.flow(inputs['flow_id'], by='id').get('name')

        message = system.message(
            subject='Delayed execution',
            body={
                'type': 'object',
                'properties': {
                    'flow_name': {
                        'label': 'Name of the flow which should be started',
                        'element': 'string',
                        'type': 'string',
                        'example': defaults.get('flow_name'),
                        'default': defaults.get('flow_name'),
                        'order': 1,
                    },
                    'label': {
                        'description': 'You can either specify a time when the execution should be started, or a delay in seconds',
                        'element': 'markdown',
                        'order': 2,
                    },
                    'time': {
                        'label': 'Time when the child execution should be started',
                        'element': 'time',
                        'type': 'string',
                        'format': 'time',
                        'order': 3,
                    },
                    'delay': {
                        'label': 'Delay in seconds after which the child execution should be started',
                        'element': 'number',
                        'type': 'number',
                        'example': defaults['delay'],
                        'default': defaults['delay'],
                        'order': 4,
                    },
                    'start': {
                        'label': 'Start delayed',
                        'element': 'submit',
                        'type': 'boolean',
                        'order': 5,
                    },
                },
                'required': [
                    'flow_name',
                ],
            },
        )
        message_id = message.get('id')
        this.save(output_value={
            'message_id': message_id,
        })
        this.flow(
            'Delayed',
            name='Delayed execution',
            message_id=message_id,
            wait=False,
        )
        return this.success('requested details')

    message = system.message(message_id)
    response = message.wait().get('response')
    this.log(response=response)
    flow_name = response['flow_name']
    scheduled_at = response.get('time')
    delay = response.get('delay')
    this.save(name=f'Delayed {flow_name}')

    if scheduled_at is not None:
        scheduled_at_t = datetime.datetime.strptime(scheduled_at, '%H:%M:%S%z').timetz()
        this.log(scheduled_at_t=scheduled_at_t)
        now = datetime.datetime.now(datetime.timezone.utc)
        this.log(now=now)
        today = now.date()
        this.log(today=today)
        scheduled = datetime.datetime.combine(today, scheduled_at_t)
        this.log(scheduled=scheduled)
        if scheduled < now:  # tomorrow
            tomorrow = today + datetime.timedelta(days=1)
            scheduled = datetime.datetime.combine(tomorrow, scheduled_at_t)
            this.log(scheduled=scheduled)
        scheduled_ts = scheduled.isoformat(sep=' ', timespec='minutes')
        this.log(scheduled_ts=scheduled_ts)
        delta_sec = (scheduled - now).total_seconds()
        this.log(delta_sec=delta_sec)
        this.save(message=scheduled_ts)
        this.sleep(delta_sec)
    elif delay is not None:
        this.save(message=f'sleeping for {delay} seconds')
        this.log(f'sleeping for {delay} seconds')
        this.sleep(delay)
    else:
        return this.error('Missing response for "time" or "delay"')

    this.flow(
        flow_name,
        inputs=inputs,
        name=f'delayed {flow_name}',
        wait=False,
    )

    return this.success(f'successfully started {flow_name}')