Skip to content

Commit 44b3071

Browse files
committed
dev changes deployment
1 parent b6e4bfa commit 44b3071

File tree

10 files changed

+556
-405
lines changed

10 files changed

+556
-405
lines changed

.github/workflows/snowpark-ci-cd.yml

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,13 @@ jobs:
151151
run: |
152152
mkdir -p ~/.snowflake/keys
153153
154-
# Debug the Snowflake account format
155-
echo "Using Snowflake account: ${SNOWFLAKE_ACCOUNT}"
156-
157154
# Store private key correctly from GitHub secrets
158155
echo "${{ secrets.SNOWFLAKE_PRIVATE_KEY }}" > ~/.snowflake/keys/rsa_key.p8
159156
chmod 600 ~/.snowflake/keys/rsa_key.p8
160157
158+
# Debug the Snowflake account format
159+
echo "Using Snowflake account: ${SNOWFLAKE_ACCOUNT}"
160+
161161
# Install required packages
162162
pip install cryptography snowflake-connector-python
163163
@@ -172,6 +172,7 @@ jobs:
172172
role = "CO2_ROLE_DEV"
173173
database = "CO2_DB_DEV"
174174
schema = "RAW_CO2"
175+
client_request_mfa_token = false
175176
176177
[prod]
177178
account = "$SNOWFLAKE_ACCOUNT"
@@ -182,6 +183,7 @@ jobs:
182183
role = "CO2_ROLE_PROD"
183184
database = "CO2_DB_PROD"
184185
schema = "RAW_CO2"
186+
client_request_mfa_token = false
185187
EOF
186188
187189
chmod 600 ~/.snowflake/connections.toml
@@ -271,59 +273,23 @@ jobs:
271273
272274
echo "🔍 Checking for changes in $component_path..."
273275
274-
# Default to not deploying
275-
should_deploy=false
276-
277-
# For pull requests, always deploy components
278-
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
279-
echo "Pull request detected - always deploying component"
280-
should_deploy=true
281-
# For workflow_dispatch, always deploy components
282-
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
283-
echo "Manual workflow dispatch detected - always deploying component"
284-
should_deploy=true
276+
# For pull requests or workflow_dispatch, always deploy components
277+
if [[ "${{ github.event_name }}" == "pull_request" || "${{ github.event_name }}" == "workflow_dispatch" ]]; then
278+
echo "Pull request or manual dispatch detected - deploying component"
279+
# Use our Python deployment script without checking changes
280+
PYTHONPATH=$PYTHONPATH:$(pwd) python -u scripts/deployment_files/snowflake_deployer.py deploy --profile $CONN_PROFILE --path "$component_path" --name "$component_name" --type "$component_type"
285281
else
286-
# Check for force_deploy file
287-
if [[ -f "$component_path/.force_deploy" ]]; then
288-
echo "🔥 Force deploy file found - forcing deployment"
289-
should_deploy=true
290-
else
291-
# For push events, check if component changed in this commit
292-
# Get changed files in the most recent commit
293-
changed_files=$(git diff --name-only HEAD HEAD~1)
294-
echo "Changed files in latest commit:"
295-
echo "$changed_files"
296-
297-
# Check if any files in the component path changed
298-
if echo "$changed_files" | grep -q "$component_path"; then
299-
echo "✅ Changes detected in component"
300-
should_deploy=true
301-
else
302-
echo "❌ No changes detected in component"
303-
fi
304-
fi
282+
# For push events, only deploy if component changed
283+
echo "Push event detected - only deploying changed components"
284+
# Use our Python deployment script with change detection
285+
PYTHONPATH=$PYTHONPATH:$(pwd) python -u scripts/deployment_files/snowflake_deployer.py deploy --profile $CONN_PROFILE --path "$component_path" --name "$component_name" --type "$component_type" --check-changes
305286
fi
306287
307-
if $should_deploy; then
308-
echo "⏳ Deploying $component_name..."
309-
310-
# Use our Python deployment script with additional logging
311-
PYTHONPATH=$PYTHONPATH:$(pwd) python -u scripts/deployment_files/snowflake_deployer.py deploy --profile $CONN_PROFILE --path "$component_path" --name "$component_name" --type "$component_type"
312-
313-
if [ $? -eq 0 ]; then
314-
echo "✅ Successfully deployed $component_name"
315-
316-
# If force deploy file exists, remove it after successful deployment
317-
if [[ -f "$component_path/.force_deploy" ]]; then
318-
echo "Removing force deploy marker"
319-
rm -f "$component_path/.force_deploy"
320-
fi
321-
else
322-
echo "❌ Deploy failed for $component_name"
323-
exit 1
324-
fi
288+
if [ $? -eq 0 ]; then
289+
echo "✅ Component $component_name processed successfully"
325290
else
326-
echo "⏭️ Skipping $component_name (no changes detected)"
291+
echo "❌ Processing failed for $component_name"
292+
exit 1
327293
fi
328294
}
329295

deployment_and_key_workflow.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Snowflake Deployment and RSA Key Authentication Flow
2+
3+
## Overview of the Deployment Architecture
4+
5+
The deployment process uses RSA key-based authentication to deploy UDFs and stored procedures to Snowflake without requiring MFA prompts. Here's how the components work together:
6+
7+
## 1. RSA Key Generation and Authentication
8+
9+
### Key Files:
10+
- **rsa_key_pair_generator.md** - Documentation with manual steps for key generation
11+
- **scripts/rsa_key_pair_authentication/generate_snowflake_keys.py** - Script to generate RSA keys
12+
- **scripts/rsa_key_pair_authentication/check_snowflake_key_auth.py** - Validates key formatting
13+
14+
### Purpose:
15+
These files help create and validate the RSA key pair needed for passwordless authentication with Snowflake. The process is:
16+
17+
1. Generate a private key (rsa_key.p8) and public key (rsa_key.pub)
18+
2. Register the public key with your Snowflake user account
19+
3. Store the private key securely locally for development and in GitHub secrets for CI/CD
20+
21+
## 2. Connection Configuration
22+
23+
### Key Files:
24+
- **~/.snowflake/connections.toml** - Stores connection profiles (local development)
25+
- **scripts/deployment_files/check_connections_file.py** - Diagnoses issues with connections.toml
26+
- **scripts/deployment_files/test_key_auth.py** - Tests key authentication
27+
28+
### Purpose:
29+
These files manage the connection details for different environments:
30+
31+
1. The connections.toml needs proper format with `private_key_path` pointing to your local key
32+
2. test_key_auth.py validates the key works before attempting deployment
33+
3. check_connections_file.py helps diagnose TOML parsing issues
34+
35+
## 3. Deployment Process
36+
37+
### Key Files:
38+
- **scripts/deployment_files/snowflake_deployer.py** - Main deployment logic
39+
- **.github/workflows/snowpark-ci-cd.yml** - CI/CD pipeline configuration
40+
41+
### Purpose:
42+
These files handle the actual deployment:
43+
44+
1. GitHub workflow sets up authentication in CI/CD environment
45+
2. snowflake_deployer.py:
46+
- Analyzes UDF function signatures
47+
- Packages code into ZIP files
48+
- Uploads to Snowflake stage
49+
- Creates UDFs/procedures with proper SQL DDL
50+
51+
## 4. UDF Parameter Handling
52+
53+
### Key Files:
54+
- **scripts/check_and_fix_udf.py** - Fixes UDF parameter mismatches
55+
- **udfs_and_spoc/*/function.py** - The actual UDF/procedure code
56+
57+
### Purpose:
58+
The UDF files have different parameter signatures:
59+
1. CO2_VOLATILITY and DAILY_CO2_CHANGES have 2 parameters (current_value, previous_value)
60+
2. Stored procedures take session parameters
61+
3. The deployment process must adapt SQL definitions to match these signatures
62+
63+
## Interrelationships Between Files
64+
65+
1. **Key Authentication Chain:**
66+
```
67+
generate_snowflake_keys.py -> rsa_key.p8/.pub -> connections.toml -> snowflake_deployer.py
68+
```
69+
70+
2. **Deployment Chain:**
71+
```
72+
CI/CD workflow -> snowflake_deployer.py -> analyze_function_signature() -> Customized SQL DDL -> Snowflake
73+
```
74+
75+
3. **Parameter Handling Chain:**
76+
```
77+
UDF function.py -> analyze_function_signature() -> SQL with matching parameters
78+
```
79+
80+
## Current Issues and Solutions
81+
82+
The issues you're experiencing are related to:
83+
84+
1. **TOML File Syntax**: The connections.toml file has `//` comments instead of `#` comments
85+
2. **Missing Key Path**: Your connections.toml doesn't have `private_key_path` configured
86+
3. **Authentication Flow**: The deployment tries password auth and triggers MFA
87+
88+
To fix:
89+
1. Update connections.toml with proper TOML syntax (use # for comments)
90+
2. Add private_key_path pointing to your local key
91+
3. Set `client_request_mfa_token = false` to explicitly disable MFA
92+
93+
This workflow ensures secure, MFA-free deployments and handles different function signatures appropriately.

poetry.lock

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ jinja2 = "3.1.4"
1616
pyyaml = "^6.0.2"
1717
boto3 = "^1.37.3"
1818
cryptography = "^44.0.2"
19+
snowflake-connector-python = ">=3.0.0"
20+
logging = "^0.4.9.6"
21+
pyarrow = ">=7.0.0"
1922

2023
[build-system]
2124
requires = ["poetry-core>=1.0.0"]

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
snowflake-snowpark-python[pandas]
2+
snowflake-connector-python>=3.0.0
3+
logging
4+
pyarrow>=7.0.0
25
snowflake-cli-labs
36
python-dotenv
47
pytest
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Check the connections.toml file and output its structure to diagnose issues.
4+
"""
5+
import os
6+
import sys
7+
import toml
8+
9+
def main():
10+
config_path = os.path.expanduser("~/.snowflake/connections.toml")
11+
print(f"Looking for connections.toml at: {config_path}")
12+
13+
if not os.path.exists(config_path):
14+
print(f"ERROR: File does not exist: {config_path}")
15+
return False
16+
17+
print(f"File exists, size: {os.path.getsize(config_path)} bytes")
18+
print(f"File permissions: {oct(os.stat(config_path).st_mode)[-3:]}")
19+
20+
try:
21+
with open(config_path, 'r') as f:
22+
content = f.read()
23+
print("\nFile content preview (first 200 chars):")
24+
print(content[:200])
25+
26+
# Try loading with toml
27+
print("\nParsing with toml:")
28+
config = toml.load(config_path)
29+
print(f"Profiles found: {list(config.keys())}")
30+
31+
# Check each profile
32+
for profile, settings in config.items():
33+
print(f"\nProfile: {profile}")
34+
for key, value in settings.items():
35+
# Don't print sensitive values
36+
if key in ['password', 'private_key']:
37+
print(f" {key}: [REDACTED]")
38+
else:
39+
print(f" {key}: {value}")
40+
41+
return True
42+
except Exception as e:
43+
print(f"ERROR parsing file: {str(e)}")
44+
45+
# Try to read the raw file content
46+
try:
47+
with open(config_path, 'rb') as f:
48+
binary_content = f.read(100)
49+
print(f"\nBinary content (first 100 bytes): {binary_content}")
50+
except Exception as e2:
51+
print(f"ERROR reading raw file: {str(e2)}")
52+
53+
return False
54+
55+
if __name__ == "__main__":
56+
success = main()
57+
sys.exit(0 if success else 1)

0 commit comments

Comments
 (0)