diff --git a/README.md b/README.md index 7748210..7cf29d3 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This repository provides examples demonstrating how to use Oracle Functions. | Display an OCI Cloud Event |[sample](./samples/oci-event-display-python)||[sample](./samples/oci-event-display-dotnet)| | Invoke another Function using the OCI SDK |[sample](./samples/oci-invoke-function-python)||[sample](./samples/oci-invoke-function-dotnet)| | Run a SQL statement against Autonomous DB using ORDS | [sample](./samples/oci-adb-ords-runsql-python) ||[sample](./samples/oci-adb-ords-runsql-dotnet)| -| Run a SQL statement against Autonomous DB using DB Client |[sample](./samples/oci-adb-client-runsql-python)|| +| Run a SQL statement against Autonomous DB using DB Client |[sample](./samples/oci-adb-client-runsql-python)|| [sample](./samples/oci-adb-client-runsql-dotnet) | Publish a notification using ONS |[sample](./samples/oci-ons-publish-python)| | Send an email using Email Delivery Service |[sample](./samples/oci-email-send-python)| | Decrypt cipher using Vault keys |[sample](./samples/oci-vault-decrypt-python)||[sample](./samples/oci-vault-decrypt-dotnet)| diff --git a/samples/oci-adb-client-runsql-dotnet/Common/DBClientHelper.cs b/samples/oci-adb-client-runsql-dotnet/Common/DBClientHelper.cs new file mode 100644 index 0000000..1c9125b --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/Common/DBClientHelper.cs @@ -0,0 +1,29 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.DatabaseService; + +namespace RunSQL +{ + public class DBClientHelper + { + public static DatabaseClient GetDBClient() + { + try + { + return new DatabaseClient(ResourcePrincipalAuthenticationDetailsProvider.GetProvider(), new ClientConfiguration()); + } + catch (Exception ex) + { + Console.WriteLine("Unable To Create Resource Principal Provider : {0}", ex.Message); + Console.WriteLine("Defaulting to Instance Provider"); + return new DatabaseClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-adb-client-runsql-dotnet/Controller/GenerateDBWalletHelper.cs b/samples/oci-adb-client-runsql-dotnet/Controller/GenerateDBWalletHelper.cs new file mode 100644 index 0000000..8f61525 --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/Controller/GenerateDBWalletHelper.cs @@ -0,0 +1,116 @@ + +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.DatabaseService; +using Oci.DatabaseService.Models; +using Oci.DatabaseService.Requests; +using Oci.DatabaseService.Responses; + + +namespace RunSQL +{ + public class GenerateDBWalletHelper + { + + public static string RandomString(int length) + { + const string pool = "abcdefghijklmnopqrstuvwxyz0123456789#%^$@"; + Random random = new Random(); + var chars = Enumerable.Range(0, length) + .Select(x => pool[random.Next(0, pool.Length)]); + return new string(chars.ToArray()); + } + + public static string GenerateWalletPassword(int length) + { + Random ran = new Random(); + String b = "abcdefghijklmnopqrstuvwxyz0123456789"; + String sc = "!@#$%^&*~"; + String random = ""; + for (int i = 0; i < length; i++) + { + int a = ran.Next(b.Length); + random = random + b.ElementAt(a); + } + for (int j = 0; j < 2; j++) + { + int sz = ran.Next(sc.Length); + random = random + sc.ElementAt(sz); + } + return random; + } + public static async Task GenWallet(DatabaseClient client, string adb_ocid, string extractPath) + + { + + try + { + var wallet_password = GenerateWalletPassword(10); + + Console.WriteLine("Inside GenWallet Method"); + var generateAutonomousDatabaseWalletDetails = new Oci.DatabaseService.Models.GenerateAutonomousDatabaseWalletDetails + { + GenerateType = Oci.DatabaseService.Models.GenerateAutonomousDatabaseWalletDetails.GenerateTypeEnum.Single, + Password = wallet_password + }; + + var generateAutonomousDatabaseWalletRequest = new Oci.DatabaseService.Requests.GenerateAutonomousDatabaseWalletRequest + { + AutonomousDatabaseId = adb_ocid, + GenerateAutonomousDatabaseWalletDetails = generateAutonomousDatabaseWalletDetails, + }; + + var response = await client.GenerateAutonomousDatabaseWallet(generateAutonomousDatabaseWalletRequest); + + using (var memoryStream = new MemoryStream()) + { + + response.InputStream.CopyTo(memoryStream); + Console.WriteLine("Generating zip file..."); + var fileName = $"gen_wallet.zip"; + string zipPath = Path.Combine(extractPath + "/" + fileName); + Console.WriteLine("Wallet location : {0}", zipPath); + using (var fs = new FileStream(zipPath, FileMode.Create, FileAccess.Write)) + { + memoryStream.WriteTo(fs); + } + Console.WriteLine("extracting : {0} to {1}", zipPath, extractPath); + + using (ZipArchive source = ZipFile.Open(zipPath, ZipArchiveMode.Read, null)) + { + foreach (ZipArchiveEntry entry in source.Entries) + { + string fullPath = Path.GetFullPath(Path.Combine(extractPath, entry.FullName)); + + if (Path.GetFileName(fullPath).Length != 0) + { + entry.ExtractToFile(fullPath, true); + } + } + } + + } + return "success"; + + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Generate wallet : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-adb-client-runsql-dotnet/Dockerfile b/samples/oci-adb-client-runsql-dotnet/Dockerfile new file mode 100644 index 0000000..f4f1ade --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/Dockerfile @@ -0,0 +1,12 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add RunSQL.csproj +RUN dotnet build RunSQL.csproj -c Release +RUN dotnet publish RunSQL.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENV TNS_ADMIN=/tmp +ENTRYPOINT ["dotnet", "RunSQL.dll"] +CMD ["RunSQL:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-adb-client-runsql-dotnet/Function.sln b/samples/oci-adb-client-runsql-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/Function.sln @@ -0,0 +1,17 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/samples/oci-adb-client-runsql-dotnet/Models/InputMessage.cs b/samples/oci-adb-client-runsql-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..e16fb43 --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/Models/InputMessage.cs @@ -0,0 +1,13 @@ +using System; + +namespace RunSQL +{ + + class InputMessage + { + public string sql { get; set; } + + + } + +} diff --git a/samples/oci-adb-client-runsql-dotnet/Models/OutputMessage.cs b/samples/oci-adb-client-runsql-dotnet/Models/OutputMessage.cs new file mode 100644 index 0000000..2a66ad1 --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/Models/OutputMessage.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +// using System.Collections; +// using System.Collections.Specialized; + +namespace RunSQL +{ + + class OutputDetails + { + public string sql { get; set; } + + public List> result { get; set; } + + } + + +} diff --git a/samples/oci-adb-client-runsql-dotnet/README.md b/samples/oci-adb-client-runsql-dotnet/README.md new file mode 100644 index 0000000..261a671 --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/README.md @@ -0,0 +1,153 @@ +# Execute a SQL statement against Autonomous Database using the Oracle client +This function connects to Oracle Autonomous Database (either Transaction Processing or Data Warehouse) using the Oracle Client and execute a SQL statement. + +As you make your way through this tutorial, look out for this icon ![user input icon](./images/userinput.png). +Whenever you see it, it's time for you to perform an action. + + +## Prerequisites +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) +* A - Set up your tenancy +* B - Create application +* C - Set up your Cloud Shell dev environment + + +## List Applications +Assuming your have successfully completed the prerequisites, you should see your +application in the list of applications. +``` +fn ls apps +``` + + +## Create or Update your Dynamic Group +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). + +When specifying the *Matching Rules*, we suggest matching all functions in a compartment with: +``` +ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaxxxxx'} +``` +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. + + +## Review and customize the function +Review the following files in the current folder: +* the code of the function, [RunSQL.cs](./RunSQL.cs) +* its dependencies, [RunSQL.csproj](./RunSQL.csproj) +* the function metadata, [func.yaml](./func.yaml) + + +## Deploy the function +In Cloud Shell, run the *fn deploy* command to build the function and its dependencies as a Docker image, +push the image to OCIR, and deploy the function to Oracle Functions in your application. + +![user input icon](./images/userinput.png) +``` +fn -v deploy --app +``` + + +## Create an Autonomous Database +Use an existing Autonomous Database (either Transaction Processing or Datawarehouse) or create a new one as follows. + +![user input icon](./images/userinput.png) + +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: +- Display Name +- Compartment +- Database Name +- Infrastructure Type: Shared +- Admin password +- License type + +![ADB create](./images/ADB-create.png) +For more information, go to https://docs.cloud.oracle.com/iaas/Content/Database/Tasks/adbcreating.htm + + +## Database Wallet and IAM Policies +The Database wallet is not part of the Docker image because it is not secure. The function downloads the wallet while it is executed. +The wallet can be retrieved from Object Storage or directly from Autonomous Database. + +![user input icon](./images/userinput.png) + +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*. + +![Download Wallet](./images/Download_wallet.png) + +Create a bucket in Object Storage and upload the wallet there. Note the name of the bucket and the wallet object name. +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. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to read objects in compartment where target.bucket.name='' +``` + +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'. +``` +Allow dynamic-group to use autonomous-databases in compartment where request.permission='AUTONOMOUS_DATABASE_CONTENT_READ' +``` + +For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Set the function configuration values +The function requires several config value to be set. + +![user input icon](./images/userinput.png) + +Use the *fn CLI* to set the config value: +``` +fn config function DBSVC +fn config function DBUSER +fn config function DBPWD_CYPHER +``` +Additionally, the DB wallet should be downloaded from the autonomous DB, specify the Autonomouns Database OCID: + +``` +fn config function ADB_OCID +``` +e.g. with a DB wallet in a bucket: +``` +fn config function myapp oci-adb-client-runsql-dotnet DBSVC "gregadw_high" +fn config function myapp oci-adb-client-runsql-dotnet DBUSER "admin" +fn config function myapp oci-adb-client-runsql-dotnet DBPWD_CYPHER "dfgjksadhfg4526897dfgkj" +fn config function myapp oci-adb-client-runsql-dotnet ADB_OCID "db-wallets" + +``` + + +## Invoke the function +![user input icon](./images/userinput.png) +``` + +echo '{"sql":""}' | fn invoke oci-adb-client-runsql-dotnet +``` +e.g.: +``` +echo '{"sql":"select sysdate from dual"}' | fn invoke myapp oci-adb-client-runsql-dotnet +``` + +Upon success, the function returns a JSON object similar to this: +``` +{ + "output": [ + { + "sql": "select sysdate from dual", + "result": [ + { + "SYSDATE": "10/27/2022 03:02:38" + } + ] + } + ] +} +``` + + +## Monitoring Functions + +Learn how to configure basic observability for your function using metrics, alarms and email alerts: +* [Basic Guidance for Monitoring your Functions](../basic-observability/functions.md) + diff --git a/samples/oci-adb-client-runsql-dotnet/RunSQL.cs b/samples/oci-adb-client-runsql-dotnet/RunSQL.cs new file mode 100644 index 0000000..7a713ee --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/RunSQL.cs @@ -0,0 +1,150 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Collections; +using System.Collections.Specialized; +using System; +using System.IO; +using System.IO.Compression; +using System.Data; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.DatabaseService; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Oracle.ManagedDataAccess.Client; + + +namespace RunSQL +{ + class Function + { + + public static String sqlDatoToJson(OracleDataReader dataReader) + { + var dataTable = new DataTable(); + dataTable.Load(dataReader); + string JSONString = string.Empty; + JSONString = JsonConvert.SerializeObject(dataTable); + return JSONString; + } + + public static string runsqlquery(string sql) + { + // var TNS_ADMIN = Environment.GetEnvironmentVariable("TNS_ADMIN"); + // var ADB_OCID = Environment.GetEnvironmentVariable("ADB_OCID"); + var DBUSER = Environment.GetEnvironmentVariable("DBUSER"); + var DBPWD_CYPHER = Environment.GetEnvironmentVariable("DBPWD_CYPHER"); + var DBSVC = Environment.GetEnvironmentVariable("DBSVC"); + + string conString = "User Id=" + DBUSER + ";Password=" + DBPWD_CYPHER + ";Data Source=" + DBSVC + ";Connection Timeout=30;"; + using (OracleConnection con = new OracleConnection(conString)) + { + + using (OracleCommand cmd = con.CreateCommand()) + { + try + { + con.Open(); + Console.WriteLine("Successfully connected to Oracle Autonomous Database"); + Console.WriteLine(); + + cmd.CommandText = sql; + OracleDataReader reader = cmd.ExecuteReader(); + string json_string = sqlDatoToJson(reader); + Console.WriteLine(json_string); + return json_string; + } + catch (Exception ex) + { + + Console.WriteLine(ex.Message); + List> rows = new List>(); + Dictionary col = new Dictionary(); + col.Add("error", ex.Message); + rows.Add(col); + string JSONString = JsonConvert.SerializeObject(rows); + return JSONString; + } + + } + } + + } + public string function_handler(InputMessage input) + { + var TNS_ADMIN = Environment.GetEnvironmentVariable("TNS_ADMIN"); + var ADB_OCID = Environment.GetEnvironmentVariable("ADB_OCID"); + var DBUSER = Environment.GetEnvironmentVariable("DBUSER"); + var DBPWD_CYPHER = Environment.GetEnvironmentVariable("DBPWD_CYPHER"); + var DBSVC = Environment.GetEnvironmentVariable("DBSVC"); + + _ = TNS_ADMIN ?? throw new ArgumentNullException(paramName: nameof(TNS_ADMIN), message: "TNS_ADMIN can't be null"); + _ = ADB_OCID ?? throw new ArgumentNullException(paramName: nameof(ADB_OCID), message: "Autonomous DB OCID can't be null"); + _ = DBUSER ?? throw new ArgumentNullException(paramName: nameof(DBUSER), message: "Autonomous DB DBUSER can't be null"); + _ = DBPWD_CYPHER ?? throw new ArgumentNullException(paramName: nameof(DBPWD_CYPHER), message: "Autonomous DB DBPWD_CYPHER can't be null"); + _ = DBSVC ?? throw new ArgumentNullException(paramName: nameof(DBSVC), message: "Autonomous DB DBSVC can't be null"); + + DatabaseClient client = DBClientHelper.GetDBClient(); + Task wallet_str = GenerateDBWalletHelper.GenWallet(client, ADB_OCID, TNS_ADMIN); + Console.WriteLine("Result of Generate wallet : {0}", wallet_str.Result); + + Dictionary> output = new Dictionary>(); + Dictionary result = new Dictionary(); + var query_result_list = new List(); + + string sql = input.sql; + string query_result = runsqlquery(sql); + Console.WriteLine("Result of query : {0}", query_result); + JArray a = JArray.Parse(query_result); + List> rows = new List>(); + + foreach (JObject o in a.Children()) + { + Dictionary col = new Dictionary(); + foreach (JProperty p in o.Properties()) + { + string name = p.Name; + string value = (string)p.Value; + col.Add(name, value); + + } + rows.Add(col); + } + + var query_detail = new OutputDetails(); + query_detail.sql = sql; + // query_detail.result = query_result; + query_detail.result = rows; + query_result_list.Add(query_detail); + output.Add("output", query_result_list); + // return query_result; + // return System.Text.Json.JsonSerializer.Serialize(output); + return JsonConvert.SerializeObject(output); + + + } + + + static void Main(string[] args) + { + + Console.WriteLine("******* HELLO FUNCTION **************"); + + var TNS_ADMIN = Environment.GetEnvironmentVariable("TNS_ADMIN"); + _ = TNS_ADMIN ?? throw new ArgumentNullException(paramName: nameof(TNS_ADMIN), message: "TNS_ADMIN can't be null"); + OracleConfiguration.TnsAdmin = TNS_ADMIN; + OracleConfiguration.WalletLocation = OracleConfiguration.TnsAdmin; + Fdk.Handle(args[0]); + + } + + + + } +} diff --git a/samples/oci-adb-client-runsql-dotnet/RunSQL.csproj b/samples/oci-adb-client-runsql-dotnet/RunSQL.csproj new file mode 100644 index 0000000..3624432 --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/RunSQL.csproj @@ -0,0 +1,21 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + + + diff --git a/samples/oci-adb-client-runsql-dotnet/func.yaml b/samples/oci-adb-client-runsql-dotnet/func.yaml new file mode 100644 index 0000000..ee21dea --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-adb-client-runsql-dotnet +version: 0.0.349 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: RunSQL:Function:function_handler +entrypoint: dotnet RunSQL.dll diff --git a/samples/oci-adb-client-runsql-dotnet/images/ADB-ORDS-URL.png b/samples/oci-adb-client-runsql-dotnet/images/ADB-ORDS-URL.png new file mode 100644 index 0000000..722db9e Binary files /dev/null and b/samples/oci-adb-client-runsql-dotnet/images/ADB-ORDS-URL.png differ diff --git a/samples/oci-adb-client-runsql-dotnet/images/ADB-create.png b/samples/oci-adb-client-runsql-dotnet/images/ADB-create.png new file mode 100644 index 0000000..52b2b75 Binary files /dev/null and b/samples/oci-adb-client-runsql-dotnet/images/ADB-create.png differ diff --git a/samples/oci-adb-client-runsql-dotnet/images/ADB-serviceconsole.png b/samples/oci-adb-client-runsql-dotnet/images/ADB-serviceconsole.png new file mode 100644 index 0000000..578473c Binary files /dev/null and b/samples/oci-adb-client-runsql-dotnet/images/ADB-serviceconsole.png differ diff --git a/samples/oci-adb-client-runsql-dotnet/images/userinput.png b/samples/oci-adb-client-runsql-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-adb-client-runsql-dotnet/images/userinput.png differ