Skip to content

Commit

Permalink
Added native app templates
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-bdufour committed Sep 26, 2024
1 parent 0c22e2d commit 7b620d5
Show file tree
Hide file tree
Showing 63 changed files with 1,328 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
* @snowflakedb/snowcli

app_* @snowflakedb/nade
2 changes: 2 additions & 0 deletions app_basic/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
snowflake.local.yml
output/**
17 changes: 17 additions & 0 deletions app_basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Introduction

This is the basic project template for a Snowflake Native App project. It contains minimal code meant to help you set up your first application object in your account quickly.

### Project Structure
| File Name | Purpose |
| --------- | ------- |
| README.md | The current file you are looking at, meant to guide you through a Snowflake Native App project. |
| app/setup_script.sql | Contains SQL statements that are run when an account installs or upgrades a Snowflake Native App. |
| app/manifest.yml | Defines properties required by the application package. Find more details at the [Manifest Documentation.](https://docs.snowflake.com/en/developer-guide/native-apps/creating-manifest)
| app/README.md | Exposed to the account installing the Snowflake Native App with details on what it does and how to use it. |
| snowflake.yml | Used by the Snowflake CLI tool to discover your project's code and interact with your Snowflake account with all relevant prvileges and grants. |

### Adding a snowflake.local.yml file
Though your project directory already comes with a `snowflake.yml` file, an individual developer can choose to customize the behavior of the Snowflake CLI by providing local overrides to `snowflake.yml`, such as a new role to test out your own application package. This is where you can use `snowflake.local.yml`, which is not a version-controlled file.

For more information, please refer to the Snowflake Documentation on installing and using Snowflake CLI to create a Snowflake Native App.
20 changes: 20 additions & 0 deletions app_basic/app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Welcome to your First Snowflake Native App!

In this Snowflake Native App, you will be able to explore some basic concepts such as application role, versioned schemas and creating procedures and functions within a setup script.

For more information about a Snowflake Native App, please read the [official Snowflake documentation](https://docs.snowflake.com/en/developer-guide/native-apps/native-apps-about) which goes in depth about many additional functionalities of this framework.

## Using the application after installation
To interact with the application after it has successfully installed in your account, switch to the application owner role first.

### Calling a stored procedure

```
CALL <your_application_name>.<schema_name>.<stored_procedure_name_with_args>;
```

### Calling a function

```
SELECT <your_application_name>.<schema_name>.<udf_with_args>;
```
9 changes: 9 additions & 0 deletions app_basic/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This is a manifest.yml file, a required component of creating a Snowflake Native App.
# This file defines properties required by the application package, including the location of the setup script and version definitions.
# Refer to https://docs.snowflake.com/en/developer-guide/native-apps/creating-manifest for a detailed understanding of this file.

manifest_version: 1

artifacts:
setup_script: setup_script.sql
readme: README.md
11 changes: 11 additions & 0 deletions app_basic/app/setup_script.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- This is the setup script that runs while installing a Snowflake Native App in a consumer account.
-- To write this script, you can familiarize yourself with some of the following concepts:
-- Application Roles
-- Versioned Schemas
-- UDFs/Procs
-- Extension Code
-- Refer to https://docs.snowflake.com/en/developer-guide/native-apps/creating-setup-script for a detailed understanding of this file.

CREATE OR ALTER VERSIONED SCHEMA core;

-- The rest of this script is left blank for purposes of your learning and exploration.
29 changes: 29 additions & 0 deletions app_basic/snowflake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This is a project definition file, a required component if you intend to use Snowflake CLI in a project directory such as this template.
<!! if snowflake_cli_version < "3.0.0" !!>
definition_version: 1
native_app:
name: <! project_name | to_snowflake_identifier !>
source_stage: app_src.stage
artifacts:
- src: app/*
dest: ./
<!! else !!>
definition_version: 2
entities:
pkg:
type: application package
identifier: <% fn.concat_ids('<! project_name | to_snowflake_identifier !>_pkg', ctx.env.suffix) %>
manifest: app/manifest.yml
artifacts:
- src: app/*
dest: ./

app:
type: application
from:
target: pkg
identifier: <% fn.concat_ids('<! project_name | to_snowflake_identifier !>', ctx.env.suffix) %>

env:
suffix: <% fn.concat_ids('_', fn.sanitize_id(fn.get_username('unknown_user')) | lower) %>
<!! endif !!>
8 changes: 8 additions & 0 deletions app_basic/template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
minimum_cli_version: "2.8.0"
files_to_render:
- snowflake.yml
variables:
- name: project_name
prompt: "Project identifier"
default: my_native_app_project
type: string
2 changes: 2 additions & 0 deletions app_spcs_basic/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
snowflake.local.yml
output/**
82 changes: 82 additions & 0 deletions app_spcs_basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Instructions

## Prerequisites

1. Install Docker
- [Windows](https://docs.docker.com/desktop/install/windows-install/)
- [Mac](https://docs.docker.com/desktop/install/mac-install/)
- [Linux](https://docs.docker.com/desktop/install/linux-install/)


## Set your SnowCLI connection (optional)

We use your default connection to connect to Snowflake and deploy the images / app. Set your
default connection by modifying your `config.toml` file or by exporting the following environment variable:

```sh
export SNOWFLAKE_DEFAULT_CONNECTION_NAME=<your connection name>
```

## Create image repository, build and push your local service image

The [service/](service/) directory contains a [Dockerfile](service/Dockerfile) that builds a
simple Python server that responds to GET health checks, a GET for `/index.html`, as well as
POSTing to `/echo` in the Snowflake External Function payload format. You can build it and
push it to an image repository called `SPCS_NA.PUBLIC.IMAGES` in your account like so:

```sh
./build-and-push.sh
```

This command will always use your default SnowCLI connection.

## Deploy the application

Deploy the app package and instance as such:

```sh
snow app run
```

> Take note of the name of the application, which is based on the name you chose when you initialized this project. The application object and package are, by default, automatically suffixed by your (local) user name (i.e. `$USER`).
## Setup the application

When the application is opened for the first time, you will be prompted to grant the following account-level privileges to it:

- CREATE COMPUTE POOL
- BIND SERVICE ENDPOINT

Click on the `Grant` button to proceed.

## Activate the application

Once privileges are granted, a new `Activate` button should appear. Click the button and wait until the application is fully activated.
The `Activate` button invokes the `grant_callback` defined in the [manifest.yml](app/manifest.yml) file, which then creates the `COMPUTE POOL` and `SERVICE` needed to launch the application.

## Launch the application

Once all services and pools are created, you will be able to launch the app by clicking on the `Launch App` button. This will navigate to the URL provided by the `Service` and `Endpoint` defined in the `default_web_endpoint` in the [manifest.yml](app/manifest.yml). You will see the contents of [index.html](service/index.html) as served by the application container.

## Test out the echo service

```sh
snow sql -q "select <app name>.services.echo('Hello world!')"
```

You should see the same text back (Hello world!).

## Clean up

You can stop the service and drop the compute pool without dropping the application by running the following statement:

```sh
snow sql -q "call <app name>.setup.drop_service_and_pool()"
```

Optionally, you can remove the app + package altogether afterwards:

```sh
snow app teardown --cascade
```
> Version `2.4.0+` of Snowflake CLI should be installed in order to execute `--cascade` command.
3 changes: 3 additions & 0 deletions app_spcs_basic/app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Snowflake Native App - SPCS

This is a sample Snowflake Native App deployed using Snowpark Container Services.
33 changes: 33 additions & 0 deletions app_spcs_basic/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# For more information on creating manifest, go to https://docs.snowflake.com/en/developer-guide/native-apps/creating-manifest
manifest_version: 1

version:
name: Dev
label: "Dev Version"
comment: "Default version used for development. Override for actual deployment."

configuration:
log_level: INFO
trace_level: ALWAYS
grant_callback: setup.create_service

artifacts:
readme: README.md

default_web_endpoint:
service: services.spcs_na_service
endpoint: my-endpoint

setup_script: setup.sql

container_services:
images:
- /spcs_na_db/public/images/spcs_na_service:latest

privileges:
- CREATE COMPUTE POOL:
required_at_setup: true
description: "Permission to create compute pools"
- BIND SERVICE ENDPOINT:
required_at_setup: true
description: "Required to create endpoints in services we can assign to functions"
13 changes: 13 additions & 0 deletions app_spcs_basic/app/service_spec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
spec:
container:
- name: my-service
image: /spcs_na_db/public/images/spcs_na_service:latest
env:
PORT: 8080
readinessProbe:
port: 8080
path: /healthcheck
endpoint:
- name: my-endpoint
port: 8080
public: true
100 changes: 100 additions & 0 deletions app_spcs_basic/app/services.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
-- namespace under which our services and their functions will live
create schema if not exists services;

-- namespace for service administration
create or alter versioned schema setup;

-- creates a compute pool, service, and service function
create or replace procedure setup.create_service(privileges ARRAY)
returns varchar
language sql
execute as owner
as $$
begin
let pool_name := (select current_database()) || '_app_pool';

create compute pool if not exists identifier(:pool_name)
MIN_NODES = 1
MAX_NODES = 1
INSTANCE_FAMILY = CPU_X64_XS;

create service if not exists services.spcs_na_service
in compute pool identifier(:pool_name)
from spec='service_spec.yml';

grant usage on service services.spcs_na_service
to application role app_public;

create or replace function services.echo(payload varchar)
returns varchar
service = services.spcs_na_service
endpoint = 'my-endpoint'
max_batch_rows = 50
AS '/echo';

grant usage on function services.echo(varchar)
to application role app_public;

return 'Done';
end;
$$;
grant usage on procedure setup.create_service(ARRAY)
to application role app_public;

create or replace procedure setup.suspend_service()
returns varchar
language sql
execute as owner
as $$
begin
alter service services.spcs_na_service suspend;
return 'Done';
end;
$$;
grant usage on procedure setup.suspend_service()
to application role app_public;

create or replace procedure setup.resume_service()
returns varchar
language sql
execute as owner
as $$
begin
alter service services.spcs_na_service resume;
return 'Done';
end;
$$;
grant usage on procedure setup.resume_service()
to application role app_public;

create or replace procedure setup.drop_service_and_pool()
returns varchar
language sql
execute as owner
as $$
begin
let pool_name := (select current_database()) || '_app_pool';
drop service if exists services.spcs_na_service;
drop compute pool if exists identifier(:pool_name);
return 'Done';
end;
$$;
grant usage on procedure setup.drop_service_and_pool()
to application role app_public;

create or replace procedure setup.service_status()
returns varchar
language sql
execute as owner
as $$
declare
service_status varchar;
begin
call system$get_service_status('services.spcs_na_service') into :service_status;
return parse_json(:service_status)[0]['status']::varchar;
end;
$$;
grant usage on procedure setup.service_status()
to application role app_public;

grant usage on schema setup to application role app_public;
2 changes: 2 additions & 0 deletions app_spcs_basic/app/setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
create application role if not exists app_public;
execute immediate from './services.sql';
19 changes: 19 additions & 0 deletions app_spcs_basic/build-and-push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
DB_NAME="spcs_na_db"
SCHEMA_NAME="public"
IMAGE_REPO_NAME="images"
SERVICE_NAME="spcs_na_service" # service_spec.yml and manifest.yml needs to agree with this name as it is the service and image name.
DIR_NAME="./service"

# make sure the target repository exists
snow sql -q "create database if not exists $DB_NAME"
snow sql -q "create schema if not exists $DB_NAME.$SCHEMA_NAME"
snow sql -q "create image repository if not exists $DB_NAME.$SCHEMA_NAME.$IMAGE_REPO_NAME"

IMAGE_REPO_URL=$(snow spcs image-repository url $IMAGE_REPO_NAME --database $DB_NAME --schema $SCHEMA_NAME)
IMAGE_FQN="$IMAGE_REPO_URL/$SERVICE_NAME"

# build and push the image (uses :latest implicitly)
docker buildx build --platform=linux/amd64 -t $IMAGE_FQN $DIR_NAME
snow spcs image-registry login
docker image push $IMAGE_FQN
12 changes: 12 additions & 0 deletions app_spcs_basic/service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3-alpine

RUN mkdir /web
COPY index.html /web
COPY main.py /web

# default port is 8080
EXPOSE 8080

WORKDIR /web

CMD ["python", "main.py"]
10 changes: 10 additions & 0 deletions app_spcs_basic/service/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>Welcome to SPCS + NA</title>
</head>
<body>
<h1>Hello World!</h1>
<p>We can show a container-based UI!</p>
</body>
</html>
Loading

0 comments on commit 7b620d5

Please sign in to comment.