Skip to content

Commit 2594c01

Browse files
authored
Merge pull request #14 from ddevadat/dotnet-samples
dotnet function for query adb using oracle client
2 parents 1cd3f9e + 72ed54a commit 2594c01

File tree

15 files changed

+538
-1
lines changed

15 files changed

+538
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ This repository provides examples demonstrating how to use Oracle Functions.
2727
| Display an OCI Cloud Event |[sample](./samples/oci-event-display-python)||[sample](./samples/oci-event-display-dotnet)|
2828
| Invoke another Function using the OCI SDK |[sample](./samples/oci-invoke-function-python)||[sample](./samples/oci-invoke-function-dotnet)|
2929
| Run a SQL statement against Autonomous DB using ORDS | [sample](./samples/oci-adb-ords-runsql-python) ||[sample](./samples/oci-adb-ords-runsql-dotnet)|
30-
| Run a SQL statement against Autonomous DB using DB Client |[sample](./samples/oci-adb-client-runsql-python)||
30+
| Run a SQL statement against Autonomous DB using DB Client |[sample](./samples/oci-adb-client-runsql-python)|| [sample](./samples/oci-adb-client-runsql-dotnet)
3131
| Publish a notification using ONS |[sample](./samples/oci-ons-publish-python)|
3232
| Send an email using Email Delivery Service |[sample](./samples/oci-email-send-python)|
3333
| Decrypt cipher using Vault keys |[sample](./samples/oci-vault-decrypt-python)||[sample](./samples/oci-vault-decrypt-dotnet)|
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
using System;
3+
using System.Threading.Tasks;
4+
using System.Text;
5+
6+
using Oci.Common;
7+
using Oci.Common.Auth;
8+
using Oci.DatabaseService;
9+
10+
namespace RunSQL
11+
{
12+
public class DBClientHelper
13+
{
14+
public static DatabaseClient GetDBClient()
15+
{
16+
try
17+
{
18+
return new DatabaseClient(ResourcePrincipalAuthenticationDetailsProvider.GetProvider(), new ClientConfiguration());
19+
}
20+
catch (Exception ex)
21+
{
22+
Console.WriteLine("Unable To Create Resource Principal Provider : {0}", ex.Message);
23+
Console.WriteLine("Defaulting to Instance Provider");
24+
return new DatabaseClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration());
25+
}
26+
}
27+
28+
}
29+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
2+
using System;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using System.Text;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.IO.Compression;
9+
using Oci.Common.Model;
10+
using Oci.Common;
11+
using Oci.Common.Auth;
12+
using Oci.DatabaseService;
13+
using Oci.DatabaseService.Models;
14+
using Oci.DatabaseService.Requests;
15+
using Oci.DatabaseService.Responses;
16+
17+
18+
namespace RunSQL
19+
{
20+
public class GenerateDBWalletHelper
21+
{
22+
23+
public static string RandomString(int length)
24+
{
25+
const string pool = "abcdefghijklmnopqrstuvwxyz0123456789#%^$@";
26+
Random random = new Random();
27+
var chars = Enumerable.Range(0, length)
28+
.Select(x => pool[random.Next(0, pool.Length)]);
29+
return new string(chars.ToArray());
30+
}
31+
32+
public static string GenerateWalletPassword(int length)
33+
{
34+
Random ran = new Random();
35+
String b = "abcdefghijklmnopqrstuvwxyz0123456789";
36+
String sc = "!@#$%^&*~";
37+
String random = "";
38+
for (int i = 0; i < length; i++)
39+
{
40+
int a = ran.Next(b.Length);
41+
random = random + b.ElementAt(a);
42+
}
43+
for (int j = 0; j < 2; j++)
44+
{
45+
int sz = ran.Next(sc.Length);
46+
random = random + sc.ElementAt(sz);
47+
}
48+
return random;
49+
}
50+
public static async Task<string> GenWallet(DatabaseClient client, string adb_ocid, string extractPath)
51+
52+
{
53+
54+
try
55+
{
56+
var wallet_password = GenerateWalletPassword(10);
57+
58+
Console.WriteLine("Inside GenWallet Method");
59+
var generateAutonomousDatabaseWalletDetails = new Oci.DatabaseService.Models.GenerateAutonomousDatabaseWalletDetails
60+
{
61+
GenerateType = Oci.DatabaseService.Models.GenerateAutonomousDatabaseWalletDetails.GenerateTypeEnum.Single,
62+
Password = wallet_password
63+
};
64+
65+
var generateAutonomousDatabaseWalletRequest = new Oci.DatabaseService.Requests.GenerateAutonomousDatabaseWalletRequest
66+
{
67+
AutonomousDatabaseId = adb_ocid,
68+
GenerateAutonomousDatabaseWalletDetails = generateAutonomousDatabaseWalletDetails,
69+
};
70+
71+
var response = await client.GenerateAutonomousDatabaseWallet(generateAutonomousDatabaseWalletRequest);
72+
73+
using (var memoryStream = new MemoryStream())
74+
{
75+
76+
response.InputStream.CopyTo(memoryStream);
77+
Console.WriteLine("Generating zip file...");
78+
var fileName = $"gen_wallet.zip";
79+
string zipPath = Path.Combine(extractPath + "/" + fileName);
80+
Console.WriteLine("Wallet location : {0}", zipPath);
81+
using (var fs = new FileStream(zipPath, FileMode.Create, FileAccess.Write))
82+
{
83+
memoryStream.WriteTo(fs);
84+
}
85+
Console.WriteLine("extracting : {0} to {1}", zipPath, extractPath);
86+
87+
using (ZipArchive source = ZipFile.Open(zipPath, ZipArchiveMode.Read, null))
88+
{
89+
foreach (ZipArchiveEntry entry in source.Entries)
90+
{
91+
string fullPath = Path.GetFullPath(Path.Combine(extractPath, entry.FullName));
92+
93+
if (Path.GetFileName(fullPath).Length != 0)
94+
{
95+
entry.ExtractToFile(fullPath, true);
96+
}
97+
}
98+
}
99+
100+
}
101+
return "success";
102+
103+
104+
}
105+
106+
catch (OciException ex)
107+
{
108+
Console.WriteLine("Unable To Generate wallet : {0}", ex.Message);
109+
return "Failed " + ex.Message;
110+
}
111+
112+
}
113+
114+
115+
}
116+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage
2+
WORKDIR /function
3+
COPY . .
4+
RUN dotnet sln add RunSQL.csproj
5+
RUN dotnet build RunSQL.csproj -c Release
6+
RUN dotnet publish RunSQL.csproj -c Release -o out
7+
FROM fnproject/dotnet:3.1-1.0.4
8+
WORKDIR /function
9+
COPY --from=build-stage /function/out/ /function/
10+
ENV TNS_ADMIN=/tmp
11+
ENTRYPOINT ["dotnet", "RunSQL.dll"]
12+
CMD ["RunSQL:Function:function_handler"]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Microsoft Visual Studio Solution File, Format Version 12.00
2+
# Visual Studio 15
3+
VisualStudioVersion = 15.0.26124.0
4+
MinimumVisualStudioVersion = 15.0.26124.0
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Debug|x64 = Debug|x64
9+
Debug|x86 = Debug|x86
10+
Release|Any CPU = Release|Any CPU
11+
Release|x64 = Release|x64
12+
Release|x86 = Release|x86
13+
EndGlobalSection
14+
GlobalSection(SolutionProperties) = preSolution
15+
HideSolutionNode = FALSE
16+
EndGlobalSection
17+
EndGlobal
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
namespace RunSQL
4+
{
5+
6+
class InputMessage
7+
{
8+
public string sql { get; set; }
9+
10+
11+
}
12+
13+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
// using System.Collections;
4+
// using System.Collections.Specialized;
5+
6+
namespace RunSQL
7+
{
8+
9+
class OutputDetails
10+
{
11+
public string sql { get; set; }
12+
13+
public List<Dictionary<string, string>> result { get; set; }
14+
15+
}
16+
17+
18+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Execute a SQL statement against Autonomous Database using the Oracle client
2+
This function connects to Oracle Autonomous Database (either Transaction Processing or Data Warehouse) using the Oracle Client and execute a SQL statement.
3+
4+
As you make your way through this tutorial, look out for this icon ![user input icon](./images/userinput.png).
5+
Whenever you see it, it's time for you to perform an action.
6+
7+
8+
## Prerequisites
9+
Before you deploy this sample function, make sure you have run step A, B and C of the [Oracle Functions Quick Start Guide for Cloud Shell](https://www.oracle.com/webfolder/technetwork/tutorials/infographics/oci_functions_cloudshell_quickview/functions_quickview_top/functions_quickview/index.html)
10+
* A - Set up your tenancy
11+
* B - Create application
12+
* C - Set up your Cloud Shell dev environment
13+
14+
15+
## List Applications
16+
Assuming your have successfully completed the prerequisites, you should see your
17+
application in the list of applications.
18+
```
19+
fn ls apps
20+
```
21+
22+
23+
## Create or Update your Dynamic Group
24+
In order to use other OCI Services, your function must be part of a dynamic group. For information on how to create a dynamic group, refer to the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Tasks/managingdynamicgroups.htm#To).
25+
26+
When specifying the *Matching Rules*, we suggest matching all functions in a compartment with:
27+
```
28+
ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaxxxxx'}
29+
```
30+
Please check the [Accessing Other Oracle Cloud Infrastructure Resources from Running Functions](https://docs.cloud.oracle.com/en-us/iaas/Content/Functions/Tasks/functionsaccessingociresources.htm) for other *Matching Rules* options.
31+
32+
33+
## Review and customize the function
34+
Review the following files in the current folder:
35+
* the code of the function, [RunSQL.cs](./RunSQL.cs)
36+
* its dependencies, [RunSQL.csproj](./RunSQL.csproj)
37+
* the function metadata, [func.yaml](./func.yaml)
38+
39+
40+
## Deploy the function
41+
In Cloud Shell, run the *fn deploy* command to build the function and its dependencies as a Docker image,
42+
push the image to OCIR, and deploy the function to Oracle Functions in your application.
43+
44+
![user input icon](./images/userinput.png)
45+
```
46+
fn -v deploy --app <app-name>
47+
```
48+
49+
50+
## Create an Autonomous Database
51+
Use an existing Autonomous Database (either Transaction Processing or Datawarehouse) or create a new one as follows.
52+
53+
![user input icon](./images/userinput.png)
54+
55+
On the OCI console, navigate to *Autonomous Transaction Processing* or *Autonomous Data Warehouse* and click *Create Autonomous Database*. In the Create Autonomous Database dialog, enter the following:
56+
- Display Name
57+
- Compartment
58+
- Database Name
59+
- Infrastructure Type: Shared
60+
- Admin password
61+
- License type
62+
63+
![ADB create](./images/ADB-create.png)
64+
For more information, go to https://docs.cloud.oracle.com/iaas/Content/Database/Tasks/adbcreating.htm
65+
66+
67+
## Database Wallet and IAM Policies
68+
The Database wallet is not part of the Docker image because it is not secure. The function downloads the wallet while it is executed.
69+
The wallet can be retrieved from Object Storage or directly from Autonomous Database.
70+
71+
![user input icon](./images/userinput.png)
72+
73+
If you choose to retrieve the wallet from Object Storage, first download the wallet from Autonomous Database. Navigate to *Autonomous Transaction Processing* or *Autonomous Data Warehouse*, click on your database abd click on *DB Connection*. On the Pop-up window, click *Download Wallet*.
74+
75+
![Download Wallet](./images/Download_wallet.png)
76+
77+
Create a bucket in Object Storage and upload the wallet there. Note the name of the bucket and the wallet object name.
78+
Create an IAM policy that allows the dynamic group to read objects in the bucket. We will grant `read` access to `objects` in your bucket in the compartment.
79+
80+
![user input icon](./images/userinput.png)
81+
82+
Your policy should look something like this:
83+
```
84+
Allow dynamic-group <dynamic-group-name> to read objects in compartment <compartment-name> where target.bucket.name='<bucket-name>'
85+
```
86+
87+
If you choose to retrieve the wallet from Autonomous Database directly during the execution of the function, note the OCID of the Autonomous Database and create an IAM policy that allows the dynamic group to use the autonomous Database with the specific permission 'AUTONOMOUS_DATABASE_CONTENT_READ'.
88+
```
89+
Allow dynamic-group <dynamic-group-name> to use autonomous-databases in compartment <compartment-name> where request.permission='AUTONOMOUS_DATABASE_CONTENT_READ'
90+
```
91+
92+
For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm).
93+
94+
95+
## Set the function configuration values
96+
The function requires several config value to be set.
97+
98+
![user input icon](./images/userinput.png)
99+
100+
Use the *fn CLI* to set the config value:
101+
```
102+
fn config function <app-name> <function-name> DBSVC <DB-service-name>
103+
fn config function <app-name> <function-name> DBUSER <DB-username>
104+
fn config function <app-name> <function-name> DBPWD_CYPHER <DB-encrypted-password>
105+
```
106+
Additionally, the DB wallet should be downloaded from the autonomous DB, specify the Autonomouns Database OCID:
107+
108+
```
109+
fn config function <app-name> <function name> ADB_OCID <Autonomous-DB-OCID>
110+
```
111+
e.g. with a DB wallet in a bucket:
112+
```
113+
fn config function myapp oci-adb-client-runsql-dotnet DBSVC "gregadw_high"
114+
fn config function myapp oci-adb-client-runsql-dotnet DBUSER "admin"
115+
fn config function myapp oci-adb-client-runsql-dotnet DBPWD_CYPHER "dfgjksadhfg4526897dfgkj"
116+
fn config function myapp oci-adb-client-runsql-dotnet ADB_OCID "db-wallets"
117+
118+
```
119+
120+
121+
## Invoke the function
122+
![user input icon](./images/userinput.png)
123+
```
124+
125+
echo '{"sql":"<sql statement>"}' | fn invoke <app-name> oci-adb-client-runsql-dotnet
126+
```
127+
e.g.:
128+
```
129+
echo '{"sql":"select sysdate from dual"}' | fn invoke myapp oci-adb-client-runsql-dotnet
130+
```
131+
132+
Upon success, the function returns a JSON object similar to this:
133+
```
134+
{
135+
"output": [
136+
{
137+
"sql": "select sysdate from dual",
138+
"result": [
139+
{
140+
"SYSDATE": "10/27/2022 03:02:38"
141+
}
142+
]
143+
}
144+
]
145+
}
146+
```
147+
148+
149+
## Monitoring Functions
150+
151+
Learn how to configure basic observability for your function using metrics, alarms and email alerts:
152+
* [Basic Guidance for Monitoring your Functions](../basic-observability/functions.md)
153+

0 commit comments

Comments
 (0)