diff --git a/README.md b/README.md index c9295e9..830150c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This repository provides examples demonstrating how to use Oracle Functions. | 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)|| +| 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)| | 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) | 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/InputMessage.cs b/samples/oci-objectstorage-copy-objects-dotnet/Models/InputMessage.cs new file mode 100644 index 0000000..e464532 --- /dev/null +++ b/samples/oci-objectstorage-copy-objects-dotnet/Models/InputMessage.cs @@ -0,0 +1,16 @@ +using System; + +namespace CopyObjects +{ + + 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-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