Einen Snowpark Container Services-Dienst erstellen, der Aufruferrechte nutzt¶
Einführung¶
In diesem Tutorial lernen Sie die Erstellung eines Dienstes kennen, der ein Web-UI präsentiert, die das Feature der Aufruferrechte bei der Ausführung von SQL-Abfragen im Namen der Benutzer verwendet.
Sie erstellen einen Dienst (namens query_service
), der eine in der Anfrage angegebene Abfrage ausführt. Standardmäßig verbinden sich Anwendungscontainer mit Snowflake als Benutzer des Dienstes mit der Eigentümerrolle des Dienstes. Diese Anwendung verwendet jedoch das Feature der Aufruferrechte, um sich mit dem Dienstendpunkt als Benutzer zu verbinden und die diesem Benutzer erteilten Berechtigungen zu nutzen.
Beim Testen verwenden Sie den Dienst von einem Webbrowser aus, da das Feature der Aufruferrechte nur beim Zugriff auf einen Dienst über den Netzwerkeingang unterstützt wird. Das Feature der Aufruferrechte ist nicht verfügbar, wenn Sie einen Dienst über eine Dienstfunktion aufrufen.
Der Dienst macht Folgendes:
Zeigt einen öffentlichen Endpunkt an.
Wenn sich ein Benutzer am Endpunkt anmeldet, stellt der Dienst eine Web-UI zur Verfügung, um eine Abfrage bereitzustellen. Der Dienst führt die Abfrage in Snowflake aus und gibt die Ergebnisse zurück. In diesem Tutorial führen Sie den folgenden SQL-Befehl aus:
SELECT CURRENT_USER(), CURRENT_ROLE();
Der Befehl gibt den Namen des derzeit angemeldeten Benutzers und die derzeit aktive Rolle zurück, die beide davon abhängen, ob Aufruferrechte verwendet werden.
Wenn Aufruferrechte verwendet werden, verbindet sich der Dienst mit Snowflake als aufrufender Benutzer und mit der Standardrolle des Benutzers. Der Befehl gibt Ihren Benutzernamen und Ihre Standardrolle zurück.
Wenn die Aufruferrechte nicht verwendet werden, tritt das Standardverhalten in Kraft, bei dem der Dienst sich mit Snowflake als Dienstbenutzer und mit der Eigentümerrolle des Dienstes verbindet. Daher gibt der Befehl den Namen des Dienstbenutzers in der folgenden Form zurück:
SF$SERVICE$unique-id
,TEST_ROLE
.
Dieses Tutorial besteht aus zwei Teilen:
Teil 1: Dienst erstellen und testen. Sie laden den für dieses Tutorial bereitgestellten Code herunter und befolgen die schrittweise Anleitung:
Dienstcode für dieses Tutorial herunterladen
Docker-Image für Snowpark Container Services erstellen und Image in Repository im eigenen Konto hochladen
Erstellen Sie einen Dienst.
Kommunizieren Sie mit dem Dienst über Netzwerkeingang, um sich mit dem öffentlichen Endpunkt zu verbinden, den der Dienst bereitstellt. Mit einem Webbrowser melden Sie sich bei dem öffentlichen Endpunkt an und führen den Befehl SELECT CURRENT_USER(); aus. Überprüfen Sie die Befehlsausgabe, um sicherzustellen, dass der Container den Befehl als angemeldeter Benutzer ausgeführt hat.
Teil 2: Erläuterungen zum Dienst. Dieser Abschnitt bietet einen Überblick über den Dienstcode und zeigt auf, wie der Anwendungscode die Aufruferrechte nutzt.
Vorbereitung¶
Führen Sie die Grundlegende Einrichtung durch, um die Voraussetzungen zu konfigurieren und Snowflake-Ressourcen zu erstellen, die für alle Snowpark Container Services-Tutorials in dieser Dokumentation erforderlich sind.
Dienstcode herunterladen¶
Zum Erstellen des Abfragedienstes wird ein Code (eine Python-Anwendung) bereitgestellt.
Laden Sie
SnowparkContainerServices-Tutorials.zip
herunter.Entpacken Sie den Inhalt, der ein Verzeichnis für jedes Tutorial enthält. Das Verzeichnis
Tutorial-6-callers-rights
enthält die folgenden Dateien:Dockerfile
main.py
templates/basic_ui.html
Image erstellen und hochladen¶
Erstellen Sie ein Image für die linux/amd64-Plattform, die von Snowpark Container Services unterstützt wird, und laden Sie das Image dann in das Image-Repository in Ihrem Konto hoch (siehe Grundlegende Einrichtung).
Sie benötigen Informationen zum Repository (die Repository-URL und den Hostnamen der Registry), bevor Sie das Image erstellen und hochladen können. Weitere Informationen dazu finden Sie unter Registry und Repositorys.
Informationen zum Repository abrufen
Um die Repository-URL zu erhalten, führen Sie den SQL-Befehl SHOW IMAGE REPOSITORIES aus.
SHOW IMAGE REPOSITORIES;
Die URL ist in der Spalte
repository_url
der Ausgabe enthalten. Siehe folgendes Beispiel:<orgname>-<acctname>.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository
Der Hostname in der Repository-URL ist der Name des Registry-Hosts. Siehe folgendes Beispiel:
<orgname>-<acctname>.registry.snowflakecomputing.com
Image erstellen und in das Repository hochladen
Öffnen Sie ein Terminalfenster, und wechseln Sie in das Verzeichnis, das die entpackten Dateien enthält.
Um ein Docker-Image zu erstellen, führen Sie den folgenden Befehl
docker build
mithilfe der Docker-CLI aus. Beachten Sie, dass der Befehl das aktuelle Verzeichnis (.
) alsPATH
für die Dateien angibt, die zum Erstellen des Images verwendet werden sollen.docker build --rm --platform linux/amd64 -t <repository_url>/<image_name> .
Für
image_name
verwenden Siequery_service:latest
:
Beispiel
docker build --rm --platform linux/amd64 -t myorg-myacct.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository/query_service:latest .
Laden Sie das Image in das Repository in Ihrem Snowflake-Konto hoch. Damit Docker ein Image in Ihrem Namen in Ihr Repository hochladen kann, müssen Sie Docker zunächst mit Snowflake authentifizieren.
Um Docker in der Snowflake-Registry zu authentifizieren, führen Sie den folgenden Befehl aus:
docker login <registry_hostname> -u <username>
Geben Sie dabei für
username
Ihren Snowflake-Benutzernamen an. Docker fordert Sie zur Eingabe Ihres Kennworts auf.
Führen Sie den folgenden Befehl aus, um das Image hochzuladen:
docker push <repository_url>/<image_name>
Beispiel
docker push myorg-myacct.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository/query_service:latest
Dienst erstellen¶
In diesem Abschnitt erstellen Sie einen Dienst (query_service).
Überprüfen Sie, ob der Computepool bereit ist und ob Sie sich im korrekten Kontext befinden, um den Dienst zu erstellen.
Zuvor haben Sie den Kontext im Schritt Grundlegende Einrichtung festgelegt. Um sicherzustellen, dass Sie sich in diesem Schritt im korrekten Kontext für die SQL-Anweisungen befinden, führen Sie Folgendes aus:
USE ROLE test_role; USE DATABASE tutorial_db; USE SCHEMA data_schema; USE WAREHOUSE tutorial_warehouse;
Um sicherzustellen, dass der unter Grundlegende Einrichtung erstellte Computepool bereit ist, führen Sie
DESCRIBE COMPUTE POOL
aus, und überprüfen Sie dann, obstate
den WertACTIVE
oderIDLE
hat. Wenn diestate
den WertSTARTING
hat, müssen Sie warten, bisstate
entwederACTIVE
oderIDLE
ist.
DESCRIBE COMPUTE POOL tutorial_compute_pool;
Um den Dienst zu erstellen, führen Sie mit der Rolle
test_role
den folgenden Befehl aus:CREATE SERVICE query_service IN COMPUTE POOL tutorial_compute_pool FROM SPECIFICATION $$ spec: containers: - name: main image: /tutorial_db/data_schema/tutorial_repository/query_service:latest env: SERVER_PORT: 8000 readinessProbe: port: 8000 path: /healthcheck endpoints: - name: execute port: 8000 public: true capabilities: securityContext: executeAsCaller: true serviceRoles: - name: ui_usage endpoints: - execute $$;
Bemerkung
Wenn bereits ein Dienst mit diesem Namen existiert, verwenden Sie den Befehl DROP SERVICE, um den zuvor erstellten Dienst zu löschen, und erstellen Sie dann diesen Dienst.
Führen Sie die folgenden SQL-Befehle aus, um detaillierte Informationen zu dem gerade erstellten Dienst zu erhalten. Weitere Informationen dazu finden Sie unter Snowpark Container Services: Verwenden von Diensten.
Um die Dienste in Ihrem Konto aufzulisten, führen Sie den Befehl SHOW SERVICES aus:
SHOW SERVICES;
Um den Status Ihres Dienstes abzurufen, führen Sie den Befehl SHOW SERVICE CONTAINERS IN SERVICE aus:
SHOW SERVICE CONTAINERS IN SERVICE query_service;
Um Informationen zu Ihrem Dienst zu erhalten, führen Sie den Befehl DESCRIBE SERVICE aus:
DESCRIBE SERVICE query_service;
Dienst verwenden¶
In diesem Abschnitt überprüfen Sie, ob die Aufruferrechte für den Dienst funktionieren. Sie melden sich über einen Browser beim öffentlichen Endpunkt an, führen eine Abfrage aus und überprüfen, ob die vom Dienst erstellte Snowflake-Sitzung als aufrufender Benutzer und nicht als Dienstbenutzer arbeitet.
Um den Kontext für die SQL-Anweisungen in diesem Abschnitt einzurichten, führen Sie zunächst Folgendes aus:
USE ROLE test_role;
USE DATABASE tutorial_db;
USE SCHEMA data_schema;
USE WAREHOUSE tutorial_warehouse;
Der Dienst stellt einen öffentlichen Endpunkt zur Verfügung (siehe die Inline-Spezifikation im Befehl CREATE SERVICE ). Melden Sie sich daher zunächst mit einem Webbrowser bei dem Endpunkt an und verwenden Sie dann die Web-UI, die der Dienst dem Internet zur Verfügung stellt, um Abfragen an den Dienstendpunkt zu senden.
Suchen Sie die URL des öffentlichen Endpunkts, den der Dienst bereitstellt:
SHOW ENDPOINTS IN SERVICE query_service;
Die Spalte
ingress_url
in der Antwort enthält die URL.Beispiel
p6bye-myorg-myacct.snowflakecomputing.app
Hängen Sie
/ui
an die Endpunkt-URL an, und geben Sie diese Zeichenfolge in den Webbrowser ein. Dies veranlasst den Dienst, die Funktionui()
auszuführen (siehemain.py
).Beachten Sie, dass Sie beim ersten Zugriff auf den Endpunkt-URL aufgefordert werden, sich bei Snowflake anzumelden.
Verwenden Sie denselben Benutzer, den Sie zum Erstellen des Dienstes verwendet haben. Nach erfolgreicher Anmeldung zeigt der Dienst das folgende Web-UI an.
Geben Sie den folgenden Befehl in das Textfeld ein und drücken Sie die Eingabetaste, um die Ergebnisse anzuzeigen.
SELECT CURRENT_USER(), CURRENT_ROLE()DONE;
Da Sie die Funktion
executeAsCaller
in die Dienstspezifikation aufgenommen haben, fügt Snowflake beim Eintreffen einer Anfrage die KopfzeileSf-Context-Current-User-Token
in die Anfrage ein und leitet die Anfrage dann an Ihren Dienstendpunkt weiter.Zur Veranschaulichung führt der Dienstcode in diesem Tutorial die Abfrage sowohl als Aufrufer als auch als Benutzer des Dienstes aus.
Führt die Abfrage im Namen des Aufrufers (Eingangsbenutzer) aus: In diesem Fall verwendet der Code das Benutzertoken, das Snowflake bereitstellt, um ein Anmeldetoken für die Verbindung mit Snowflake zu erstellen. Der Dienst nutzt also die Aufruferrechte. Snowflake führt die Abfrage im Namen des Aufrufers aus und zeigt den Namen des Aufrufers und den Namen der aktiven Rolle im Abfrageergebnis an. Beispiel:
['TESTUSER, PUBLIC']
Führt die Abfrage im Namen des Dienstbenutzers aus: In diesem Fall verwendet der Code nicht das Benutzertoken, ddas Snowflake in der Anfrage bereitstellt, wenn das Anmeldetoken für die Verbindung mit Snowflake erstellt wird. Der Dienst nutzt also nicht die Aufruferrechte und veranlasst Snowflake, die Abfrage im Namen des Benutzers des Dienstes auszuführen. Das Ergebnis der Abfrage zeigt den Namen des Dienstbenutzers (der mit dem Namen des Dienstes identisch ist) und die aktive Rolle.
['QUERY_SERVICE, TEST_ROLE']
Wenn der Dienst die Abfrage (SELECT CURRENT_USER(), CURRENT_ROLE();
) im Namen des Aufrufers ausführt, benötigt Snowflake das Warehouse des Benutzers nicht, um diese einfache Abfrage auszuführen. Daher benötigte der Dienst keine Aufruferrechte. Im nächsten Abschnitt führt der Dienst eine nicht-triviale Abfrage im Namen des aufrufenden Benutzers aus, für die Sie dem Dienst Aufruferrechte erteilen müssen.
Bemerkung
Sie können auf den Eingangsendpunkt programmgesteuert zugreifen. Ein Beispiel für Code finden Sie unter Zugriff auf öffentliche Endpunkte von außerhalb von Snowflake und Authentifizierung. Beachten Sie, dass Sie im Code an die Endpunkt-/ui
die Pfadangabe URL anhängen müssen,damit Snowflake die Anfrage an die ui()
-Funktion im Dienstcode weiterleiten kann.
Dienst mit Aufruferrechten nutzen¶
In diesem Abschnitt führt der Dienst die folgende Abfrage im Namen des Aufrufers (des Benutzers, der sich am Eingangsendpunkt des Dienstes anmeldet) aus.
SELECT * FROM ingress_user_db.ingress_user_schema.ingress_user_table;
Der Dienst verfügt nicht über die Berechtigungen, auf die Tabelle zuzugreifen und die Abfrage im Standard-Warehouse auszuführen. Um den Dienst in die Lage zu versetzen, diese Abfrage im Namen des Aufrufers auszuführen, erteilen Sie dem Dienst die erforderlichen Aufruferrechte.
Um das Szenario zu demonstrieren, erstellen Sie eine neue Rolle (ingress_user_role
) und eine Tabelle (ingress_user_table
), auf die die neue Rolle Zugriff hat, nicht aber die Eigentümerrolle des Dienstes (test_role
). Wenn der Dienst also versucht, die Abfrage mit den Anmeldeinformationen für den Dienst auszuführen, gibt Snowflake einen Fehler zurück. Aber wenn der Dienst die Abfrage im Namen des Benutzers ausführt, führt Snowflake die Abfrage aus und gibt das Ergebnis zurück.
Rollen und Ressourcen erstellen¶
Erstellen Sie eine Rolle (
ingress_user_role
) und eine Datenbank (ingress_user_db
), auf die nur diese Rolle zugreifen kann. Anschließend erteilen Sie Ihrem Benutzer diese Rolle, sodass sich der Benutzer am öffentlichen Endpunkt des Dienstes anmelden und diese Tabelle abfragen kann.USE ROLE accountadmin; CREATE ROLE ingress_user_role; GRANT ROLE ingress_user_role TO USER <your_user_name>; GRANT USAGE ON WAREHOUSE tutorial_warehouse TO ROLE ingress_user_role; CREATE DATABASE IF NOT EXISTS ingress_user_db; GRANT OWNERSHIP ON DATABASE ingress_user_db TO ROLE ingress_user_role COPY CURRENT GRANTS;
Erstellen Sie eine Tabelle (
ingress_user_table
), auf die nur die Rolleingress_user_role
zugreifen kann.USE ROLE ingress_user_role; CREATE SCHEMA IF NOT EXISTS ingress_user_db.ingress_user_schema; USE WAREHOUSE tutorial_warehouse; CREATE TABLE ingress_user_db.ingress_user_schema.ingress_user_table (col string) AS ( SELECT 'this table is only accessible to the ingress_user_role' );
Beachten Sie, dass der Dienst, wenn er versucht, die Tabelle im Namen des Aufrufers abzufragen, nur als
test_role
arbeitet, und zwar in der Rolle, die zur Erstellung des Dienstes verwendet wurde (die Eigentümerrolle des Dienstes). Diese Rolle hat keine Berechtigung für den Zugriff auf die Benutzertabelle.Erteilen Sie der Eigentümerrolle des Dienstes (
test_role
) Aufruferrechte, um Tabellen in der Datenbankingress_user_db
abzufragen. Diese Berechtigung erlaubt es dem Dienst, Tabellen in dieser Datenbank nur dann abzufragen, wenn die folgenden Bedingungen erfüllt sind:Der Dienst verwendet eine Aufruferrechte-Sitzung.
In der Sitzung hat der Aufrufer auch die Berechtigung, diese Abfragen auszuführen.
USE ROLE accountadmin; GRANT CALLER USAGE ON DATABASE ingress_user_db TO ROLE test_role; GRANT INHERITED CALLER USAGE ON ALL SCHEMAS IN DATABASE ingress_user_db TO ROLE test_role; GRANT INHERITED CALLER SELECT ON ALL TABLES IN DATABASE ingress_user_db TO ROLE test_role; GRANT CALLER USAGE ON WAREHOUSE tutorial_warehouse TO ROLE test_role; SHOW CALLER GRANTS TO ROLE test_role;
Konfigurieren Sie das Standard-Warehouse und die Standard-Sekundärrollen.
Wenn eine Sitzung für einen Benutzer erstellt wird, aktiviert Snowflake die Standard-Primärrolle, die Standard-Sekundärrollen und das Standard-Warehouse des angemeldeten Benutzers. In diesem Tutorial,
setzen Sie
DEFAULT_SECONDARY_ROLES
auf ALL, sodass Snowflake bei der Erstellung einer Sitzung für den aktuellen Benutzer alle Rollen, die dem Benutzer erteilt wurden, als aktuelle Sekundärrollen einstellt.Außerdem legen Sie das Standard-Warehouse auf
tutorial_warehouse
fest, in dem die Abfragen voningress_user_table
ausgeführt werden.
ALTER USER SET DEFAULT_SECONDARY_ROLES = ('ALL'); ALTER USER SET DEFAULT_WAREHOUSE = TUTORIAL_WAREHOUSE;
Beachten Sie Folgendes:
In diesem Tutorial melden Sie sich beim öffentlichen Endpunkt des Dienstes an. Der Benutzer hat
test_role
als Primärrolle undingress_user_role
als Sekundärrolle. Damit kann die Sitzung alles tun, was dieingress_user_role
erlaubt.Die Standardrolle und das Standard-Warehouse wirken sich nur auf die Rolle und das Warehouse aus, die aktiviert werden, wenn der Dienst eine Sitzung im Namen Ihres Benutzers aufbaut. Nachdem die Sitzung mit den Rechten eines Aufrufers eingerichtet wurde, können Sie die Rolle nicht mehr ändern, aber Sie können das Warehouse ändern.
Nutzen Sie den Dienst und testen Sie die Aufruferrechte¶
Suchen Sie die URL des öffentlichen Endpunkts, den der Dienst bereitstellt:
SHOW ENDPOINTS IN SERVICE tutorial_db.data_schema.query_service;
Die Spalte
ingress_url
in der Antwort enthält die URL.Beispiel
p6bye-myorg-myacct.snowflakecomputing.app
Hängen Sie
/ui
an die Endpunkt-URL an, und geben Sie diese Zeichenfolge in den Webbrowser ein. Dies veranlasst den Dienst, die Funktionui()
auszuführen (sieheecho_service.py
).: Beachten Sie, dass Sie beim ersten Zugriff auf die Endpunkt-URL aufgefordert werden, sich bei Snowflake anzumelden. Verwenden Sie für diesen Test denselben Benutzer, mit dem Sie den Dienst erstellt haben, um sicherzustellen, dass der Benutzer über die erforderlichen Berechtigungen verfügt:Verwenden Sie denselben Benutzer, den Sie zum Erstellen des Dienstes verwendet haben. Nach erfolgreicher Anmeldung zeigt der Dienst das folgende Web-UI an.
Geben Sie den folgenden Befehl in das Textfeld ein und drücken Sie die Eingabetaste, um die Ergebnisse anzuzeigen.
SELECT * FROM ingress_user_db.ingress_user_schema.ingress_user_table;
Zur Veranschaulichung führt der Dienstcode in diesem Tutorial die Abfrage sowohl als Aufrufer als auch als Dienstbenutzer aus.
Führt die Abfrage im Namen des Aufrufers (Eingangsbenutzers) aus: In diesem Fall verwendet der Code das von Snowflake bereitgestellte Benutzertoken, um ein Anmeldetoken für die Verbindung mit Snowflake zu erstellen. Der Dienst nutzt also die Aufruferrechte. Snowflake führt die Abfrage im Auftrag des Aufrufers aus. Da der Aufrufer die
ingress_user_role role
verwendet, die die Berechtigung hat, die Tabelleingress_user_table
abzufragen, liefert die Abfrage eine Zeile im Ergebnis:['this table is only accessible to ingress_user_role']
Führt die Abfrage im Namen des Dienstbenutzers aus: In diesem Fall verwendet der Code nicht den Benutzertoken, den Snowflake in der Anfrage bereitstellt, wenn er den Anmeldetoken für die Verbindung mit Snowflake erstellt. Snowflake führt also die Abfrage im Namen des Dienstbenutzers aus. Da der Eigentümer des Dienstes den Standard
test_role
verwendet, der keine Berechtigung zur Abfrage der Tabelle hat, sehen Sie einen Fehler:Encountered an error when executing query:... SQL compilation error: Database 'INGRESS_USER_DB' does not exist or not authorized.
Bereinigen¶
Sie sollten die von Ihnen erstellten abrechenbaren Ressourcen entfernen. Weitere Informationen dazu finden Sie in Schritt 5 von Tutorial 3.
Dienstcode überprüfen¶
In diesem Abschnitt werden die folgenden Themen behandelt:
Prüfung des Codes des Tutorials: Überprüfen der Codedateien, die den Abfragedienst implementieren.
Prüfung des Codes des Tutorials¶
Die ZIP-Datei, die Sie in Schritt 1 heruntergeladen haben, enthält die folgenden Dateien:
Dockerfile
main.py
templates/basic_ui.html
Beim Erstellen des Dienstes verwenden Sie außerdem Dienstspezifikationen. Im folgenden Abschnitt wird erläutert, wie diese Codekomponenten beim Erstellen des Dienstes zusammenwirken.
Datei „main.py“¶
Diese Python-Datei enthält den Code, der einen minimalen HTTP-Server implementiert, der eine Abfrage in der Anfrage ausführt und Abfrageergebnisse zurückgibt. Der Code bietet eine Weboberfläche (UI) für die Übermittlung von Echoanforderungen.
from flask import Flask
from flask import request
from flask import render_template
import logging
import os
import sys
from snowflake.snowpark import Session
from snowflake.snowpark.exceptions import *
# Environment variables below will be automatically populated by Snowflake.
SNOWFLAKE_ACCOUNT = os.getenv("SNOWFLAKE_ACCOUNT")
SNOWFLAKE_HOST = os.getenv("SNOWFLAKE_HOST")
SNOWFLAKE_DATABASE = os.getenv("SNOWFLAKE_DATABASE")
SNOWFLAKE_SCHEMA = os.getenv("SNOWFLAKE_SCHEMA")
# Custom environment variables
SNOWFLAKE_USER = os.getenv("SNOWFLAKE_USER")
SNOWFLAKE_PASSWORD = os.getenv("SNOWFLAKE_PASSWORD")
SNOWFLAKE_ROLE = os.getenv("SNOWFLAKE_ROLE")
SNOWFLAKE_WAREHOUSE = os.getenv("SNOWFLAKE_WAREHOUSE")
SERVICE_HOST = os.getenv("SERVER_HOST", "0.0.0.0")
SERVER_PORT = os.getenv("SERVER_PORT", 8080)
def get_logger(logger_name):
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
handler.setFormatter(
logging.Formatter("%(name)s [%(asctime)s] [%(levelname)s] %(message)s")
)
logger.addHandler(handler)
return logger
def get_login_token():
"""
Read the login token supplied automatically by Snowflake. These tokens
are short lived and should always be read right before creating any new connection.
"""
with open("/snowflake/session/token", "r") as f:
return f.read()
def get_connection_params(ingress_user_token=None):
"""
Construct Snowflake connection params from environment variables.
"""
if os.path.exists("/snowflake/session/token"):
if ingress_user_token:
logger.info("Creating a session on behalf of user.")
token = get_login_token() + "." + ingress_user_token
else:
logger.info("Creating a session as service user.")
token = get_login_token()
return {
"account": SNOWFLAKE_ACCOUNT,
"host": SNOWFLAKE_HOST,
"authenticator": "oauth",
"token": token,
"warehouse": SNOWFLAKE_WAREHOUSE,
"database": SNOWFLAKE_DATABASE,
"schema": SNOWFLAKE_SCHEMA,
}
else:
return {
"account": SNOWFLAKE_ACCOUNT,
"host": SNOWFLAKE_HOST,
"user": SNOWFLAKE_USER,
"password": SNOWFLAKE_PASSWORD,
"role": SNOWFLAKE_ROLE,
"warehouse": SNOWFLAKE_WAREHOUSE,
"database": SNOWFLAKE_DATABASE,
"schema": SNOWFLAKE_SCHEMA,
}
logger = get_logger("query-service")
app = Flask(__name__)
@app.get("/healthcheck")
def readiness_probe():
return "I'm ready!"
@app.route("/ui", methods=["GET", "POST"])
def ui():
"""
Main handler for providing a web UI.
"""
if request.method == "POST":
# get ingress user token
ingress_user = request.headers.get("Sf-Context-Current-User")
ingress_user_token = request.headers.get("Sf-Context-Current-User-Token")
if ingress_user:
logger.info(f"Received a request from user {ingress_user}")
# getting input in HTML form
query = request.form.get("query")
if query:
logger.info(f"Received a request for query: {query}.")
query_result_ingress_user = (
run_query(query, ingress_user_token)
if ingress_user_token
else "Token is missing. Can't execute as ingress user."
)
query_result_service_user = run_query(query)
return render_template(
"basic_ui.html",
query_input=query,
query_result_ingress_user=query_result_ingress_user,
query_result_service_user=query_result_service_user,
)
return render_template("basic_ui.html")
@app.route("/query", methods=["GET"])
def query():
"""
Main handler for providing programmatic access.
"""
# get ingress user token
query = request.args.get("query")
logger.info(f"Received query request: {query}.")
if query:
ingress_user = request.headers.get("Sf-Context-Current-User")
ingress_user_token = request.headers.get("Sf-Context-Current-User-Token")
if ingress_user:
logger.info(f"Received a request from user {ingress_user}")
res = run_query(query, ingress_user_token)
return str(res)
return "DONE"
def run_query(query, ingress_user_token=None):
# start a Snowflake session as the ingress user
try:
with Session.builder.configs(
get_connection_params(ingress_user_token)
).create() as session:
logger.info(
f"Snowflake connection established (id={session.session_id}). Now executing query: {query}."
)
try:
res = session.sql(query).collect()
logger.info(f"Query execution done: {query}.")
return (
"[Empty Result]"
if len(res) == 0
else [", ".join(row) for row in res]
)
except Exception as e:
return "Encountered an error when executing query: " + str(e)
except Exception as e:
return "Encountered an error when connecting to Snowflake: " + str(e)
if __name__ == '__main__':
app.run(host=SERVICE_HOST, port=SERVER_PORT)
Erläuterungen zum Code:
Die Funktion
ui
zeigt das folgende Webformular an und verarbeitet Abfragen, die über das Webformular übermittelt werden.Diese Funktion verwendet den Decorator
@app.route()
, um anzugeben, dass Anforderungen für/ui
von dieser Funktion bearbeitet werden:@app.route("/ui", methods=["GET", "POST"]) def ui():
Der Abfragedienst macht den Endpunkt
execute
öffentlich zugänglich (siehe die Inline-Dienstpezifikation, die Sie bei der Erstellung des Dienstes angegeben haben) und ermöglicht so die Kommunikation mit dem Dienst über das Web. Wenn Sie die URL des öffentlichen Endpunkts mit angehängtem „/ui“ in Ihrem Browser laden, sendet der Browser eine HTTP-GET-Anforderung für diesen Pfad, und der Server leitet die Anforderung an diese Funktion weiter. Die Funktion wird ausgeführt und gibt ein einfaches HTML-Formular zurück, in das der Benutzer eine Abfrage eingeben kann.Nachdem der Benutzer eine Abfrage eingegeben und das Formular übermittelt hat, sendet der Browser eine HTTP POST-Anfrage für diesen Pfad. Da die Dienstspezifikation die Funktion
executeAsCaller
enthält, fügt Snowflake der eingehenden Anfrage den HeaderSf-Context-Current-User-Token
hinzu und leitet die Anfrage an dieselbe Funktion weiter (siehe Verbinden mit Snowflake über die Aufruferrechte).Der Code führt die Funktion
run_query
zweimal aus:Als Eingangsbenutzer. In diesem Fall ist das Anmeldetoken eine Verkettung aus OAuth-Token und Eingangsbenutzertoken.
token = get_login_token() + "." + ingress_user_token
Als Dienstbenutzer. In diesem Fall ist das Anmeldetoken nur das OAuth-Token.
token = get_login_token()
Die Funktion
readiness_probe
verwendet den Decorator@app.get()
, um zu spezifizieren, dass Anforderungen für/healthcheck
von dieser Funktion verarbeitet werden:@app.get("/healthcheck") def readiness_probe():
Mit dieser Funktion kann Snowflake die Bereitschaft des Dienstes überprüfen. Wenn der Container startet, möchte Snowflake bestätigen, dass die Anwendung funktioniert und der Dienst bereit ist, Anforderungen zu bedienen. Snowflake sendet eine HTTP-GET-Anforderung mit diesem Pfad, um auf Funktionsfähigkeit (Health Probe) und Bereitschaft (Readiness Probe) zu testen und somit sicherzustellen, dass nur funktionsfähige Container den Datenverkehr bedienen. Die Funktion kann alles tun, was Sie möchten.
Die Funktion
get_logger
hilft beim Einrichten der Protokollierung.
Datei „Dockerfile“¶
Diese Datei enthält alle Befehle, um ein Image mit Docker zu erstellen.
ARG BASE_IMAGE=python:3.10-slim-buster
FROM $BASE_IMAGE
COPY main.py ./
COPY templates/ ./templates/
RUN pip install --upgrade pip && pip install flask snowflake-snowpark-python
CMD ["python", "main.py"]
Die Dockerdatei enthält Anweisungen zur Installation der Flask-Bibliothek im Docker-Container. Der Code in main.py
stützt sich auf die Flask-Bibliothek, um HTTP-Anforderungen zu verarbeiten.
Datei „/template/basic_ui.html“¶
Der Abfragedient macht den Endpunkt echoendpoint
öffentlich zugänglich (siehe Dienstspezifikation) und ermöglicht so die Kommunikation mit dem Dienst über das Web. Wenn Sie die öffentliche Endpunkt-URL mit angehängtem /ui
in Ihrem Browser laden, zeigt der Abfragedienst dieses Formular an.

Sie können eine Abfrage in das Formular eingeben und das Formular abschicken, und der Dienst gibt die Ergebnisse in einer HTTP-Antwort zurück.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Welcome to the query service!</title>
</head>
<body>
<h1>Welcome to the query service!</h1>
<form action="{{ url_for("ui") }}" method="post">
<label for="query">query:<label><br>
<input type="text" id="query" name="query" size="50"><br>
</form>
<h2>Query:</h2>
{{ query_input }}
<h2>Result (executed on behalf of ingress user):</h2>
{{ query_result_ingress_user }}
<h2>Result (executed as service user):</h2>
{{ query_result_service_user }}
</body>
</html>
Dienstspezifikation¶
Snowflake verwendet die von Ihnen in dieser Spezifikation angegebenen Informationen zur Konfiguration und Ausführung Ihres Dienstes.
spec:
containers:
- name: main
image: /tutorial_db/data_schema/tutorial_repository/query_service:latest
env:
SERVER_PORT: 8000
readinessProbe:
port: 8000
path: /healthcheck
endpoints:
- name: execute
port: 8000
public: true
capabilities:
securityContext:
executeAsCaller: true
serviceRoles:
- name: ui_usage
endpoints:
- execute
In der Dienstspezifikation sind die Felder spec
, capabilities
und serviceRoles
die oberste Felder der Spezifikation.
spec
liefert Angaben zur Spezifikation (siehe Referenz der Dienstspezifikation). Beachten Sie, dass der Dienst einen öffentlichen Endpunkt (execute
) zur Verfügung stellt, der den Zugriff auf den Dienst aus dem öffentlichen Web ermöglicht.capabilities
Gibt dieexecuteAsCaller
-Funktion an. Dies teilt Snowflake mit, dass die Anwendung beabsichtigt, die Aufruferrechte zu verwenden.serviceRoles
gibt eine Dienstrolle (ui_usage
) und einen Endpunktnamen (execute
) an, für die die Berechtigung USAGE erteilt werden soll.Das Feld
readinessProbe
identifiziert die Werte vonport
undpath
, mit denen Snowflake eine HTTP-GET-Anforderung an den Bereitschaftstest (Readiness Probe) senden kann, um zu überprüfen, ob der Dienst bereit ist, Datenverkehr zu verarbeiten.Der Dienstcode (
echo_python.py
) implementiert die Bereitschaftsprüfung wie folgt:@app.get("/healthcheck") def readiness_probe():
Daher enthält die Spezifikationsdatei das entsprechende Feld
container.readinessProbe
.
Weitere Informationen zu Dienstspezifikationen finden Sie unter Referenz der Dienstspezifikation.
Nächste Schritte¶
Nachdem Sie nun dieses Tutorial abgeschlossen haben, können Sie zu Verwenden von Diensten zurückkehren, um weitere Themen zu erkunden.