Um dos recursos que o Object Storage nos fornece é disparar eventos quando ocorre uma interação com ele, nesse artigo mostro como usar essa funcionalidade com o OCI Functions para gerar uma PAR e enviar um e-mail quando um objeto é enviado/alterado, além disso também vamos usar o custom metadata.
Configuração do bucket
Ao criar o Bucket, marque a opção Emit Object Events.
Custom metadata
Antes de criar a FN, um dos pontos que levei em consideração foi como permitir ao usuário passar como parâmetro para ela o endereço de destino e o tempo que o PAR seria válido, a opção mais lógica seria o uso de tags, mas elas não estão disponíveis em nível de Objeto (apenas a nível de bucket), então a saída foi usar metada customizado:
Via oci cli você pode usar o parâmetro –metadata
Function
A function tem 3 partes:
- create_PAR, essa função recebe como parâmetro o signer, nome do bucket, nome do PAR, o tempo de validade dele e o nome do objeto, ela retorna o PAR gerado.
- getMetadata, essa é a função que recupera os metadados do objeto e alimenta o envio de e-mail
- handler, essa função é chamada pelo Evento, chama as outras duas e envia o email
import io
import json
from pickle import OBJ
from fdk import response
import os
import oci
import oci.object_storage
from datetime import datetime, timedelta
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def create_PAR(signer, bucket_name, PAR_name, lifetime,OBJ_name):
client = oci.object_storage.ObjectStorageClient(config={}, signer=signer)
namespace = client.get_namespace().data
par_expiration = datetime.utcnow() + timedelta(minutes=lifetime)
object_storage_endpoint = "https://objectstorage." + signer.region + ".oraclecloud.com"
par_details = oci.object_storage.models.CreatePreauthenticatedRequestDetails(name=PAR_name, access_type='ObjectRead', time_expires=par_expiration,object_name=OBJ_name)
par = client.create_preauthenticated_request(namespace_name=namespace, bucket_name=bucket_name, create_preauthenticated_request_details=par_details)
par_url = object_storage_endpoint + par.data.access_uri
print (par_url)
return par_url
def getMetadata(signer, bucket_name,OBJ_name):
signer = oci.auth.signers.get_resource_principals_signer()
client = oci.object_storage.ObjectStorageClient(config={}, signer=signer)
namespace = client.get_namespace().data
meta = client.head_object(namespace_name=namespace, bucket_name=bucket_name, object_name=OBJ_name)
#print(meta.headers['opc-meta-email'])
return meta.headers
def handler(ctx, data: io.BytesIO = None):
resp = None
#f = open('/home/opc/fn-obj/teste.json')
try:
#body = json.load(f)
body = json.loads(data.getvalue())
bucket_name = body["data"]["additionalDetails"]["bucketName"]
PAR_name = "par_" + body["data"]["resourceName"]
OBJ_name = body["data"]["resourceName"]
print (bucket_name)
print (PAR_name)
print (OBJ_name)
signer = oci.auth.signers.get_resource_principals_signer()
emailMetadata = getMetadata(signer, bucket_name,OBJ_name)
#print (emailMetadata)
#print (emailMetadata['opc-meta-tempopar'])
if 'opc-meta-tempopar' in emailMetadata:
lifetime = int(emailMetadata['opc-meta-tempopar'])
else:
lifetime=10
resp = create_PAR(signer, bucket_name, PAR_name, lifetime,OBJ_name)
validade = datetime.utcnow() + timedelta(minutes=lifetime)
mail_content = "Ola, seu PAR para o arquivo " + OBJ_name + " no bucket " +bucket_name+" é o seguinte: \n" + resp + "\nValido até " + str(validade)
print (mail_content)
#The mail addresses and password
sender_address = 'EMAIL_ORIGEM'
sender_pass = 'SENHA'
receiver_address = emailMetadata['opc-meta-emailto']
#Setup the MIME
message = MIMEMultipart()
message['From'] = sender_address
message['To'] = receiver_address
message['Subject'] = 'PAR ' + OBJ_name #The subject line
#The body and the attachments for the mail
message.attach(MIMEText(mail_content, 'plain'))
#Create SMTP session for sending the mail
session = smtplib.SMTP('smtp.gmail.com', 587) #use gmail with port
session.starttls() #enable security
session.login(sender_address, sender_pass) #login with mail_id and password
text = message.as_string()
session.sendmail(sender_address, receiver_address, text)
session.quit()
print('Mail Sent')
return response.Response(
ctx, response_data=json.dumps(resp),
headers={"Content-Type": "application/json"}
)
except (Exception, ValueError) as e:
print("Error " + str(e), flush=True)
Comentários:
- Se a pessoa não entrar o metadado tempopar o PAR vai ter 10 minutos por padrão
- Ajuste os valores sender_address e sender_pass, nesse exemplo estou usando um gmail como origem mas você pode alterar o servidor de e-mail no session
- Caso também use gmail, você precisa criar uma senha do tipo APP nas configurações de segurança
Deploy da function
Crie um app para receber sua função:
Siga o Getting Started que ele vai te guiar na criação dos recursos básicos para a fn funcionar, aqui usei a modalidade Cloud Shell
Após isso, coloque o código da função junto com o arquivo yaml de informações e o requirements.txt dentro de um diretório do seu Cloud Shell e faça o deploy:
Dica, nesse momento você pode ativar a opção de log da função para acompanhar o que está acontecendo nela.
Events
Vamos criar uma Rule que vai monitorar os eventos que ocorrem no Bucket, vá em Observability & Management -> Events Service -> Rules
Aqui no meu ambiente, estou monitorando criação (Object – Create) e atualização (Object Update), além disso também coloquei como condição apenas eventos que ocorram no Bucket com nome bucket-fast:
Como ação, coloquei um tópico do tipo e-mail (Tnk-OGG) para validar se o evento está sendo disparado(você pode remover essa ação depois de validar a FN) e a FN que criamos:
Testando
Agora que já temos todos os recursos provisionados, vamos enviar um objeto e colocar os metadados necessários:
Via console, basta cliar em Advanced e entrar os metadados emailto(quem vai receber o e-mail com o PAR) e tempopar (quanto tempo o PAR vai ser válido)
Via oci cli
oci os object put -ns xxxxxx -bn bucket-fast --file history.log --metadata
'{"emailto":"adriano.tanaka@oracle.com","tempopar":"25"}'
Rule
Aqui podemos ver que a rule foi disparada (Rule has matched event) e que ela executou nossas duas ações.
PAR
E-mail recebido