diff --git a/README.md b/README.md index 46c38c2..603f50e 100644 --- a/README.md +++ b/README.md @@ -13,25 +13,25 @@ This repository provides examples demonstrating how to use Oracle Functions. * [Notifications Service](./samples/basic-observability/notifications.md) ## How To -| Description | Python | Java | -|------------------------------------------------------|:------:|:----:| -| Hello World |[sample](./samples/helloworld)|[sample](./samples/helloworld)| -| List OCI Compute instances |[sample](./samples/oci-list-instances-python)|[sample](./samples/oci-list-instances-java)| -| Control OCI Compute instances (start/stop/status) |[sample](./samples/oci-compute-control-python)| -| List OCI compartments |[sample](./samples/oci-list-compartments-python)| -| List objects in OCI Object Storage |[sample](./samples/oci-objectstorage-list-objects-python)|[sample](./samples/oci-objectstorage-list-objects-java)| -| Read an object in OCI Object Storage |[sample](./samples/oci-objectstorage-get-object-python)|[sample](./samples/oci-objectstorage-get-object-java)| -| Create an object in OCI Object Storage |[sample](./samples/oci-objectstorage-put-object-python)|[sample](./samples/oci-objectstorage-put-object-java)| -| Create a PAR in OCI Object Storage |[sample](./samples/oci-objectstorage-create-par-python)|| -| Copy object from one OCI Object Storage bucket to another |[sample](./samples/oci-objectstorage-copy-objects-python)|| -| Display an OCI Cloud Event |[sample](./samples/oci-event-display-python)| -| Invoke another Function using the OCI SDK |[sample](./samples/oci-invoke-function-python)||| -| Run a SQL statement against Autonomous DB using ORDS | [sample](./samples/oci-adb-ords-runsql-python) | -| Run a SQL statement against Autonomous DB using DB Client |[sample](./samples/oci-adb-client-runsql-python)|| -| 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) -| Get a secret from Vault |[sample](./samples/oci-vault-get-secret-python)| +| Description | Python | Java | Dotnet | +|------------------------------------------------------|:------:|:----:|:----:| +| Hello World |[sample](./samples/helloworld)|[sample](./samples/helloworld)|[sample](./samples/helloworld)| +| List OCI Compute instances |[sample](./samples/oci-list-instances-python)|[sample](./samples/oci-list-instances-java)|[sample](./samples/oci-list-instances-dotnet)| +| Control OCI Compute instances (start/stop/status) |[sample](./samples/oci-compute-control-python)||[sample](./samples/oci-compute-control-dotnet)| +| List OCI compartments |[sample](./samples/oci-list-compartments-python)||[sample](./samples/oci-list-compartments-dotnet)| +| List objects in OCI Object Storage |[sample](./samples/oci-objectstorage-list-objects-python)|[sample](./samples/oci-objectstorage-list-objects-java)|[sample](./samples/oci-objectstorage-list-objects-dotnet)| +| Read an object in OCI Object Storage |[sample](./samples/oci-objectstorage-get-object-python)|[sample](./samples/oci-objectstorage-get-object-java)|[sample](./samples/oci-objectstorage-get-object-dotnet)| +| Create an object in OCI Object Storage |[sample](./samples/oci-objectstorage-put-object-python)|[sample](./samples/oci-objectstorage-put-object-java)|[sample](./samples/oci-objectstorage-put-object-dotnet)| +| Create a PAR in OCI Object Storage |[sample](./samples/oci-objectstorage-create-par-python)||[sample](./samples/oci-objectstorage-create-par-dotnet)| +| Copy object from one OCI Object Storage bucket to another |[sample](./samples/oci-objectstorage-copy-objects-python)||[sample](./samples/oci-objectstorage-copy-objects-dotnet)| +| 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)|| [sample](./samples/oci-adb-client-runsql-dotnet)| +| Publish a notification using ONS |[sample](./samples/oci-ons-publish-python)||[sample](./samples/oci-ons-publish-dotnet)| +| Send an email using Email Delivery Service |[sample](./samples/oci-email-send-python)||[sample](./samples/oci-email-send-dotnet)| +| Decrypt cipher using Vault keys |[sample](./samples/oci-vault-decrypt-python)||[sample](./samples/oci-vault-decrypt-dotnet)| +| Get a secret from Vault |[sample](./samples/oci-vault-get-secret-python)||[sample](./samples/oci-vault-get-secret-dotnet)| | Write IAM policies that enables Functions in a tenancy to access resources in other tenancies ||[sample](./samples/oci-cross-tenancy-policies-java) | Trace a function with APM and add custom child spans using Zipkin |[sample](./samples/trace-functions-with-apm)| diff --git a/samples/helloworld/README.md b/samples/helloworld/README.md index eb90d48..02d0c4a 100644 --- a/samples/helloworld/README.md +++ b/samples/helloworld/README.md @@ -105,6 +105,8 @@ fn -v deploy --app myapp #### Test +![user input icon](./images/userinput.png) + To invoke the Java `hello-java` function, run: ``` fn invoke myapp hello-java @@ -123,6 +125,52 @@ The `Hello, Bob!` output is displayed. Congratulations! You've just created, deployed, and invoked a Java HelloWorld function using Oracle Functions! +### Dotnet + +#### Code + +The [Dotnet folder](./dotnet) contains the files to deploy the `hello-dotnet` function in Dotnet: +* the code of the function, [HelloWorld.cs](./dotnet/HelloWorld.cs) +* its dependencies, [HelloWorld.csproj](./dotnet/HelloWorld.csproj) +* the function metadata, [func.yaml](./dotnet/func.yaml) + + +#### Deploy + +![user input icon](./images/userinput.png) + +Change directory to *dotnet* and deploy the `hello-dotnet` function using: + +``` +fn -v deploy --app +``` + +e.g. +``` +fn -v deploy --app myapp +``` + +#### Test + +![user input icon](./images/userinput.png) + +To invoke the Dotnet `hello-dotnet` function, run: +``` +fn invoke myapp hello-dotnet +``` +The Dotnet version displays `Hello, world!` + + + +To invoke the Dotnet function with a payload, run: +``` +echo -n "Bob" | fn invoke myapp hello-dotnet +``` +The `Hello, Bob!` output is displayed. + + +Congratulations! You've just created, deployed, and invoked a Dotnet HelloWorld function using Oracle Functions! + ### Node, Golang, and Ruby diff --git a/samples/helloworld/dotnet/Dockerfile b/samples/helloworld/dotnet/Dockerfile new file mode 100644 index 0000000..6233d59 --- /dev/null +++ b/samples/helloworld/dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add HelloWorld.csproj +RUN dotnet build HelloWorld.csproj -c Release +RUN dotnet publish HelloWorld.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "HelloWorld.dll"] +CMD ["Function:Greeter:greet"] \ No newline at end of file diff --git a/samples/helloworld/dotnet/Function.sln b/samples/helloworld/dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/helloworld/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/helloworld/dotnet/HelloWorld.cs b/samples/helloworld/dotnet/HelloWorld.cs new file mode 100644 index 0000000..05dec2f --- /dev/null +++ b/samples/helloworld/dotnet/HelloWorld.cs @@ -0,0 +1,13 @@ +using Fnproject.Fn.Fdk; + +using System.Runtime.CompilerServices; +namespace Function { + class Greeter { + public string greet(string input) { + return string.Format("Hello {0}!", + string.IsNullOrEmpty(input) ? "World" : input.Trim()); + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + } +} diff --git a/samples/helloworld/dotnet/HelloWorld.csproj b/samples/helloworld/dotnet/HelloWorld.csproj new file mode 100644 index 0000000..5477b7d --- /dev/null +++ b/samples/helloworld/dotnet/HelloWorld.csproj @@ -0,0 +1,11 @@ + + + + Exe + netcoreapp3.1 + + + + + + diff --git a/samples/helloworld/dotnet/func.yaml b/samples/helloworld/dotnet/func.yaml new file mode 100644 index 0000000..384e775 --- /dev/null +++ b/samples/helloworld/dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: hello-dotnet +version: 0.0.4 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: Function:Greeter:greet +entrypoint: dotnet HelloWorld.dll 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..dbe76c1 --- /dev/null +++ b/samples/oci-adb-client-runsql-dotnet/README.md @@ -0,0 +1,139 @@ +# 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 directly from Autonomous Database while it is executed. + +![user input icon](./images/userinput.png) + + +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-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/Download_wallet.png b/samples/oci-adb-client-runsql-dotnet/images/Download_wallet.png new file mode 100644 index 0000000..d7a524f Binary files /dev/null and b/samples/oci-adb-client-runsql-dotnet/images/Download_wallet.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 diff --git a/samples/oci-adb-ords-runsql-dotnet/Common/HttpClientHelper.cs b/samples/oci-adb-ords-runsql-dotnet/Common/HttpClientHelper.cs new file mode 100644 index 0000000..b776df9 --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/Common/HttpClientHelper.cs @@ -0,0 +1,33 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.Common.Http.Signing; +using System.Net.Http; + + +namespace RunSQL +{ + public class HttpClientHelper + { + public static HttpClient GetHttpClient() + { + try + { + // return new KmsCryptoClient(ResourcePrincipalAuthenticationDetailsProvider.GetProvider(), new ClientConfiguration(), crypto_endpoint); + return new HttpClient(OciHttpClientHandler.FromAuthProvider(ResourcePrincipalAuthenticationDetailsProvider.GetProvider())); + } + catch (Exception ex) + { + Console.WriteLine("Unable To Create Resource Principal Provider: {0}", ex.Message); + Console.WriteLine("Defaulting to Instance Provider"); + return new HttpClient(OciHttpClientHandler.FromAuthProvider(new InstancePrincipalsAuthenticationDetailsProvider())); + } + } + + } +} diff --git a/samples/oci-adb-ords-runsql-dotnet/Dockerfile b/samples/oci-adb-ords-runsql-dotnet/Dockerfile new file mode 100644 index 0000000..6b69d5a --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/Dockerfile @@ -0,0 +1,11 @@ +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/ +ENTRYPOINT ["dotnet", "RunSQL.dll"] +CMD ["RunSQL:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-adb-ords-runsql-dotnet/Function.sln b/samples/oci-adb-ords-runsql-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-adb-ords-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-ords-runsql-dotnet/Models/InputMessage.cs b/samples/oci-adb-ords-runsql-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..e16fb43 --- /dev/null +++ b/samples/oci-adb-ords-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-ords-runsql-dotnet/Models/OutputMessage.cs b/samples/oci-adb-ords-runsql-dotnet/Models/OutputMessage.cs new file mode 100644 index 0000000..a9452a4 --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/Models/OutputMessage.cs @@ -0,0 +1,20 @@ +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; } + + // public string error { get; set; } + public List error { get; set; } + + } + +} diff --git a/samples/oci-adb-ords-runsql-dotnet/README.md b/samples/oci-adb-ords-runsql-dotnet/README.md new file mode 100644 index 0000000..0693694 --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/README.md @@ -0,0 +1,273 @@ +# Function that executes a SQL statement using ORDS + +This function connects to an Autonomous Database using ORDS and executes 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) + +In the code, we assume the schema and the database username are the same. Feel free to change this. + + +## 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. + +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 + +On the Autonomous Database detail page, click *Service Console* +![ADB Service Console](./images/ADB-serviceconsole.png) + +On the Service Console, navigate to Development and copy the ORDS Base URL, we will need it in the next section. +![ADB ORDS URL](./images/ADB-ORDS-URL.png) + +The *admin* schema is enabled for REST access by default, so you can test the function using the *admin* schema. For Production, it is recommended to create a separate schema and enable REST Service. For more information on how to do this, check the documentation at https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/19.1/index.html. + + +## Set the function configuration values +The function requires the config value *ords-base-url*, *db-schema* and *db-pwd-cypher* to be set. +![user input icon](../images/userinput.png) + +Use the *fn CLI* to set the config value: +``` +fn config function ords_base_url +fn config function db_schema +fn config function db_pwd_cypher +``` +e.g. +``` +fn config function myapp oci-adb-ords-runsql-dotnet ords_base_urll "https://xxxxxx-db123456.adb.us-region.oraclecloudapps.com/ords/" +fn config function myapp oci-adb-ords-runsql-dotnet db_schema "admin" +fn config function myapp oci-adb-ords-runsql-dotnet db_pwd_cypher "xxxxxxxxx" +``` + + +## Invoke the function +![user input icon](./images/userinput.png) +``` + +echo '{"sql":""}' | fn invoke oci-adb-ords-runsql-dotnet +``` +e.g.: +``` +echo '{"sql":"select sysdate from dual"}' | fn invoke myapp oci-adb-ords-runsql-dotnet +``` + +Upon success, the function returns a JSON object similar to this: +```json +{"output":[{"sql":"select sysdate from dual","result":[{"sysdate":"10/20/2022 00:29:35"}],"error":[]}]} +``` +Here is another example with the table EMP created in the ADMIN schema. Ref. https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/19.4/qsord/get-started-with-oracle-rest-data-services.html#GUID-14BE2F08-842E-4D2F-86B9-EA245B8487F9. + +```bash +echo '{"sql":"select * from emp"}' | fn invoke myapp oci-adb-ords-runsql-dotnet | jq . +``` +``` +{ + "output": [ + { + "sql": "select * from emp", + "result": [ + { + "empno": "7369", + "ename": "SMITH", + "job": "CLERK", + "mgr": "7902", + "hiredate": "12/17/1980 00:00:00", + "sal": "800", + "comm": null, + "deptno": "20" + }, + { + "empno": "7499", + "ename": "ALLEN", + "job": "SALESMAN", + "mgr": "7698", + "hiredate": "02/20/1981 00:00:00", + "sal": "1600", + "comm": "300", + "deptno": "30" + }, + { + "empno": "7521", + "ename": "WARD", + "job": "SALESMAN", + "mgr": "7698", + "hiredate": "02/22/1981 00:00:00", + "sal": "1250", + "comm": "500", + "deptno": "30" + }, + { + "empno": "7566", + "ename": "JONES", + "job": "MANAGER", + "mgr": "7839", + "hiredate": "04/02/1981 00:00:00", + "sal": "2975", + "comm": null, + "deptno": "20" + }, + { + "empno": "7654", + "ename": "MARTIN", + "job": "SALESMAN", + "mgr": "7698", + "hiredate": "09/28/1981 00:00:00", + "sal": "1250", + "comm": "1400", + "deptno": "30" + }, + { + "empno": "7698", + "ename": "BLAKE", + "job": "MANAGER", + "mgr": "7839", + "hiredate": "05/01/1981 00:00:00", + "sal": "2850", + "comm": null, + "deptno": "30" + }, + { + "empno": "7782", + "ename": "CLARK", + "job": "MANAGER", + "mgr": "7839", + "hiredate": "06/09/1981 00:00:00", + "sal": "2450", + "comm": null, + "deptno": "10" + }, + { + "empno": "7788", + "ename": "SCOTT", + "job": "ANALYST", + "mgr": "7566", + "hiredate": "04/19/1987 00:00:00", + "sal": "3000", + "comm": null, + "deptno": "20" + }, + { + "empno": "7839", + "ename": "KING", + "job": "PRESIDENT", + "mgr": null, + "hiredate": "11/17/1981 00:00:00", + "sal": "5000", + "comm": null, + "deptno": "10" + }, + { + "empno": "7844", + "ename": "TURNER", + "job": "SALESMAN", + "mgr": "7698", + "hiredate": "09/08/1981 00:00:00", + "sal": "1500", + "comm": "0", + "deptno": "30" + }, + { + "empno": "7876", + "ename": "ADAMS", + "job": "CLERK", + "mgr": "7788", + "hiredate": "05/23/1987 00:00:00", + "sal": "1100", + "comm": null, + "deptno": "20" + }, + { + "empno": "7900", + "ename": "JAMES", + "job": "CLERK", + "mgr": "7698", + "hiredate": "12/03/1981 00:00:00", + "sal": "950", + "comm": null, + "deptno": "30" + }, + { + "empno": "7902", + "ename": "FORD", + "job": "ANALYST", + "mgr": "7566", + "hiredate": "12/03/1981 00:00:00", + "sal": "3000", + "comm": null, + "deptno": "20" + }, + { + "empno": "7934", + "ename": "MILLER", + "job": "CLERK", + "mgr": "7782", + "hiredate": "01/23/1982 00:00:00", + "sal": "1300", + "comm": null, + "deptno": "10" + } + ], + "error": [] + } + ] +} +``` + + +## 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-ords-runsql-dotnet/RunSQL.cs b/samples/oci-adb-ords-runsql-dotnet/RunSQL.cs new file mode 100644 index 0000000..314852c --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/RunSQL.cs @@ -0,0 +1,107 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Collections; +using System.Collections.Specialized; +using System; +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.Common.Http.Signing; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + + +namespace RunSQL +{ + class Function + { + public string function_handler(InputMessage input) + { + + + Dictionary> output = new Dictionary>(); + Dictionary result = new Dictionary(); + var query_result_list = new List(); + + var ords_base_url = Environment.GetEnvironmentVariable("ords_base_url"); + var db_schema = Environment.GetEnvironmentVariable("db_schema"); + var db_pwd_cypher = Environment.GetEnvironmentVariable("db_pwd_cypher"); + + + string sql = input.sql; + + string dbsqlurl = ords_base_url + db_schema + "/_/sql"; + string auth_str = db_schema + ":" + db_pwd_cypher; + string authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth_str)); + + HttpClient client = HttpClientHelper.GetHttpClient(); + HttpClient client1 = new HttpClient(); + + Task response_value = InvokeHttpCall(client1, dbsqlurl, authString, sql); + string response_str = response_value.Result; + JObject json = JObject.Parse(response_str); + var query_detail = new OutputDetails(); + foreach (JToken item in json.SelectToken("items")) + { + + Console.WriteLine(item.SelectToken("statementText")); + query_detail.sql = item.SelectToken("statementText").ToString(); + if (item.SelectToken("resultSet.items") != null) + { + query_detail.result = item.SelectToken("resultSet.items").ToObject>>(); + query_detail.error = new List(); + Console.WriteLine(item.SelectToken("resultSet.items").ToString(Newtonsoft.Json.Formatting.Indented)); + } + else if (item.SelectToken("errorDetails") != null) + { + query_detail.result = new List>(); + string error = item.SelectToken("errorDetails").ToString(); + query_detail.error = new List(); + query_detail.error.Add(error); + } + query_result_list.Add(query_detail); + + + } + output.Add("output", query_result_list); + return System.Text.Json.JsonSerializer.Serialize(output); + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + public static async Task InvokeHttpCall(HttpClient client, string dbsqlurl, string authString, string sql) + { + try + { + + HttpRequestMessage request = new HttpRequestMessage + { + Method = HttpMethod.Post, + RequestUri = new Uri(dbsqlurl), + Content = new StringContent(sql, Encoding.UTF8, "application/sql"), + }; + + client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString); + + var response = await client.SendAsync(request); + + Console.WriteLine("Is rest call successful: {0}", response.IsSuccessStatusCode); + var responseJson = await response.Content.ReadAsStringAsync(); + Console.WriteLine("Parsed Response: {0}", responseJson); + + return responseJson; + } + catch (OciException ex) + { + Console.WriteLine("Unable To Invoke HTTP : {0}", ex.Message); + return "Failed " + ex.Message; + } + } + + } +} diff --git a/samples/oci-adb-ords-runsql-dotnet/RunSQL.csproj b/samples/oci-adb-ords-runsql-dotnet/RunSQL.csproj new file mode 100644 index 0000000..e00218f --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/RunSQL.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-adb-ords-runsql-dotnet/func.yaml b/samples/oci-adb-ords-runsql-dotnet/func.yaml new file mode 100644 index 0000000..467140d --- /dev/null +++ b/samples/oci-adb-ords-runsql-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-adb-ords-runsql-dotnet +version: 0.0.183 +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-ords-runsql-dotnet/images/ADB-ORDS-URL.png b/samples/oci-adb-ords-runsql-dotnet/images/ADB-ORDS-URL.png new file mode 100644 index 0000000..722db9e Binary files /dev/null and b/samples/oci-adb-ords-runsql-dotnet/images/ADB-ORDS-URL.png differ diff --git a/samples/oci-adb-ords-runsql-dotnet/images/ADB-create.png b/samples/oci-adb-ords-runsql-dotnet/images/ADB-create.png new file mode 100644 index 0000000..52b2b75 Binary files /dev/null and b/samples/oci-adb-ords-runsql-dotnet/images/ADB-create.png differ diff --git a/samples/oci-adb-ords-runsql-dotnet/images/ADB-serviceconsole.png b/samples/oci-adb-ords-runsql-dotnet/images/ADB-serviceconsole.png new file mode 100644 index 0000000..578473c Binary files /dev/null and b/samples/oci-adb-ords-runsql-dotnet/images/ADB-serviceconsole.png differ diff --git a/samples/oci-adb-ords-runsql-dotnet/images/userinput.png b/samples/oci-adb-ords-runsql-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-adb-ords-runsql-dotnet/images/userinput.png differ diff --git a/samples/oci-compute-control-dotnet/Common/ComputeClient.cs b/samples/oci-compute-control-dotnet/Common/ComputeClient.cs new file mode 100644 index 0000000..29d99e6 --- /dev/null +++ b/samples/oci-compute-control-dotnet/Common/ComputeClient.cs @@ -0,0 +1,29 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.CoreService; + + +namespace ControlInstance +{ + public class ComputeClientHelper + { + public static ComputeClient GetComputeClient() + { + try{ + + return new ComputeClient(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 ComputeClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-compute-control-dotnet/ControlInstance.cs b/samples/oci-compute-control-dotnet/ControlInstance.cs new file mode 100644 index 0000000..550b4ad --- /dev/null +++ b/samples/oci-compute-control-dotnet/ControlInstance.cs @@ -0,0 +1,56 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.CoreService; + +namespace ControlInstance +{ + class Function + { + public string function_handler(InputMessage input) + { + string command; + string instance_ocid; + Dictionary output = new Dictionary(); + try{ + command=input.command.ToLower(); + instance_ocid=input.instance_ocid; + ComputeClient client = ComputeClientHelper.GetComputeClient(); + if (command=="status") + { + Task instance_state = ComputeController.GetComputeStatus(client,instance_ocid); + output.Add("result", instance_state.Result); + } + else if (command=="start") + { + Task instance_state = ComputeController.StartCompute(client,instance_ocid); + output.Add("result", instance_state.Result); + } + else if (command=="stop") + { + Task instance_state = ComputeController.StopCompute(client,instance_ocid); + output.Add("result", instance_state.Result); + } + else + { + output.Add("result", "Invalid Command"); + } + + return JsonSerializer.Serialize(output); + + } + catch(Exception ex){ + Console.WriteLine($"Invalid Payload: {ex.Message}"); + output.Add("result", "Invalid Payload"); + return JsonSerializer.Serialize(output); + } + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-compute-control-dotnet/ControlInstance.csproj b/samples/oci-compute-control-dotnet/ControlInstance.csproj new file mode 100644 index 0000000..6b4798f --- /dev/null +++ b/samples/oci-compute-control-dotnet/ControlInstance.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/oci-compute-control-dotnet/Controller/ComputeController.cs b/samples/oci-compute-control-dotnet/Controller/ComputeController.cs new file mode 100644 index 0000000..880485b --- /dev/null +++ b/samples/oci-compute-control-dotnet/Controller/ComputeController.cs @@ -0,0 +1,94 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.CoreService; +using Oci.CoreService.Models; +using Oci.CoreService.Requests; +using Oci.CoreService.Responses; + +namespace ControlInstance +{ + public class ComputeController + { + public static async Task GetComputeStatus(ComputeClient client,string instance_ocid) + { + try + { + var getInstanceRequest = new Oci.CoreService.Requests.GetInstanceRequest + { + InstanceId = instance_ocid + }; + + var getInstancesResponse = await client.GetInstance(getInstanceRequest); + return getInstancesResponse.Instance.LifecycleState.ToString(); + } + catch (OciException ex) + { + return ex.ServiceCode; + } + } + + public static async Task StartCompute(ComputeClient client,string instance_ocid) + { + Console.WriteLine($"Starting Instance Id: {instance_ocid}"); + try + { + if (GetComputeStatus(client,instance_ocid).Result == "Stopped" ) + { + var instanceActionRequest = new Oci.CoreService.Requests.InstanceActionRequest + { + InstanceId = instance_ocid, + Action = "START", + }; + + var getInstancesResponse = await client.InstanceAction(instanceActionRequest); + Console.WriteLine($"Start Response Code : {getInstancesResponse.Instance.LifecycleState.ToString()}"); + return GetComputeStatus(client,instance_ocid).Result; + } + else + { + Console.WriteLine($"Instance Is Already Running"); + return "Instance Is Already Running"; + } + } + catch (OciException ex) + { + return ex.Message; + } + } + + public static async Task StopCompute(ComputeClient client,string instance_ocid) + { + Console.WriteLine($"Stopping Instance Id: {instance_ocid}"); + try + { + if (GetComputeStatus(client,instance_ocid).Result == "Running" ) + { + var instanceActionRequest = new Oci.CoreService.Requests.InstanceActionRequest + { + InstanceId = instance_ocid, + Action = "STOP", + }; + + var getInstancesResponse = await client.InstanceAction(instanceActionRequest); + Console.WriteLine($"Stop Response Code : {getInstancesResponse.Instance.LifecycleState.ToString()}"); + return GetComputeStatus(client,instance_ocid).Result; + } + else + { + Console.WriteLine($"Instance Is Already Stopped"); + return "Instance Is Already Stopped"; + } + } + catch (OciException ex) + { + return ex.Message; + } + } + + } +} diff --git a/samples/oci-compute-control-dotnet/Dockerfile b/samples/oci-compute-control-dotnet/Dockerfile new file mode 100644 index 0000000..a8be4cd --- /dev/null +++ b/samples/oci-compute-control-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add ControlInstance.csproj +RUN dotnet build ControlInstance.csproj -c Release +RUN dotnet publish ControlInstance.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "ControlInstance.dll"] +CMD ["ControlInstance:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-compute-control-dotnet/Function.sln b/samples/oci-compute-control-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-compute-control-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-compute-control-dotnet/Models/InputMessage.cs b/samples/oci-compute-control-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..89a9d2a --- /dev/null +++ b/samples/oci-compute-control-dotnet/Models/InputMessage.cs @@ -0,0 +1,14 @@ +using System; + +namespace ControlInstance +{ + + class InputMessage + { + public string command { get; set; } + + public string instance_ocid { get; set; } + + } + +} diff --git a/samples/oci-compute-control-dotnet/README.md b/samples/oci-compute-control-dotnet/README.md new file mode 100644 index 0000000..a582790 --- /dev/null +++ b/samples/oci-compute-control-dotnet/README.md @@ -0,0 +1,111 @@ +# Function that controls a Compute instance + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/iaas/tools/dotnet/latest/api/index.html). +It returns a list of all instances within the compartment that calls the function. + +The function calls the following OCI Dotnet SDK classes: +* [ResourcePrincipalAuthenticationDetailsProvider](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [ComputeClient](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.CoreService.ComputeClient.html) to interact with Compute + +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 steps 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 you 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'} +``` + + +## Create or Update IAM Policies + +Create a new policy that allows the dynamic group to `inspect instances` in +the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to inspect instances in compartment +``` +e.g. +``` +Allow dynamic-group demo-func-dyn-group to inspect instances in compartment demo-func-compartment +``` + +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize your function + +Review the following files in the current folder: +- [ControlInstance.csproj](./ControlInstance.csproj) specifies all the dependencies for your function +- [func.yaml](./func.yaml) that contains metadata about your function and declares properties +- [ControlInstance.cs](./ControlInstance.cs) which contains the Dotnet code + +The name of your function *oci-compute-control-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Test + +Use the *fn* CLI to invoke your function with your app name and the compartment OCID: + +![user input icon](./images/userinput.png) +``` +echo '{"command":"", "instance_ocid":""}' | fn invoke +``` +e.g. +``` +echo '{"command":"stop", "instance_ocid":"ocid1.fnfunc.oc1.iad.aaaaaxxxxx"}' | fn invoke myapp oci-compute-control-dotnet +``` +The supported values for command are "status", "start" and "stop". + + +## 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-compute-control-dotnet/func.yaml b/samples/oci-compute-control-dotnet/func.yaml new file mode 100644 index 0000000..46cf06a --- /dev/null +++ b/samples/oci-compute-control-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-compute-control-dotnet +version: 0.0.2 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: ControlInstance:Function:function_handler +entrypoint: dotnet ControlInstance.dll diff --git a/samples/oci-compute-control-dotnet/images/userinput.png b/samples/oci-compute-control-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-compute-control-dotnet/images/userinput.png differ diff --git a/samples/oci-email-send-dotnet/Dockerfile b/samples/oci-email-send-dotnet/Dockerfile new file mode 100644 index 0000000..7c1e978 --- /dev/null +++ b/samples/oci-email-send-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add SendEmail.csproj +RUN dotnet build SendEmail.csproj -c Release +RUN dotnet publish SendEmail.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "SendEmail.dll"] +CMD ["SendEmail:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-email-send-dotnet/Function.sln b/samples/oci-email-send-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-email-send-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-email-send-dotnet/Models/InputMessage.cs b/samples/oci-email-send-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..366a5d2 --- /dev/null +++ b/samples/oci-email-send-dotnet/Models/InputMessage.cs @@ -0,0 +1,17 @@ +using System; + +namespace SendEmail +{ + + class InputMessage + { + public string sender_email { get; set; } + public string sender_name { get; set; } + public string recipient { get; set; } + public string subject { get; set; } + public string body { get; set; } + + + } + +} diff --git a/samples/oci-email-send-dotnet/Models/Output.cs b/samples/oci-email-send-dotnet/Models/Output.cs new file mode 100644 index 0000000..fc7035a --- /dev/null +++ b/samples/oci-email-send-dotnet/Models/Output.cs @@ -0,0 +1,17 @@ +using System; + +namespace SendEmail +{ + + class Output + { + public string message + { + get; + set; + } + + public Output(string message) { this.message = message; } + } + +} diff --git a/samples/oci-email-send-dotnet/README.md b/samples/oci-email-send-dotnet/README.md new file mode 100644 index 0000000..92f84e8 --- /dev/null +++ b/samples/oci-email-send-dotnet/README.md @@ -0,0 +1,107 @@ +# Function that sends an email using OCI Email Delivery +This function sends an email using the OCI Email Delivery Service. + +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 +``` + + +## Review and customize the function +Review the following files in the current folder: +* the code of the function, [SendEmail.cs](./SendEmail.cs) +* its dependencies, [SendEmail.csproj](./SendEmail.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 +``` + + +## Configure the Email Delivery Service +![user input icon](./images/userinput.png) + +On the OCI console, navigate to *Email Delivery*. Click `Create Approved Sender`. +![create topic](./images/create_approved_sender-1.png) + +Enter the email address of the approved sender and click `Create Approved Sender`. + +![create subscription](./images/create_approved_sender-2.png) + +The creation of the *Approved Sender* takes several minutes. + +Navigate to *Email Delivery* > *Email Configuration* and write down the SMTP server name. +Click `Manage SMTP Credentials` which takes you to your *User Details*, click on `Generate SMTP Credentials` and enter a description. Your SMTP Username and password will be displayed, write them down, you will need them to configure the function. + + +## Set the function configuration values +The function requires the following configuration values to be set: +- smtp-username +- smtp-password +- smtp-host +- smtp-port (25 or 587) + +![user input icon](./images/userinput.png) + +Use the *fn CLI* to set the config value: +``` +fn config function smtp-username +fn config function smtp-password +fn config function smtp-host +fn config function smtp-port +``` +e.g. +``` +fn config function myapp oci-email-send-dotnet smtp-username "ocid1.user.oc1..aaaaaaaause3s2zw3kn3qvxxc5c5ouc4pu6byfxiuplncjkzzxinijhmqj5q@ocid1.tenancy.oc1..aaaaaaaaydrjm77otncda2xn7qtv7l3hqnd3zxn2u6siwdhniibwfv4wwhta.7g.com" +fn config function myapp oci-email-send-dotnet smtp-password '{$M$mWmvlN&P#o>!14F8' +fn config function myapp oci-email-send-dotnet smtp-host "smtp.us-phoenix-1.oraclecloud.com" +fn config function myapp oci-email-send-dotnet smtp-port 587 +``` + + +## Invoke the function +The function requires the following keys in the payload to be invoked: +- sender_email +- sender_name +- recipient +- subject +- body + +![user input icon](./images/userinput.png) +``` +echo '{ "sender_email":"", "sender_name":"", "recipient":"", +"subject":"", "body":"" }' | fn invoke oci-email-send-dotnet +``` +e.g.: +``` +echo '{ "sender_email":"no-reply@oracle.com", "sender-name":"Test", "recipient":"abc@xyz.com", +"subject":"Hello!", "body":"This is a test email" }' | fn invoke myapp oci-email-send-dotnet +``` + +Upon success, the function will return "Email successfully sent!" and the recipient will receive an email. + + +## 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-email-send-dotnet/SendEmail.cs b/samples/oci-email-send-dotnet/SendEmail.cs new file mode 100644 index 0000000..15bebe3 --- /dev/null +++ b/samples/oci-email-send-dotnet/SendEmail.cs @@ -0,0 +1,67 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using MailKit.Net.Smtp; +using MailKit.Security; +using MailKit; +using MimeKit; + +namespace SendEmail +{ + class Function + { + public Output function_handler(InputMessage input) + { + + string smtp_password = Environment.GetEnvironmentVariable("smtp-password"); + string smtp_user_name = Environment.GetEnvironmentVariable("smtp-username"); + string smtp_endpoint = Environment.GetEnvironmentVariable("smtp-host"); + string smtp_port_str = Environment.GetEnvironmentVariable("smtp-port"); + int smtp_port = Int32.Parse(smtp_port_str); + string from_email = input.sender_email; + string from_name = input.sender_name; + + var message = new MimeMessage(); + message.From.Add(new MailboxAddress(from_name, from_email)); + message.To.Add(new MailboxAddress("Dear recipient", input.recipient)); + message.Subject = input.subject; + + message.Body = new TextPart("plain") + { + Text = @input.body + }; + + try + { + + + using (var client = new SmtpClient()) + { + client.Connect(smtp_endpoint, smtp_port, SecureSocketOptions.StartTls); + client.Authenticate(smtp_user_name, smtp_password); + client.Send(message); + client.Disconnect(true); + } + + return new Output(string.Format( + "Mail Sent to {0}!", + string.IsNullOrEmpty(input.recipient) ? "" : input.recipient.Trim())); + + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + return new Output(string.Format(ex.Message)); + + } + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-email-send-dotnet/SendEmail.csproj b/samples/oci-email-send-dotnet/SendEmail.csproj new file mode 100644 index 0000000..0a33e1a --- /dev/null +++ b/samples/oci-email-send-dotnet/SendEmail.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/oci-email-send-dotnet/func.yaml b/samples/oci-email-send-dotnet/func.yaml new file mode 100644 index 0000000..18e5fb7 --- /dev/null +++ b/samples/oci-email-send-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-email-send-dotnet +version: 0.0.107 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: SendEmail:Function:function_handler +entrypoint: dotnet SendEmail.dll diff --git a/samples/oci-email-send-dotnet/images/create_approved_sender-1.png b/samples/oci-email-send-dotnet/images/create_approved_sender-1.png new file mode 100644 index 0000000..1588283 Binary files /dev/null and b/samples/oci-email-send-dotnet/images/create_approved_sender-1.png differ diff --git a/samples/oci-email-send-dotnet/images/create_approved_sender-2.png b/samples/oci-email-send-dotnet/images/create_approved_sender-2.png new file mode 100644 index 0000000..eb2b9f1 Binary files /dev/null and b/samples/oci-email-send-dotnet/images/create_approved_sender-2.png differ diff --git a/samples/oci-email-send-dotnet/images/userinput.png b/samples/oci-email-send-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-email-send-dotnet/images/userinput.png differ diff --git a/samples/oci-event-display-dotnet/Dockerfile b/samples/oci-event-display-dotnet/Dockerfile new file mode 100644 index 0000000..9ebfdd5 --- /dev/null +++ b/samples/oci-event-display-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add LogEvents.csproj +RUN dotnet build LogEvents.csproj -c Release +RUN dotnet publish LogEvents.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "LogEvents.dll"] +CMD ["LogEvents:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-event-display-dotnet/Function.sln b/samples/oci-event-display-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-event-display-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-event-display-dotnet/LogEvents.cs b/samples/oci-event-display-dotnet/LogEvents.cs new file mode 100644 index 0000000..360ff5e --- /dev/null +++ b/samples/oci-event-display-dotnet/LogEvents.cs @@ -0,0 +1,38 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Newtonsoft.Json; + + +namespace LogEvents +{ + class Function + { + public string function_handler(String input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + dynamic event_json = JsonConvert.DeserializeObject(input); + var event_str = Newtonsoft.Json.JsonConvert.SerializeObject(event_json, Newtonsoft.Json.Formatting.Indented); + Console.WriteLine("event type: {0} ", event_json.eventType); + Console.WriteLine("compartment name: {0} ", event_json.data.compartmentName); + Console.WriteLine("Full Cloud event json data:"); + Console.WriteLine(event_str); + var object_detail = new ObjectDetails(); + + object_detail.result = event_str; + + output.Add("results", object_details_list); + return System.Text.Json.JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-event-display-dotnet/LogEvents.csproj b/samples/oci-event-display-dotnet/LogEvents.csproj new file mode 100644 index 0000000..7d80a83 --- /dev/null +++ b/samples/oci-event-display-dotnet/LogEvents.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + diff --git a/samples/oci-event-display-dotnet/Models/ObjectDetails.cs b/samples/oci-event-display-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..adc608f --- /dev/null +++ b/samples/oci-event-display-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,13 @@ +using System; + +namespace LogEvents +{ + + class ObjectDetails + { + public string result { get; set; } + + + } + +} diff --git a/samples/oci-event-display-dotnet/README.md b/samples/oci-event-display-dotnet/README.md new file mode 100644 index 0000000..29f7069 --- /dev/null +++ b/samples/oci-event-display-dotnet/README.md @@ -0,0 +1,99 @@ +# Function that logs the details of a Cloud event + +This function logs the details of a Cloud event. + +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 your function +Review the following files in the current folder: +* the code of the function, [LogEvents.cs](./LogEvents.cs) +* its dependencies, [LogEvents.csproj](./LogEvents.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 the Cloud Event rule +Create a Cloud Event rule on the console navigating to Application Integration > Event Service. Click *Create Rule*. + +![user input icon](./images/1-create_event_rule.png) + +Assign a display name and a description, customize the Rule Conditions or leave them empty to match all events. In the *Actions* section, set the *Action type* as "Functions", select your *Function Compartment*, your *Function Application*, and your *Function ID*. + +![user input icon](./images/2-create_event_rule.png) + +## Test +Go to the logs, you should see events from your compartment. You can create some resource such as an Object Storage bucket to generate an event. +For example: +```json +event type: com.oraclecloud.objectstorage.createbucket +compartment name: greg-verstraeten +Full Cloud event json data: +{ + "eventType": "com.oraclecloud.objectstorage.createbucket", + "cloudEventsVersion": "0.1", + "eventTypeVersion": "2.0", + "source": "ObjectStorage", + "eventTime": "2019-12-12T22:25:08.502Z", + "contentType": "application/json", + "data": { + "compartmentId": "ocid1.compartment.oc1..aaaaaaaal66tw5k262fsjsrgdqan5cbbfxvoydbhxx5hijth2h3qlbwmtdlq", + "compartmentName": "greg-verstraeten", + "resourceName": "bucket-20191212-1425", + "resourceId": "/n/devrel/b/", + "availabilityDomain": "PHX-AD-2", + "additionalDetails": { + "bucketName": "bucket-20191212-1425", + "publicAccessType": "NoPublicAccess", + "namespace": "devrel", + "eTag": "47b12898-1925-449a-a761-7d1db57f0695" + } + }, + "eventID": "fca0653f-85c5-9466-8812-001c51d338a4", + "extensions": { + "compartmentId": "ocid1.compartment.oc1..aaaaaaaal66tw5k262fsjsrgdqan5cbbfxvoydbhxx5hijth2h3qlbwmtdlq" + } +} +``` + + +## 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-event-display-dotnet/func.yaml b/samples/oci-event-display-dotnet/func.yaml new file mode 100644 index 0000000..137c932 --- /dev/null +++ b/samples/oci-event-display-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-event-display-dotnet +version: 0.0.71 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: LogEvents:Function:function_handler +entrypoint: dotnet LogEvents.dll diff --git a/samples/oci-event-display-dotnet/images/1-create_event_rule.png b/samples/oci-event-display-dotnet/images/1-create_event_rule.png new file mode 100644 index 0000000..33bf415 Binary files /dev/null and b/samples/oci-event-display-dotnet/images/1-create_event_rule.png differ diff --git a/samples/oci-event-display-dotnet/images/2-create_event_rule.png b/samples/oci-event-display-dotnet/images/2-create_event_rule.png new file mode 100644 index 0000000..d1bace0 Binary files /dev/null and b/samples/oci-event-display-dotnet/images/2-create_event_rule.png differ diff --git a/samples/oci-event-display-dotnet/images/userinput.png b/samples/oci-event-display-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-event-display-dotnet/images/userinput.png differ diff --git a/samples/oci-invoke-function-dotnet/Common/FunctionClient.cs b/samples/oci-invoke-function-dotnet/Common/FunctionClient.cs new file mode 100644 index 0000000..a1fca7c --- /dev/null +++ b/samples/oci-invoke-function-dotnet/Common/FunctionClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.FunctionsService; + + +namespace InvokeFn +{ + public class FunctionClientHelper + { + public static FunctionsInvokeClient GetFunctionClient(string function_endpoint) + { + try + { + return new FunctionsInvokeClient(ResourcePrincipalAuthenticationDetailsProvider.GetProvider(), new ClientConfiguration(), function_endpoint); + } + catch (Exception ex) + { + Console.WriteLine("Unable To Create Resource Principal Provider: {0}", ex.Message); + Console.WriteLine("Defaulting to Instance Provider"); + return new FunctionsInvokeClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration(), function_endpoint); + } + } + + } +} diff --git a/samples/oci-invoke-function-dotnet/Controller/InvokeFnHelper.cs b/samples/oci-invoke-function-dotnet/Controller/InvokeFnHelper.cs new file mode 100644 index 0000000..394f264 --- /dev/null +++ b/samples/oci-invoke-function-dotnet/Controller/InvokeFnHelper.cs @@ -0,0 +1,64 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.FunctionsService; +using Oci.FunctionsService.Models; +using Oci.FunctionsService.Requests; +using Oci.FunctionsService.Responses; + + +namespace InvokeFn +{ + public class InvokeFnHelper + { + public static Stream GenerateStreamFromString(string s) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + writer.Write(s); + writer.Flush(); + stream.Position = 0; + return stream; + } + public static async Task TriggerFunction(FunctionsInvokeClient client, string function_ocid, string function_endpoint, string function_body) + + { + + try + { + + Console.WriteLine("Function endpoint : {0}", function_endpoint); + var invokeFunctionRequest = new Oci.FunctionsService.Requests.InvokeFunctionRequest + { + FunctionId = function_ocid, + InvokeFunctionBody = GenerateStreamFromString(function_body), + }; + + var response = await client.InvokeFunction(invokeFunctionRequest); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + response.InputStream.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + return Encoding.UTF8.GetString(bytes); + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Invoke Function : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-invoke-function-dotnet/Dockerfile b/samples/oci-invoke-function-dotnet/Dockerfile new file mode 100644 index 0000000..8e8ad3b --- /dev/null +++ b/samples/oci-invoke-function-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add InvokeFn.csproj +RUN dotnet build InvokeFn.csproj -c Release +RUN dotnet publish InvokeFn.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "InvokeFn.dll"] +CMD ["InvokeFn:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-invoke-function-dotnet/Function.sln b/samples/oci-invoke-function-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-invoke-function-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-invoke-function-dotnet/InvokeFn.cs b/samples/oci-invoke-function-dotnet/InvokeFn.cs new file mode 100644 index 0000000..eeed17e --- /dev/null +++ b/samples/oci-invoke-function-dotnet/InvokeFn.cs @@ -0,0 +1,38 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.FunctionsService; +using Oci.FunctionsService.Models; + +namespace InvokeFn +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + string function_ocid = input.function_ocid; + string function_endpoint = input.function_endpoint; + string function_body = input.function_body; + + FunctionsInvokeClient client = FunctionClientHelper.GetFunctionClient(function_endpoint); + Task object_str = InvokeFnHelper.TriggerFunction(client, function_ocid, function_endpoint, function_body); + var object_detail = new ObjectDetails(); + object_detail.result = object_str.Result; + object_details_list.Add(object_detail); + + output.Add("results", object_details_list); + return System.Text.Json.JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-invoke-function-dotnet/InvokeFn.csproj b/samples/oci-invoke-function-dotnet/InvokeFn.csproj new file mode 100644 index 0000000..6f13ee1 --- /dev/null +++ b/samples/oci-invoke-function-dotnet/InvokeFn.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-invoke-function-dotnet/Models/InputMessage.cs b/samples/oci-invoke-function-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..3486553 --- /dev/null +++ b/samples/oci-invoke-function-dotnet/Models/InputMessage.cs @@ -0,0 +1,15 @@ +using System; + +namespace InvokeFn +{ + + class InputMessage + { + public string function_ocid { get; set; } + public string function_endpoint { get; set; } + public string function_body { get; set; } + + + } + +} diff --git a/samples/oci-invoke-function-dotnet/Models/ObjectDetails.cs b/samples/oci-invoke-function-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..c802176 --- /dev/null +++ b/samples/oci-invoke-function-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,14 @@ +using System; + +namespace InvokeFn +{ + + class ObjectDetails + { + + public string result { get; set; } + + + } + +} diff --git a/samples/oci-invoke-function-dotnet/README.md b/samples/oci-invoke-function-dotnet/README.md new file mode 100644 index 0000000..52a57df --- /dev/null +++ b/samples/oci-invoke-function-dotnet/README.md @@ -0,0 +1,98 @@ +# Function that invoke another function using the OCI Dotnet SDK + +This function invokes another function using the OCI Dotnet SDK and the Functions Resource Principal. + +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. + + +## Create or Update IAM Policies +Now that your dynamic group is created, create a new policy that allows the +dynamic group to use any resources you are interested in receiving +information about, in this case we will grant access to `invoke functions` in +the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to use fn-invocation in compartment +``` + +For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function +Review the following files in the current folder: +* the code of the function, [InvokeFn.cs](./InvokeFn.cs) +* its dependencies, [InvokeFn.csproj](./InvokeFn.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 +``` + + +## Invoke the function + +The function requires the following keys in the payload when invoked: +- function_ocid, the OCID `ocid1.fnfunc.oc1.phx.aaaxxx` of the other function we are calling (HelloWorld for example) +- function_endpoint, the endpoint `https://xxxxxx.ap-osaka-1.functions.oci.oraclecloud.com/xxxxx/actions/invoke` of the other function +- function_body, the body for the invocation of the other function + +To test the function, we need another function to invoke. If you do not have any, create a +HelloWorld function for example and get its OCID and endpoint and add it to the [test.json](./test.json) file. + +![functions information](./images/function-information.png) + +![user input icon](./images/userinput.png) +``` +fn invoke oci-invoke-function-python < test.json +``` +e.g.: +``` +fn invoke myapp oci-invoke-function-python < test.json +``` + +Assuming the other function we are calling is a HelloWorld function, you should see the following output : +```json +{"message": "Hello World"} +``` + + +## 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-invoke-function-dotnet/func.yaml b/samples/oci-invoke-function-dotnet/func.yaml new file mode 100644 index 0000000..e02e4c0 --- /dev/null +++ b/samples/oci-invoke-function-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-invoke-function-dotnet +version: 0.0.97 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: InvokeFn:Function:function_handler +entrypoint: dotnet InvokeFn.dll diff --git a/samples/oci-invoke-function-dotnet/images/function-information.png b/samples/oci-invoke-function-dotnet/images/function-information.png new file mode 100644 index 0000000..1f551e4 Binary files /dev/null and b/samples/oci-invoke-function-dotnet/images/function-information.png differ diff --git a/samples/oci-invoke-function-dotnet/images/userinput.png b/samples/oci-invoke-function-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-invoke-function-dotnet/images/userinput.png differ diff --git a/samples/oci-invoke-function-dotnet/test.json b/samples/oci-invoke-function-dotnet/test.json new file mode 100644 index 0000000..1a6d78f --- /dev/null +++ b/samples/oci-invoke-function-dotnet/test.json @@ -0,0 +1,5 @@ +{ + "function_ocid": "ocid1.fnfunc.oc1.ap-osaka-1.aaaaaaaa..", + "function_endpoint": "https://xxxxxx.ap-osaka-1.functions.oci.oraclecloud.com/xxxxx/actions/invoke", + "function_body": "" +} \ No newline at end of file diff --git a/samples/oci-list-compartments-dotnet/Common/IdentityClient.cs b/samples/oci-list-compartments-dotnet/Common/IdentityClient.cs new file mode 100644 index 0000000..bc9a45f --- /dev/null +++ b/samples/oci-list-compartments-dotnet/Common/IdentityClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.IdentityService; + + +namespace ListCompartment +{ + public class IdentityClientHelper + { + public static IdentityClient GetIdentityClient() + { + try + { + return new IdentityClient(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 IdentityClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-list-compartments-dotnet/Controller/GetComparments.cs b/samples/oci-list-compartments-dotnet/Controller/GetComparments.cs new file mode 100644 index 0000000..78a547e --- /dev/null +++ b/samples/oci-list-compartments-dotnet/Controller/GetComparments.cs @@ -0,0 +1,61 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.IdentityService; +using Oci.IdentityService.Models; +using Oci.IdentityService.Requests; +using Oci.IdentityService.Responses; + + +namespace ListCompartment +{ + public class ListComparmentHelper + { + public static async Task> GetComparmentList(IdentityClient client, string parent_compartment_ocid) + + { + string nextpage = ""; + List comp_list = new List(); + while (true) + { + try + { + + var listCompartmentsRequest = new Oci.IdentityService.Requests.ListCompartmentsRequest + { + CompartmentId = parent_compartment_ocid, + AccessLevel = Oci.IdentityService.Requests.ListCompartmentsRequest.AccessLevelEnum.Any, + CompartmentIdInSubtree = true, + Page = nextpage, + Limit = 1000, + SortBy = Oci.IdentityService.Requests.ListCompartmentsRequest.SortByEnum.Name, + SortOrder = Oci.IdentityService.Requests.ListCompartmentsRequest.SortOrderEnum.Asc, + LifecycleState = Oci.IdentityService.Models.Compartment.LifecycleStateEnum.Active + }; + + var response = await client.ListCompartments(listCompartmentsRequest); + nextpage = response.OpcNextPage; + comp_list.AddRange(response.Items); + if (string.IsNullOrEmpty(nextpage)) + { + break; + } + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Get Compartment List: {0}", ex.Message); + return new List(); + } + } + return comp_list; + } + + + } +} diff --git a/samples/oci-list-compartments-dotnet/Dockerfile b/samples/oci-list-compartments-dotnet/Dockerfile new file mode 100644 index 0000000..2ecd429 --- /dev/null +++ b/samples/oci-list-compartments-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add ListCompartment.csproj +RUN dotnet build ListCompartment.csproj -c Release +RUN dotnet publish ListCompartment.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "ListCompartment.dll"] +CMD ["ListCompartment:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-list-compartments-dotnet/Function.sln b/samples/oci-list-compartments-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-list-compartments-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-list-compartments-dotnet/ListCompartment.cs b/samples/oci-list-compartments-dotnet/ListCompartment.cs new file mode 100644 index 0000000..5121321 --- /dev/null +++ b/samples/oci-list-compartments-dotnet/ListCompartment.cs @@ -0,0 +1,42 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.IdentityService; +using Oci.IdentityService.Models; + +namespace ListCompartment +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var compartment_details_list = new List(); + string parent_compartment_ocid; + parent_compartment_ocid = input.compartment_ocid; + Console.WriteLine($"Getting Compartment tree for parent ocid : {parent_compartment_ocid}"); + IdentityClient client = IdentityClientHelper.GetIdentityClient(); + Task> compartment_list = ListComparmentHelper.GetComparmentList(client, parent_compartment_ocid); + + foreach (Compartment comp in compartment_list.Result) + { + var compartment_detail = new CompartmentDetails(); + compartment_detail.name = comp.Name; + compartment_detail.ocid = comp.Id; + compartment_details_list.Add(compartment_detail); + } + + output.Add("results", compartment_details_list); + return JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-list-compartments-dotnet/ListCompartment.csproj b/samples/oci-list-compartments-dotnet/ListCompartment.csproj new file mode 100644 index 0000000..6b4798f --- /dev/null +++ b/samples/oci-list-compartments-dotnet/ListCompartment.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/oci-list-compartments-dotnet/Models/CompartmentDetails.cs b/samples/oci-list-compartments-dotnet/Models/CompartmentDetails.cs new file mode 100644 index 0000000..1787324 --- /dev/null +++ b/samples/oci-list-compartments-dotnet/Models/CompartmentDetails.cs @@ -0,0 +1,14 @@ +using System; + +namespace ListCompartment +{ + + class CompartmentDetails + { + public string name { get; set; } + + public string ocid { get; set; } + + } + +} diff --git a/samples/oci-list-compartments-dotnet/Models/InputMessage.cs b/samples/oci-list-compartments-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..46a19d3 --- /dev/null +++ b/samples/oci-list-compartments-dotnet/Models/InputMessage.cs @@ -0,0 +1,12 @@ +using System; + +namespace ListCompartment +{ + + class InputMessage + { + public string compartment_ocid { get; set; } + + } + +} diff --git a/samples/oci-list-compartments-dotnet/README.md b/samples/oci-list-compartments-dotnet/README.md new file mode 100644 index 0000000..ea46884 --- /dev/null +++ b/samples/oci-list-compartments-dotnet/README.md @@ -0,0 +1,91 @@ +# Function that returns the list of compartments in a user's tenancy + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/index.html). +It returns a list of all compartments within the tenancy. + +The function calls the following OCI Dotnet SDK classes: +* [Resource Principals](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [Identity Client](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.IdentityService.IdentityClient.html) to interact with Identity and Access Management + +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 steps 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 you 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'} +``` + + +## Create or Update Policies +This function does not require any particular IAM policy. + + +## Review and customize the function +Review the following files in the current folder: +- [ListCompartment.csproj](./ListCompartment.csproj) which specifies all the dependencies for your function +- [func.yaml](./func.yaml) which contains metadata about your function and declares properties +- [ListCompartment.cs](./ListCompartment.cs) which is your actual Dotnet function + +The name of your function *oci-list-compartments-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Test +![user input icon](./images/userinput.png) +``` +echo '{"compartment_ocid":""}' |fn invoke +``` +e.g. +``` +echo '{"compartment_ocid":""}' | fn invoke myapp oci-list-compartments-dotnet +``` +You should see all the compartments in your tenancy listed in the terminal. + + +## 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-list-compartments-dotnet/func.yaml b/samples/oci-list-compartments-dotnet/func.yaml new file mode 100644 index 0000000..aeb2dad --- /dev/null +++ b/samples/oci-list-compartments-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-list-compartment-dotnet +version: 0.0.48 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: ListCompartment:Function:function_handler +entrypoint: dotnet ListCompartment.dll diff --git a/samples/oci-list-compartments-dotnet/images/userinput.png b/samples/oci-list-compartments-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-list-compartments-dotnet/images/userinput.png differ diff --git a/samples/oci-list-instances-dotnet/Dockerfile b/samples/oci-list-instances-dotnet/Dockerfile new file mode 100644 index 0000000..ca55e3c --- /dev/null +++ b/samples/oci-list-instances-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add ListInstance.csproj +RUN dotnet build ListInstance.csproj -c Release +RUN dotnet publish ListInstance.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "ListInstance.dll"] +CMD ["ListInstance:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-list-instances-dotnet/Function.sln b/samples/oci-list-instances-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-list-instances-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-list-instances-dotnet/ListInstance.cs b/samples/oci-list-instances-dotnet/ListInstance.cs new file mode 100644 index 0000000..226304d --- /dev/null +++ b/samples/oci-list-instances-dotnet/ListInstance.cs @@ -0,0 +1,59 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.CoreService; + +using Oci.CoreService.Models; +using Oci.CoreService.Requests; +using Oci.CoreService.Responses; + +namespace ListInstance +{ + class Function + { + public string function_handler(string compartment_id) + { + Task> inst_list = list_instances(compartment_id); + var instance_details_list = new List(); + Dictionary> output = new Dictionary>(); + + foreach (Instance inst in inst_list.Result) + { + var instance_detail = new InstanceDetails(); + instance_detail.name = inst.DisplayName; + instance_detail.ocid = inst.Id; + instance_detail.state = inst.LifecycleState.ToString(); + instance_details_list.Add(instance_detail); + } + output.Add("instances", instance_details_list); + return JsonSerializer.Serialize(output); + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + public async Task> list_instances(string comp_id) + { + + var provider = ResourcePrincipalAuthenticationDetailsProvider.GetProvider(); + var listInstancesRequest = new Oci.CoreService.Requests.ListInstancesRequest + { + CompartmentId = comp_id + }; + // Create a service client and send the request. + using (var client = new ComputeClient(provider, new ClientConfiguration())) + { + var listInstancesResponse = await client.ListInstances(listInstancesRequest); + return listInstancesResponse.Items; + } + + + } + } +} diff --git a/samples/oci-list-instances-dotnet/ListInstance.csproj b/samples/oci-list-instances-dotnet/ListInstance.csproj new file mode 100644 index 0000000..6b4798f --- /dev/null +++ b/samples/oci-list-instances-dotnet/ListInstance.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/oci-list-instances-dotnet/Models/InstanceDetails.cs b/samples/oci-list-instances-dotnet/Models/InstanceDetails.cs new file mode 100644 index 0000000..aaa18d3 --- /dev/null +++ b/samples/oci-list-instances-dotnet/Models/InstanceDetails.cs @@ -0,0 +1,16 @@ +using System; + +namespace ListInstance +{ + + class InstanceDetails + { + public string name { get; set; } + + public string ocid { get; set; } + + public string state { get; set; } + + } + +} diff --git a/samples/oci-list-instances-dotnet/README.md b/samples/oci-list-instances-dotnet/README.md new file mode 100644 index 0000000..fb8d634 --- /dev/null +++ b/samples/oci-list-instances-dotnet/README.md @@ -0,0 +1,111 @@ +# Function that returns the list of instances in the calling Compartment + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/iaas/tools/dotnet/latest/api/index.html). +It returns a list of all instances within the compartment that calls the function. + +The function calls the following OCI Dotnet SDK classes: +* [ResourcePrincipalAuthenticationDetailsProvider](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [ComputeClient](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.CoreService.ComputeClient.html) to interact with Compute + +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 steps 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 you 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'} +``` + + +## Create or Update IAM Policies + +Create a new policy that allows the dynamic group to `inspect instances` in +the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to inspect instances in compartment +``` +e.g. +``` +Allow dynamic-group demo-func-dyn-group to inspect instances in compartment demo-func-compartment +``` + +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize your function + +Review the following files in the current folder: +- [ListInstance.csproj](./ListInstance.csproj) specifies all the dependencies for your function +- [func.yaml](./func.yaml) that contains metadata about your function and declares properties +- [ListInstance.cs](./ListInstance.cs) which contains the Dotnet code + +The name of your function *oci-list-instances-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Test + +Use the *fn* CLI to invoke your function with your app name and the compartment OCID: + +![user input icon](./images/userinput.png) +``` +echo -n '' | fn invoke +``` +e.g. +``` +echo -n 'ocid1.compartment.oc1...2jn3htfoobar' | fn invoke myapp oci-list-instances-dotnet +``` +You should see a map of instances in your compartment appear on your terminal. + + +## 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-list-instances-dotnet/func.yaml b/samples/oci-list-instances-dotnet/func.yaml new file mode 100644 index 0000000..ba47b4d --- /dev/null +++ b/samples/oci-list-instances-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-list-instances-dotnet +version: 0.0.19 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: ListInstance:Function:function_handler +entrypoint: dotnet ListInstance.dll diff --git a/samples/oci-list-instances-dotnet/images/userinput.png b/samples/oci-list-instances-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-list-instances-dotnet/images/userinput.png differ diff --git a/samples/oci-objectstorage-copy-objects-dotnet/Common/ObjectStorageClient.cs b/samples/oci-objectstorage-copy-objects-dotnet/Common/ObjectStorageClient.cs new file mode 100644 index 0000000..13216fb --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/Common/ObjectStorageClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; + + +namespace CopyObjects +{ + public class ObjectStorageClientHelper + { + public static ObjectStorageClient GetObjectStorageClient() + { + try + { + return new ObjectStorageClient(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 ObjectStorageClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-objectstorage-copy-objects-dotnet/Controller/CopyObjectsHelper.cs b/samples/oci-objectstorage-copy-objects-dotnet/Controller/CopyObjectsHelper.cs new file mode 100644 index 0000000..dca4594 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/Controller/CopyObjectsHelper.cs @@ -0,0 +1,60 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; +using Oci.ObjectstorageService.Requests; +using Oci.ObjectstorageService.Responses; + + +namespace CopyObjects +{ + public class CopyObjectsHelper + { + public static async Task CopyObject(ObjectStorageClient client, string src_bucketName, string dest_bucketName, string namespaceName, string objectName) + + { + + try + { + + var copyObjectDetails = new Oci.ObjectstorageService.Models.CopyObjectDetails + { + DestinationBucket = dest_bucketName, + DestinationNamespace = namespaceName, + SourceObjectName = objectName, + DestinationObjectName = objectName, + DestinationRegion = Environment.GetEnvironmentVariable("REGION"), + }; + + var copyObjectRequest = new Oci.ObjectstorageService.Requests.CopyObjectRequest + { + NamespaceName = namespaceName, + BucketName = src_bucketName, + CopyObjectDetails = copyObjectDetails, + }; + + var response = await client.CopyObject(copyObjectRequest); + var opcWorkRequestIdValue = response.OpcWorkRequestId; + + return opcWorkRequestIdValue; + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Put Object : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-objectstorage-copy-objects-dotnet/CopyObjects.cs b/samples/oci-objectstorage-copy-objects-dotnet/CopyObjects.cs new file mode 100644 index 0000000..d6452fd --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/CopyObjects.cs @@ -0,0 +1,43 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; + +namespace CopyObjects +{ + class Function + { + public string function_handler(String input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + + dynamic event_json = JsonConvert.DeserializeObject(input); + + string src_bucketName = event_json.data.additionalDetails["bucketName"]; + string dest_bucketName = event_json.data.additionalDetails["bucketName"] + "_IMMUTABLE"; + string namespaceName = event_json.data.additionalDetails["namespace"]; + string objectName = event_json.data.resourceName; + + ObjectStorageClient client = ObjectStorageClientHelper.GetObjectStorageClient(); + Task object_str = CopyObjectsHelper.CopyObject(client, src_bucketName, dest_bucketName, namespaceName, objectName); + var object_detail = new ObjectDetails(); + object_detail.result = object_str.Result; + object_details_list.Add(object_detail); + + output.Add("results", object_details_list); + return System.Text.Json.JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-objectstorage-copy-objects-dotnet/CopyObjects.csproj b/samples/oci-objectstorage-copy-objects-dotnet/CopyObjects.csproj new file mode 100644 index 0000000..1b6c309 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/CopyObjects.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/oci-objectstorage-copy-objects-dotnet/Dockerfile b/samples/oci-objectstorage-copy-objects-dotnet/Dockerfile new file mode 100644 index 0000000..d5223f0 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add CopyObjects.csproj +RUN dotnet build CopyObjects.csproj -c Release +RUN dotnet publish CopyObjects.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "CopyObjects.dll"] +CMD ["CopyObjects:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-objectstorage-copy-objects-dotnet/Function.sln b/samples/oci-objectstorage-copy-objects-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-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-objectstorage-copy-objects-dotnet/Models/ObjectDetails.cs b/samples/oci-objectstorage-copy-objects-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..ab85e96 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,14 @@ +using System; + +namespace CopyObjects +{ + + class ObjectDetails + { + + public string result { get; set; } + + + } + +} diff --git a/samples/oci-objectstorage-copy-objects-dotnet/README.md b/samples/oci-objectstorage-copy-objects-dotnet/README.md new file mode 100644 index 0000000..c4ed9e3 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/README.md @@ -0,0 +1,157 @@ +# Function that copies an object in an OCI Object Storage bucket to another bucket using the OCI Dotnet SDK +This function can be used to create *"Immutable Copies"* of objects in an OCI bucket by copying them, during creation/update, to another bucket with defined retention policies. This is especially useful when using the [Oracle Backup Cloud Service](https://docs.oracle.com/en/cloud/paas/db-backup-cloud/csdbb/oracle-database-backup-cloud-service.html) to ensure backups are maintained for a specified amount of time. + + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/index.html). +It creates an object in a bucket in Object Storage and returns a message with a status. + + +The function calls the following OCI Dotnet SDK classes: +* [Resource Principals](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [Object Storage Client](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.ObjectstorageService.ObjectStorageClient.html) to interact with Object Storage + +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 + +1. Before you deploy this sample function, make sure you have run steps 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 + +2. Have your Oracle Object Storage Namespace available. This can be found by +logging into your [cloud account](https://console.us-ashburn-1.oraclecloud.com/), +under your user profile, click on your Tenancy. Your Object Storage Namespace +is shown there. + + +## List Applications + +Assuming you 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'} +``` + + +## Create or Update IAM Policies + +Create a new policy that allows the dynamic group to `manage objects` and `manage buckets` in the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow service objectstorage- to manage object-family in compartment +Allow dynamic-group to manage objects in compartment +Allow dynamic-group to manage buckets in compartment +``` +e.g. +``` +Allow service objectstorage-eu-frankfurt-1 to manage object-family in compartment demo-func-compartment +Allow dynamic-group demo-func-dyn-group to manage objects in compartment demo-func-compartment +Allow dynamic-group demo-func-dyn-group to manage buckets in compartment demo-func-compartment +``` +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function + +Review the following files in the current folder: + +- [CopyObjects.csproj](./CopyObjects.csproj) specifies all the dependencies for your function +- [func.yaml](./func.yaml) that contains metadata about your function and declares properties +- [CopyObjects.cs](./CopyObjects.cs) which is your actual Python function + +The name of your function *oci-objectstorage-copy-objects-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) + +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + +## Set the function configuration values +The function requires the config value *REGION* to be set. + +![user input icon](./images/userinput.png) + +Use the *fn* CLI to set the config value: +``` +fn config function REGION +``` +e.g. +``` +fn config function myapp oci-objectstorage-create-par-dotnet REGION 'eu-frankfurt-1' +``` + +## Create Object Store Buckets + +![user input icon](./images/userinput.png) + +From the OCI Console > Storage > Object Storage > Create Bucket with bucket name = "TEST" and enable "Emit Object Events" + +From the OCI Console > Storage > Object Storage > Create Bucket with bucket name = "TEST_IMMUTABLE" and apply a retention policy + +## Create an Event Rule on Bucket + +![user input icon](./images/userinput.png) + +From the OCI Console > Observability & Management > Event Service > Create Rule: + + Display Name: IMMUTABLE-OBJECT_STORE + Rule Conditions: + Event Type: Object Storage: Object - Create; Object - Update + Attribute: bucketName: TEST + Actions ( as per the `fn -v deploy --app `) : + Function: (root): + +![event](./images/create_rule.png) + +## Test + +![user input icon](./images/userinput.png) + +From the OCI Console > Storage > Object Storage > TEST + +Objects -> Upload; Drop file to upload + +From the OCI Console > Storage > Object Storage > TEST_IMMUTABLE + +__The file uploaded to the TEST bucket should now be present in the TEST_IMMUTABLE bucket.__ + + +## 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-objectstorage-copy-objects-dotnet/func.yaml b/samples/oci-objectstorage-copy-objects-dotnet/func.yaml new file mode 100644 index 0000000..24ae2b7 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-objectstorage-copy-objects-dotnet +version: 0.0.84 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: CopyObjects:Function:function_handler +entrypoint: dotnet CopyObjects.dll diff --git a/samples/oci-objectstorage-copy-objects-dotnet/images/create_rule.png b/samples/oci-objectstorage-copy-objects-dotnet/images/create_rule.png new file mode 100644 index 0000000..1a79f30 Binary files /dev/null and b/samples/oci-objectstorage-copy-objects-dotnet/images/create_rule.png differ diff --git a/samples/oci-objectstorage-copy-objects-dotnet/images/userinput.png b/samples/oci-objectstorage-copy-objects-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-objectstorage-copy-objects-dotnet/images/userinput.png differ diff --git a/samples/oci-objectstorage-create-par-dotnet/Common/ObjectStorageClient.cs b/samples/oci-objectstorage-create-par-dotnet/Common/ObjectStorageClient.cs new file mode 100644 index 0000000..0beeed9 --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/Common/ObjectStorageClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; + + +namespace CreatePAR +{ + public class ObjectStorageClientHelper + { + public static ObjectStorageClient GetObjectStorageClient() + { + try + { + return new ObjectStorageClient(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 ObjectStorageClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-objectstorage-create-par-dotnet/Controller/CreatePARHelper.cs b/samples/oci-objectstorage-create-par-dotnet/Controller/CreatePARHelper.cs new file mode 100644 index 0000000..2acd09e --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/Controller/CreatePARHelper.cs @@ -0,0 +1,60 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; +using Oci.ObjectstorageService.Requests; +using Oci.ObjectstorageService.Responses; + + +namespace CreatePAR +{ + public class CreatePARHelper + { + + public static async Task CreatePAR(ObjectStorageClient client, string bucketName, string namespaceName, string parName, int lifetime, string region) + + { + + try + { + DateTime currentTime = DateTime.Now; + DateTime parexpiry = currentTime.AddMinutes(lifetime); + string object_storage_endpoint = "https://objectstorage." + region + ".oraclecloud.com"; + var createPreauthenticatedRequestDetails = new Oci.ObjectstorageService.Models.CreatePreauthenticatedRequestDetails + { + Name = parName, + BucketListingAction = Oci.ObjectstorageService.Models.PreauthenticatedRequest.BucketListingActionEnum.ListObjects, + AccessType = Oci.ObjectstorageService.Models.CreatePreauthenticatedRequestDetails.AccessTypeEnum.AnyObjectWrite, + TimeExpires = parexpiry + }; + var createPreauthenticatedRequestRequest = new Oci.ObjectstorageService.Requests.CreatePreauthenticatedRequestRequest + { + NamespaceName = namespaceName, + BucketName = bucketName, + CreatePreauthenticatedRequestDetails = createPreauthenticatedRequestDetails, + }; + + var response = await client.CreatePreauthenticatedRequest(createPreauthenticatedRequestRequest); + return object_storage_endpoint + response.PreauthenticatedRequest.AccessUri; + + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Put Object : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-objectstorage-create-par-dotnet/CreatePAR.cs b/samples/oci-objectstorage-create-par-dotnet/CreatePAR.cs new file mode 100644 index 0000000..c9bf4eb --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/CreatePAR.cs @@ -0,0 +1,41 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; + +namespace CreatePAR +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + string parName = input.PARName; + string bucketName = Environment.GetEnvironmentVariable("BUCKET_NAME"); + string namespaceName = Environment.GetEnvironmentVariable("NAMESPACE"); + int lifetime = Int32.Parse(Environment.GetEnvironmentVariable("LIFETIME")); + string region = Environment.GetEnvironmentVariable("REGION"); + ObjectStorageClient client = ObjectStorageClientHelper.GetObjectStorageClient(); + Task object_str = CreatePARHelper.CreatePAR(client, bucketName, namespaceName, parName, lifetime, region); + var object_detail = new ObjectDetails(); + object_detail.parname = parName; + object_detail.bucketname = bucketName; + object_detail.parurl = object_str.Result; + object_details_list.Add(object_detail); + + output.Add("results", object_details_list); + return JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-objectstorage-create-par-dotnet/CreatePAR.csproj b/samples/oci-objectstorage-create-par-dotnet/CreatePAR.csproj new file mode 100644 index 0000000..84b6070 --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/CreatePAR.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-objectstorage-create-par-dotnet/Dockerfile b/samples/oci-objectstorage-create-par-dotnet/Dockerfile new file mode 100644 index 0000000..9e25431 --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add CreatePAR.csproj +RUN dotnet build CreatePAR.csproj -c Release +RUN dotnet publish CreatePAR.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "CreatePAR.dll"] +CMD ["CreatePAR:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-objectstorage-create-par-dotnet/Function.sln b/samples/oci-objectstorage-create-par-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-objectstorage-create-par-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-objectstorage-create-par-dotnet/Models/InputMessage.cs b/samples/oci-objectstorage-create-par-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..14f0668 --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/Models/InputMessage.cs @@ -0,0 +1,12 @@ +using System; + +namespace CreatePAR +{ + + class InputMessage + { + public string PARName { get; set; } + + } + +} diff --git a/samples/oci-objectstorage-create-par-dotnet/Models/ObjectDetails.cs b/samples/oci-objectstorage-create-par-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..51663bd --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,16 @@ +using System; + +namespace CreatePAR +{ + + class ObjectDetails + { + public string parname { get; set; } + + public string bucketname { get; set; } + public string parurl { get; set; } + + + } + +} diff --git a/samples/oci-objectstorage-create-par-dotnet/README.md b/samples/oci-objectstorage-create-par-dotnet/README.md new file mode 100644 index 0000000..ab92a7e --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/README.md @@ -0,0 +1,103 @@ +# Function that creates a PAR +This function creates a PAR (Pre-Authenticated Request) for a bucket in Object Storage. + +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. + + +## Create or Update IAM Policies +Create a new policy that allows the dynamic group to manage compute instances. We will grant `manage` access to a specific `bucket` and `objects` in that bucket for a given compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to manage buckets in compartment where target.bucket.name= +Allow dynamic-group to manage objects in compartment where target.bucket.name= +``` +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize your function +Review the following files in the current folder: +* the code of the function, [CreatePAR.cs](./CreatePAR.cs) +* its dependencies, [CreatePAR.proj](./CreatePAR.proj) +* 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 +``` + + +## Set the function configuration values +The function requires the config value *BUCKET_NAME* , *NAMESPACE* , *LIFETIME* and *REGION* to be set. + +![user input icon](./images/userinput.png) + +Use the *fn* CLI to set the config value: +``` +fn config function BUCKET_NAME +fn config function NAMESPACE +fn config function LIFETIME +fn config function REGION +``` +e.g. +``` +fn config function myapp oci-objectstorage-create-par-dotnet BUCKET_NAME 'my-bucket' +fn config function myapp oci-objectstorage-create-par-dotnet NAMESPACE 'samplenamespace' +fn config function myapp oci-objectstorage-create-par-dotnet LIFETIME '30' +fn config function myapp oci-objectstorage-create-par-dotnet REGION 'ap-osaka-1' +``` + +## Invoke the function +The function requires the name of the PAR in the payload to be invoked. + +![user input icon](./images/userinput.png) +``` + +echo '{"PAR name": }' | fn invoke oci-objectstorage-create-par-dotnet +``` +e.g.: +``` +echo '{"PAR name": "myPAR" }' | fn invoke myapp oci-objectstorage-create-par-dotnet +``` + +Upon success, the function returns the PAR URL. + + +## 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-objectstorage-create-par-dotnet/func.yaml b/samples/oci-objectstorage-create-par-dotnet/func.yaml new file mode 100644 index 0000000..fe298ea --- /dev/null +++ b/samples/oci-objectstorage-create-par-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-objectstorage-create-par-dotnet +version: 0.0.79 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: CreatePAR:Function:function_handler +entrypoint: dotnet CreatePAR.dll diff --git a/samples/oci-objectstorage-create-par-dotnet/images/userinput.png b/samples/oci-objectstorage-create-par-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-objectstorage-create-par-dotnet/images/userinput.png differ diff --git a/samples/oci-objectstorage-get-object-dotnet/Common/ObjectStorageClient.cs b/samples/oci-objectstorage-get-object-dotnet/Common/ObjectStorageClient.cs new file mode 100644 index 0000000..5539ab2 --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/Common/ObjectStorageClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; + + +namespace GetObjects +{ + public class ObjectStorageClientHelper + { + public static ObjectStorageClient GetObjectStorageClient() + { + try + { + return new ObjectStorageClient(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 ObjectStorageClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-objectstorage-get-object-dotnet/Controller/GetObjects.cs b/samples/oci-objectstorage-get-object-dotnet/Controller/GetObjects.cs new file mode 100644 index 0000000..a1552aa --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/Controller/GetObjects.cs @@ -0,0 +1,55 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; +using Oci.ObjectstorageService.Requests; +using Oci.ObjectstorageService.Responses; + + +namespace GetObjects +{ + public class GetObjectHelper + { + public static async Task GetObject(ObjectStorageClient client, string bucketName, string namespaceName, string objectName) + + { + + try + { + + var getObjectRequest = new Oci.ObjectstorageService.Requests.GetObjectRequest + { + NamespaceName = namespaceName, + BucketName = bucketName, + ObjectName = objectName, + }; + + var response = await client.GetObject(getObjectRequest); + byte[] bytes; + using (var memoryStream = new MemoryStream()) + { + response.InputStream.CopyTo(memoryStream); + bytes = memoryStream.ToArray(); + } + + return Convert.ToBase64String(bytes); + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Get Object : {0}", ex.Message); + return "object not found!"; + } + + } + + + } +} diff --git a/samples/oci-objectstorage-get-object-dotnet/Dockerfile b/samples/oci-objectstorage-get-object-dotnet/Dockerfile new file mode 100644 index 0000000..a064c03 --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add GetObjects.csproj +RUN dotnet build GetObjects.csproj -c Release +RUN dotnet publish GetObjects.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "GetObjects.dll"] +CMD ["GetObjects:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-objectstorage-get-object-dotnet/Function.sln b/samples/oci-objectstorage-get-object-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-objectstorage-get-object-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-objectstorage-get-object-dotnet/GetObjects.cs b/samples/oci-objectstorage-get-object-dotnet/GetObjects.cs new file mode 100644 index 0000000..a49fce6 --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/GetObjects.cs @@ -0,0 +1,43 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; + +namespace GetObjects +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + string bucketName = input.bucketName; + string objectName = input.objectName; + string namespaceName = input.namespaceName; + ObjectStorageClient client = ObjectStorageClientHelper.GetObjectStorageClient(); + Task object_str = GetObjectHelper.GetObject(client, bucketName, namespaceName, objectName); + var object_detail = new ObjectDetails(); + if (object_str.Result != "object not found!") + { + byte[] data = Convert.FromBase64String(object_str.Result); + string decodedString = Encoding.UTF8.GetString(data); + object_detail.name = objectName; + object_detail.content = decodedString; + object_details_list.Add(object_detail); + } + + output.Add("results", object_details_list); + return JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-objectstorage-get-object-dotnet/GetObjects.csproj b/samples/oci-objectstorage-get-object-dotnet/GetObjects.csproj new file mode 100644 index 0000000..84b6070 --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/GetObjects.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-objectstorage-get-object-dotnet/Models/InputMessage.cs b/samples/oci-objectstorage-get-object-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..a5d48fc --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/Models/InputMessage.cs @@ -0,0 +1,14 @@ +using System; + +namespace GetObjects +{ + + class InputMessage + { + public string objectName { get; set; } + public string bucketName { get; set; } + public string namespaceName { get; set; } + + } + +} diff --git a/samples/oci-objectstorage-get-object-dotnet/Models/ObjectDetails.cs b/samples/oci-objectstorage-get-object-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..07c2808 --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,14 @@ +using System; + +namespace GetObjects +{ + + class ObjectDetails + { + public string name { get; set; } + public string content { get; set; } + + + } + +} diff --git a/samples/oci-objectstorage-get-object-dotnet/README.md b/samples/oci-objectstorage-get-object-dotnet/README.md new file mode 100644 index 0000000..20ab5ec --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/README.md @@ -0,0 +1,114 @@ +# Function that retrieves an object from a bucket in Object Storage using the OCI Dotnet SDK + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/index.html). +It returns the content of an object from a bucket in Object Storage. + +The function calls the following OCI Dotnet SDK classes: +* [Resource Principals](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [Object Storage Client](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.ObjectstorageService.ObjectStorageClient.html) to interact with Object Storage + +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 + +1. Before you deploy this sample function, make sure you have run steps 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 + +2. Have your Oracle Object Storage Namespace available. This can be found by +logging into your [cloud account](https://console.us-ashburn-1.oraclecloud.com/), +under your user profile, click on your Tenancy. Your Object Storage Namespace +is shown there. + + +## List Applications + +Assuming you 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'} +``` + + +## Create or Update IAM Policies + +Create a new policy that allows the dynamic group to `inspect objects` in +the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to inspect objects in compartment +``` +e.g. +``` +Allow dynamic-group demo-func-dyn-group to inspect objects in compartment demo-func-compartment +``` +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function + +Review the following files in the current folder: + +- [GetObjects.csproj](./GetObjects.csproj) specifies all the dependencies for your function +- [func.yaml](./func.yaml) that contains metadata about your function and declares properties +- [GetObjects.cs](./GetObjects.cs) which is your actual Dotnet function + +The name of your function *oci-objectstorage-get-object-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) + +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Test + +![user input icon](./images/userinput.png) +``` +echo -n | fn invoke +``` +e.g. +``` +echo -n '{"objectName": "","bucketName": "","namespaceName":"", }' | fn invoke myapp oci-objectstorage-get-object-dotnet +``` +You should see the contents of the object appear in the terminal. + +## 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-objectstorage-get-object-dotnet/func.yaml b/samples/oci-objectstorage-get-object-dotnet/func.yaml new file mode 100644 index 0000000..e695cbd --- /dev/null +++ b/samples/oci-objectstorage-get-object-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-objectstorage-get-object-dotnet +version: 0.0.67 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: GetObjects:Function:function_handler +entrypoint: dotnet GetObjects.dll diff --git a/samples/oci-objectstorage-get-object-dotnet/images/userinput.png b/samples/oci-objectstorage-get-object-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-objectstorage-get-object-dotnet/images/userinput.png differ diff --git a/samples/oci-objectstorage-list-objects-dotnet/Common/ObjectStorageClient.cs b/samples/oci-objectstorage-list-objects-dotnet/Common/ObjectStorageClient.cs new file mode 100644 index 0000000..bf82807 --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/Common/ObjectStorageClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; + + +namespace ListObjects +{ + public class ObjectStorageClientHelper + { + public static ObjectStorageClient GetObjectStorageClient() + { + try + { + return new ObjectStorageClient(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 ObjectStorageClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-objectstorage-list-objects-dotnet/Controller/GetObjects.cs b/samples/oci-objectstorage-list-objects-dotnet/Controller/GetObjects.cs new file mode 100644 index 0000000..64d95a3 --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/Controller/GetObjects.cs @@ -0,0 +1,57 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; +using Oci.ObjectstorageService.Requests; +using Oci.ObjectstorageService.Responses; + + +namespace ListObjects +{ + public class ListObjectHelper + { + public static async Task> GetObjectList(ObjectStorageClient client, string bucketName, string namespaceName) + + { + string nextpage = ""; + List object_list = new List(); + while (true) + { + try + { + + var listObjectsRequest = new Oci.ObjectstorageService.Requests.ListObjectsRequest + { + NamespaceName = namespaceName, + BucketName = bucketName, + Limit = 1000, + Start = nextpage, + }; + + var response = await client.ListObjects(listObjectsRequest); + nextpage = response.ListObjects.NextStartWith; + object_list.AddRange(response.ListObjects.Objects); + if (string.IsNullOrEmpty(nextpage)) + { + break; + } + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Get Object List: {0}", ex.Message); + return new List(); + } + } + return object_list; + } + + + } +} diff --git a/samples/oci-objectstorage-list-objects-dotnet/Dockerfile b/samples/oci-objectstorage-list-objects-dotnet/Dockerfile new file mode 100644 index 0000000..d4b246a --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add ListObjects.csproj +RUN dotnet build ListObjects.csproj -c Release +RUN dotnet publish ListObjects.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "ListObjects.dll"] +CMD ["ListObjects:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-objectstorage-list-objects-dotnet/Function.sln b/samples/oci-objectstorage-list-objects-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-objectstorage-list-objects-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-objectstorage-list-objects-dotnet/ListObjects.cs b/samples/oci-objectstorage-list-objects-dotnet/ListObjects.cs new file mode 100644 index 0000000..94484ff --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/ListObjects.cs @@ -0,0 +1,40 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; + +namespace ListObjects +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + string bucketName = input.bucketName; + string namespaceName = input.namespaceName; + ObjectStorageClient client = ObjectStorageClientHelper.GetObjectStorageClient(); + Task> object_list = ListObjectHelper.GetObjectList(client, bucketName, namespaceName); + + foreach (ObjectSummary oss_object in object_list.Result) + { + var object_detail = new ObjectDetails(); + object_detail.name = oss_object.Name; + object_details_list.Add(object_detail); + } + + output.Add("results", object_details_list); + return JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-objectstorage-list-objects-dotnet/ListObjects.csproj b/samples/oci-objectstorage-list-objects-dotnet/ListObjects.csproj new file mode 100644 index 0000000..0139d56 --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/ListObjects.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + diff --git a/samples/oci-objectstorage-list-objects-dotnet/Models/InputMessage.cs b/samples/oci-objectstorage-list-objects-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..42b920c --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/Models/InputMessage.cs @@ -0,0 +1,13 @@ +using System; + +namespace ListObjects +{ + + class InputMessage + { + public string bucketName { get; set; } + public string namespaceName { get; set; } + + } + +} diff --git a/samples/oci-objectstorage-list-objects-dotnet/Models/ObjectDetails.cs b/samples/oci-objectstorage-list-objects-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..20c815c --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,13 @@ +using System; + +namespace ListObjects +{ + + class ObjectDetails + { + public string name { get; set; } + + + } + +} diff --git a/samples/oci-objectstorage-list-objects-dotnet/README.md b/samples/oci-objectstorage-list-objects-dotnet/README.md new file mode 100644 index 0000000..844aa73 --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/README.md @@ -0,0 +1,115 @@ +# Function that lists objects from a bucket in Object Storage using the OCI Dotnet SDK + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/index.html). +It returns a list of objects from a given bucket in Object Storage. + +The function calls the following OCI Dotnet SDK classes: +* [Resource Principals](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [Object Storage Client](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.ObjectstorageService.ObjectStorageClient.html) to interact with Object Storage + +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 + +1. Before you deploy this sample function, make sure you have run steps 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 + +2. Have your Oracle Object Storage Namespace available. This can be found by +logging into your [cloud account](https://console.us-ashburn-1.oraclecloud.com/), +under your user profile, click on your Tenancy. Your Object Storage Namespace +is shown there. + + +## List Applications + +Assuming you 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'} +``` + + +## Create or Update IAM Policies + +Create a new policy that allows the dynamic group to `inspect objects` in +the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to inspect objects in compartment +``` +e.g. +``` +Allow dynamic-group demo-func-dyn-group to inspect objects in compartment demo-func-compartment +``` +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function + +Review the following files in the current folder: + +- [ListObjects.csproj](./ListObjects.csproj) specifies all the dependencies for your function +- [func.yaml](./func.yaml) that contains metadata about your function and declares properties +- [ListObjects.cs](./ListObjects.cs) which is your actual Dotnet function + +The name of your function *oci-objectstorage-list-objects-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) + +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Test + +![user input icon](./images/userinput.png) +``` +echo -n | fn invoke +``` +e.g. +``` +echo -n '{"bucketName": "","namespaceName":""}' | fn invoke myapp oci-objectstorage-list-objects-dotnet +``` +You should see a list of objects from the bucket in the terminal. + + +## 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-objectstorage-list-objects-dotnet/func.yaml b/samples/oci-objectstorage-list-objects-dotnet/func.yaml new file mode 100644 index 0000000..209db5f --- /dev/null +++ b/samples/oci-objectstorage-list-objects-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-objectstorage-list-objects-dotnet +version: 0.0.63 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: ListObjects:Function:function_handler +entrypoint: dotnet ListObjects.dll diff --git a/samples/oci-objectstorage-list-objects-dotnet/images/userinput.png b/samples/oci-objectstorage-list-objects-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-objectstorage-list-objects-dotnet/images/userinput.png differ diff --git a/samples/oci-objectstorage-put-object-dotnet/Common/ObjectStorageClient.cs b/samples/oci-objectstorage-put-object-dotnet/Common/ObjectStorageClient.cs new file mode 100644 index 0000000..5c3777d --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/Common/ObjectStorageClient.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; + + +namespace PutObjects +{ + public class ObjectStorageClientHelper + { + public static ObjectStorageClient GetObjectStorageClient() + { + try + { + return new ObjectStorageClient(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 ObjectStorageClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-objectstorage-put-object-dotnet/Controller/PutObjectHelper.cs b/samples/oci-objectstorage-put-object-dotnet/Controller/PutObjectHelper.cs new file mode 100644 index 0000000..38c1bf9 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/Controller/PutObjectHelper.cs @@ -0,0 +1,61 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; +using Oci.ObjectstorageService.Requests; +using Oci.ObjectstorageService.Responses; + + +namespace PutObjects +{ + public class PutObjectsHelper + { + private static Stream GenerateStreamFromString(string inputString) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + writer.Write(inputString); + writer.Flush(); + stream.Position = 0; + return stream; + } + public static async Task PutObject(ObjectStorageClient client, string bucketName, string namespaceName, string objectName, string content) + + { + + try + { + + var putObjectRequest = new Oci.ObjectstorageService.Requests.PutObjectRequest + { + NamespaceName = namespaceName, + BucketName = bucketName, + ObjectName = objectName, + PutObjectBody = GenerateStreamFromString(content), + }; + + var response = await client.PutObject(putObjectRequest); + var hasValueValue = response.LastModified.HasValue; + + return "Success!"; + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Put Object : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-objectstorage-put-object-dotnet/Dockerfile b/samples/oci-objectstorage-put-object-dotnet/Dockerfile new file mode 100644 index 0000000..7b2f1b3 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add PutObjects.csproj +RUN dotnet build PutObjects.csproj -c Release +RUN dotnet publish PutObjects.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "PutObjects.dll"] +CMD ["PutObjects:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-objectstorage-put-object-dotnet/Function.sln b/samples/oci-objectstorage-put-object-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-objectstorage-put-object-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-objectstorage-put-object-dotnet/Models/InputMessage.cs b/samples/oci-objectstorage-put-object-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..41f65e6 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/Models/InputMessage.cs @@ -0,0 +1,16 @@ +using System; + +namespace PutObjects +{ + + class InputMessage + { + public string objectName { get; set; } + public string bucketName { get; set; } + public string namespaceName { get; set; } + + public string content { get; set; } + + } + +} diff --git a/samples/oci-objectstorage-put-object-dotnet/Models/ObjectDetails.cs b/samples/oci-objectstorage-put-object-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..3260eb6 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,16 @@ +using System; + +namespace PutObjects +{ + + class ObjectDetails + { + public string name { get; set; } + + public string bucketname { get; set; } + public string result { get; set; } + + + } + +} diff --git a/samples/oci-objectstorage-put-object-dotnet/PutObjects.cs b/samples/oci-objectstorage-put-object-dotnet/PutObjects.cs new file mode 100644 index 0000000..b2068e1 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/PutObjects.cs @@ -0,0 +1,40 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.ObjectstorageService; +using Oci.ObjectstorageService.Models; + +namespace PutObjects +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + string bucketName = input.bucketName; + string objectName = input.objectName; + string namespaceName = input.namespaceName; + string content = input.content; + ObjectStorageClient client = ObjectStorageClientHelper.GetObjectStorageClient(); + Task object_str = PutObjectsHelper.PutObject(client, bucketName, namespaceName, objectName, content); + var object_detail = new ObjectDetails(); + object_detail.name = objectName; + object_detail.bucketname = bucketName; + object_detail.result = object_str.Result; + object_details_list.Add(object_detail); + + output.Add("results", object_details_list); + return JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-objectstorage-put-object-dotnet/PutObjects.csproj b/samples/oci-objectstorage-put-object-dotnet/PutObjects.csproj new file mode 100644 index 0000000..84b6070 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/PutObjects.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-objectstorage-put-object-dotnet/README.md b/samples/oci-objectstorage-put-object-dotnet/README.md new file mode 100644 index 0000000..064a2d9 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/README.md @@ -0,0 +1,114 @@ +# Function that creates an object in a bucket in Object Storage using the OCI Dotnet SDK + +This function uses Resource Principals to securely authorize a function to make +API calls to OCI services using the [OCI Dotnet SDK](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/index.html). +It creates an object in a bucket in Object Storage and returns a message with a status. + +The function calls the following OCI Dotnet SDK classes: +* [Resource Principals](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.Common.Auth.ResourcePrincipalAuthenticationDetailsProvider.html) to authenticate +* [Object Storage Client](https://docs.oracle.com/en-us/iaas/tools/dotnet/latest/api/Oci.ObjectstorageService.ObjectStorageClient.html) to interact with Object Storage + +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 + +1. Before you deploy this sample function, make sure you have run steps 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 + +2. Have your Oracle Object Storage Namespace available. This can be found by +logging into your [cloud account](https://console.us-ashburn-1.oraclecloud.com/), +under your user profile, click on your Tenancy. Your Object Storage Namespace +is shown there. + + +## List Applications + +Assuming you 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'} +``` + + +## Create or Update IAM Policies + +Create a new policy that allows the dynamic group to `inspect objects` in +the functions related compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to inspect objects in compartment +``` +e.g. +``` +Allow dynamic-group demo-func-dyn-group to inspect objects in compartment demo-func-compartment +``` +For more information on how to create policies, go [here](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function + +Review the following files in the current folder: + +- [PutObjects.csproj](./PutObjects.csproj) specifies all the dependencies for your function +- [func.yaml](./func.yaml) that contains metadata about your function and declares properties +- [PutObjects.cs](./PutObjects.cs) which is your actual Dotnet function + +The name of your function *oci-objectstorage-put-object-dotnet* is specified in [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 the specified Docker registry, and deploy the function to Oracle Functions +in the application created earlier: + +![user input icon](./images/userinput.png) + +``` +fn -v deploy --app +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Test + +![user input icon](./images/userinput.png) +``` +echo -n | fn invoke +``` +e.g. +``` +echo -n '{"objectName": "","bucketName": "","namespaceName":"","content":"" }' | fn invoke myapp oci-objectstorage-get-object-dotnet +``` +You should see the contents of the object appear in the terminal. + +## 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-objectstorage-put-object-dotnet/func.yaml b/samples/oci-objectstorage-put-object-dotnet/func.yaml new file mode 100644 index 0000000..61a2ef0 --- /dev/null +++ b/samples/oci-objectstorage-put-object-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-objectstorage-put-object-dotnet +version: 0.0.70 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: PutObjects:Function:function_handler +entrypoint: dotnet PutObjects.dll diff --git a/samples/oci-objectstorage-put-object-dotnet/images/userinput.png b/samples/oci-objectstorage-put-object-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-objectstorage-put-object-dotnet/images/userinput.png differ diff --git a/samples/oci-ons-publish-dotnet/Common/ONSClientHelper.cs b/samples/oci-ons-publish-dotnet/Common/ONSClientHelper.cs new file mode 100644 index 0000000..3f03057 --- /dev/null +++ b/samples/oci-ons-publish-dotnet/Common/ONSClientHelper.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.OnsService; + + +namespace PublishONS +{ + public class ONSClientHelper + { + public static NotificationDataPlaneClient GetONSClient() + { + try + { + return new NotificationDataPlaneClient(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 NotificationDataPlaneClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-ons-publish-dotnet/Controller/InvokeONSHelper.cs b/samples/oci-ons-publish-dotnet/Controller/InvokeONSHelper.cs new file mode 100644 index 0000000..bc7a599 --- /dev/null +++ b/samples/oci-ons-publish-dotnet/Controller/InvokeONSHelper.cs @@ -0,0 +1,56 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.OnsService; +using Oci.OnsService.Models; +using Oci.OnsService.Requests; +using Oci.OnsService.Responses; + + +namespace PublishONS +{ + public class InvokeONSHelper + { + public static async Task SendMessage(NotificationDataPlaneClient client, string topic_id, string msg_title, string msg_body) + + { + + try + { + + // Create a request and dependent object(s). + var messageDetails = new Oci.OnsService.Models.MessageDetails + { + Title = msg_title, + Body = msg_body + }; + var publishMessageRequest = new Oci.OnsService.Requests.PublishMessageRequest + { + TopicId = topic_id, + MessageDetails = messageDetails, + MessageType = Oci.OnsService.Requests.PublishMessageRequest.MessageTypeEnum.RawText + }; + + var response = await client.PublishMessage(publishMessageRequest); + var messageIdValue = response.PublishResult.MessageId; + return messageIdValue; + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Send Message to ONS : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-ons-publish-dotnet/Dockerfile b/samples/oci-ons-publish-dotnet/Dockerfile new file mode 100644 index 0000000..984e8f8 --- /dev/null +++ b/samples/oci-ons-publish-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add PublishONS.csproj +RUN dotnet build PublishONS.csproj -c Release +RUN dotnet publish PublishONS.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "PublishONS.dll"] +CMD ["PublishONS:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-ons-publish-dotnet/Function.sln b/samples/oci-ons-publish-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-ons-publish-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-ons-publish-dotnet/Models/InputMessage.cs b/samples/oci-ons-publish-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..9cc7fcb --- /dev/null +++ b/samples/oci-ons-publish-dotnet/Models/InputMessage.cs @@ -0,0 +1,15 @@ +using System; + +namespace PublishONS +{ + + class InputMessage + { + public string topic_id { get; set; } + public string msg_title { get; set; } + public string msg_body { get; set; } + + + } + +} diff --git a/samples/oci-ons-publish-dotnet/Models/ObjectDetails.cs b/samples/oci-ons-publish-dotnet/Models/ObjectDetails.cs new file mode 100644 index 0000000..d5d4a73 --- /dev/null +++ b/samples/oci-ons-publish-dotnet/Models/ObjectDetails.cs @@ -0,0 +1,14 @@ +using System; + +namespace PublishONS +{ + + class ObjectDetails + { + + public string result { get; set; } + + + } + +} diff --git a/samples/oci-ons-publish-dotnet/PublishONS.cs b/samples/oci-ons-publish-dotnet/PublishONS.cs new file mode 100644 index 0000000..b4de871 --- /dev/null +++ b/samples/oci-ons-publish-dotnet/PublishONS.cs @@ -0,0 +1,39 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.OnsService; +using Oci.OnsService.Models; + +namespace PublishONS +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var object_details_list = new List(); + string topic_id = input.topic_id; + string msg_title = input.msg_title; + string msg_body = input.msg_body; + + NotificationDataPlaneClient client = ONSClientHelper.GetONSClient(); + Task messageIdValue = InvokeONSHelper.SendMessage(client, topic_id, msg_title, msg_body); + + var object_detail = new ObjectDetails(); + object_detail.result = messageIdValue.Result; + object_details_list.Add(object_detail); + + output.Add("results", object_details_list); + return System.Text.Json.JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-ons-publish-dotnet/PublishONS.csproj b/samples/oci-ons-publish-dotnet/PublishONS.csproj new file mode 100644 index 0000000..bac1f04 --- /dev/null +++ b/samples/oci-ons-publish-dotnet/PublishONS.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-ons-publish-dotnet/README.md b/samples/oci-ons-publish-dotnet/README.md new file mode 100644 index 0000000..fc5901c --- /dev/null +++ b/samples/oci-ons-publish-dotnet/README.md @@ -0,0 +1,99 @@ +# Function that publishes a notification +This function publishes a notification. + +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. + + +## Create or Update IAM Policies +Create a new policy that allows the dynamic group to use the Notificaton Service. We will grant `use` access to `ons-topics` in the compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to use ons-topics in compartment +``` + +For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function +Review the following files in the current folder: +* the code of the function, [PublishONS.cs](./PublishONS.cs) +* its dependencies, [PublishONS.csproj](./PublishONS.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 the Notification topic +Create a Notification topic with an email subscription. + +![user input icon](./images/userinput.png) + +Go to the OCI console, navigate to Application Integration > Notifications. Click *Create Topic*. + +![create topic](./images/ons-create-topic.png) + +Click on your topic and create a email subscription to this topic by clicking *Create Subscription* and entering your email address. You will receive an email to confirm your subscription. + +![create subscription](./images/ons-create-email-subscription.png) + +Note the OCID of your topic. + + +## Invoke the function +Run the following command to test your function. + +![user input icon](./images/userinput.png) + +``` +echo '{"topic_id": "", "msg_title": "message-title", "msg_body": "message-body"}' | fn invoke oci-ons-publish-dotnet +``` +e.g.: +``` +echo '{"topic_id": "ocid1.onstopic.xxxx", "msg_title": "a message from Functions", "msg_body": "This email was sent by Oracle Functions!"}' | fn invoke myapp oci-ons-publish-dotnet +``` + +Upon success, you should receive an email from the Notification Service. + + +## Monitoring Functions and Notifications Topics + +Learn how to configure basic observability for your function and topic using metrics, alarms and email alerts: +* [Basic Guidance for Monitoring your Functions](../basic-observability/functions.md) +* [Basic Guidance for Monitoring your Notifications Topics](../basic-observability/notifications.md) diff --git a/samples/oci-ons-publish-dotnet/func.yaml b/samples/oci-ons-publish-dotnet/func.yaml new file mode 100644 index 0000000..44ddcfc --- /dev/null +++ b/samples/oci-ons-publish-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-ons-publish-dotnet +version: 0.0.99 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: PublishONS:Function:function_handler +entrypoint: dotnet PublishONS.dll diff --git a/samples/oci-ons-publish-dotnet/images/ons-create-email-subscription.png b/samples/oci-ons-publish-dotnet/images/ons-create-email-subscription.png new file mode 100644 index 0000000..8793f4a Binary files /dev/null and b/samples/oci-ons-publish-dotnet/images/ons-create-email-subscription.png differ diff --git a/samples/oci-ons-publish-dotnet/images/ons-create-topic.png b/samples/oci-ons-publish-dotnet/images/ons-create-topic.png new file mode 100644 index 0000000..01b1ca1 Binary files /dev/null and b/samples/oci-ons-publish-dotnet/images/ons-create-topic.png differ diff --git a/samples/oci-ons-publish-dotnet/images/ons-topic.png b/samples/oci-ons-publish-dotnet/images/ons-topic.png new file mode 100644 index 0000000..d6aab20 Binary files /dev/null and b/samples/oci-ons-publish-dotnet/images/ons-topic.png differ diff --git a/samples/oci-ons-publish-dotnet/images/userinput.png b/samples/oci-ons-publish-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-ons-publish-dotnet/images/userinput.png differ diff --git a/samples/oci-vault-decrypt-dotnet/Common/KmsCryptoClientHelper.cs b/samples/oci-vault-decrypt-dotnet/Common/KmsCryptoClientHelper.cs new file mode 100644 index 0000000..ac8fa08 --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/Common/KmsCryptoClientHelper.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.KeymanagementService; + + +namespace VaultDecrypt +{ + public class KmsCryptoClientHelper + { + public static KmsCryptoClient GetVaultDecryptClient(string crypto_endpoint) + { + try + { + return new KmsCryptoClient(ResourcePrincipalAuthenticationDetailsProvider.GetProvider(), new ClientConfiguration(), crypto_endpoint); + } + catch (Exception ex) + { + Console.WriteLine("Unable To Create Resource Principal Provider: {0}", ex.Message); + Console.WriteLine("Defaulting to Instance Provider"); + return new KmsCryptoClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration(), crypto_endpoint); + } + } + + } +} diff --git a/samples/oci-vault-decrypt-dotnet/Controller/GetSecretsHelper.cs b/samples/oci-vault-decrypt-dotnet/Controller/GetSecretsHelper.cs new file mode 100644 index 0000000..ed807a9 --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/Controller/GetSecretsHelper.cs @@ -0,0 +1,59 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.KeymanagementService; +using Oci.KeymanagementService.Models; +using Oci.KeymanagementService.Requests; +using Oci.KeymanagementService.Responses; + + +namespace VaultDecrypt +{ + public class GetSecretsHelper + { + public static async Task getSecretValue(KmsCryptoClient client, string vault_key_ocid, string cipher) + + { + + try + { + + + // Create a request and dependent object(s). + var decryptDataDetails = new Oci.KeymanagementService.Models.DecryptDataDetails + { + Ciphertext = cipher, + KeyId = vault_key_ocid, + }; + + var decryptRequest = new Oci.KeymanagementService.Requests.DecryptRequest + { + DecryptDataDetails = decryptDataDetails, + }; + + + var response = await client.Decrypt(decryptRequest); + var value_b64 = response.DecryptedData.Plaintext; + byte[] secretValueDecoded = Convert.FromBase64String(value_b64); + string secretIdValue = Encoding.Default.GetString(secretValueDecoded); + return secretIdValue; + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Get Secret : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-vault-decrypt-dotnet/Dockerfile b/samples/oci-vault-decrypt-dotnet/Dockerfile new file mode 100644 index 0000000..dc8e00c --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add VaultDecrypt.csproj +RUN dotnet build VaultDecrypt.csproj -c Release +RUN dotnet publish VaultDecrypt.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "VaultDecrypt.dll"] +CMD ["VaultDecrypt:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-vault-decrypt-dotnet/Function.sln b/samples/oci-vault-decrypt-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-vault-decrypt-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-vault-decrypt-dotnet/Models/InputMessage.cs b/samples/oci-vault-decrypt-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..aeb92f5 --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/Models/InputMessage.cs @@ -0,0 +1,13 @@ +using System; + +namespace VaultDecrypt +{ + + class InputMessage + { + public string cipher { get; set; } + + + } + +} diff --git a/samples/oci-vault-decrypt-dotnet/Models/SecretContent.cs b/samples/oci-vault-decrypt-dotnet/Models/SecretContent.cs new file mode 100644 index 0000000..26bed9d --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/Models/SecretContent.cs @@ -0,0 +1,14 @@ +using System; + +namespace VaultDecrypt +{ + + class SecretContent + { + + public string secret_content { get; set; } + + + } + +} diff --git a/samples/oci-vault-decrypt-dotnet/README.md b/samples/oci-vault-decrypt-dotnet/README.md new file mode 100644 index 0000000..96daec6 --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/README.md @@ -0,0 +1,130 @@ +# Function that decrypts a cipher text using Vault keys +This function decrypts a cipher text using a Vault key. As a best practice, we do not recommend to expose your secrets via a return value of a function. This sample just demonstrate to use Vault keys in a function. + +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. + + +## Create or Update IAM Policies +Create a new policy that allows the dynamic group to manage compute instances. We will grant `use` access to `keys` in the compartment. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to use keys in compartment +``` + +For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize the function +Review the following files in the current folder: +* the code of the function, [VaultDecrypt.cs](./VaultDecrypt.cs) +* its dependencies, [VaultDecrypt.csproj](./VaultDecrypt.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 the Vault key and a cipher text +![user input icon](./images/userinput.png) + +On the OCI console, navigate to *Security* > *Key Management*. If you don't already have a Vault created, create one. Create a key by clicking on `Create Key`. Provide a name for the key and click `Create Key`. + +In your vault, note the *Cryptographic Endpoint* and your key OCID. + +![Cryptographic Endpoint anf Key OCID](./images/vault.png) + +Set the `KEY_OCID` and `CRYPTOGRAPHIC_ENDPOINTT` environement variables with the OCID of your Vault key and the Vault Endpoint. For example: +``` +KEY_OCID='ocid1.key.oc1.phx.acdfdfna.abyxxxxxxxsqhycfq' +CRYPTOGRAPHIC_ENDPOINT='https://a5pdddfdfna-crypto.kms.us-phoenix-1.oraclecloud.com' +``` + +Set the `PLAIN_TEXT` environement variable with the text you will encrypt: +``` +PLAIN_TEXT="my text" +``` +Run the following command to get the encrypted version of your text: +``` +oci kms crypto encrypt --key-id "$KEY_OCID" --endpoint "$CRYPTOGRAPHIC_ENDPOINT" \ + --plaintext "$( echo -n $PLAIN_TEXT | base64 -b0 )" | jq -r .data.ciphertext +``` +The above command is for MacOS. For Linux, replace `base64 -b0` with `base64 -w0`. + +The command returns a Cipher text, you will use it to invoke your function. + + +## Set the function configuration values +The function requires the following configuration values to be set: +- key_ocid +- cryptographic_endpoint + +![user input icon](./images/userinput.png) + +Use the *fn CLI* to set the config value: +``` +fn config function key_ocid +fn config function cryptographic_endpoint +``` +e.g. +``` +fn config function myapp oci-vault-decrypt-dotnet key_ocid "ocid1.key.oc1.phx.a5pedhchaafna.abyhqljt63augu4nwptqrvaw7gymh7zp7ihvgayo72pehd3sqhfproiaycfq" +fn config function myapp oci-vault-decrypt-dotnet cryptographic_endpoint 'https://a5pedhchaafna-crypto.kms.us-phoenix-1.oraclecloud.com' +``` + + +## Invoke the function +The function requires the cipher to be specified in the payload to be invoked. "cipher-text" is encrypted text you generated in the section [Create the Vault key and a cipher text](#Create the Vault key and a cipher text) + +![user input icon](./images/userinput.png) +``` +echo '{"cipher": ""}' | fn invoke oci-vault-decrypt-dotnet +``` +e.g.: +``` +echo '{"cipher": "Ia+hS8+UYAEV8gr00ItHxsC1jhfslbzAA="}' | fn invoke myapp oci-vault-decrypt-dotnet +``` + +Upon success, the function should return the decrypted text: +{"secret_content":[{"secret_content":"my text"}]} + + +## 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-vault-decrypt-dotnet/VaultDecrypt.cs b/samples/oci-vault-decrypt-dotnet/VaultDecrypt.cs new file mode 100644 index 0000000..cf0c5d1 --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/VaultDecrypt.cs @@ -0,0 +1,42 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.KeymanagementService; +using Oci.KeymanagementService.Models; + +namespace VaultDecrypt +{ + class Function + { + public string function_handler(InputMessage input) + { + + Dictionary> output = new Dictionary>(); + var secret_details_list = new List(); + + string cipher = input.cipher; + string vault_key_ocid = Environment.GetEnvironmentVariable("key_ocid"); + string crypto_endpoint = Environment.GetEnvironmentVariable("cryptographic_endpoint"); + + + KmsCryptoClient client = KmsCryptoClientHelper.GetVaultDecryptClient(crypto_endpoint); + + Task secret_value = GetSecretsHelper.getSecretValue(client, vault_key_ocid, cipher); + + var secret_detail = new SecretContent(); + secret_detail.secret_content = secret_value.Result; + secret_details_list.Add(secret_detail); + + output.Add("secret_content", secret_details_list); + return System.Text.Json.JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-vault-decrypt-dotnet/VaultDecrypt.csproj b/samples/oci-vault-decrypt-dotnet/VaultDecrypt.csproj new file mode 100644 index 0000000..58c4680 --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/VaultDecrypt.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-vault-decrypt-dotnet/func.yaml b/samples/oci-vault-decrypt-dotnet/func.yaml new file mode 100644 index 0000000..11f9e2e --- /dev/null +++ b/samples/oci-vault-decrypt-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-vault-decrypt-dotnet +version: 0.0.90 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: VaultDecrypt:Function:function_handler +entrypoint: dotnet VaultDecrypt.dll diff --git a/samples/oci-vault-decrypt-dotnet/images/userinput.png b/samples/oci-vault-decrypt-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-vault-decrypt-dotnet/images/userinput.png differ diff --git a/samples/oci-vault-decrypt-dotnet/images/vault.png b/samples/oci-vault-decrypt-dotnet/images/vault.png new file mode 100644 index 0000000..c36177a Binary files /dev/null and b/samples/oci-vault-decrypt-dotnet/images/vault.png differ diff --git a/samples/oci-vault-get-secret-dotnet/Common/SecretsClientHelper.cs b/samples/oci-vault-get-secret-dotnet/Common/SecretsClientHelper.cs new file mode 100644 index 0000000..2098223 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/Common/SecretsClientHelper.cs @@ -0,0 +1,30 @@ + +using System; +using System.Threading.Tasks; +using System.Text; + +using Oci.Common; +using Oci.Common.Auth; +using Oci.SecretsService; + + +namespace GetSecrets +{ + public class SecretsClientHelper + { + public static SecretsClient GetSecretsClient() + { + try + { + return new SecretsClient(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 SecretsClient(new InstancePrincipalsAuthenticationDetailsProvider(), new ClientConfiguration()); + } + } + + } +} diff --git a/samples/oci-vault-get-secret-dotnet/Controller/GetSecretsHelper.cs b/samples/oci-vault-get-secret-dotnet/Controller/GetSecretsHelper.cs new file mode 100644 index 0000000..a8342d7 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/Controller/GetSecretsHelper.cs @@ -0,0 +1,52 @@ + +using System; +using System.Threading.Tasks; +using System.Text; +using System.Collections.Generic; +using System.IO; +using Oci.Common.Model; +using Oci.Common; +using Oci.Common.Auth; +using Oci.SecretsService; +using Oci.SecretsService.Models; +using Oci.SecretsService.Requests; +using Oci.SecretsService.Responses; + + +namespace GetSecrets +{ + public class GetSecretsHelper + { + public static async Task getSecretValue(SecretsClient client, string secret_ocid) + + { + + try + { + + var getSecretBundleRequest = new Oci.SecretsService.Requests.GetSecretBundleRequest + { + SecretId = secret_ocid, + Stage = Oci.SecretsService.Requests.GetSecretBundleRequest.StageEnum.Latest + }; + + + var response = await client.GetSecretBundle(getSecretBundleRequest); + Base64SecretBundleContentDetails b64_secret_contents = (Base64SecretBundleContentDetails)response.SecretBundle.SecretBundleContent; + byte[] secretValueDecoded = Convert.FromBase64String(b64_secret_contents.Content); + string secretIdValue = Encoding.Default.GetString(secretValueDecoded); + return secretIdValue; + + } + + catch (OciException ex) + { + Console.WriteLine("Unable To Get Secret : {0}", ex.Message); + return "Failed " + ex.Message; + } + + } + + + } +} diff --git a/samples/oci-vault-get-secret-dotnet/Dockerfile b/samples/oci-vault-get-secret-dotnet/Dockerfile new file mode 100644 index 0000000..1052f57 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/Dockerfile @@ -0,0 +1,11 @@ +FROM fnproject/dotnet:3.1-1.0.4-dev as build-stage +WORKDIR /function +COPY . . +RUN dotnet sln add GetSecrets.csproj +RUN dotnet build GetSecrets.csproj -c Release +RUN dotnet publish GetSecrets.csproj -c Release -o out +FROM fnproject/dotnet:3.1-1.0.4 +WORKDIR /function +COPY --from=build-stage /function/out/ /function/ +ENTRYPOINT ["dotnet", "GetSecrets.dll"] +CMD ["GetSecrets:Function:function_handler"] \ No newline at end of file diff --git a/samples/oci-vault-get-secret-dotnet/Function.sln b/samples/oci-vault-get-secret-dotnet/Function.sln new file mode 100644 index 0000000..f87916a --- /dev/null +++ b/samples/oci-vault-get-secret-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-vault-get-secret-dotnet/GetSecrets.cs b/samples/oci-vault-get-secret-dotnet/GetSecrets.cs new file mode 100644 index 0000000..3f1a5f3 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/GetSecrets.cs @@ -0,0 +1,39 @@ +using Fnproject.Fn.Fdk; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Oci.SecretsService; +using Oci.SecretsService.Models; + +namespace GetSecrets +{ + class Function + { + public string function_handler() + { + + Dictionary> output = new Dictionary>(); + var secret_details_list = new List(); + + string secret_ocid = Environment.GetEnvironmentVariable("secret_ocid"); + + SecretsClient client = SecretsClientHelper.GetSecretsClient(); + + Task secret_value = GetSecretsHelper.getSecretValue(client, secret_ocid); + + var secret_detail = new SecretContent(); + secret_detail.secret_content = secret_value.Result; + secret_details_list.Add(secret_detail); + + output.Add("secret_content", secret_details_list); + return System.Text.Json.JsonSerializer.Serialize(output); + + } + + static void Main(string[] args) { Fdk.Handle(args[0]); } + + } +} diff --git a/samples/oci-vault-get-secret-dotnet/GetSecrets.csproj b/samples/oci-vault-get-secret-dotnet/GetSecrets.csproj new file mode 100644 index 0000000..62981d2 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/GetSecrets.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + diff --git a/samples/oci-vault-get-secret-dotnet/Models/SecretContent.cs b/samples/oci-vault-get-secret-dotnet/Models/SecretContent.cs new file mode 100644 index 0000000..c3ce8bf --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/Models/SecretContent.cs @@ -0,0 +1,14 @@ +using System; + +namespace GetSecrets +{ + + class SecretContent + { + + public string secret_content { get; set; } + + + } + +} diff --git a/samples/oci-vault-get-secret-dotnet/README.md b/samples/oci-vault-get-secret-dotnet/README.md new file mode 100644 index 0000000..2a9ae67 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/README.md @@ -0,0 +1,121 @@ +# Function that retrieves a secret +This function retrieves a secret from OCI Vault. The content of binary secrets are stored on the function filesystem and text secretx are returned decrypted by the function. +As a best practice, we do not recommend to expose your secrets via a return value of a function. This sample just demonstrate to use OCI Vault secrets in a function. + +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. + + +## Create or Update IAM Policies +Create a new policy that allows the dynamic group to *use secret-family*. + +![user input icon](./images/userinput.png) + +Your policy should look something like this: +``` +Allow dynamic-group to use secret-family in compartment +``` +For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm). + + +## Review and customize your function +Review the following files in the current folder: +* the code of the function, [GetSecrets.cs](./GetSecrets.cs) +* its dependencies, [GetSecrets.csproj](./GetSecrets.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 +``` +e.g. +``` +fn -v deploy --app myapp +``` + + +## Create a Vault and a Secret +![user input icon](./images/userinput.png) + +On the OCI console, navigate to *Security* > *Vault*. If you don't already have a Vault created, create one. Create a key by clicking on `Create Key`. Provide a name for the key and click `Create Key`. + +To create a secret, click on *Create Secret* and provide a name, a description, a Vault key, the secret type (either Plain-Text or Base64), and the secret content. + +![Create secret](./images/secret-create.png) + + +Click on the secret and copy its OCID + +![Create secret](./images/secret-ocid.png) + + +## Set the function configuration values +The function requires the following configuration values to be set: +- secret_ocid + +![user input icon](./images/userinput.png) + +Use the *fn CLI* to set the config value: +``` +fn config function secret_ocid +``` +e.g. +``` +fn config function myapp oci-vault-get-secret-python secret_ocid ocid1.vaultsecret.oc1.phx.xxxxxx +``` + + +## Invoke the function +Invoke the function as follows: + +![user input icon](./images/userinput.png) +``` +fn invoke oci-vault-get-secret-dotnet +``` +e.g.: +``` +fn invoke myapp oci-vault-get-secret-dotnet +``` + +The function returns the content of the secret, for example: +``` +{"secret_content":[{"secret_content":"Oracle Functions rock!"}]} +``` + + + + +## 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-vault-get-secret-dotnet/func.yaml b/samples/oci-vault-get-secret-dotnet/func.yaml new file mode 100644 index 0000000..128d4b5 --- /dev/null +++ b/samples/oci-vault-get-secret-dotnet/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: oci-vault-get-secret-dotnet +version: 0.0.86 +runtime: dotnet3.1 +build_image: fnproject/dotnet:3.1-1.0.4-dev +run_image: fnproject/dotnet:3.1-1.0.4 +cmd: GetSecrets:Function:function_handler +entrypoint: dotnet GetSecrets.dll diff --git a/samples/oci-vault-get-secret-dotnet/images/secret-create.png b/samples/oci-vault-get-secret-dotnet/images/secret-create.png new file mode 100644 index 0000000..abc897c Binary files /dev/null and b/samples/oci-vault-get-secret-dotnet/images/secret-create.png differ diff --git a/samples/oci-vault-get-secret-dotnet/images/secret-ocid.png b/samples/oci-vault-get-secret-dotnet/images/secret-ocid.png new file mode 100644 index 0000000..b485bbb Binary files /dev/null and b/samples/oci-vault-get-secret-dotnet/images/secret-ocid.png differ diff --git a/samples/oci-vault-get-secret-dotnet/images/userinput.png b/samples/oci-vault-get-secret-dotnet/images/userinput.png new file mode 100644 index 0000000..ce6a202 Binary files /dev/null and b/samples/oci-vault-get-secret-dotnet/images/userinput.png differ