Example #1
0
def new_model_server(name,
                     model_class: str,
                     models: dict = None,
                     filename='',
                     protocol='',
                     image='',
                     endpoint='',
                     explainer=False,
                     workers=8,
                     canary=None,
                     handler=None):
    f = RemoteRuntime()
    if not image:
        name, spec, code = nuclio.build_file(filename,
                                             name=name,
                                             handler=serving_handler,
                                             kind='serving')
        f.spec.base_spec = spec
    elif handler:
        f.spec.function_handler = handler

    f.metadata.name = name
    f.serving(models,
              model_class,
              protocol,
              image=image,
              endpoint=endpoint,
              explainer=explainer,
              workers=workers,
              canary=canary)
    return f
Example #2
0
    def deploy(self, source='', dashboard='', project='', tag='',
               kind=None):

        self.set_config('metadata.labels.mlrun/class', self.kind)
        spec = nuclio.ConfigSpec(env=self.spec.env, config=self.spec.config)
        spec.cmd = self.spec.build_commands
        kind = kind or self.spec.function_kind
        project = project or self.metadata.project or 'mlrun'
        source = source or self.spec.source
        handler = self.spec.function_handler

        if self.spec.base_spec:
            config = nuclio.config.extend_config(self.spec.base_spec, spec, tag,
                                                 self.spec.source)
            update_in(config, 'metadata.name', self.metadata.name)
            update_in(config, 'spec.volumes', self.spec.volumes)

            addr = nuclio.deploy.deploy_config(
                config, dashboard, name=self.metadata.name,
                project=project, tag=tag, verbose=self.verbose,
                create_new=True)
        else:

            name, config, code = nuclio.build_file(source, name=self.metadata.name,
                                            project=project,
                                            handler=handler,
                                            tag=tag, spec=spec,
                                            kind=kind, verbose=self.verbose)

            update_in(config, 'spec.volumes', self.spec.volumes)
            addr = deploy_config(config, dashboard_url=dashboard, name=name, project=project,
                                 tag=tag, verbose=self.verbose, create_new=True)

        self.spec.command = 'http://{}'.format(addr)
        return self.spec.command
Example #3
0
def new_v2_model_server(
    name,
    model_class: str,
    models: dict = None,
    filename="",
    protocol="",
    image="",
    endpoint="",
    workers=8,
    canary=None,
):
    f = ServingRuntime()
    if not image:
        name, spec, code = nuclio.build_file(filename,
                                             name=name,
                                             handler="handler",
                                             kind=serving_subkind)
        f.spec.base_spec = spec

    f.metadata.name = name
    f.spec.default_class = model_class
    params = None
    if protocol:
        params = {"protocol": protocol}
    if models:
        for name, model_path in models.items():
            f.add_model(name, model_path=model_path, parameters=params)

    f.with_http(workers, host=endpoint, canary=canary)
    if image:
        f.from_image(image)

    return f
Example #4
0
    def deploy(self, dashboard='', project='', tag='', kind=None):

        self.set_config('metadata.labels.mlrun/class', self.kind)
        env_dict = {
            get_item_name(v): get_item_name(v, 'value')
            for v in self.spec.env
        }
        spec = nuclio.ConfigSpec(env=env_dict, config=self.spec.config)
        spec.cmd = self.spec.build.commands or []
        project = project or self.metadata.project or 'default'
        handler = self.spec.function_handler
        if self.spec.no_cache:
            spec.set_config('spec.build.noCache', True)

        if self.spec.base_spec:
            if kind:
                raise ValueError('kind cannot be specified on built functions')
            config = nuclio.config.extend_config(self.spec.base_spec, spec,
                                                 tag,
                                                 self.spec.build.code_origin)
            update_in(config, 'metadata.name', self.metadata.name)
            update_in(config, 'spec.volumes', self.spec.to_nuclio_vol())

            logger.info('deploy started')
            addr = nuclio.deploy.deploy_config(config,
                                               dashboard,
                                               name=self.metadata.name,
                                               project=project,
                                               tag=tag,
                                               verbose=self.verbose,
                                               create_new=True)
        else:

            kind = kind if kind is not None else self.spec.function_kind
            name, config, code = nuclio.build_file(self.spec.source,
                                                   name=self.metadata.name,
                                                   project=project,
                                                   handler=handler,
                                                   tag=tag,
                                                   spec=spec,
                                                   kind=kind,
                                                   verbose=self.verbose)

            update_in(config, 'spec.volumes', self.spec.to_nuclio_vol())
            addr = deploy_config(config,
                                 dashboard_url=dashboard,
                                 name=name,
                                 project=project,
                                 tag=tag,
                                 verbose=self.verbose,
                                 create_new=True)

        self.spec.command = 'http://{}'.format(addr)
        return self.spec.command
Example #5
0
    def with_code(self, from_file='', body=None):
        if (not body and not from_file) or (from_file and from_file.endswith('.ipynb')):
            from nuclio import build_file
            name, spec, code = build_file(from_file)
            self.spec.build.inline_code = get_in(spec, 'spec.build.functionSourceCode')
            return self

        if from_file:
            with open(from_file) as fp:
                body = fp.read()
        self.spec.build.inline_code = b64encode(body.encode('utf-8')).decode('utf-8')
        return self
Example #6
0
def code_to_function(name='',
                     filename='',
                     handler='',
                     runtime=None,
                     image=None):
    """convert code or notebook to function object with embedded code
    code stored in the function spec and can be refreshed using .with_code()
    eliminate the need to build container images everytime we edit the code

    :param name:      function name
    :param filename:  blank for current notebook, or path to .py/.ipynb file
    :param handler:   name of function handler (if not main)
    :param runtime:   optional, runtime type local, job, dask, mpijob, ..
    :param image:     optional, container image

    :return:
           function object
    """
    if runtime == 'nuclio':
        r = RemoteRuntime()
        r.metadata.name = name
        return r

    from nuclio import build_file
    bname, spec, code = build_file(filename, handler=handler)

    if runtime is None or runtime in ['', 'local']:
        r = LocalRuntime()
    elif runtime in runtime_dict:
        r = runtime_dict[runtime]()
    else:
        raise Exception('unsupported runtime ({})'.format(runtime))

    h = get_in(spec, 'spec.handler', '').split(':')
    r.handler = h[0] if len(h) <= 1 else h[1]
    r.metadata = get_in(spec, 'spec.metadata')
    r.metadata.name = name or bname or 'mlrun'
    r.spec.image = get_in(spec, 'spec.image', image)
    build = r.spec.build
    build.base_image = get_in(spec, 'spec.build.baseImage')
    build.commands = get_in(spec, 'spec.build.commands')
    build.functionSourceCode = get_in(spec, 'spec.build.functionSourceCode')
    build.image = get_in(spec, 'spec.build.image')
    build.secret = get_in(spec, 'spec.build.secret')
    if r.kind != 'local':
        r.spec.env = get_in(spec, 'spec.env')
        for vol in get_in(spec, 'spec.volumes', []):
            r.spec.volumes.append(vol.get('volume'))
            r.spec.volume_mounts.append(vol.get('volumeMount'))
    return r
Example #7
0
def make_nuclio_job(filename='', handler='', image=None, secret=None, kind=None):
    from nuclio import build_file
    name, spec, code = build_file(filename, handler=handler)
    r = K8sRuntime()
    r.kind = kind or 'job'
    h = get_in(spec, 'spec.handler', '').split(':')
    r.handler = h[0] if len(h) <= 1 else h[1]
    r.metadata = get_in(spec, 'spec.metadata')
    r.build.base_image = get_in(spec, 'spec.build.baseImage')
    r.build.commands = get_in(spec, 'spec.build.commands')
    r.build.inline_code = get_in(spec, 'spec.build.functionSourceCode')
    r.build.image = get_in(spec, 'spec.build.image', image)
    r.build.secret = get_in(spec, 'spec.build.secret', secret)
    r.env = get_in(spec, 'spec.env')
    for vol in get_in(spec, 'spec.volumes', []):
        r.volumes.append(vol.get('volume'))
        r.volume_mounts.append(vol.get('volumeMount'))
    return r
Example #8
0
def new_v1_model_server(
    name,
    model_class: str,
    models: dict = None,
    filename="",
    protocol="",
    image="",
    endpoint="",
    workers=8,
    canary=None,
):
    f = RemoteRuntime()
    if not image:
        name, spec, code = nuclio.build_file(filename,
                                             name=name,
                                             handler=serving_handler,
                                             kind="serving")
        f.spec.base_spec = spec

    f.metadata.name = name

    if models:
        for k, v in models.items():
            f.set_env(f"SERVING_MODEL_{k}", v)

    if protocol:
        f.set_env("TRANSPORT_PROTOCOL", protocol)
    if model_class:
        f.set_env("MODEL_CLASS", model_class)
    f.with_http(workers, host=endpoint, canary=canary)
    f.spec.function_kind = "serving"

    if image:
        f.from_image(image)

    return f
Example #9
0
    def with_code(self, from_file="", body=None, with_doc=True):
        """Update the function code
        This function eliminates the need to build container images every time we edit the code

        :param from_file:   blank for current notebook, or path to .py/.ipynb file
        :param body:        will use the body as the function code
        :param with_doc:    update the document of the function parameters

        :return: function object
        """
        if (not body and not from_file) or (from_file
                                            and from_file.endswith(".ipynb")):
            from nuclio import build_file

            _, _, body = build_file(from_file)

        if from_file:
            with open(from_file) as fp:
                body = fp.read()
        self.spec.build.functionSourceCode = b64encode(
            body.encode("utf-8")).decode("utf-8")
        if with_doc:
            update_function_entry_points(self, body)
        return self
Example #10
0
def code_to_function(
    name: str = "",
    project: str = "",
    tag: str = "",
    filename: str = "",
    handler: str = "",
    kind: str = "",
    image: str = None,
    code_output="",
    embed_code=True,
    description="",
    requirements: Union[str, List[str]] = None,
    categories: list = None,
    labels: dict = None,
    with_doc=True,
):
    """convert code or notebook to function object with embedded code
    code stored in the function spec and can be refreshed using .with_code()
    eliminate the need to build container images every time we edit the code

    :param name:         function name
    :param project:      function project (none for 'default')
    :param tag:          function tag (none for 'latest')
    :param filename:     blank for current notebook, or path to .py/.ipynb file
    :param handler:      name of function handler (if not main)
    :param kind:          optional, runtime type local, job, dask, mpijob, ..
    :param image:        optional, container image
    :param code_output:  save the generated code (from notebook) in that path
    :param embed_code:   embed the source code into the function spec
    :param description:  function description
    :param requirements: python requirements file path or list of packages
    :param categories:   list of categories (for function marketplace)
    :param labels:       dict of label names and values to tag the function
    :param with_doc:     document the function parameters

    :return:
           function object
    """
    filebase, _ = path.splitext(path.basename(filename))

    def add_name(origin, name=""):
        name = filename or (name + ".ipynb")
        if not origin:
            return name
        return "{}:{}".format(origin, name)

    def update_meta(fn):
        fn.spec.description = description
        fn.metadata.project = project or mlconf.default_project
        fn.metadata.tag = tag
        fn.metadata.categories = categories
        fn.metadata.labels = labels

    def resolve_nuclio_subkind(kind):
        is_nuclio = kind.startswith("nuclio")
        subkind = kind[kind.find(":") +
                       1:] if is_nuclio and ":" in kind else None
        if kind == RuntimeKinds.serving:
            is_nuclio = True
            subkind = serving_subkind
        return is_nuclio, subkind

    if (not embed_code and not code_output
            and (not filename or filename.endswith(".ipynb"))):
        raise ValueError("a valid code file must be specified "
                         "when not using the embed_code option")

    is_nuclio, subkind = resolve_nuclio_subkind(kind)
    code_origin = add_name(add_code_metadata(filename), name)

    name, spec, code = build_file(filename,
                                  name=name,
                                  handler=handler or "handler",
                                  kind=subkind)
    spec_kind = get_in(spec, "kind", "")
    if not kind and spec_kind not in ["", "Function"]:
        kind = spec_kind.lower()

        # if its a nuclio subkind, redo nb parsing
        is_nuclio, subkind = resolve_nuclio_subkind(kind)
        if is_nuclio:
            name, spec, code = build_file(filename,
                                          name=name,
                                          handler=handler or "handler",
                                          kind=subkind)

    if code_output:
        if code_output == ".":
            code_output = name + ".py"
        if filename == "" or filename.endswith(".ipynb"):
            with open(code_output, "w") as fp:
                fp.write(code)
        else:
            raise ValueError("code_output option is only used with notebooks")

    if is_nuclio:
        if subkind == serving_subkind:
            r = ServingRuntime()
        else:
            r = RemoteRuntime()
            r.spec.function_kind = subkind
        if image:
            r.spec.image = image
        r.spec.default_handler = handler
        if embed_code:
            update_in(spec, "kind", "Function")
            r.spec.base_spec = spec
            if with_doc:
                update_function_entry_points(r, code)
        else:
            r.spec.source = filename
            r.spec.function_handler = handler

        if not name:
            raise ValueError("name must be specified")
        r.metadata.name = name
        r.spec.build.code_origin = code_origin
        if requirements:
            r.with_requirements(requirements)
        update_meta(r)
        return r

    if kind is None or kind in ["", "Function"]:
        raise ValueError("please specify the function kind")
    elif kind in ["local"]:
        r = LocalRuntime()
    elif kind in RuntimeKinds.all():
        r = get_runtime_class(kind)()
    else:
        raise ValueError("unsupported runtime ({})".format(kind))

    name, spec, code = build_file(filename, name=name)

    if not name:
        raise ValueError("name must be specified")
    h = get_in(spec, "spec.handler", "").split(":")
    r.handler = h[0] if len(h) <= 1 else h[1]
    r.metadata = get_in(spec, "spec.metadata")
    r.metadata.name = name
    r.spec.image = image or get_in(spec, "spec.image", "")
    build = r.spec.build
    build.code_origin = code_origin
    build.base_image = get_in(spec, "spec.build.baseImage")
    build.commands = get_in(spec, "spec.build.commands")
    build.extra = get_in(spec, "spec.build.extra")
    if embed_code:
        build.functionSourceCode = get_in(spec,
                                          "spec.build.functionSourceCode")
    else:
        if code_output:
            r.spec.command = code_output
        else:
            r.spec.command = filename

    build.image = get_in(spec, "spec.build.image")
    build.secret = get_in(spec, "spec.build.secret")
    if requirements:
        r.with_requirements(requirements)

    if r.kind != "local":
        r.spec.env = get_in(spec, "spec.env")
        for vol in get_in(spec, "spec.volumes", []):
            r.spec.volumes.append(vol.get("volume"))
            r.spec.volume_mounts.append(vol.get("volumeMount"))

    if with_doc:
        update_function_entry_points(r, code)
    r.spec.default_handler = handler
    update_meta(r)
    return r
Example #11
0
def code_to_function(
    name: str = '',
    project: str = '',
    tag: str = '',
    filename: str = '',
    handler: str = '',
    kind: str = '',
    image: str = None,
    code_output='',
    embed_code=True,
    description='',
    categories: list = None,
    labels: dict = None,
    with_doc=True,
):
    """convert code or notebook to function object with embedded code
    code stored in the function spec and can be refreshed using .with_code()
    eliminate the need to build container images every time we edit the code

    :param name:        function name
    :param project:     function project (none for 'default')
    :param tag:         function tag (none for 'latest')
    :param filename:    blank for current notebook, or path to .py/.ipynb file
    :param handler:     name of function handler (if not main)
    :param kind:        optional, runtime type local, job, dask, mpijob, ..
    :param image:       optional, container image
    :param code_output: save the generated code (from notebook) in that path
    :param embed_code:  embed the source code into the function spec
    :param description: function description
    :param categories:  list of categories (for function marketplace)
    :param labels:      dict of label names and values to tag the function
    :param with_doc:    document the function parameters

    :return:
           function object
    """
    filebase, _ = path.splitext(path.basename(filename))

    def add_name(origin, name=''):
        name = filename or (name + '.ipynb')
        if not origin:
            return name
        return '{}:{}'.format(origin, name)

    def update_meta(fn):
        fn.spec.description = description
        fn.metadata.project = project
        fn.metadata.tag = tag
        fn.metadata.categories = categories
        fn.metadata.labels = labels

    if (not embed_code and not code_output
            and (not filename or filename.endswith('.ipynb'))):
        raise ValueError('a valid code file must be specified '
                         'when not using the embed_code option')

    subkind = kind[kind.find(':') + 1:] if kind.startswith('nuclio:') else None
    code_origin = add_name(add_code_metadata(filename), name)

    name, spec, code = build_file(filename,
                                  name=name,
                                  handler=handler or 'handler',
                                  kind=subkind)
    spec_kind = get_in(spec, 'kind', '')
    if spec_kind not in ['', 'Function']:
        kind = spec_kind.lower()

        # if its a nuclio subkind, redo nb parsing
        if kind.startswith('nuclio:'):
            subkind = kind[kind.find(':') + 1:]
            name, spec, code = build_file(filename,
                                          name=name,
                                          handler=handler or 'handler',
                                          kind=subkind)

    if code_output:
        if code_output == '.':
            code_output = name + '.py'
        if filename == '' or filename.endswith('.ipynb'):
            with open(code_output, 'w') as fp:
                fp.write(code)
        else:
            raise ValueError('code_output option is only used with notebooks')

    if kind.startswith('nuclio'):
        r = RemoteRuntime()
        r.spec.function_kind = subkind
        if embed_code:
            update_in(spec, 'kind', 'Function')
            r.spec.base_spec = spec
            if with_doc:
                handlers = find_handlers(code)
                r.spec.entry_points = {h['name']: as_func(h) for h in handlers}
        else:
            r.spec.source = filename
            r.spec.function_handler = handler

        if not name:
            raise ValueError('name must be specified')
        r.metadata.name = name
        r.spec.build.code_origin = code_origin
        update_meta(r)
        return r

    if kind is None or kind in ['', 'Function']:
        raise ValueError('please specify the function kind')
    elif kind in ['local']:
        r = LocalRuntime()
    elif kind in RuntimeKinds.all():
        r = get_runtime_class(kind)()
    else:
        raise ValueError('unsupported runtime ({})'.format(kind))

    name, spec, code = build_file(filename, name=name)

    if not name:
        raise ValueError('name must be specified')
    h = get_in(spec, 'spec.handler', '').split(':')
    r.handler = h[0] if len(h) <= 1 else h[1]
    r.metadata = get_in(spec, 'spec.metadata')
    r.metadata.name = name
    r.spec.image = get_in(spec, 'spec.image', image)
    build = r.spec.build
    build.code_origin = code_origin
    build.base_image = get_in(spec, 'spec.build.baseImage')
    build.commands = get_in(spec, 'spec.build.commands')
    if embed_code:
        build.functionSourceCode = get_in(spec,
                                          'spec.build.functionSourceCode')
    else:
        if code_output:
            r.spec.command = code_output
        else:
            r.spec.command = filename

    build.image = get_in(spec, 'spec.build.image')
    build.secret = get_in(spec, 'spec.build.secret')
    if r.kind != 'local':
        r.spec.env = get_in(spec, 'spec.env')
        for vol in get_in(spec, 'spec.volumes', []):
            r.spec.volumes.append(vol.get('volume'))
            r.spec.volume_mounts.append(vol.get('volumeMount'))

    if with_doc:
        handlers = find_handlers(code)
        r.spec.entry_points = {h['name']: as_func(h) for h in handlers}
    r.spec.default_handler = handler
    update_meta(r)
    return r
Example #12
0
def compile_function_config(function: RemoteRuntime):
    function.set_config("metadata.labels.mlrun/class", function.kind)

    # Add vault configurations to function's pod spec, if vault secret source was added.
    # Needs to be here, since it adds env params, which are handled in the next lines.
    function.add_secrets_config_to_spec()

    env_dict = {get_item_name(v): get_item_name(v, "value") for v in function.spec.env}
    for key, value in function._get_runtime_env().items():
        env_dict[key] = value
    spec = nuclio.ConfigSpec(env=env_dict, config=function.spec.config)
    spec.cmd = function.spec.build.commands or []
    project = function.metadata.project or "default"
    tag = function.metadata.tag
    handler = function.spec.function_handler

    # In Nuclio >= 1.6.x default serviceType has changed to "ClusterIP".
    spec.set_config("spec.serviceType", mlconf.httpdb.nuclio.default_service_type)
    if function.spec.readiness_timeout:
        spec.set_config("spec.readinessTimeoutSeconds", function.spec.readiness_timeout)
    if function.spec.resources:
        spec.set_config("spec.resources", function.spec.resources)
    if function.spec.no_cache:
        spec.set_config("spec.build.noCache", True)
    if function.spec.build.functionSourceCode:
        spec.set_config(
            "spec.build.functionSourceCode", function.spec.build.functionSourceCode
        )

    if function.spec.replicas:
        spec.set_config("spec.minReplicas", function.spec.replicas)
        spec.set_config("spec.maxReplicas", function.spec.replicas)
    else:
        spec.set_config("spec.minReplicas", function.spec.min_replicas)
        spec.set_config("spec.maxReplicas", function.spec.max_replicas)

    if function.spec.base_spec or function.spec.build.functionSourceCode:
        config = function.spec.base_spec
        if not config:
            # if base_spec was not set (when not using code_to_function) and we have base64 code
            # we create the base spec with essential attributes
            config = nuclio.config.new_config()
            update_in(config, "spec.handler", handler or "main:handler")

        config = nuclio.config.extend_config(
            config, spec, tag, function.spec.build.code_origin
        )
        update_in(config, "metadata.name", function.metadata.name)
        update_in(config, "spec.volumes", function.spec.generate_nuclio_volumes())
        base_image = get_in(config, "spec.build.baseImage") or function.spec.image
        if base_image:
            update_in(config, "spec.build.baseImage", enrich_image_url(base_image))

        logger.info("deploy started")
        name = get_fullname(function.metadata.name, project, tag)
        function.status.nuclio_name = name
        update_in(config, "metadata.name", name)
    else:

        name, config, code = nuclio.build_file(
            function.spec.source,
            name=function.metadata.name,
            project=project,
            handler=handler,
            tag=tag,
            spec=spec,
            kind=function.spec.function_kind,
            verbose=function.verbose,
        )

        update_in(config, "spec.volumes", function.spec.generate_nuclio_volumes())
        if function.spec.image:
            update_in(
                config, "spec.build.baseImage", enrich_image_url(function.spec.image)
            )
        name = get_fullname(name, project, tag)
        function.status.nuclio_name = name

        update_in(config, "metadata.name", name)

    return name, project, config
Example #13
0
def deploy_nuclio_function(function: RemoteRuntime, dashboard="", watch=False):
    function.set_config("metadata.labels.mlrun/class", function.kind)
    env_dict = {
        get_item_name(v): get_item_name(v, "value")
        for v in function.spec.env
    }
    for key, value in function._get_runtime_env().items():
        env_dict[key] = value
    spec = nuclio.ConfigSpec(env=env_dict, config=function.spec.config)
    spec.cmd = function.spec.build.commands or []
    project = function.metadata.project or "default"
    tag = function.metadata.tag
    handler = function.spec.function_handler

    # In Nuclio 1.6.0 default serviceType changed to "ClusterIP", make sure we're using NodePort
    spec.set_config("spec.serviceType", "NodePort")
    if function.spec.readiness_timeout:
        spec.set_config("spec.readinessTimeoutSeconds",
                        function.spec.readiness_timeout)
    if function.spec.resources:
        spec.set_config("spec.resources", function.spec.resources)
    if function.spec.no_cache:
        spec.set_config("spec.build.noCache", True)
    if function.spec.replicas:
        spec.set_config("spec.minReplicas", function.spec.replicas)
        spec.set_config("spec.maxReplicas", function.spec.replicas)
    else:
        spec.set_config("spec.minReplicas", function.spec.min_replicas)
        spec.set_config("spec.maxReplicas", function.spec.max_replicas)

    dashboard = dashboard or mlconf.nuclio_dashboard_url
    if function.spec.base_spec:
        config = nuclio.config.extend_config(function.spec.base_spec, spec,
                                             tag,
                                             function.spec.build.code_origin)
        update_in(config, "metadata.name", function.metadata.name)
        update_in(config, "spec.volumes", function.spec.to_nuclio_vol())
        base_image = get_in(config,
                            "spec.build.baseImage") or function.spec.image
        if base_image:
            update_in(config, "spec.build.baseImage",
                      enrich_image_url(base_image))

        logger.info("deploy started")
        name = get_fullname(function.metadata.name, project, tag)
        function.status.nuclio_name = name
        update_in(config, "metadata.name", name)
        return nuclio.deploy.deploy_config(
            config,
            dashboard,
            name=name,
            project=project,
            tag=tag,
            verbose=function.verbose,
            create_new=True,
            watch=watch,
        )
    else:

        name, config, code = nuclio.build_file(
            function.spec.source,
            name=function.metadata.name,
            project=project,
            handler=handler,
            tag=tag,
            spec=spec,
            kind=function.spec.function_kind,
            verbose=function.verbose,
        )

        update_in(config, "spec.volumes", function.spec.to_nuclio_vol())
        if function.spec.image:
            update_in(config, "spec.build.baseImage",
                      enrich_image_url(function.spec.image))
        name = get_fullname(name, project, tag)
        function.status.nuclio_name = name

        update_in(config, "metadata.name", name)
        return deploy_config(
            config,
            dashboard_url=dashboard,
            name=name,
            project=project,
            tag=tag,
            verbose=function.verbose,
            create_new=True,
            watch=watch,
        )
Example #14
0
def code_to_function(
    name: str = "",
    project: str = "",
    tag: str = "",
    filename: str = "",
    handler: str = "",
    kind: str = "",
    image: str = None,
    code_output: str = "",
    embed_code: bool = True,
    description: str = "",
    requirements: Union[str, List[str]] = None,
    categories: List[str] = None,
    labels: Dict[str, str] = None,
    with_doc: bool = True,
    ignored_tags=None,
) -> Union[MpiRuntimeV1Alpha1, MpiRuntimeV1, RemoteRuntime, ServingRuntime,
           DaskCluster, KubejobRuntime, LocalRuntime, Spark2Runtime,
           Spark3Runtime, RemoteSparkRuntime, ]:
    """Convenience function to insert code and configure an mlrun runtime.

    Easiest way to construct a runtime type object. Provides the most often
    used configuration options for all runtimes as parameters.

    Instantiated runtimes are considered "functions" in mlrun, but they are
    anything from nuclio functions to generic kubernetes pods to spark jobs.
    Functions are meant to be focused, and as such limited in scope and size.
    Typically a function can be expressed in a single python module with
    added support from custom docker images and commands for the environment.
    The returned runtime object can be further configured if more
    customization is required.

    One of the most important parameters is "kind". This is what is used to
    specify the chosen runtimes. The options are:

    - local: execute a local python or shell script
    - job: insert the code into a Kubernetes pod and execute it
    - nuclio: insert the code into a real-time serverless nuclio function
    - serving: insert code into orchestrated nuclio function(s) forming a DAG
    - dask: run the specified python code / script as Dask Distributed job
    - mpijob: run distributed Horovod jobs over the MPI job operator
    - spark: run distributed Spark job using Spark Kubernetes Operator
    - remote-spark: run distributed Spark job on remote Spark service

    Learn more about function runtimes here:
    https://docs.mlrun.org/en/latest/runtimes/functions.html#function-runtimes

    :param name:         function name, typically best to use hyphen-case
    :param project:      project used to namespace the function, defaults to "default"
    :param tag:          function tag to track multiple versions of the same function, defaults to "latest"
    :param filename:     path to .py/.ipynb file, defaults to current jupyter notebook
    :param handler:      The default function handler to call for the job or nuclio function, in batch functions
                         (job, mpijob, ..) the handler can also be specified in the `.run()` command, when not specified
                         the entire file will be executed (as main).
                         for nuclio functions the handler is in the form of module:function, defaults to "main:handler"
    :param kind:         function runtime type string - nuclio, job, etc. (see docstring for all options)
    :param image:        base docker image to use for building the function container, defaults to None
    :param code_output:  specify "." to generate python module from the current jupyter notebook
    :param embed_code:   indicates whether or not to inject the code directly into the function runtime spec,
                         defaults to True
    :param description:  short function description, defaults to ""
    :param requirements: list of python packages or pip requirements file path, defaults to None
    :param categories:   list of categories for mlrun function marketplace, defaults to None
    :param labels:       immutable name/value pairs to tag the function with useful metadata, defaults to None
    :param with_doc:     indicates whether to document the function parameters, defaults to True
    :param ignored_tags: notebook cells to ignore when converting notebooks to py code (separated by ";")

    :return:
           pre-configured function object from a mlrun runtime class

    example::
        import mlrun

        # create job function object from notebook code and add doc/metadata
        fn = mlrun.code_to_function('file_utils', kind='job',
                                    handler='open_archive', image='mlrun/mlrun',
                                    description = "this function opens a zip archive into a local/mounted folder",
                                    categories = ['fileutils'],
                                    labels = {'author': 'me'})

    example::
        import mlrun
        from pathlib import Path

        # create file
        Path('mover.py').touch()

        # create nuclio function object from python module call mover.py
        fn = mlrun.code_to_function('nuclio-mover', kind='nuclio',
                                    filename='mover.py', image='python:3.7',
                                    description = "this function moves files from one system to another",
                                    requirements = ["pandas"],
                                    labels = {'author': 'me'})

    """
    filebase, _ = path.splitext(path.basename(filename))
    ignored_tags = ignored_tags or mlconf.ignored_notebook_tags

    def add_name(origin, name=""):
        name = filename or (name + ".ipynb")
        if not origin:
            return name
        return f"{origin}:{name}"

    def update_meta(fn):
        fn.spec.description = description
        fn.metadata.project = project or mlconf.default_project
        fn.metadata.tag = tag
        fn.metadata.categories = categories
        fn.metadata.labels = labels

    def resolve_nuclio_subkind(kind):
        is_nuclio = kind.startswith("nuclio")
        subkind = kind[kind.find(":") +
                       1:] if is_nuclio and ":" in kind else None
        if kind == RuntimeKinds.serving:
            is_nuclio = True
            subkind = serving_subkind
        return is_nuclio, subkind

    if (not embed_code and not code_output
            and (not filename or filename.endswith(".ipynb"))):
        raise ValueError("a valid code file must be specified "
                         "when not using the embed_code option")

    is_nuclio, subkind = resolve_nuclio_subkind(kind)
    code_origin = add_name(add_code_metadata(filename), name)

    name, spec, code = build_file(
        filename,
        name=name,
        handler=handler or "handler",
        kind=subkind,
        ignored_tags=ignored_tags,
    )
    spec_kind = get_in(spec, "kind", "")
    if not kind and spec_kind not in ["", "Function"]:
        kind = spec_kind.lower()

        # if its a nuclio subkind, redo nb parsing
        is_nuclio, subkind = resolve_nuclio_subkind(kind)
        if is_nuclio:
            name, spec, code = build_file(
                filename,
                name=name,
                handler=handler or "handler",
                kind=subkind,
                ignored_tags=ignored_tags,
            )

    if code_output:
        if code_output == ".":
            code_output = name + ".py"
        if filename == "" or filename.endswith(".ipynb"):
            with open(code_output, "w") as fp:
                fp.write(code)
        else:
            raise ValueError("code_output option is only used with notebooks")

    if is_nuclio:
        if subkind == serving_subkind:
            r = ServingRuntime()
        else:
            r = RemoteRuntime()
            r.spec.function_kind = subkind
        if image:
            r.spec.image = image
        r.spec.default_handler = handler
        if embed_code:
            update_in(spec, "kind", "Function")
            r.spec.base_spec = spec
        else:
            r.spec.source = filename
            r.spec.function_handler = handler

        if not name:
            raise ValueError("name must be specified")
        r.metadata.name = name
        r.spec.build.code_origin = code_origin
        r.spec.build.origin_filename = filename or (name + ".ipynb")
        if requirements:
            r.with_requirements(requirements)
        update_meta(r)
        return r

    if kind is None or kind in ["", "Function"]:
        raise ValueError("please specify the function kind")
    elif kind in RuntimeKinds.all():
        r = get_runtime_class(kind)()
    else:
        raise ValueError(f"unsupported runtime ({kind})")

    name, spec, code = build_file(filename,
                                  name=name,
                                  ignored_tags=ignored_tags)

    if not name:
        raise ValueError("name must be specified")
    h = get_in(spec, "spec.handler", "").split(":")
    r.handler = h[0] if len(h) <= 1 else h[1]
    r.metadata = get_in(spec, "spec.metadata")
    r.metadata.name = name
    r.spec.image = image or get_in(spec, "spec.image", "")
    build = r.spec.build
    build.code_origin = code_origin
    build.origin_filename = filename or (name + ".ipynb")
    build.base_image = get_in(spec, "spec.build.baseImage")
    build.commands = get_in(spec, "spec.build.commands")
    build.extra = get_in(spec, "spec.build.extra")
    if embed_code:
        build.functionSourceCode = get_in(spec,
                                          "spec.build.functionSourceCode")
    else:
        if code_output:
            r.spec.command = code_output
        else:
            r.spec.command = filename

    build.image = get_in(spec, "spec.build.image")
    build.secret = get_in(spec, "spec.build.secret")
    if requirements:
        r.with_requirements(requirements)

    if r.kind != "local":
        r.spec.env = get_in(spec, "spec.env")
        for vol in get_in(spec, "spec.volumes", []):
            r.spec.volumes.append(vol.get("volume"))
            r.spec.volume_mounts.append(vol.get("volumeMount"))

    if with_doc:
        update_function_entry_points(r, code)
    r.spec.default_handler = handler
    update_meta(r)
    return r
Example #15
0
def compile_function_config(function: RemoteRuntime):
    labels = function.metadata.labels or {}
    labels.update({"mlrun/class": function.kind})
    for key, value in labels.items():
        function.set_config(f"metadata.labels.{key}", value)

    # Add vault configurations to function's pod spec, if vault secret source was added.
    # Needs to be here, since it adds env params, which are handled in the next lines.
    function.add_secrets_config_to_spec()

    env_dict, external_source_env_dict = function.get_nuclio_config_spec_env()
    spec = nuclio.ConfigSpec(
        env=env_dict,
        external_source_env=external_source_env_dict,
        config=function.spec.config,
    )
    spec.cmd = function.spec.build.commands or []
    project = function.metadata.project or "default"
    tag = function.metadata.tag
    handler = function.spec.function_handler

    # In Nuclio >= 1.6.x default serviceType has changed to "ClusterIP".
    spec.set_config("spec.serviceType",
                    mlconf.httpdb.nuclio.default_service_type)
    if function.spec.readiness_timeout:
        spec.set_config("spec.readinessTimeoutSeconds",
                        function.spec.readiness_timeout)
    if function.spec.resources:
        spec.set_config("spec.resources", function.spec.resources)
    if function.spec.no_cache:
        spec.set_config("spec.build.noCache", True)
    if function.spec.build.functionSourceCode:
        spec.set_config("spec.build.functionSourceCode",
                        function.spec.build.functionSourceCode)
    # don't send node selections if nuclio is not compatible
    if validate_nuclio_version_compatibility("1.5.20", "1.6.10"):
        if function.spec.node_selector:
            spec.set_config("spec.nodeSelector", function.spec.node_selector)
        if function.spec.node_name:
            spec.set_config("spec.nodeName", function.spec.node_name)
        if function.spec.affinity:
            spec.set_config("spec.affinity",
                            function.spec._get_sanitized_affinity())
    # don't send default or any priority class name if nuclio is not compatible
    if (function.spec.priority_class_name
            and validate_nuclio_version_compatibility("1.6.18")
            and len(mlconf.get_valid_function_priority_class_names())):
        spec.set_config("spec.priorityClassName",
                        function.spec.priority_class_name)

    if function.spec.replicas:
        spec.set_config("spec.minReplicas", function.spec.replicas)
        spec.set_config("spec.maxReplicas", function.spec.replicas)
    else:
        spec.set_config("spec.minReplicas", function.spec.min_replicas)
        spec.set_config("spec.maxReplicas", function.spec.max_replicas)

    if function.spec.service_account:
        spec.set_config("spec.serviceAccount", function.spec.service_account)

    if function.spec.base_spec or function.spec.build.functionSourceCode:
        config = function.spec.base_spec
        if not config:
            # if base_spec was not set (when not using code_to_function) and we have base64 code
            # we create the base spec with essential attributes
            config = nuclio.config.new_config()
            update_in(config, "spec.handler", handler or "main:handler")

        config = nuclio.config.extend_config(config, spec, tag,
                                             function.spec.build.code_origin)
        update_in(config, "metadata.name", function.metadata.name)
        update_in(config, "spec.volumes",
                  function.spec.generate_nuclio_volumes())
        base_image = (get_in(config, "spec.build.baseImage")
                      or function.spec.image or function.spec.build.base_image)
        if base_image:
            update_in(config, "spec.build.baseImage",
                      enrich_image_url(base_image))

        logger.info("deploy started")
        name = get_fullname(function.metadata.name, project, tag)
        function.status.nuclio_name = name
        update_in(config, "metadata.name", name)
    else:

        name, config, code = nuclio.build_file(
            function.spec.source,
            name=function.metadata.name,
            project=project,
            handler=handler,
            tag=tag,
            spec=spec,
            kind=function.spec.function_kind,
            verbose=function.verbose,
        )

        update_in(config, "spec.volumes",
                  function.spec.generate_nuclio_volumes())
        base_image = function.spec.image or function.spec.build.base_image
        if base_image:
            update_in(
                config,
                "spec.build.baseImage",
                enrich_image_url(base_image),
            )

        name = get_fullname(name, project, tag)
        function.status.nuclio_name = name

        update_in(config, "metadata.name", name)

    return name, project, config
Example #16
0
    def deploy(self, dashboard='', project='', tag='', kind=None):
        def get_fullname(config, name, project, tag):
            if project:
                name = '{}-{}'.format(project, name)
            if tag:
                name = '{}-{}'.format(name, tag)
            update_in(config, 'metadata.name', name)
            return name

        self.set_config('metadata.labels.mlrun/class', self.kind)
        env_dict = {
            get_item_name(v): get_item_name(v, 'value')
            for v in self.spec.env
        }
        spec = nuclio.ConfigSpec(env=env_dict, config=self.spec.config)
        spec.cmd = self.spec.build.commands or []
        project = project or self.metadata.project or 'default'
        handler = self.spec.function_handler
        if self.spec.no_cache:
            spec.set_config('spec.build.noCache', True)
        if self.spec.replicas:
            spec.set_config('spec.minReplicas', self.spec.replicas)
            spec.set_config('spec.maxReplicas', self.spec.replicas)
        else:
            spec.set_config('spec.minReplicas', self.spec.min_replicas)
            spec.set_config('spec.maxReplicas', self.spec.max_replicas)

        dashboard = get_auth_filled_platform_dashboard_url(dashboard)
        if self.spec.base_spec:
            if kind:
                raise ValueError('kind cannot be specified on built functions')
            config = nuclio.config.extend_config(self.spec.base_spec, spec,
                                                 tag,
                                                 self.spec.build.code_origin)
            update_in(config, 'metadata.name', self.metadata.name)
            update_in(config, 'spec.volumes', self.spec.to_nuclio_vol())
            base_image = get_in(config, 'spec.build.baseImage')
            if base_image:
                update_in(config, 'spec.build.baseImage',
                          tag_image(base_image))

            logger.info('deploy started')
            name = get_fullname(config, self.metadata.name, project, tag)
            addr = nuclio.deploy.deploy_config(
                config,
                dashboard,
                name=name,
                project=project,
                tag=tag,
                verbose=self.verbose,
                create_new=True,
            )
        else:

            kind = kind if kind is not None else self.spec.function_kind
            name, config, code = nuclio.build_file(
                self.spec.source,
                name=self.metadata.name,
                project=project,
                handler=handler,
                tag=tag,
                spec=spec,
                kind=kind,
                verbose=self.verbose,
            )

            update_in(config, 'spec.volumes', self.spec.to_nuclio_vol())
            name = get_fullname(config, name, project, tag)
            addr = deploy_config(
                config,
                dashboard_url=dashboard,
                name=name,
                project=project,
                tag=tag,
                verbose=self.verbose,
                create_new=True,
            )

        self.spec.command = 'http://{}'.format(addr)
        self.status.nuclio_name = name
        if addr:
            self.status.state = 'ready'
            self.status.address = addr
            self.save()
        return self.spec.command