Example #1
0
    def __init__(
        self,
        config,
        default_value=FIELD_NO_DEFAULT_PROVIDED,
        is_optional=None,
        is_required=None,
        description=None,
    ):
        from .validate import validate_config
        from .post_process import post_process_config

        self.config_type = check.inst(self._resolve_config_arg(config), ConfigType)

        self.description = check.opt_str_param(description, 'description')

        check.opt_bool_param(is_optional, 'is_optional')
        check.opt_bool_param(is_required, 'is_required')

        canonical_is_required = canonicalize_backcompat_args(
            new_val=is_required,
            new_arg='is_required',
            old_val=is_optional,
            old_arg='is_optional',
            coerce_old_to_new=lambda val: not val,
            additional_warn_txt='"is_optional" deprecated in 0.7.0 and will be removed in 0.8.0. Users should use "is_required" instead.',
        )

        if canonical_is_required is True:
            check.param_invariant(
                default_value == FIELD_NO_DEFAULT_PROVIDED,
                'default_value',
                'required arguments should not specify default values',
            )
        self._default_value = default_value

        # check explicit default value
        if self.default_provided:
            # invoke through property in case it is callable
            value = self.default_value
            evr = validate_config(self.config_type, value)
            if not evr.success:
                raise DagsterInvalidConfigError(
                    'Invalid default_value for Field.', evr.errors, default_value,
                )

        if canonical_is_required is None:
            # neither is_required nor is_optional were specified
            canonical_is_required = not all_optional_type(self.config_type)

            # on implicitly optional - set the default value
            # by resolving the defaults of the type
            if not canonical_is_required and self._default_value == FIELD_NO_DEFAULT_PROVIDED:
                evr = post_process_config(self.config_type, None)
                if not evr.success:
                    raise DagsterInvalidConfigError(
                        'Unable to resolve implicit default_value for Field.', evr.errors, None,
                    )
                self._default_value = evr.value

        self.is_optional = not canonical_is_required
Example #2
0
    def build(pipeline_def, run_config=None, mode=None):
        '''This method validates a given run config against the pipeline config schema. If
        successful, we instantiate an EnvironmentConfig object.

        In case the run_config is invalid, this method raises a DagsterInvalidConfigError
        '''
        from dagster.config.validate import process_config
        from .composite_descent import composite_descent

        check.inst_param(pipeline_def, 'pipeline_def', PipelineDefinition)
        run_config = check.opt_dict_param(run_config, 'run_config')
        check.opt_str_param(mode, 'mode')

        mode = mode or pipeline_def.get_default_mode_name()
        environment_type = create_environment_type(pipeline_def, mode)

        config_evr = process_config(environment_type, run_config)
        if not config_evr.success:
            raise DagsterInvalidConfigError(
                'Error in config for pipeline {}'.format(pipeline_def.name),
                config_evr.errors,
                run_config,
            )

        config_value = config_evr.value

        mode_def = pipeline_def.get_mode_definition(mode)
        resource_configs = config_value.get('resources', {})
        processed_resource_configs = {}
        for resource_key, resource_def in mode_def.resource_defs.items():
            resource_config = resource_configs.get(resource_key, {})
            resource_config_evr = resource_def.process_config(resource_config)
            if not resource_config_evr.success:
                raise DagsterInvalidConfigError(
                    'Error in config for resource {}'.format(resource_key),
                    resource_config_evr.errors,
                    resource_config,
                )
            else:
                processed_resource_configs[
                    resource_key] = resource_config_evr.value

        solid_config_dict = composite_descent(pipeline_def,
                                              config_value.get('solids', {}))
        # TODO:  replace this with a simple call to from_dict of the config.get when ready to fully deprecate
        temp_intermed = config_value.get('intermediate_storage')
        if config_value.get('storage'):
            if temp_intermed is None:
                temp_intermed = {EmptyIntermediateStoreBackcompatConfig(): {}}

        return EnvironmentConfig(
            solids=solid_config_dict,
            execution=ExecutionConfig.from_dict(config_value.get('execution')),
            storage=StorageConfig.from_dict(config_value.get('storage')),
            intermediate_storage=IntermediateStorageConfig.from_dict(
                temp_intermed),
            loggers=config_value.get('loggers'),
            original_config_dict=run_config,
            resources=processed_resource_configs,
        )
Example #3
0
def _resolve_bound_config(solid_config: Any,
                          solid_def: SolidDefinition) -> Any:
    """Validate config against config schema, and return validated config."""
    from dagster.config.validate import process_config

    # Config processing system expects the top level config schema to be a dictionary, but solid
    # config schema can be scalar. Thus, we wrap it in another layer of indirection.
    outer_config_shape = Shape({"config": solid_def.get_config_field()})
    config_evr = process_config(
        outer_config_shape, {"config": solid_config} if solid_config else {})
    if not config_evr.success:
        raise DagsterInvalidConfigError(
            f"Error in config for {solid_def.node_type_str} ",
            config_evr.errors,
            solid_config,
        )
    validated_config = cast(Dict, config_evr.value).get("config")
    mapped_config_evr = solid_def.apply_config_mapping(
        {"config": validated_config})
    if not mapped_config_evr.success:
        raise DagsterInvalidConfigError(
            f"Error in config for {solid_def.node_type_str} ",
            mapped_config_evr.errors,
            solid_config,
        )
    validated_config = cast(Dict, mapped_config_evr.value).get("config")
    return validated_config
Example #4
0
    def __init__(
        self, config, default_value=FIELD_NO_DEFAULT_PROVIDED, is_required=None, description=None,
    ):
        from .validate import validate_config
        from .post_process import resolve_defaults

        self.config_type = check.inst(self._resolve_config_arg(config), ConfigType)

        self.description = check.opt_str_param(description, "description")

        check.opt_bool_param(is_required, "is_required")

        if default_value != FIELD_NO_DEFAULT_PROVIDED:
            check.param_invariant(
                not (callable(default_value)), "default_value", "default_value cannot be a callable"
            )

        if is_required is True:
            check.param_invariant(
                default_value == FIELD_NO_DEFAULT_PROVIDED,
                "default_value",
                "required arguments should not specify default values",
            )

        self._default_value = default_value

        # check explicit default value
        if self.default_provided:
            if self.config_type.kind == ConfigTypeKind.ENUM and is_enum_value(default_value):
                raise DagsterInvalidDefinitionError(
                    (
                        "You have passed into a python enum value as the default value "
                        "into of a config enum type {name}. You must pass in the underlying "
                        "string represention as the default value. One of {value_set}."
                    ).format(
                        value_set=[ev.config_value for ev in self.config_type.enum_values],
                        name=self.config_type.given_name,
                    )
                )

            evr = validate_config(self.config_type, default_value)
            if not evr.success:
                raise DagsterInvalidConfigError(
                    "Invalid default_value for Field.", evr.errors, default_value,
                )

        if is_required is None:
            is_optional = has_implicit_default(self.config_type) or self.default_provided
            is_required = not is_optional

            # on implicitly optional - set the default value
            # by resolving the defaults of the type
            if is_optional and not self.default_provided:
                evr = resolve_defaults(self.config_type, None)
                if not evr.success:
                    raise DagsterInvalidConfigError(
                        "Unable to resolve implicit default_value for Field.", evr.errors, None,
                    )
                self._default_value = evr.value
        self._is_required = is_required
Example #5
0
def dagster_instance_config(
    base_dir,
    config_filename=DAGSTER_CONFIG_YAML_FILENAME,
    overrides=None,
):
    check.str_param(base_dir, "base_dir")
    check.invariant(os.path.isdir(base_dir), "base_dir should be a directory")
    overrides = check.opt_dict_param(overrides, "overrides")

    config_yaml_path = os.path.join(base_dir, config_filename)

    if not os.path.exists(config_yaml_path):
        warnings.warn((
            "The dagster instance configuration file ({config_filename}) is not present at "
            "{base_dir}. Dagster uses this file to know where and how to store "
            "local artifacts, information about past runs, and structured events.\n"
            "If nothing is specified, Dagster will store this information "
            "in the local filesystem in the {base_dir} directory.").format(
                config_filename=config_filename, base_dir=base_dir))

    dagster_config_dict = merge_dicts(load_yaml_from_globs(config_yaml_path),
                                      overrides)

    if "custom_instance_class" in dagster_config_dict:
        custom_instance_class_data = dagster_config_dict[
            "custom_instance_class"]

        validate_custom_config = validate_config(
            configurable_class_schema(),
            custom_instance_class_data,
        )
        if not validate_custom_config.success:
            raise DagsterInvalidConfigError(
                "Errors whilst loading dagster custom class config at {}".
                format(config_filename),
                validate_custom_config.errors,
                custom_instance_class_data,
            )

        custom_instance_class = class_from_code_pointer(
            custom_instance_class_data["module"],
            custom_instance_class_data["class"])

        schema = merge_dicts(dagster_instance_config_schema(),
                             custom_instance_class.config_schema())
    else:
        custom_instance_class = None
        schema = dagster_instance_config_schema()

    dagster_config = validate_config(schema, dagster_config_dict)
    if not dagster_config.success:
        raise DagsterInvalidConfigError(
            "Errors whilst loading dagster instance config at {}.".format(
                config_filename),
            dagster_config.errors,
            dagster_config_dict,
        )

    return (dagster_config.value, custom_instance_class)
Example #6
0
def _check_invocation_requirements(
    solid_def: "SolidDefinition", context: Optional["DirectSolidExecutionContext"]
) -> "DirectSolidExecutionContext":
    """Ensure that provided context fulfills requirements of solid definition.

    If no context was provided, then construct an enpty DirectSolidExecutionContext
    """
    from dagster.core.execution.context.invocation import DirectSolidExecutionContext
    from dagster.config.validate import validate_config

    # Check resource requirements
    if solid_def.required_resource_keys and context is None:
        raise DagsterInvalidInvocationError(
            f'Solid "{solid_def.name}" has required resources, but no context was provided. Use the'
            "`build_solid_context` function to construct a context with the required "
            "resources."
        )

    if context is not None and solid_def.required_resource_keys:
        resources_dict = cast(  # type: ignore[attr-defined]
            "DirectSolidExecutionContext",
            context,
        ).resources._asdict()

        for resource_key in solid_def.required_resource_keys:
            if resource_key not in resources_dict:
                raise DagsterInvalidInvocationError(
                    f'Solid "{solid_def.name}" requires resource "{resource_key}", but no resource '
                    "with that key was found on the context."
                )

    # Check config requirements
    if not context and solid_def.config_schema.as_field().is_required:
        raise DagsterInvalidInvocationError(
            f'Solid "{solid_def.name}" has required config schema, but no context was provided. '
            "Use the `build_solid_context` function to create a context with config."
        )

    config = None
    if solid_def.config_field:
        solid_config = check.opt_dict_param(
            context.solid_config if context else None, "solid_config"
        )
        config_evr = validate_config(solid_def.config_field.config_type, solid_config)
        if not config_evr.success:
            raise DagsterInvalidConfigError(
                "Error in config for solid ", config_evr.errors, solid_config
            )
        mapped_config_evr = solid_def.apply_config_mapping({"config": solid_config})
        if not mapped_config_evr.success:
            raise DagsterInvalidConfigError(
                "Error in config for solid ", mapped_config_evr.errors, solid_config
            )
        config = mapped_config_evr.value.get("config", {})
    return (
        context
        if context
        else DirectSolidExecutionContext(solid_config=config, resources_dict=None, instance=None)
    )
Example #7
0
def dagster_instance_config(
    base_dir,
    config_filename=DAGSTER_CONFIG_YAML_FILENAME,
    overrides=None,
):
    check.str_param(base_dir, "base_dir")
    check.invariant(os.path.isdir(base_dir), "base_dir should be a directory")
    overrides = check.opt_dict_param(overrides, "overrides")

    config_yaml_path = os.path.join(base_dir, config_filename)

    if not os.path.exists(config_yaml_path) and is_dagster_home_set():
        warnings.warn(
            f"No dagster instance configuration file ({config_filename}) found at "
            f"{base_dir}. Defaulting to loading and storing all metadata with {base_dir}. "
            f"If this is the desired behavior, create an empty {config_filename} file in {base_dir}."
        )

    dagster_config_dict = merge_dicts(load_yaml_from_globs(config_yaml_path),
                                      overrides)

    if "instance_class" in dagster_config_dict:
        custom_instance_class_data = dagster_config_dict["instance_class"]

        validate_custom_config = validate_config(
            configurable_class_schema(),
            custom_instance_class_data,
        )
        if not validate_custom_config.success:
            raise DagsterInvalidConfigError(
                "Errors whilst loading dagster custom class config at {}".
                format(config_filename),
                validate_custom_config.errors,
                custom_instance_class_data,
            )

        custom_instance_class = class_from_code_pointer(
            custom_instance_class_data["module"],
            custom_instance_class_data["class"])

        schema = merge_dicts(dagster_instance_config_schema(),
                             custom_instance_class.config_schema())
    else:
        custom_instance_class = None
        schema = dagster_instance_config_schema()

    dagster_config = validate_config(schema, dagster_config_dict)
    if not dagster_config.success:
        raise DagsterInvalidConfigError(
            "Errors whilst loading dagster instance config at {}.".format(
                config_filename),
            dagster_config.errors,
            dagster_config_dict,
        )

    return (dagster_config.value, custom_instance_class)
Example #8
0
    def resolve_from_unvalidated_config(self, config: Any) -> Any:
        """Validates config against outer config schema, and calls mapping against validated config."""

        receive_processed_config_values = check.opt_bool_param(
            self.receive_processed_config_values,
            "receive_processed_config_values",
            default=True)
        if receive_processed_config_values:
            outer_evr = process_config(
                self.config_schema.config_type,
                config,
            )
        else:
            outer_evr = validate_config(
                self.config_schema.config_type,
                config,
            )
        if not outer_evr.success:
            raise DagsterInvalidConfigError(
                "Error in config mapping ",
                outer_evr.errors,
                config,
            )

        outer_config = outer_evr.value
        if not receive_processed_config_values:
            outer_config = resolve_defaults(
                cast(ConfigType, self.config_schema.config_type),
                outer_config,
            ).value

        return self.config_fn(outer_config)
Example #9
0
def config_map_resources(
    resource_defs: Dict[str, ResourceDefinition],
    resource_configs: Dict[str, Any],
) -> Dict[str, ResourceConfig]:
    """This function executes the config mappings for resources with respect to ConfigurableDefinition.
    It iterates over resource_defs and looks up the corresponding config because resources need to
    be mapped regardless of whether they receive config from run_config."""

    config_mapped_resource_configs = {}
    for resource_key, resource_def in resource_defs.items():
        resource_config = resource_configs.get(resource_key, {})
        resource_config_evr = resource_def.apply_config_mapping(
            resource_config)
        if not resource_config_evr.success:
            raise DagsterInvalidConfigError(
                "Error in config for resource {}".format(resource_key),
                resource_config_evr.errors,
                resource_config,
            )
        else:
            config_mapped_resource_configs[
                resource_key] = ResourceConfig.from_dict(
                    resource_config_evr.value)

    return config_mapped_resource_configs
Example #10
0
def config_map_objects(config_value, defs, keyed_by, def_type, name_of_def_type):
    """This function executes the config mappings for executors and {system, intermediate} storage
    definitions with respect to ConfigurableDefinition. It calls the ensure_single_item macro on the
    incoming config and then applies config mapping to the result and the first executor_def with
    the same name on the mode_def."""

    config = config_value.get(keyed_by)

    check.opt_dict_param(config, "config", key_type=str)
    if not config:
        return None

    obj_name, obj_config = ensure_single_item(config)

    obj_def = next(
        (defi for defi in defs if defi.name == obj_name), None
    )  # obj_defs are stored in a list and we want to find the def matching name
    check.inst(
        obj_def,
        def_type,
        (
            "Could not find a {def_type} definition on the selected mode that matches the "
            '{def_type} "{obj_name}" given in run config'
        ).format(def_type=def_type, obj_name=obj_name),
    )

    obj_config_evr = obj_def.apply_config_mapping(obj_config)
    if not obj_config_evr.success:
        raise DagsterInvalidConfigError(
            'Invalid configuration provided for {} "{}"'.format(name_of_def_type, obj_name),
            obj_config_evr.errors,
            obj_config,
        )

    return {obj_name: obj_config_evr.value}
Example #11
0
def raise_composite_descent_config_error(descent_stack, failed_config_value,
                                         evr):
    check.inst_param(descent_stack, 'descent_stack', DescentStack)
    check.inst_param(evr, 'evr', EvaluateValueResult)

    solid = descent_stack.current_solid
    message = 'In pipeline {pipeline_name} at stack {stack}: \n'.format(
        pipeline_name=descent_stack.pipeline_def.name,
        stack=':'.join(descent_stack.handle.path),
    )
    message += (
        'Solid "{solid_name}" with definition "{solid_def_name}" has a '
        'configuration error. '
        'It has produced config a via its config_fn that fails to '
        'pass validation in the solids that it contains. '
        'This indicates an error in the config mapping function itself. It must '
        'produce correct config for its constiuent solids in all cases. The correct '
        'resolution is to fix the mapping function. Details on the error (and the paths '
        'on this error are relative to config mapping function "root", not the entire document): '
    ).format(
        solid_name=solid.name,
        solid_def_name=solid.definition.name,
    )

    raise DagsterInvalidConfigError(message, evr.errors, failed_config_value)
Example #12
0
def _get_host_mode_executor(recon_pipeline, run_config, get_executor_def_fn,
                            instance):
    execution_config = run_config.get("execution")
    if execution_config:
        executor_name, executor_config = ensure_single_item(execution_config)
    else:
        executor_name = None
        executor_config = {}

    executor_def = get_executor_def_fn(executor_name)

    executor_config_type = def_config_field(executor_def).config_type

    config_evr = process_config(executor_config_type, executor_config)
    if not config_evr.success:
        raise DagsterInvalidConfigError(
            "Error in executor config for executor {}".format(
                executor_def.name),
            config_evr.errors,
            executor_config,
        )
    executor_config_value = config_evr.value

    init_context = InitExecutorContext(
        pipeline=recon_pipeline,
        executor_def=executor_def,
        executor_config=executor_config_value["config"],
        instance=instance,
    )
    check_cross_process_constraints(init_context)
    return executor_def.executor_creation_fn(init_context)
Example #13
0
    def rehydrate(self):
        from dagster.config.field import resolve_to_config_type
        from dagster.config.validate import process_config
        from dagster.core.errors import DagsterInvalidConfigError

        try:
            module = importlib.import_module(self.module_name)
        except ModuleNotFoundError:
            check.failed(
                f"Couldn't import module {self.module_name} when attempting to load the "
                f"configurable class {self.module_name}.{self.class_name}")
        try:
            klass = getattr(module, self.class_name)
        except AttributeError:
            check.failed(
                f"Couldn't find class {self.class_name} in module when attempting to load the "
                f"configurable class {self.module_name}.{self.class_name}")

        if not issubclass(klass, ConfigurableClass):
            raise check.CheckError(
                klass,
                f"class {self.class_name} in module {self.module_name}",
                ConfigurableClass,
            )

        config_dict = self.config_dict
        result = process_config(resolve_to_config_type(klass.config_type()),
                                config_dict)
        if not result.success:
            raise DagsterInvalidConfigError(
                f"Errors whilst loading configuration for {klass.config_type()}.",
                result.errors,
                config_dict,
            )
        return klass.from_config_value(self, result.value)
Example #14
0
    def build(pipeline, environment_dict=None, run_config=None):
        from dagster.core.types.config.evaluator.composite_descent import composite_descent
        from dagster.core.types.config.evaluator.validate import process_config

        check.inst_param(pipeline, 'pipeline', PipelineDefinition)
        environment_dict = check.opt_dict_param(environment_dict, 'environment_dict')
        run_config = check.opt_inst_param(run_config, 'run_config', IRunConfig, default=RunConfig())

        mode = run_config.mode or pipeline.get_default_mode_name()
        environment_type = create_environment_type(pipeline, mode)

        config_evr = process_config(environment_type, environment_dict)
        if not config_evr.success:
            raise DagsterInvalidConfigError(
                'Error in config for pipeline {}'.format(pipeline.name),
                config_evr.errors,
                environment_dict,
            )

        config_value = config_evr.value

        solid_config_dict = composite_descent(pipeline, config_value.get('solids', {}), run_config)

        return EnvironmentConfig(
            solids=solid_config_dict,
            execution=ExecutionConfig.from_dict(config_value.get('execution')),
            storage=StorageConfig.from_dict(config_value.get('storage')),
            loggers=config_value.get('loggers'),
            original_config_dict=environment_dict,
            resources=config_value.get('resources'),
        )
Example #15
0
    def create_from_config(run_container_context):
        run_docker_container_context = (run_container_context.get(
            "docker", {}) if run_container_context else {})

        if not run_docker_container_context:
            return DockerContainerContext()

        processed_container_context = process_config(
            DOCKER_CONTAINER_CONTEXT_SCHEMA, run_docker_container_context)

        if not processed_container_context.success:
            raise DagsterInvalidConfigError(
                "Errors while parsing Docker container context",
                processed_container_context.errors,
                run_docker_container_context,
            )

        processed_context_value = processed_container_context.value

        return DockerContainerContext(
            registry=processed_context_value.get("registry"),
            env_vars=processed_context_value.get("env_vars", []),
            networks=processed_context_value.get("networks", []),
            container_kwargs=processed_context_value.get("container_kwargs"),
        )
Example #16
0
def _get_host_mode_executor(recon_pipeline, run_config, executor_defs,
                            instance):
    execution_config = run_config.get("execution", {})
    execution_config_type = Field(selector_for_named_defs(executor_defs),
                                  default_value={
                                      executor_defs[0].name: {}
                                  }).config_type

    config_evr = process_config(execution_config_type, execution_config)
    if not config_evr.success:
        raise DagsterInvalidConfigError(
            "Error processing execution config {}".format(execution_config),
            config_evr.errors,
            execution_config,
        )

    execution_config_value = config_evr.value

    executor_name, executor_config = ensure_single_item(execution_config_value)

    executor_defs_by_name = {
        executor_def.name: executor_def
        for executor_def in executor_defs
    }
    executor_def = executor_defs_by_name[executor_name]

    init_context = InitExecutorContext(
        job=recon_pipeline,
        executor_def=executor_def,
        executor_config=executor_config["config"],
        instance=instance,
    )
    check_cross_process_constraints(init_context)
    return executor_def.executor_creation_fn(init_context)
Example #17
0
    def create_from_config(run_container_context) -> "K8sContainerContext":
        run_k8s_container_context = (run_container_context.get("k8s", {})
                                     if run_container_context else {})

        if not run_k8s_container_context:
            return K8sContainerContext()

        processed_container_context = process_config(
            DagsterK8sJobConfig.config_type_container_context(),
            run_k8s_container_context)

        if not processed_container_context.success:
            raise DagsterInvalidConfigError(
                "Errors while parsing k8s container context",
                processed_container_context.errors,
                run_k8s_container_context,
            )

        processed_context_value = processed_container_context.value

        return K8sContainerContext(
            image_pull_policy=processed_context_value.get("image_pull_policy"),
            image_pull_secrets=processed_context_value.get(
                "image_pull_secrets"),
            service_account_name=processed_context_value.get(
                "service_account_name"),
            env_config_maps=processed_context_value.get("env_config_maps"),
            env_secrets=processed_context_value.get("env_secrets"),
            env_vars=processed_context_value.get("env_vars"),
            volume_mounts=processed_context_value.get("volume_mounts"),
            volumes=processed_context_value.get("volumes"),
            labels=processed_context_value.get("labels"),
            namespace=processed_context_value.get("namespace"),
        )
Example #18
0
def config_map_executors(pipeline_def, config_value, mode):
    '''This function executes the config mappings for executors with respect to IConfigMappable.
    It calls the ensure_single_item macro on the incoming config and then applies config mapping to
    the result and the first executor_def with the same name on the mode_def.'''

    config = config_value.get('execution')

    check.opt_dict_param(config, 'config', key_type=str)
    if not config:
        return None

    executor_name, executor_config = ensure_single_item(config)

    mode_def = pipeline_def.get_mode_definition(mode)
    executor_def = next(
        (defi
         for defi in mode_def.executor_defs if defi.name == executor_name),
        None
    )  # executor_defs are stored in a list and we want to find the def matching name
    check.inst(executor_def, ExecutorDefinition)

    executor_config_evr = executor_def.apply_config_mapping(executor_config)
    if not executor_config_evr.success:
        raise DagsterInvalidConfigError(
            'Error in config for executor {}'.format(executor_name),
            executor_config_evr.errors,
            executor_config,
        )

    return {executor_name: executor_config_evr.value}
Example #19
0
def get_user_defined_k8s_config(tags):
    check.inst_param(tags, "tags", frozentags)

    if not any(key in tags for key in [K8S_RESOURCE_REQUIREMENTS_KEY, USER_DEFINED_K8S_CONFIG_KEY]):
        return UserDefinedDagsterK8sConfig()

    user_defined_k8s_config = {}

    if USER_DEFINED_K8S_CONFIG_KEY in tags:
        user_defined_k8s_config_value = json.loads(tags[USER_DEFINED_K8S_CONFIG_KEY])
        result = validate_config(USER_DEFINED_K8S_CONFIG_SCHEMA, user_defined_k8s_config_value)

        if not result.success:
            raise DagsterInvalidConfigError(
                "Error in tags for {}".format(USER_DEFINED_K8S_CONFIG_KEY), result.errors, result,
            )

        user_defined_k8s_config = result.value

    container_config = user_defined_k8s_config.get("container_config", {})

    # Backcompat for resource requirements key
    if K8S_RESOURCE_REQUIREMENTS_KEY in tags:
        resource_requirements_config = get_k8s_resource_requirements(tags)
        container_config = merge_dicts(
            container_config, {"resources": resource_requirements_config}
        )

    return UserDefinedDagsterK8sConfig(
        container_config=container_config,
        pod_template_spec_metadata=user_defined_k8s_config.get("pod_template_spec_metadata"),
        pod_spec_config=user_defined_k8s_config.get("pod_spec_config"),
        job_config=user_defined_k8s_config.get("job_config"),
        job_spec_config=user_defined_k8s_config.get("job_spec_config"),
    )
Example #20
0
def _composite_descent(parent_stack, solids_config_dict, resource_defs):
    """
    The core implementation of composite_descent. This yields a stream of
    SolidConfigEntry. This is used by composite_descent to construct a
    dictionary.

    It descends over the entire solid hierarchy, constructing an entry
    for every handle. If it encounters a composite solid instance
    with a config mapping, it will invoke that config mapping fn,
    producing the config that is necessary to configure the child solids.

    This process unrolls recursively as you descend down the tree.
    """

    for solid in parent_stack.current_container.solids:

        current_stack = parent_stack.descend(solid)
        current_handle = current_stack.handle

        current_solid_config = solids_config_dict.get(solid.name, {})

        # the base case
        if isinstance(solid.definition, SolidDefinition):
            config_mapped_solid_config = solid.definition.apply_config_mapping(
                {"config": current_solid_config.get("config")})
            if not config_mapped_solid_config.success:
                raise DagsterInvalidConfigError(
                    "Error in config for solid {}".format(solid.name),
                    config_mapped_solid_config.errors,
                    config_mapped_solid_config,
                )

            complete_config_object = merge_dicts(
                current_solid_config, config_mapped_solid_config.value)
            yield SolidConfigEntry(
                current_handle, SolidConfig.from_dict(complete_config_object))
            continue

        graph_def = check.inst(solid.definition, GraphDefinition)

        yield SolidConfigEntry(
            current_handle,
            SolidConfig.from_dict({
                "inputs":
                current_solid_config.get("inputs"),
                "outputs":
                current_solid_config.get("outputs"),
            }),
        )

        # If there is a config mapping, invoke it and get the descendent solids
        # config that way. Else just grabs the solids entry of the current config
        solids_dict = (_get_mapped_solids_dict(
            solid, graph_def, current_stack, current_solid_config,
            resource_defs) if graph_def.config_mapping else
                       current_solid_config.get("solids", {}))

        yield from _composite_descent(current_stack, solids_dict,
                                      resource_defs)
Example #21
0
def _composite_descent(parent_stack, solids_config_dict):
    '''
    The core implementation of composite_descent. This yields a stream of
    SolidConfigEntry. This is used by composite_descent to construct a
    dictionary.

    It descends over the entire solid hierarchy, constructing an entry
    for every handle. If it encounters a composite solid instance
    with a config mapping, it will invoke that config mapping fn,
    producing the config that is necessary to configure the child solids.

    This process unrolls recursively as you descend down the tree.
    '''

    for solid in parent_stack.current_container.solids:

        current_stack = parent_stack.descend(solid)
        current_handle = current_stack.handle

        current_solid_config = solids_config_dict.get(solid.name, {})

        # the base case
        if not isinstance(solid.definition, CompositeSolidDefinition):
            transformed_current_solid_config = solid.definition.apply_config_mapping(
                current_solid_config)
            if not transformed_current_solid_config.success:
                raise DagsterInvalidConfigError(
                    'Error in config for solid {}'.format(solid.name),
                    transformed_current_solid_config.errors,
                    transformed_current_solid_config,
                )

            yield SolidConfigEntry(
                current_handle,
                SolidConfig.from_dict(transformed_current_solid_config.value))
            continue

        composite_def = check.inst(solid.definition, CompositeSolidDefinition)

        yield SolidConfigEntry(
            current_handle,
            SolidConfig.from_dict({
                'inputs':
                current_solid_config.get('inputs'),
                'outputs':
                current_solid_config.get('outputs'),
            }),
        )

        # If there is a config mapping, invoke it and get the descendent solids
        # config that way. Else just grabs the solids entry of the current config
        solids_dict = (_get_mapped_solids_dict(composite_def, current_stack,
                                               current_solid_config)
                       if composite_def.config_mapping else
                       current_solid_config.get('solids', {}))

        for sce in _composite_descent(current_stack, solids_dict):
            yield sce
Example #22
0
def dagster_instance_config(base_dir):
    dagster_config_dict = load_yaml_from_globs(
        os.path.join(base_dir, DAGSTER_CONFIG_YAML_FILENAME))
    dagster_config_type = define_dagster_config_cls().inst()
    dagster_config = evaluate_config(dagster_config_type, dagster_config_dict)
    if not dagster_config.success:
        raise DagsterInvalidConfigError(None, dagster_config.errors,
                                        dagster_config_dict)
    return dagster_config.value
Example #23
0
def _get_mapped_solids_dict(
    composite,
    graph_def,
    current_stack,
    current_solid_config,
    resource_defs,
    is_using_graph_job_op_apis,
):
    # the spec of the config mapping function is that it takes the dictionary at:
    # solid_name:
    #    config: {dict_passed_to_user}

    # and it returns the dictionary rooted at solids
    # solid_name:
    #    solids: {return_value_of_config_fn}

    # We must call the config mapping function and then validate it against
    # the child schema.

    # apply @configured config mapping to the composite's incoming config before we get to the
    # composite's own config mapping process
    config_mapped_solid_config = graph_def.apply_config_mapping(
        current_solid_config)
    if not config_mapped_solid_config.success:
        raise DagsterInvalidConfigError(
            "Error in config for composite solid {}".format(composite.name),
            config_mapped_solid_config.errors,
            config_mapped_solid_config,
        )

    with user_code_error_boundary(DagsterConfigMappingFunctionError,
                                  _get_error_lambda(current_stack)):
        mapped_solids_config = graph_def.config_mapping.resolve_from_validated_config(
            config_mapped_solid_config.value.get("config", {}))

    # Dynamically construct the type that the output of the config mapping function will
    # be evaluated against

    type_to_evaluate_against = define_solid_dictionary_cls(
        solids=graph_def.solids,
        ignored_solids=None,
        dependency_structure=graph_def.dependency_structure,
        parent_handle=current_stack.handle,
        resource_defs=resource_defs,
        is_using_graph_job_op_apis=is_using_graph_job_op_apis,
    )

    # process against that new type

    evr = process_config(type_to_evaluate_against, mapped_solids_config)

    if not evr.success:
        raise_composite_descent_config_error(current_stack,
                                             mapped_solids_config, evr)

    return evr.value
Example #24
0
def dagster_instance_config(base_dir, config_filename=DAGSTER_CONFIG_YAML_FILENAME, overrides=None):
    overrides = check.opt_dict_param(overrides, 'overrides')
    dagster_config_dict = merge_dicts(
        load_yaml_from_globs(os.path.join(base_dir, config_filename)), overrides
    )
    dagster_config_type = define_dagster_config_cls().inst()
    dagster_config = evaluate_config(dagster_config_type, dagster_config_dict)
    if not dagster_config.success:
        raise DagsterInvalidConfigError(None, dagster_config.errors, dagster_config_dict)
    return dagster_config.value
Example #25
0
def ensure_workspace_config(workspace_config, yaml_path):
    check.dict_param(workspace_config, 'workspace_config')
    check.str_param(yaml_path, 'yaml_path')

    validation_result = process_workspace_config(workspace_config)
    if not validation_result.success:
        raise DagsterInvalidConfigError(
            'Errors while loading workspace config at {}.'.format(yaml_path),
            validation_result.errors,
            workspace_config,
        )
    return validation_result
Example #26
0
File: cli.py Project: keyz/dagster
def get_validated_config(config_yaml=None):
    config_type = celery_executor.config_schema.config_type
    config_value = get_config_value_from_yaml(config_yaml)
    config = validate_config(config_type, config_value)
    if not config.success:
        raise DagsterInvalidConfigError(
            "Errors while loading Celery executor config at {}.".format(
                config_yaml),
            config.errors,
            config_value,
        )
    return post_process_config(config_type, config_value).value
Example #27
0
def ensure_workspace_config(workspace_config, yaml_path):
    check.str_param(yaml_path, "yaml_path")
    check.dict_param(workspace_config, "workspace_config")

    validation_result = process_workspace_config(workspace_config)
    if not validation_result.success:
        raise DagsterInvalidConfigError(
            "Errors while loading workspace config from {yaml_path}.".format(
                yaml_path=os.path.abspath(yaml_path)),
            validation_result.errors,
            workspace_config,
        )
    return validation_result
Example #28
0
def _get_mapped_resource_config(
        resource_defs: Dict[str, ResourceDefinition],
        run_config: Dict[str, Any]) -> Dict[str, ResourceConfig]:
    resource_config_schema = define_resource_dictionary_cls(resource_defs)
    config_evr = process_config(resource_config_schema, run_config)
    if not config_evr.success:
        raise DagsterInvalidConfigError(
            "Error in config for resources ",
            config_evr.errors,
            run_config,
        )
    config_value = config_evr.value
    return config_map_resources(resource_defs, config_value)
Example #29
0
def config_map_executor(
    executor_config: Dict[str, Any],
    executor_def: ExecutorDefinition,
) -> Dict[str, Any]:
    executor_config_evr = executor_def.apply_config_mapping(executor_config)
    if not executor_config_evr.success:
        raise DagsterInvalidConfigError(
            f"Invalid configuration provided for executor '{executor_def.name}'",
            executor_config_evr.errors,
            executor_config,
        )

    return {executor_def.name: executor_config_evr.value}
Example #30
0
def get_k8s_resource_requirements(tags):
    check.inst_param(tags, "tags", frozentags)
    check.invariant(K8S_RESOURCE_REQUIREMENTS_KEY in tags)

    resource_requirements = json.loads(tags[K8S_RESOURCE_REQUIREMENTS_KEY])
    result = validate_config(K8S_RESOURCE_REQUIREMENTS_SCHEMA, resource_requirements)

    if not result.success:
        raise DagsterInvalidConfigError(
            "Error in tags for {}".format(K8S_RESOURCE_REQUIREMENTS_KEY), result.errors, result,
        )

    return result.value