Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for pipeline parameters #3001

Merged
merged 95 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
673797c
Initial changes to support pipeline parameters
kiersten-stokes Oct 31, 2022
dc48568
Fix some formatting and linting errors
kiersten-stokes Nov 7, 2022
bba0e13
Add some TODOs and comments
kiersten-stokes Nov 7, 2022
fc51af8
Fix type hints for builds using python 3.7
kiersten-stokes Nov 9, 2022
4eae657
Add a call to the API
Nov 9, 2022
dc853b0
Update status code and message for runtimes w/o param support
kiersten-stokes Nov 10, 2022
c0e2f73
Update to use code sandbox
Nov 10, 2022
0467800
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
Nov 10, 2022
bc9cfdf
Update how runtime processors determine parameter support
kiersten-stokes Nov 15, 2022
644fe78
Merge branch 'main' into pipeline-params
kiersten-stokes Nov 15, 2022
61a3216
Update select tests
kiersten-stokes Nov 15, 2022
ed17b46
Fix lint
kiersten-stokes Nov 15, 2022
3f6d30c
Change status code for processors that do not implement params
kiersten-stokes Nov 15, 2022
342a75a
Convert 405 response message to JSON
kiersten-stokes Nov 16, 2022
c205cf4
Allow oneOf for types of parameter values
kiersten-stokes Nov 17, 2022
83b539d
Allow oneOf for types of parameter values
kiersten-stokes Nov 17, 2022
fa2934b
Fix rendering for oneOf types
kiersten-stokes Nov 18, 2022
bbce4c8
Add parameters to generic properties template inputs
kiersten-stokes Nov 18, 2022
3044454
Add wrapper around oneOf schema
kiersten-stokes Nov 18, 2022
7892963
Fix KeyError
kiersten-stokes Nov 18, 2022
7e11a07
Initial changes to support parameter processing
kiersten-stokes Nov 21, 2022
e52a8bb
Merge branch 'bootstrapper-changes' into pipeline-params
kiersten-stokes Nov 21, 2022
0ac7142
Update frontend to new oneOf support
Nov 21, 2022
900436f
Add export dialog support
Nov 21, 2022
06004f1
Add style for tooltip
Nov 21, 2022
71edb03
Make input type determination runtime-specific
kiersten-stokes Nov 22, 2022
b8039e3
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Nov 22, 2022
33c2ec4
Merge branch 'main' into pipeline-params
kiersten-stokes Nov 22, 2022
174f281
Add parameters population to node properties
Nov 22, 2022
9ebc4e4
Move Kfp-specific properties to their own file
kiersten-stokes Nov 23, 2022
77c8dbe
Comment out unsupported parameter types
kiersten-stokes Nov 23, 2022
50d163b
Fix style for very long strings
Nov 28, 2022
3024be0
Fix tooltip style
Nov 28, 2022
7ef3c1b
Add types to export/submit dialog
Nov 28, 2022
818c314
Conslidate parameter form for two dialogs
Nov 28, 2022
3e3a320
Update style and lint
Nov 28, 2022
eb63c2c
Add asterisk for required fields
Nov 28, 2022
c23cffd
Filter by selected parameters from nodes
Nov 28, 2022
1e97a93
Adjust schema for parameter widget
kiersten-stokes Nov 29, 2022
c601ecd
Add parameters in oneOf fields
Nov 29, 2022
435e7b0
Add description to parameter attributes
kiersten-stokes Nov 29, 2022
cff4103
Fix rendering of KFP icons
kiersten-stokes Nov 29, 2022
0e5bb4e
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Nov 29, 2022
460efde
Fix empty parameter bug
Nov 29, 2022
781f10a
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Nov 29, 2022
4c5c500
Change field from parameters to pipeline_parameters
Nov 29, 2022
1d680fe
Merge branch 'main' into pipeline-params
kiersten-stokes Nov 29, 2022
53b8e5d
Fix lint after merge with main
kiersten-stokes Nov 30, 2022
3fa38d3
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Nov 30, 2022
00430f4
Remove extraneous f in Python f-string
kiersten-stokes Nov 30, 2022
8d3159b
Update style and codesandbox
Nov 30, 2022
8680f44
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Nov 30, 2022
79cdc38
Save parsed component property type in object
kiersten-stokes Dec 1, 2022
50a8fec
Merge branch 'main' into pipeline-params
kiersten-stokes Dec 1, 2022
b0a9314
Make small tweaks to schema to support review comments
kiersten-stokes Dec 1, 2022
3ba7209
Fix required behavior
Dec 2, 2022
ff08dfe
Add additional cases during validation
kiersten-stokes Dec 2, 2022
3027f89
Add description support
Dec 2, 2022
9fb3ba8
Add new codesandbox
Dec 2, 2022
13a6b24
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Dec 2, 2022
0dca97c
Update parameter access method based on pipeline JSON format changes
kiersten-stokes Dec 3, 2022
a5382fe
Change 'property' to 'parameter' in parameter required attribute
kiersten-stokes Dec 6, 2022
9754d6c
Add parsed type information to schema if present
kiersten-stokes Dec 6, 2022
8eebd41
Add title to type field
kiersten-stokes Dec 6, 2022
369497c
Update style to show correct titles in oneOf field
Dec 6, 2022
811c3f9
Fix passing parameter values to backend
Dec 6, 2022
cf1801c
Move parsed type to parameter oneOf
kiersten-stokes Dec 6, 2022
bb1e893
Merge branch 'pipeline-params' of github.com:kiersten-stokes/elyra in…
kiersten-stokes Dec 6, 2022
f840748
Update code sandbox
Dec 6, 2022
ac5c994
Include parameters selected in custom components
Dec 6, 2022
225458f
Address TODO items in backend
kiersten-stokes Dec 6, 2022
83de0e6
Fix list access to bring in line with new pipeline format
kiersten-stokes Dec 7, 2022
a42915a
Add round of backend tests
kiersten-stokes Dec 7, 2022
dc98d4d
Update schema to add tooltip description to param type
kiersten-stokes Dec 7, 2022
e1aa37f
Update style for tooltips and add new codesandbox
Dec 7, 2022
21aea93
Move test to appropriate location; add handler test
kiersten-stokes Dec 7, 2022
0b0eb52
Fix scroll bar
Dec 8, 2022
1d0f800
Add padding to clean up look
Dec 8, 2022
42667b1
Add remaining backend tests
kiersten-stokes Dec 8, 2022
d10fc4c
Add description, required, default for generic inputs
kiersten-stokes Dec 8, 2022
c0e4f5f
Merge branch 'main' into pipeline-params
kiersten-stokes Dec 8, 2022
1176786
Address review feedback re: parameter attribute descriptions
kiersten-stokes Dec 8, 2022
db09200
Convert Bool-types to Boolean for rendering in generic component def
kiersten-stokes Dec 9, 2022
7afbbf7
Update style to export/submit dialog
Dec 9, 2022
7114581
Fix padding issue
Dec 12, 2022
da36465
Protect against checking empty pipeline param list
kiersten-stokes Dec 12, 2022
0642638
Merge branch 'main' into pipeline-params
kiersten-stokes Dec 12, 2022
1b612c0
Cherry pick custom export name changes lost during merge
kiersten-stokes Dec 12, 2022
c9601c0
Fix bug
Dec 12, 2022
eac1c90
Update code sandbox
Dec 12, 2022
a20d6a4
Add parameter description to to_dict method
kiersten-stokes Dec 12, 2022
31eb598
Get parameter type from instance instead of raw value
kiersten-stokes Dec 12, 2022
1ffcd9f
Fix form validation and convert string to number
Dec 13, 2022
3e22dc6
Prevent incorrect default population
Dec 13, 2022
c7c5d30
Update pipeline-editor package to v1.12.0
akchinSTC Dec 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions elyra/elyra_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from elyra.pipeline.handlers import PipelineComponentHandler
from elyra.pipeline.handlers import PipelineComponentPropertiesHandler
from elyra.pipeline.handlers import PipelineExportHandler
from elyra.pipeline.handlers import PipelineParametersHandler
from elyra.pipeline.handlers import PipelinePropertiesHandler
from elyra.pipeline.handlers import PipelineRuntimeTypesHandler
from elyra.pipeline.handlers import PipelineSchedulerHandler
Expand Down Expand Up @@ -109,6 +110,7 @@ def initialize_handlers(self):
),
(f"/{self.name}/pipeline/export", PipelineExportHandler),
(f"/{self.name}/pipeline/{processor_regex}/properties", PipelinePropertiesHandler),
(f"/{self.name}/pipeline/{processor_regex}/parameters", PipelineParametersHandler),
(f"/{self.name}/pipeline/runtimes/types", PipelineRuntimeTypesHandler),
(f"/{self.name}/pipeline/schedule", PipelineSchedulerHandler),
(f"/{self.name}/pipeline/validate", PipelineValidationHandler),
Expand Down
8 changes: 7 additions & 1 deletion elyra/pipeline/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from abc import abstractmethod
from dataclasses import dataclass
from dataclasses import field
from importlib import import_module
import json
from logging import Logger
Expand Down Expand Up @@ -307,6 +308,11 @@ def determine_type_information(self, parsed_type: str) -> "ParameterTypeInfo":
else: # Let this be undetermined. Callers should check for this status and adjust
data_type_info = ParameterTypeInfo(parsed_data=parsed_type_lowered, undetermined=True)

from elyra.pipeline.processor import PipelineProcessorManager # placed here to avoid circular reference
Fixed Show fixed Hide fixed

if PipelineProcessorManager.instance().supports_parameters(runtime_type=self.component_platform):
data_type_info.allowed_input_types.append("parameter")

return data_type_info


Expand All @@ -328,7 +334,7 @@ class ParameterTypeInfo:

parsed_data: str
json_data_type: Optional[str] = "string"
allowed_input_types: Optional[List[str]] = None
allowed_input_types: Optional[List[str]] = field(default_factory=lambda: ["inputvalue", "inputpath", "file"])
default_value: Optional[Any] = ""
required: Optional[bool] = True
undetermined: Optional[bool] = False
82 changes: 73 additions & 9 deletions elyra/pipeline/component_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from elyra.pipeline.pipeline_constants import KUBERNETES_SHARED_MEM_SIZE
from elyra.pipeline.pipeline_constants import KUBERNETES_TOLERATIONS
from elyra.pipeline.pipeline_constants import MOUNTED_VOLUMES
from elyra.pipeline.pipeline_constants import PIPELINE_PARAMETERS
from elyra.util.kubernetes import is_valid_annotation_key
from elyra.util.kubernetes import is_valid_annotation_value
from elyra.util.kubernetes import is_valid_kubernetes_key
Expand Down Expand Up @@ -193,10 +194,10 @@ def get_classes_for_component_type(cls, component_type: str, runtime_type: Optio
processor_props = set()
for processor in PipelineProcessorManager.instance().get_all_processors():
props = getattr(processor, "supported_properties", set())
if processor.type.name == runtime_type and props:
processor_props = props # correct processor is found, and it explicitly specifies its properties
break
processor_props.update(props)
if not runtime_type or processor.type.name == runtime_type:
processor_props.update(props)
# if processor.type.name == runtime_type and props:
# break # TODO we don't want to break this soon, considering the 1:M notion of runtime type processors

all_subclasses = set()
for sc in cls.all_subclasses():
Expand Down Expand Up @@ -969,8 +970,8 @@ def __init__(
name: str,
json_data_type: str,
description: str,
allowed_input_types: List[Optional[str]],
value: Optional[Any] = "",
allowed_input_types: Optional[List[Optional[str]]] = None,
required: Optional[bool] = False,
allow_no_options: Optional[bool] = False,
items: Optional[List[str]] = None,
Expand Down Expand Up @@ -1018,11 +1019,7 @@ def __init__(
self._value = value

self._description = description

if not allowed_input_types:
allowed_input_types = ["inputvalue", "inputpath", "file"]
self._allowed_input_types = allowed_input_types

self._items = items or []

# Check description for information about 'required' parameter
Expand Down Expand Up @@ -1161,3 +1158,70 @@ class InputTypeDescriptionMap(Enum):
file = "Please select a file to use as input:"
inputpath = "Please select an output from a parent:"
outputpath = None # outputs are read-only and don't require a description
parameter = "Please select a parameter to use as input:"


class PipelineParameter(ElyraPropertyListItem):
"""TODO"""

property_id = PIPELINE_PARAMETERS
property_display_name = "Pipeline Parameters"
property_description = """TODO""" # TODO
property_attributes = [
ListItemPropertyAttribute(
attribute_id="name",
display_name="Parameter Name",
input_type="string",
hidden=False,
required=True,
use_in_key=True,
),
ListItemPropertyAttribute(
attribute_id="value",
display_name="Default Value",
placeholder="default_val",
input_type="string",
hidden=False,
required=False, # TODO?
use_in_key=False,
),
ListItemPropertyAttribute(
attribute_id="input_type",
display_name="Type",
placeholder="String",
input_type="string",
enum=["String"],
hidden=False,
required=True,
use_in_key=False,
),
ListItemPropertyAttribute(
attribute_id="required",
display_name="Required",
input_type="boolean",
hidden=False,
required=True,
use_in_key=False,
),
]

def __init__(self, name, value, input_type, required, **kwargs):
self.name = name
self.value = value
self.input_type = input_type
self.required = required

def get_value_for_dict_entry(self) -> Union[str, Dict[str, Any]]:
"""
Returns the value to be used when constructing a dict from a list of ElyraPropertyListItem.
A PipelineParameter dict entry will be of the form: {self.name: self.value}
"""
return self.value

def get_all_validation_errors(self) -> List[str]:
# TODO Ensure param name adheres to python variable naming rules
pass

def add_to_execution_object(self, runtime_processor: RuntimePipelineProcessor, execution_object: Any, **kwargs):
# TODO this path may or may not be reached depending on how processing is implemented
pass
33 changes: 27 additions & 6 deletions elyra/pipeline/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from elyra.pipeline.component import Component
from elyra.pipeline.component_catalog import ComponentCache
from elyra.pipeline.component_catalog import RefreshInProgressError
from elyra.pipeline.component_parameter import PipelineParameter
from elyra.pipeline.parser import PipelineParser
from elyra.pipeline.pipeline_definition import PipelineDefinition
from elyra.pipeline.processor import PipelineProcessorManager
Expand Down Expand Up @@ -200,24 +201,44 @@ class PipelinePropertiesHandler(HttpErrorMixin, APIHandler):

@web.authenticated
async def get(self, runtime_type):
self.log.debug(f"Retrieving pipeline components for runtime type: {runtime_type}")
self.log.debug(f"Retrieving pipeline properties for runtime type: {runtime_type}")

runtime_processor_type = get_runtime_processor_type(runtime_type, self.log, self.request.path)
if not runtime_processor_type:
raise web.HTTPError(400, f"Invalid runtime type '{runtime_type}'")

# Get pipeline properties json
pipeline_properties_json = PipelineDefinition.get_canvas_properties_from_template(
package_name="templates/pipeline",
template_name="pipeline_properties_template.jinja2",
runtime_type=runtime_processor_type.name,
)
pipeline_properties_json = PipelineDefinition.get_pipeline_properties(runtime_type=runtime_processor_type.name)

self.set_status(200)
self.set_header("Content-Type", "application/json")
await self.finish(pipeline_properties_json)


class PipelineParametersHandler(HttpErrorMixin, APIHandler):
"""Handler to expose method calls to retrieve pipeline parameters"""

@web.authenticated
async def get(self, runtime_type):
self.log.debug(f"Retrieving pipeline parameters for runtime type: {runtime_type}")

runtime_processor_type = get_runtime_processor_type(runtime_type, self.log, self.request.path)
if not runtime_processor_type:
raise web.HTTPError(400, f"Invalid runtime type '{runtime_type}'")

if PipelineProcessorManager.instance().supports_parameters(runtime_type=runtime_processor_type):
# Get pipeline parameters json
pipeline_properties_json = PipelineParameter.get_schema()
self.set_status(200)
self.set_header("Content-Type", "application/json")
await self.finish(pipeline_properties_json)
else:
# Pipeline parameters are not supported, no content to return
self.set_status(204)
processor_name = RuntimeProcessorType.get_instance_by_name(runtime_type)
await self.finish(f"{processor_name} does not support pipeline parameters.")


class PipelineComponentPropertiesHandler(HttpErrorMixin, APIHandler):
"""Handler to expose method calls to retrieve pipeline component_id properties"""

Expand Down
1 change: 0 additions & 1 deletion elyra/pipeline/kfp/component_parser_kfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ def determine_type_information(self, parsed_type: str) -> SimpleNamespace:
"""
data_type_info = super().determine_type_information(parsed_type)

data_type_info.allowed_input_types = []
# By default, original input type (determined by parent) is stored as the `json_data_type`
# and then overridden with Kubeflow Pipeline's meta-type
if data_type_info.undetermined:
Expand Down
5 changes: 5 additions & 0 deletions elyra/pipeline/kfp/processor_kfp.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,11 @@ def supported_properties(self) -> Set[str]:
pipeline_constants.KUBERNETES_SHARED_MEM_SIZE,
}

@property
def supports_pipeline_parameters(self) -> bool:
"""KfpPipelineProcessor does support pipeline parameters."""
return True


class KfpPipelineProcessorResponse(RuntimePipelineProcessorResponse):
_type = RuntimeProcessorType.KUBEFLOW_PIPELINES
Expand Down
3 changes: 2 additions & 1 deletion elyra/pipeline/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def parse(self, pipeline_json: Dict) -> Pipeline:
runtime_config=runtime_config,
source=source,
description=description,
pipeline_properties=primary_pipeline.get_pipeline_default_properties(),
pipeline_properties=primary_pipeline.pipeline_default_properties,
pipeline_parameters=primary_pipeline.pipeline_parameters,
)

nodes = primary_pipeline.nodes
Expand Down
55 changes: 11 additions & 44 deletions elyra/pipeline/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from typing import List
from typing import Optional

from elyra.pipeline.component_parameter import ElyraPropertyList
from elyra.pipeline.component_parameter import ElyraPropertyList, PipelineParameter
Fixed Show fixed Hide fixed
from elyra.pipeline.component_parameter import EnvironmentVariable
from elyra.pipeline.pipeline_constants import ENV_VARIABLES
from elyra.pipeline.pipeline_constants import RUNTIME_IMAGE
Expand Down Expand Up @@ -342,6 +342,7 @@ def __init__(
source: Optional[str] = None,
description: Optional[str] = None,
pipeline_properties: Optional[Dict[str, Any]] = None,
pipeline_parameters: ElyraPropertyList[PipelineParameter] = None,
):
"""
:param id: Generated UUID, 128 bit number used as a unique identifier
Expand All @@ -352,6 +353,7 @@ def __init__(
:param source: The pipeline source, e.g. a pipeline file or a notebook.
:param description: Pipeline description
:param pipeline_properties: Key/value pairs representing the properties of this pipeline
:param pipeline_parameters: an ElyraPropertyList of the pipeline parameters
"""

if not name:
Expand All @@ -366,6 +368,7 @@ def __init__(
self._runtime = runtime
self._runtime_config = runtime_config
self._pipeline_properties = pipeline_properties or {}
self._pipeline_parameters = pipeline_parameters or []
self._operations = {}

@property
Expand Down Expand Up @@ -401,6 +404,13 @@ def pipeline_properties(self) -> Dict[str, Any]:
"""
return self._pipeline_properties

@property
def pipeline_parameters(self) -> ElyraPropertyList[PipelineParameter]:
"""
The list of parameters associated with this pipeline
"""
return self._pipeline_parameters

@property
def operations(self) -> Dict[str, Operation]:
return self._operations
Expand Down Expand Up @@ -432,46 +442,3 @@ def __eq__(self, other: "Pipeline") -> bool:
and self.runtime == other.runtime
and self.operations == other.operations
)


class KeyValueList(list):
"""
A list class that exposes functionality specific to lists whose entries are
key-value pairs separated by a pre-defined character.
"""

_key_value_separator: str = "="

def to_dict(self) -> Dict[str, str]:
"""
Properties consisting of key-value pairs are stored in a list of separated
strings, while most processing steps require a dictionary - so we must convert.
If no key/value pairs are specified, an empty dictionary is returned, otherwise
pairs are converted to dictionary entries, stripped of whitespace, and returned.
"""
kv_dict = {}
for kv in self:
if not kv:
continue

if self._key_value_separator not in kv:
raise ValueError(
f"Property {kv} does not contain the expected "
f"separator character: '{self._key_value_separator}'."
)

key, value = kv.split(self._key_value_separator, 1)

key = key.strip()
if not key:
# Invalid entry; skip inclusion and continue
continue

if isinstance(value, str):
value = value.strip()
if not value:
# Invalid entry; skip inclusion and continue
continue

kv_dict[key] = value
return kv_dict
1 change: 1 addition & 0 deletions elyra/pipeline/pipeline_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#

PIPELINE_DEFAULTS = "pipeline_defaults"
PIPELINE_PARAMETERS = "pipeline_parameters"
RUNTIME_IMAGE = "runtime_image"
ENV_VARIABLES = "env_vars"
MOUNTED_VOLUMES = "mounted_volumes"
Expand Down
Loading