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 optional subdirectory for stage in application package #1916

Open
wants to merge 41 commits into
base: main
Choose a base branch
from

Conversation

sfc-gh-pjafari
Copy link
Contributor

@sfc-gh-pjafari sfc-gh-pjafari commented Dec 3, 2024

Pre-review checklist

  • I've confirmed that instructions included in README.md are still correct after my changes in the codebase.
  • I've added or updated automated unit tests to verify correctness of my new code.
  • I've added or updated integration tests to verify correctness of my new code.
  • I've confirmed that my changes are working by executing CLI's commands manually on MacOS.
  • I've confirmed that my changes are working by executing CLI's commands manually on Windows.
  • I've confirmed that my changes are up-to-date with the target branch.
  • I've described my changes in the release notes.
  • I've described my changes in the section below.

Changes description

Added an optional stage_subdirectory to the application package.
This change means that the root of the stage is no longer the assumed path to finding the artifacts and the manifest.yml. If specified, the application package will only ever operate on stage/stage_subdirectory. This includes deploy, diff, and version create.

For detailed information on how this affects each command, if specified, refer to the design document.

@sfc-gh-pjafari sfc-gh-pjafari changed the title Pj subartifacts subdirs subdirs Dec 3, 2024
@sfc-gh-pjafari sfc-gh-pjafari marked this pull request as ready for review December 5, 2024 16:35
@sfc-gh-pjafari sfc-gh-pjafari requested review from a team as code owners December 5, 2024 16:35
@sfc-gh-pjafari sfc-gh-pjafari changed the title subdirs Add optional subdirectory for stage in application package Dec 5, 2024
Comment on lines +241 to +242
# TODO: get all apps created from this application package from snowflake, compare, confirm and drop.
# TODO: add messaging/confirmation here for extra apps found as part of above
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept current teardown functionality. Next PR, we will get and drop all app objects created from this package, not just what's listed in pdf.

Comment on lines +107 to +115
diff = ws.perform_action(
package_id,
EntityActions.BUNDLE,
)
stage_fqn = f"{package.fqn.name}.{package.stage}"
diff: DiffResult = compute_stage_diff(
local_root=Path(package.deploy_root), stage_fqn=stage_fqn
EntityActions.DIFF,
print_to_console=cli_context.output_format != OutputFormat.JSON,
)
if cli_context.output_format == OutputFormat.JSON:
return ObjectResult(diff.to_dict())
else:
print_diff_to_console(diff, bundle_map)
return None # don't print any output

return None
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made diff its own entity action. action_diff

@sfc-gh-pjafari sfc-gh-pjafari changed the base branch from main to pj-template-processor-error December 5, 2024 18:15
@sfc-gh-pjafari sfc-gh-pjafari changed the base branch from pj-template-processor-error to main December 5, 2024 18:33
RELEASE-NOTES.md Outdated Show resolved Hide resolved
Comment on lines 221 to 222
@property
def scratch_stage_fqn(self) -> str:
return f"{self.name}.{self._entity_model.scratch_stage}"
def stage_path(self) -> DefaultStagePathParts:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should it be cached? I don't expect recreating is too expensive, but ...

@@ -592,7 +592,7 @@ def upgrade_application(

@param name: Name of the application object
@param install_method: Method of installing the application
@param stage_fqn: FQN of the stage housing the application artifacts
@param stage_path_to_artifacts: Path to directory in stage housing the application artifacts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it's called version directory in the public docs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I debated using that but I didn't. updated.

tests/nativeapp/test_application_package_entity.py Outdated Show resolved Hide resolved
tests/stage/test_diff.py Outdated Show resolved Hide resolved
for package_entity in project.get_entities_by_type(
ApplicationPackageEntityModel.get_type()
).values()
if package_entity.identifier == app_package_entity.identifier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work with identifiers (quoted/unquoted)? or is this == overridden to do that, and if so, identifier could be String or Identifier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternatively, you can take a look into package_entity.fqn

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, replaced with same_identifier

@@ -123,6 +128,11 @@ class ApplicationPackageEntityModel(EntityModelBase):
default="",
)

stage_subdirectory: Optional[str] = Field(
title="Subfolder in stage",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we elaborate a bit here? something along the line: "Subfolder in stage where artifacts are uploaded"?

return (
self.project_root
/ self._entity_model.deploy_root
/ self._entity_model.stage_subdirectory
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this changing? Seems like an unnecessary complication

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deploy_root is now deploy_root/subdir. So all the actions (bundle, deploy, etc) that need to refer to deploy root to write/read artifacts look at this subdir.
I don't think I understand what you mean by unnecessary complication.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean why is the deploy root now deploy_root/subdir?

def scratch_stage_fqn(self) -> str:
return f"{self.name}.{self._entity_model.scratch_stage}"
@cached_property
def stage_path(self) -> DefaultStagePathParts:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: I know DefaultStagePathParts existed before - but would have been simpler if named something like "StageLocation" or "StagePath"

stage_fqn = f"{self.name}.{self._entity_model.stage}"
subdir = self._entity_model.stage_subdirectory
full_path = f"{stage_fqn}/{subdir}" if subdir else stage_fqn
return DefaultStagePathParts(full_path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could also accept different constructors in DefaultStagePathParts, so DefaultStagePathParts to undo the concat.

"""
@param path: file path on the stage.
@param stage_subirectory: subdirectory of stage.
@return: path of file relative to the stage_path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no stage_path in input.

Comment on lines 92 to 93
wo_stage_name = StagePathType(*path.split("/")[1:])
relative_path = str(wo_stage_name).removeprefix(stage_subirectory).lstrip("/")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't tell what these are doing. I see the first command removing first part left of /, and the second line doing something similar.

  • Adding comments might help.
  • Curious if Path can somehow be leveraged to calculate relative path instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment and updated method.
Can't use Path at all since stage name can be a quoted identifier which can have any character including a "/" in its name

"""
Returns a mapping of relative stage paths to their md5sums.
Returns a mapping of file paths to their md5sums. File paths are relative to the stage_path.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto about stage_path

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, thank you

"""
Diffs the files in a stage with a local folder.
Diffs the files in the local_root with files in the stage path that is stage_path_parts's full_path.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stage_path_parts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

stage_name = self.stage.split(".")[-1]
match = re.fullmatch(STAGE_PATH_REGEX, stage_path)
if match is None:
raise ClickException("Invalid stage path")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this validation should be done in Pydantic model. For users, this might not be clear where this exception is coming from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have validations in the pydantic model for NA. this is for other uses of this stage object.

@@ -0,0 +1,31 @@
definition_version: 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we use PDF factory instead of these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you caught me haha
I didn't want to re-write a bunch of our tests or add more fixtures to accommodate them. I understand that is not the greatest pattern but sadly the factories work way better for unit tests and have been harder to adopt for integ tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not asking to rewrite existing tests. New tests added 2 new directories (napp_stage_subdirs*). We shouldn't add any more of these test directories if the factory can do that.

src/snowflake/cli/_plugins/nativeapp/commands.py Outdated Show resolved Hide resolved
@@ -0,0 +1,31 @@
definition_version: 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not asking to rewrite existing tests. New tests added 2 new directories (napp_stage_subdirs*). We shouldn't add any more of these test directories if the factory can do that.

@@ -254,15 +255,15 @@ def get_account_event_table(self, role: str | None = None) -> str | None:
def create_version_in_package(
self,
package_name: str,
stage_fqn: str,
path_to_version_directory: str,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am confused about the naming here. Why we mention version_directory here (while the rest uses stage_subdirectory), and doesn't this also include stage info? Is stage_fqn not accurate anymore? Or stage_with_subdirectory? or StageParts class?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or stage_full_path

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path_to_version_directory is the naming used in the official docs for these two operations, so the facade naming is matching that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants