Example #1
0
def agent(
    config: CowaitConfig,
    detach: bool = False,
    upstream: str = None,
) -> None:
    logger = RunLogger(quiet=False, raw=False)
    try:
        cluster = config.get_cluster()

        if cluster.type == 'api':
            raise CliError('Error: Cant deploy agent using an API cluster')

        token = uuid()
        if cluster.type == 'docker':
            token = ''

        cluster.destroy('agent')

        # create task definition
        taskdef = TaskDefinition(
            id='agent',
            name='cowait.tasks.agent',
            image=DEFAULT_BASE_IMAGE,
            upstream=upstream,
            routes={
                '/': 80,
            },
            meta={
                'http_token': token,
            },
        )

        # submit task to cluster
        task = cluster.spawn(taskdef)

        if detach:
            logger.header('detached')
            return

        def destroy(*args):
            logger.header('interrupt')
            cluster.destroy(task.id)
            sys.exit(0)

        with ExitTrap(destroy):
            # capture & print logs
            logs = cluster.logs(task)
            logger.header('task output')
            for log in logs:
                logger.handle(log)

        logger.header()

    except ProviderError as e:
        raise CliError(f'Provider error: {e}')

    except TaskCreationError as e:
        raise CliError(f'Task creation error: {e}')
Example #2
0
def agent(
    config: CowaitConfig,
    detach: bool = False,
    upstream: str = None,
) -> None:
    try:
        context = CowaitContext.open()
        cluster_name = context.get('cluster', config.default_cluster)
        cluster = config.get_cluster(cluster_name)

        if cluster.type == 'api':
            raise CliError('Error: Cant deploy agent using an API cluster')

        cluster.destroy('agent')

        # create task definition
        taskdef = TaskDefinition(
            id='agent',
            name='cowait.tasks.agent',
            image=DEFAULT_BASE_IMAGE,
            upstream=upstream,
            routes={
                '/': 80,
            },
            meta={
                'http_token': uuid(),
            },
        )

        # submit task to cluster
        task = cluster.spawn(taskdef)

        if detach:
            printheader('detached')
            return

        def destroy(*args):
            print()
            printheader('interrupt')
            cluster.destroy(task.id)
            sys.exit(0)

        with ExitTrap(destroy):
            # capture & print logs
            logs = cluster.logs(task)
            printheader('task output')
            for log in logs:
                print(log, flush=True)

        printheader()

    except ProviderError as e:
        raise CliError(f'Provider error: {e}')

    except TaskCreationError as e:
        raise CliError(f'Task creation error: {e}')
Example #3
0
def test_max_env_length():
    """ Passing too large inputs should raise a ProviderError """
    random_data = uuid(2 * MAX_ENV_LENGTH, lower=False)

    with pytest.raises(ProviderError):
        cp = ClusterProvider('test')
        cp.create_env(TaskDefinition(
            'test-task',
            image='imaginary-image',
            inputs={
                'ohshit': random_data,
            },
        ))
Example #4
0
 def get_token(self) -> str:
     token = uuid(32)
     self.tokens[token] = True
     return token
Example #5
0
def notebook(config, image: str = None, cluster_name: str = None) -> None:
    context = Context.open(config)

    if not context.notebook:
        print('Notebook funcitonaility is not enabled.')
        print('To enable, set features.notebook to True in cowait.yml and rebuild.')
        sys.exit(1)

    if image is not None:
        print('Remote images are currently not supported')
        sys.exit(1)

    volumes = {
        '/var/task': {
            'bind': {
                'src': os.getcwd(),
                'mode': 'rw',
                'inherit': 'same-image',
            },
        }
    }

    cluster = context.get_cluster(cluster_name)

    # Docker
    if cluster.type == 'docker':
        return run_cmd(
            config=config,
            task='cowait.notebook',
            build=False,
            image=image,
            routes={
                '/': '8888',
            },
            cluster_name=cluster_name,
            volumes=volumes,
        )

    # check for clientfs
    clientfs_executable = './clientfs-' + platform.system().lower()
    if not os.path.exists(clientfs_executable):
        print('Kubernetes notebooks are not supported in this build of Cowait')
        sys.exit(1)

    # Kubernetes
    core = client.CoreV1Api()
    notebook_id = 'notebook-' + uuid(4)

    core.create_namespaced_persistent_volume_claim(
        namespace=cluster.namespace,
        body=client.V1PersistentVolumeClaim(
            metadata=client.V1ObjectMeta(
                name=notebook_id,
                namespace=cluster.namespace,
            ),
            spec=client.V1PersistentVolumeClaimSpec(
                storage_class_name='clientfs',
                access_modes=['ReadWriteMany'],
                resources=client.V1ResourceRequirements(
                    requests={
                        'storage': '1G',
                    },
                ),
            ),
        ),
    )

    def delete_pvc(task_id):
        print('destroy', task_id)
        if task_id != notebook_id:
            return

        print('* stopping clientfs')
        clientfs.terminate()

        print('* deleting volume')
        core.delete_namespaced_persistent_volume_claim(notebook_id, cluster.namespace)

    cluster.on('kill', delete_pvc)

    pvc_id = None

    while True:
        time.sleep(1)
        volume = core.read_namespaced_persistent_volume_claim(notebook_id, cluster.namespace)
        if volume.status.phase == 'Bound':
            pvc_id = 'pvc-' + volume.metadata.uid
            print('* created volume', notebook_id, '/', pvc_id)
            break

    volumes['/var/task'] = {
        'persistent_volume_claim': {
            'claim_name': notebook_id,
        },
    }

    # start clientfs
    clientfs_host = cluster.args.get('clientfs', {}).get('host')
    print(f'* connecting clientfs volume to {clientfs_host}...')
    clientfs = subprocess.Popen([
        clientfs_executable,
        f"--proxy={clientfs_host}",
        f"--volume={pvc_id}"
    ])

    logger = RunLogger()
    try:
        # default to agent as upstream
        agent = cluster.find_agent()

        # create task definition
        taskdef = TaskDefinition(
            id=notebook_id,
            name='cowait.notebook',
            image=context.image,
            env={
                **context.extend('environment', {}),
                **context.dotenv,
            },
            routes={
                '/': '8888',
            },
            parent=None,  # root task
            upstream=agent,
            owner=getpass.getuser(),
            volumes=context.extend('volumes', volumes),
        )

        # print execution info
        logger.print_info(taskdef, cluster)

        # submit task to cluster
        task = cluster.spawn(taskdef)

        detach = False
        if detach:
            logger.header('detached')
            return

        def destroy(*args):
            logger.header('interrupt')
            cluster.destroy(task.id)
            sys.exit(1)

        with ExitTrap(destroy):
            # capture & print logs
            logs = cluster.logs(task.id)
            logger.header('task output')
            for msg in logs:
                logger.handle(msg)

    except Exception:
        traceback.print_exc()
        sys.exit(1)