Oracle Cloud Infrastructure Functions In Go e Usando o OCI Go SDK para acessar serviços do OCI In Go

Esta é a terceira parcela de uma série de cinco partes sobre Go e Oracle Cloud Infrastructure. Esta série discute como os aplicativos Go podem ser criados e executados na Oracle Cloud Infrastructure (OCI) em Instâncias de Computação (VMs), conteinerizados no Kubernetes ou como Funções sem servidor. Os artigos mostram como automatizar a criação e a implantação desses aplicativos Go usando o OCI DevOps. Um tópico importante é como usar serviços do OCI de aplicativos Go, tanto aqueles em execução no OCI quanto no código Go em execução em outro lugar. Alguns dos serviços da OCI discutidos são Armazenamento de Objetos, Streaming, Key Vault e Autonomous Database.

Para acompanhar esses artigos, os leitores devem ter pelo menos conhecimento básico de como criar aplicativos Go. Supõe-se que os leitores tenham acesso ao seu próprio ambiente de desenvolvimento Go. Alguns dos exemplos e capturas de tela mencionarão especificamente o VS Code como ferramenta de desenvolvimento. No entanto, outros editores e IDEs também podem ser usados. O código Go apresentado nesses artigos demonstra uma série de mecanismos em sua forma mais simples para máxima clareza e com menos dependências. Os leitores não devem esperar uma funcionalidade significativa ou um código pronto para produção.

Os artigos descrevem como começar a usar a OCI. Para experimentar os exemplos, os leitores precisarão ter acesso a uma tenancy da OCI com permissões para criar os recursos da OCI discutidos nestes artigos. A maioria dos recursos usados está disponível no Aways Free Tier (Instância de Computação, VCN, Autonomous Database, Armazenamento de Objetos, Registro em Log, Gerenciador de Recursos) ou tem uma camada de alocação gratuita para uso mensal limitado (Functions, API Gateway, Streaming, Vault, DevOps).

A primeira parte desta série descreve o provisionamento de uma Instância de Computação com base na imagem do Oracle Linux Cloud Developer, abrindo-a para atividades de rede de entrada e saída, criando e executando um aplicativo Go que atende a solicitações HTTP e conectando o registro em log produzido pelo aplicativo ao OCI Logging. A segunda parte lida com engenharia de software, automação de criação e implantação do aplicativo com o serviço DevOps da OCI. Esse serviço é usado para armazenar o código-fonte Go, criar o executável do aplicativo, armazená-lo como artefato implantável e implantar esse artefato em uma Instância do Serviço Compute. O segundo artigo também mostra como expor um ponto final HTTP para o aplicativo por meio de um Gateway de API do OCI.

Esta terceira parte mostra como criar funções sem servidor no Go e implantá-las no OCI. O Go SDK para OCI foi introduzido – primeiro para aplicativos Go locais autônomos e, posteriormente, para uso de funções – aproveitando a autenticação do controlador de recursos. Esse SDK é usado para interagir com o serviço OCI Object Storage para criar buckets e gravar e ler arquivos.

Inicialmente, a função é criada e implantada manualmente. Uma rota é adicionada à implantação no Gateway de API para chamar a função de um cliente externo ao OCI. Em seguida, um Pipeline de Implantação DevOps do OCI é criado para implantar a função de uma imagem no Container Image Registry. Por fim, um pipeline de build é configurado para obter origens no repositório de código, criar e publicar uma imagem de contêiner e, em seguida, acionar o pipeline de implantação para build e implantação de ponta a ponta.

OCI Functions em qualquer lugar

A função sem servidor na OCI é baseada na tecnologia do Project Fn de código aberto. A lógica de negócios da função é escrita em sua linguagem favorita - neste caso Go - e incorporada no framework Fn que lida com o ciclo de vida da função e as interações com a função. O framework Fn pode ser executado em qualquer lugar: em sua máquina local, em uma VM em qualquer nuvem ou on-premise. O Oracle Cloud Infrastructure oferece um serviço PaaS totalmente gerenciado do OCI Functions para funções sem servidor baseadas nessa mesma tecnologia.

Uma Função é criada em uma imagem de contêiner. Esta imagem é enviada para um registro de imagem do contêiner. Para publicar a função, esta imagem é transferida para um Servidor Fn. Sempre que a função é chamada, um contêiner é iniciado na imagem e a solicitação é processada. Os contêineres são mantidos em execução por algum tempo após lidar com uma chamada, em um estado quente, prontos para lidar com solicitações adicionais. Quando o número de solicitações simultâneas aumenta, contêineres adicionais para a mesma função serão iniciados pelo Servidor Fn para garantir que todas as solicitações possam ser tratadas.

A atração de funções para desenvolvedores e operadores de aplicativos é o fato de que nenhuma energia precisa ser derramada na concepção, criação e gerenciamento da plataforma que executa a lógica de negócios. Todo o foco pode ser em escrever essa lógica.

Vamos agora olhar para a criação da função em Go, construindo-a em uma imagem de contêiner, implantando e executando-a localmente. Em seguida, levaremos essa função ao serviço OCI Functions e a executaremos no lado da nuvem.

O Ambiente de Desenvolvimento Fn

Para desenvolver funções, você precisará de um ambiente que suporte o Project Fn. O Fn é uma plataforma leve de funções sem servidor baseada no Docker que você pode executar em seu laptop, servidor ou nuvem. Você pode instalar o Fn facilmente no Linux ou em MacOS seguindo as instruções em Tutoriais do Projeto Fn – Instalar o Fn.

Você pode optar por trabalhar na instância de computação go-app-vm que criamos na primeira e usamos também na segunda parcela desta série. Esse ambiente do Oracle Linux não vem com a configuração do Fn, mas a instalação é bastante simples.

Como alternativa, você pode trabalhar no OCI Cloud Shell. Este ambiente acessível ao navegador é configurado com Fn. Para trabalhar com Fn no OCI Cloud Shell, consulte Funções de Documentação do OCI: Conceitos Básicos do Uso do Cloud Shell.

Desenvolver uma Função Fn

No ambiente de desenvolvimento com a CLI do Fn instalada, navegue até um diretório no qual você deseja criar o subdiretório da função. Na linha de comando, digite este comando e execute-o:

fn init --runtime go --trigger http greeter

Um subdiretório chamado greeter é criado. Navegue até ele e verifique seu conteúdo:

cd greeter ls -l

O arquivo func.yaml contém os metadados sobre a função, a serem interpretados pelo framework Fn ao criar e, posteriormente, ao executar a função. O arquivo go.mod contém a dependência que a função tem no pacote fdk-go. A própria função está em func.go. A estrutura da função e sua interação com o runtime do Fn pode ser vista aqui: a função principal registra a função myHandler com o runtime do Fn, que instrui e permite que o runtime chame essa função para qualquer solicitação HTTP recebida. A função recebe o corpo da solicitação em um parâmetro io.Reader. Ele também recebe a saída de io.Writer, na qual o corpo da resposta pode ser gravado. O parâmetro ctx context.Context contém metadados para a solicitação original, incluindo cabeçalhos HTTP, o URL completo, o método de solicitação e a própria função, incluindo todas as variáveis de configuração definidas para ele.

No momento, a função myHandler decodifica o corpo da solicitação, esperando que ele contenha um payload JSON com um campo chamado nome. Ele cria uma Pessoa com o nome definido como o valor deste campo ou, na sua ausência, assume World como padrão. Em seguida, ele cria a resposta esperada: um objeto JSON com um único campo chamado mensagem, que contém uma string composta de Hello e o valor do nome.

Embora não faça nada verdadeiramente espetacular, a função é sólida e completa e podemos implantá-la localmente e invocá-la. Para isso, precisamos de um contexto local e do servidor Fn local ativo e em execução. Verifique os contextos usando:

fn list contexts

Isso mostra uma lista de pelo menos um contexto – possivelmente mais de um. Para trabalhar com o servidor Fn local, certifique-se de que o contexto padrão seja o ativo. Se necessário para definir o contexto atual como padrão, use:

fn use context default

Agora crie um aplicativo como host para a função:

fn create app go-oci-app fn list apps

Se a primeira dessas instruções falhar com a conexão recusada, o servidor provavelmente ainda não está em execução. Use o próximo comando para iniciar o servidor e tente novamente criar o aplicativo.

fn start

Com o aplicativo criado com sucesso, a função agora pode ser implantada nele. O próximo comando cuida dessa implantação; ele é precedido pelo processo de criação da imagem do contêiner.

fn --verbose deploy --app go-oci-app --local

Especificar --local faz a implantação no servidor local, mas não envia a imagem da função para um registro do Docker, o que seria necessário se estivéssemos implantando em um servidor Fn remoto.

Como ele libera uma quantidade impressionante de mensagens de log a serem produzidas, a bandeira --verbosa não é uma que você usará o tempo todo. No entanto, isso lhe dá uma boa visão do que está acontecendo. Várias imagens de contêiner são extraídas e, em seguida, um processo de criação de contêiner de dois estágios é executado para criar uma imagem de contêiner para a função greeter. As imagens predefinidas do projeto Fn são usadas para o estágio de build (fnproject/go:1.15-dev no momento da gravação) e como base para a imagem de runtime (fnproject/go:1.15).

A saída final terá esta aparência:

Updating function greeter using image greeter:0.0.2... Successfully created function: greeter with greeter:0.0.2 Successfully created trigger: greeter Trigger Endpoint: http://localhost:8080/t/go-oci-app/greeter

A imagem da função é chamada de greeter:0.0.2. Você encontrará essa imagem no registro de contêiner local com:

docker images | grep greeter

A função pode ser chamada por meio da CLI do Fn, usando seu nome e aplicativo, como este:

fn invoke go-oci-app greeter

A função espera um payload JSON que contenha um campo de nome; portanto, vamos fornecê-lo exatamente com isso:

echo -n '{"name":"Clark Kent"}' | fn invoke go-oci-app greeter --content-type application/json

A saída da implantação da função também nos deu o Ponto Final do Trigger para a função. Este é um ponto final HTTP para o qual podemos enviar uma solicitação HTTP e fazer com que ela acione a função. Não temos interação (visível) com o Fn, embora o ponto final que chamamos seja realmente o ponto final do Fn Server. O caminho do URL informa ao Fn o aplicativo e a função específica a serem acionados.

curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' http://localhost:8080/t/go-oci-app/greeter

Criar Função do OCI

Agora, vamos criar essa Função na OCI, em vez de apenas em nosso ambiente de desenvolvimento. As etapas são muito semelhantes às que usamos para criar a função localmente; só precisamos usar um contexto diferente. Não o contexto local, mas um para a OCI.

Criar aplicativo

Vamos primeiro criar o aplicativo por meio da Console do OCI. Digite o aplicativo na caixa de pesquisa e clique em Funções de Aplicativos na Área de Serviços.

Clique no botão Criar Aplicativo. Digite o nome do aplicativo: go-on-oci-app. Selecione a VCN que foi criada na parte um da série de artigos e sua sub-rede pública. Em seguida, clique em Criar para criar o aplicativo.

artigo sobre oci (way-to-go-on-oci)

Preparar Ambiente Local para Push da Imagem de Interação e Função do OCI

Depois que o aplicativo é criado, as Informações Gerais do aplicativo são apresentadas. A página também contém instruções para criar sua primeira função, no OCI Cloud Shell, ou em uma configuração local (que poderia, é claro, também ser a instância de computação go-app-vm).

artigo sobre oci (way-to-go-on-oci)

Se você estiver usando o OCI Cloud Shell, as etapas para criar esse contexto serão um pouco diferentes (e mais simples) do que quando você trabalha em um ambiente de desenvolvimento regular. Sinta-se à vontade para seguir a configuração do OCI Shell. Neste artigo, tomaremos o outro caminho, usado para qualquer ambiente de desenvolvimento local.

Há várias etapas a serem executadas em um ambiente local (no qual a CLI do Fn foi instalada anteriormente):

  1. Configure uma Chave de Assinatura de API e armazene a chave privada em um arquivo .pem no diretório HOME/.OCI local e faça upload da chave pública para a Console do OCI. Consulte as instruções em Documentação do OCI – Chaves Obrigatórias.
  2. Crie a configuração do arquivo no diretório .oci no ambiente local; copie o snippet de Visualização do Arquivo de Configuração para o arquivo de configuração. Atualize a entrada key_file no arquivo: faça referência ao arquivo pem da chave privada. Documentação do OCI – Arquivo de Configuração de SDK e CLI.
  3. Para enviar imagens de contêiner para o OCI Container Image Registry, você precisa de um token de autenticação. Na parte um desta série de artigos, você criou um token a ser usado para fazer log-in no Repositório de Código DevOps do cliente git. Você pode reutilizar esse token para registrar o cliente Docker no registro de imagem do contêiner ou pode criar um novo token de autenticação. Nesse último caso, consulte Documentação do OCI – Obtendo um Token de Autenticação.
  4. Você precisará da CLI do OCI. As instruções para instalar essa ferramenta podem ser encontradas na Documentação do OCI: Trabalhando com a Interface de Linha de Comando do OCI – Início Rápido. A CLI do OCI usará o arquivo HOME/.oci/config e a chave privada referenciada para estabelecer conexões seguras com as APIs do OCI.

Após essas etapas, você pode experimentar o sucesso das etapas 1, 2 e 4 com esse comando, que deve retornar uma lista dos compartimentos em sua tenancy:

oci iam compartment list

Opcional: Criar Repositório de Registro de Imagem do Contêiner

Se a conta de usuário usada para implantar a função tiver as permissões necessárias do serviço IAM, a implantação criará o repositório para as imagens de função no Container Image Registry. Caso esses privilégios não estejam disponíveis ou você queira preparar o repositório, faça o seguinte.

  1. Digite regi na barra de pesquisa. Clique no link Registro de Contêiner > Contêineres e Artefatos.
  2. Clique em Criar repositório. Digite o nome do repositório: go-on-oci/greeter. Isso é composto pelo prefixo do repositório e o nome da função, na qual o repositório conterá as imagens. Defina o Acesso ao Público.
  3. Clique no botão Criar repositório. Após alguns segundos, um repositório de imagens de contêiner novo e vazio é criado, pronto para receber as imagens de função (contêiner) que enviaremos usando a CLI do Fn.
artigo sobre oci (way-to-go-on-oci)

Criar um Contexto para o OCI na CLI do Fn

Voltando para a linha de comando do ambiente local, precisamos criar um contexto Fn para o compartimento atual no OCI e, em seguida, selecioná-lo para uso em operações Fn. Execute estes comandos (que você pode copiar na guia Conceitos Básicos da página go-on-oci-app):

fn create context go-on-oci --provider oracle fn use context go-on-oci
artigo sobre oci (way-to-go-on-oci)

Copie os comandos na etapa 4 para atualizar o contexto com o OCID do compartimento e o URL da API do Oracle Functions. No meu caso:

fn update context oracle.compartment-id ocid1.compartment.oc1..aaaaaaaaqb4vxvxuho5h7eewd3fl6dmlh4xg5qaqmtlcmzjtpxszfc7nzbyq fn update context api-url https://functions.us-ashburn-1.oraclecloud.com

O comando será semelhante, mas diferente para você.

Forneça o prefixo do nome do repositório exclusivo. Use go-on-oci e especifique o compartimento que contém o repositório de registro de imagens no qual a imagem de função deve ser publicada:

fn update context registry iad.ocir.io/idtwlqf2hanz/go-on-oci fn update context oracle.image-compartment-id <compartment-ocid> </compartment-ocid>

Faça log-in no Registro usando o Token de Autenticação como sua senha:

docker login iad.ocir.io

No meu caso, a região em que trabalho é Ashburn, identificada pela chave de região iad.ocir.io. Sou solicitado a informar o nome de usuário. Esta é uma string que inclui o prefixo de namespace incluído no nome do Container Image Registry e em cada repositório. Em seguida, a senha é solicitada. Aqui, você fornece um token de autenticação configurado para o usuário, que usamos anteriormente no artigo anterior quando o login foi executado no repositório de código.

O próximo comando mostra uma listagem dos aplicativos no contexto Fn atual:

fn list apps

A lista contém um aplicativo, chamado go-on-oci-app.

A função greeter criada, implantada localmente e chamada anteriormente agora também pode ser implantada no Aplicativo OCI para se tornar uma função nativa da nuvem sem servidor. O comando que usamos para implantação é o mesmo que usamos antes. Seu efeito é dramaticamente diferente devido ao contexto alterado. Em vez de um contexto local, agora há um contexto baseado em um Provedor do OCI e vinculado a um Tenancy e Compartimento do OCI. A imagem do contêiner é enviada para o OCI Container Image Registry e a Função é criada no serviço OCI Function.

fn -v deploy --app go-on-oci-app

A saída é semelhante ao que foi gerado antes, mas o processo de criação é exatamente o mesmo. Quando a imagem do contêiner de funções estiver pronta, as coisas começarão a se desviar. A imagem é enviada para o OCI Container Image Registry e a função é implantada na nuvem. As linhas relacionadas na saída:

=> exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:008dc3b990f1e69d67a7dd8649fbd63649d72f0bf1a161b2c2e073064f16c918 0.0s => => naming to iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 0.0s Parts: [iad.ocir.io idtwlqf2hanz go-on-oci greeter:0.0.3] Using Container engine docker to push Pushing iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 to docker registry...The push refers to repository [iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter] ... e57f007acf74: Pushed 0.0.3: digest: sha256:bb4f2abde44d97517520571a21c407e349ddfc6572583a8ba53717436fd0b7f5 size: 1155 Updating function greeter using image iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3... Successfully created function: greeter with iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 Fn: HTTP Triggers are not supported on Oracle Functions

Neste ponto, a função está na nuvem e pode ser chamada (ainda usando a CLI Fn):

fn invoke go-on-oci-app greeter

A primeira chamada levará algum tempo porque a função começa a frio e a imagem do contêiner subjacente precisa ser instanciada em um contêiner em execução. Cada chamada subsequente da função será muito mais rápida. Observe que, se você esperar dez minutos e a função esfriar, o contêiner será interrompido.

Esta imagem descreve a situação em que chegamos:

artigo sobre oci (way-to-go-on-oci)

Você pode verificar na Console do OCI a evidência do que acabou de acontecer. Digite greeter na caixa de pesquisa no console. Em Recursos, haverá uma entrada >greeter > Functions>. Clique no link para ir até a página mostrando os detalhes da função. Você encontrará referências à imagem da função, à definição de memória e ao ponto final para chamar a função. Em métricas, você deve encontrar evidências da chamada para a função feita usando a CLI Fn.

Nos resultados da pesquisa para greeter, você também encontrará o go-on-oci/greeter do Repositório de Contêiner. Ao navegar até o repositório, você encontrará detalhes das imagens publicadas nele.

Criar Rota do Gateway de API para Função

O OCI Functions não pode apenas ser chamado. Mesmo que eles tenham um ponto final HTTP que parece sugerir que você pode simplesmente chamá-los do seu navegador ou enrolar na linha de comando, não é realmente tão simples assim. As chamadas HTTP para funções precisam ser assinadas, e esse processo de assinatura não é simples e direto.

Uma maneira melhor de permitir que os consumidores chamem funções é por meio de um Gateway de API. Usamos um Gateway de API no artigo anterior para abrir uma rota pública para o aplicativo myserver em execução em uma instância de computação privada (potencialmente). Agora faremos o mesmo para a função greeter usando uma rota adicional no API Gateway the-API-gateway e na implantação myserver-API criada no artigo anterior.

artigo sobre oci (way-to-go-on-oci)

Configurar o Acesso ao IAM para o Gateway de API

O Gateway de API precisa ter permissão para chamar a função, usando uma política que forneça permissão para que o Gateway de API chame funções.

Crie a política do Gateway de API para chamar funções. Para ir, crie uma política no console: digite poli na barra de pesquisa e clique em >Políticas > Identidade> na área Serviços do pop-up de resultados da pesquisa. Isso leva você à página de visão geral de Políticas do compartimento atual.

A política define a permissão para que os Gateways de API acessem recursos no compartimento. Crie uma nova política, digite um nome (invoke-function-for-api-gateway), uma descrição e a seguinte instrução:

ALLOW any-user to use functions-family in compartment <compartment-name> where ALL {request.principal.type= 'ApiGateway', request.resource.compartment.id = '<compartment-id></compartment-id>'}</compartment-name>

Substitua pelo nome do compartimento, que provavelmente é go-on-oci. Substitua pelo identificador do compartimento no qual você está trabalhando.

Definir a Rota da Função na Implantação no Gateway de API

Com as permissões atendidas, agora podemos definir a rota no Gateway de API. Digite gat na barra de pesquisa no console. Clique em Gateways > Gerenciamento de API. Clique no link para o *api-gateway. Clique em Implantações. Na lista de implantações (que contém uma única implantação), clique no link myserver-api.

Clique no botão Editar para abrir a especificação de implantação. Clique no link para a segunda etapa: Rotas. Role para baixo e clique no botão + Outra Rota.

Digite /greeting como o caminho para esta nova rota. Selecione GET como o método e o Oracle Functions como o Tipo (de backend). Selecione o aplicativo go-on-oci-app e, em seguida, defina o Nome da Função como greeter.

artigo sobre oci (way-to-go-on-oci)

Pressione Próximo. Em seguida, pressione Salvar Alterações para aplicar as alterações e tornar a nova rota real.

Chamar a Função por meio do Gateway de API

Com a nova rota configurada e a implantação atualizada no Gateway de API, agora podemos fazer uma solicitação HTTP simples e direta para o ponto final público do Gateway de API, acionando indiretamente o cumprimentador da função e recebendo sua resposta.

Usando este URL em qualquer navegador, você deve ser capaz de obter a resposta da função:

https://<url for="" api="" gateway="">/my-api/greeting</url>

A resposta é um pouco decepcionante, mas isso é esperado com uma função tão simplista.

Usando curl, você pode enviar um payload JSON para a função e receber uma resposta um pouco mais interessante.

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<url for="" api="" gateway="">/my-api/greeting</url>

A resposta lê {"message":"Olá Mickey Mouse"}.

Portanto, agora estabelecemos o fluxo de ponta a ponta do Gateway de API para a função sem servidor. E temos uma maneira de implantar manualmente a função com base nas fontes em nosso ambiente de desenvolvimento local. Para aproveitar nosso trabalho, você pode fazer algumas alterações no código-fonte em func.go e, em seguida, implantar a função mais uma vez – um único comando com a CLI do Fn – e chamar a rota de saudação no Gateway de API para ver se nossa alteração está ativa.

Por exemplo: altere a linha que define o valor de Mensagem para

Msg: fmt.Sprintf("Warmest greetings from your function dear %s", p.Name)

Salve a origem func.go atualizada. Em seguida, execute esses comandos para implantar a função atualizada e, em seguida, chamá-la:

fn -v deploy --app go-on-oci-app
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<url for="" api="" gateway="">/my-api/greeting
    </url>

Isso deve resultar em uma resposta melhorada. O processo de criação e implantação é condensado em um único comando manual em um ambiente preparado. Em seguida, analisaremos um processo de implantação automatizado para funções que usam o OCI DevOps, seguido pelo processo de build automatizado anterior baseado na origem em um repositório de código. Em seguida, passaremos para funções que fazem um pouco mais do que retornar cumprimentos simples.

Implantação Automatizada de Funções

Na parcela anterior desta série, vimos o uso de Pipelines de Implantação DevOps do OCI para implantar um aplicativo em uma instância de computação. Agora, usaremos um pipeline para implantação automatizada de uma função. A abordagem geral e os ingredientes são semelhantes. Precisamos de um artefato, um ambiente (de destino) e do pipeline de implantação com um estágio de Implantação de Função, bem como permissões do serviço IAM para que o pipeline leia o artefato e implante a função.

artigo sobre oci (way-to-go-on-oci)

Estes ingredientes em mais detalhes:

  1. Um Artefato: a referência a, neste caso, uma Imagem de Contêiner específica no OCI Container Image Registry, usando o caminho totalmente qualificado para o repositório e a imagem e a versão específicas.
  2. Um Ambiente: a referência à Função que desejo (re)implantar. O Ambiente, no caso da implantação da Função, não é o compartimento ou um aplicativo em um compartimento (como se poderia supor), mas a própria função que, portanto, já precisa existir para que possa ser implantada por meio de um Pipeline de Implantação DevOps do OCI. (Observe que a Função não precisa ser útil – ela pode ser baseada na imagem do contêiner Scratch.)
  3. Um Pipeline de Implantação com um Estágio de Pipeline de Implantação do tipo Implantação de Função que conecta o Artefato e o Ambiente.
  4. Um grupo dinâmico que contém o pipeline de implantação e políticas do serviço IAM que permitem que o grupo dinâmico leia artefatos (como imagens de contêiner de funções) e implante funções (em termos gerais, gerencie recursos do OCI).

Criar Artefato DevOps para a Imagem do Contêiner da Função

Na Console do OCI, navegue até a home page do go-on-OCI do Projeto DevOps. Abra a guia Artefatos. Clique no botão Adicionar artefato. Observe que o que definimos aqui é um link ou um proxy do Projeto DevOps para um artefato real, não o próprio artefato.

Digite greeter-function como o nome do artefato DevOps. O tipo deve ser definido como repositório de imagens do Contêiner. O caminho totalmente qualificado para a imagem consiste na chave da região, no namespace e prefixo do repositório, no nome da função e na tag da versão da função. Nesse caso, use um espaço reservado para a tag de versão. O caminho agora é definido da seguinte forma:

<region key="">/<namespace>/<repository prefix="">/greeter:${imageVersion}</repository></namespace></region>

Defina o campo suspenso Substituir parâmetros usados neste artefato como Sim, substituir placeholders.

artigo sobre oci (way-to-go-on-oci)

Clique no botão Adicionar para concluir e salve a definição do artefato.

Definir o Ambiente DevOps para a Função

Abra a guia Ambientes no projeto DevOps. Ele contém o ambiente go-on-oci-vm que foi criado para a implantação do myserver na instância do serviço Compute (no artigo anterior). Clique no botão Criar ambiente.

Na primeira etapa, Informações básicas, clique no bloco Funções - Criar um ambiente para uma Função. Digite greeter-function-in-app-go-on-oci-app como o nome do ambiente. Pressione Próximo para ir para a segunda etapa com detalhes do Ambiente. Confirme a Região, o Compartimento, o Aplicativo e a Função – você provavelmente não precisará alterar nenhuma dessas definições. Se você fizer isso, certifique-se de que a função greeter no aplicativo go-on-oci-app esteja selecionada.

Clique em Criar ambiente para salvar a definição.

artigo sobre oci (way-to-go-on-oci)

Criar o Pipeline de Implantação para Implantar a Função

Na página de visão geral do Projeto DevOps, clique em Criar pipeline. O formulário Criar pipeline é apresentado. Digite um nome (deploy-greeter-function-to-go-on-oci-app) e, opcionalmente, uma descrição. Em seguida, clique em Create pipeline. O pipeline de implantação é criado, embora esteja bastante vazio: não é um ambiente no qual ele deve ser implantado, nenhum artefato que deve ser implantado e nenhum arquivo de configuração para definir as etapas a serem executadas.

No editor de pipeline que aparece, clique no mosaico Adicionar Estágio (ou no ícone de mais). A próxima página mostra uma lista de tipos de estágio. Clique no mosaico rotulado Usa a estratégia de atualização incorporada do Functions.

Pressione o botão Avançar.

Digite o nome do estágio, por exemplo, update-function-greeter. Selecione o ambiente que foi definido anteriormente para a função: greeter-function-in-app-go-on-oci-app.

No cabeçalho Artefato, clique em Selecionar Artefato. Uma lista de todos os artefatos no projeto DevOps do tipo Imagem do Docker é apresentada. Selecione a única entrada, a que foi criada anteriormente para a Imagem do Contêiner de Funções.

Observe que o botão Selecionar Artefato não está mais ativado: somente uma única imagem de contêiner pode ser associada a este estágio.

artigo sobre oci (way-to-go-on-oci)

Clique em Adicionar. O estágio do pipeline é criado no pipeline. E o pipeline agora está pronto para ser executado – sua definição está completa. Ou é? O artefato usado por este pipeline não está definido inequivocamente: o label da versão no caminho da imagem do contêiner contém o placeholder ${imageVersion}. Para garantir que a versão adequada seja usada para implantação, esse espaço reservado precisa ser substituído pelo valor correto. E isso é organizado definindo um parâmetro no pipeline que é chamado imageVersion e é definido como um label de versão existente.

Clique na guia Parâmetros do pipeline. Defina um novo parâmetro chamado imageVersion. Seu valor padrão pode ser qualquer coisa, mas também pode corresponder a um rótulo de versão existente para a imagem do contêiner da função greeter. Salve a definição de parâmetro.

Parece que o pipeline está pronto para ser executado, mas ainda temos que ter certeza de que ele tem permissão para fazer seu trabalho. Antes de tentar qualquer erupção cutânea, leia a próxima seção.

Grupo Dinâmico e Políticas

No artigo anterior, um grupo dinâmico foi definido para todos os pipelines de implantação no compartimento. O novo pipeline é automaticamente um membro desse grupo. Também definimos uma política que concedeu permissões ao grupo dinâmico para ler todos os artefatos, o que inclui Imagens do Contêiner (Função) nos repositórios do Container Image Registry do compartimento. Outra política que também já foi criada concede ao grupo dinâmico a permissão muito ampla para gerenciar todos os recursos no compartimento. Podemos beneficiar do amplo âmbito dessa política, uma vez que também abrange a criação e atualização de funções.

Executar o Pipeline de Implantação

Execute o Pipeline de Implantação pressionando Run pipeline.

Uma vez concluída a implantação, você verá os marcadores verdes que proclamam o sucesso. No entanto, não há outra indicação óbvia deste sucesso, porque o resultado final é exatamente a situação que tínhamos alcançado com a implantação manual da função a partir da linha de comando Fn CLI.

artigo sobre oci (way-to-go-on-oci)

Para tornar as coisas um pouco mais interessantes, vamos fazer uma mudança no código da função. Em seguida, crie a imagem do contêiner para a função (localmente) e envie a nova imagem de função para o registro de imagem do contêiner. Em seguida, iniciaremos o pipeline de implantação mais uma vez; desta vez, quando bem-sucedido, ele renderá uma nova situação que podemos experimentar chamando a rota my-API/saudação no Gateway de API.

Implementação da Função de Alteração

Faça uma alteração pequena, mas visível, para func.go em seu ambiente local: certifique-se de que a resposta da nova versão da função pareça visivelmente diferente da versão atual. Salve a alteração.

Nas próximas seções, criaremos uma nova versão da imagem do contêiner de funções com base na origem alterada e, eventualmente, a executaremos no OCI Functions.

Criar uma nova Imagem do Contêiner de Funções (localmente)

Esses próximos comandos primeiro modificarão o rótulo da versão usado para marcar a função com um aumento no terceiro dígito (bm é abreviado para bump). Em seguida, a imagem do contêiner de funções é criada usando as origens alteradas. O terceiro comando lista as imagens de contêiner local, filtrando imagens com greeter em seu nome. Agora, execute os comandos.

fn bm
fn build 
docker images | grep greeter

Você deverá ser capaz de encontrar a imagem recém-criada com seu nome totalmente qualificado, incluindo a chave da região do OCI, o namespace, o prefixo do repositório e o cumprimentador do nome da função, com o label da versão anexado.

Imagem do Contêiner de Tag com um Label de Nova Versão e Enviar para o Registro

Vamos definir um novo identificador para a imagem, usando este comando que define o rótulo da versão como 0.1.0:

docker tag <fully qualified="" image="" name="">:<newly assigned="" bumped="" version="" label="">  <fully qualified="" image="" name="">:0.1.0

    </fully></newly></fully>

Em seguida, envie a nova imagem do contêiner de funções para o repositório do OCI Container Image Registry usando:

docker push <fully qualified="" image="" name="">:0.1.0

    </fully>

Observe que neste ponto não reimplantamos a função com base nesta nova versão da imagem do contêiner. Tudo o que fizemos foi criar a imagem e enviá-la para o registro na OCI. Chamar a Função do OCI não mostrará nenhuma diferença.

Executar o Pipeline de Implantação (para a nova Imagem da Função)

Execute o pipeline de implantação mais uma vez. Defina o valor do parâmetro imageVersion como 0.1.0.

Quando o pipeline for concluído com sucesso, a nova versão da função com todas as alterações interessantes que você aplicou a ele estará ativa.

Chamar a Função Recém-Implantada

Você pode ver a nova versão da função em ação chamando-a na linha de comando usando a CLI Fn:

fn invoke go-on-oci-app greeter

(Como o contexto da CLI do Fn ainda é go-on-OCI do provedor Oracle e configurado para o compartimento go-on-OCI que contém a função greeter, essa chamada será direcionada para a Função OCI, que neste momento é baseada na nova versão da imagem do contêiner.)

Como alternativa, você pode alternar para a rota no Gateway de API que chama a função:

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<api gateway="" endpoint="">/my-api/greeting

    </api>

Criação Automatizada de Funções

Até agora, criamos a imagem do contêiner de funções manualmente no ambiente de desenvolvimento local usando a CLI do Fn. No entanto, assim como fizemos no artigo anterior para o aplicativo Go que foi produzido como executável por um Pipeline de Build, agora transformaremos a construção da função em um processo automatizado. Um Pipeline de Build DevOps do OCI é criado para obter origens do Repositório de Código, executar um estágio de build gerenciado que produz uma imagem de contêiner local e publicar essa imagem como artefato no repositório do Container Image Registry. Como última etapa, o Pipeline de Build aciona o Pipeline de Implantação para publicar a definição mais recente da função no ambiente de runtime do OCI Functions.

Quando todos os elementos estão em vigor, o conjunto total interconectado de componentes da OCI parece ser visualizado na próxima figura.

artigo sobre oci (way-to-go-on-oci)

O artefato e o pipeline de implantação mostrados nesta figura já estão definidos no projeto DevOps, assim como o repositório de Aplicativo, Função e Registro de Imagem do Contêiner para as imagens da função. Usaremos o Repositório de Código configurado no artigo anterior. Tudo o que precisamos criar é a função Build Pipeline build-greeter com seus três estágios.

Criar o Pipeline de Build

Na página de visão geral do DevOps Project go-on-oci, clique no botão Criar pipeline de build. Uma página é apresentada para especificar o nome – por exemplo, função build-greeter – e uma descrição. Pressione Criar para que o pipeline de build seja adicionado ao Projeto DevOps.

Clique no vínculo build-greeter-function na lista para navegar até a página de detalhes.

Primeiro Estágio - Build Gerenciado

O primeiro estágio em qualquer pipeline de build é um estágio de Build Gerenciado. Este estágio fornece instruções para o pipeline obter retenção de um servidor de build, copiar origens especificadas de repositórios de código para o servidor e executar várias ações nesse servidor. No momento desta redação, podemos usar uma única imagem para o servidor de compilação. É uma imagem do Oracle Linux (8 GB de memória, 1 OCPU) que tem várias ferramentas pré-instaladas e tempos de execução de linguagem. Para criar a imagem do contêiner de funções, é relevante que o servidor de build esteja equipado com a CLI do Docker e do Fn.

Clique no ícone de mais ou no cartão Adicionar Estágio. O assistente Adicionar estágio em duas etapas é exibido. Na primeira etapa do assistente, certifique-se de que o cartão de Build Gerenciado esteja selecionado para o tipo de estágio. Pressione Próximo.

A segunda página é exibida. Defina um nome para o estágio de construção: build-go-source-to-function-container-image. Opcionalmente, adicione uma descrição.

Atualmente, não podemos selecionar uma imagem de construção diferente, então nos contentamos com a disponível, o que é bom para nossos propósitos.

Defina o caminho do arquivo de especificação de Build como /functions/greeter/go-function-build-spec.yaml. Esse arquivo contém as instruções para criar as origens Go na função greeter (ou em qualquer outra função Go) e, finalmente, criar a imagem do contêiner da função.

Clique no botão Select em Primary code repository. Agora podemos especificar a partir de qual repositório de código o build obterá suas origens. Selecione OCI Code Repository como o Tipo de Conexão de Origem. Em seguida, selecione o repositório go-on-oci-repo. Trabalharemos com fontes na ramificação principal, portanto, não altere esse padrão. Digite go-on-oci-sources como o valor do nome de origem do Build. Um estágio de build gerenciado pode usar origens de vários repositórios. Na especificação de build, podemos fazer referência a cada uma das localizações raiz dessas origens usando o label definido como nome de origem de Build. Clique em Salvar.

artigo sobre oci (way-to-go-on-oci)

Pressione o botão Adicionar. Isso conclui a definição do estágio de build gerenciado. Isso é tudo o que é necessário para pegar fontes e processá-las em artefatos. As instruções detalhadas executadas por este estágio de build gerenciado e no servidor de build são definidas no arquivo go-function-build-spec.yaml. É este arquivo que contém as instruções para as etapas detalhadas reais executadas no servidor de compilação.

version: 0.1
component: build
timeoutInSeconds: 6000
runAs: root
shell: bash
env:
# these are local variables to the build config
variables:
SOURCE_DIRECTORY: "go-on-oci-sources/functions/greeter"
FUNCTION_NAME: "greeter"

# # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service
# you can then access the value of that secret in your build_spec.yaml commands
vaultVariables:

# exportedVariables are made available to use in sucessor stages in this Build Pipeline
# For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set
exportedVariables:
- BUILDRUN_HASH


steps:
- type: Command
name: "Export variables"
timeoutInSeconds: 40
command: |
export BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7`
echo "BUILDRUN_HASH: " $BUILDRUN_HASH
echo "fully qual sources" ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
echo "container image version from build pipeline parameter" ${imageVersion}      
go version

- type: Command
timeoutInSeconds: 600
name: "Install golangci-lint"
command: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.1

- type: Command
timeoutInSeconds: 600
name: "Verify golangci-lint version"
command: |
/root/go/bin/golangci-lint version

- type: Command
timeoutInSeconds: 600
name: "Run go mod tidy for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go mod tidy

- type: Command
timeoutInSeconds: 600
name: "Run go vet for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go vet .

- type: Command
timeoutInSeconds: 600
name: "Run gofmt for Go Application"
command: |
gofmt -w ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}

- type: Command
timeoutInSeconds: 600
name: "Run Lint for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
/root/go/bin/golangci-lint run .

- type: Command
timeoutInSeconds: 600
name: "Run Unit Tests for Go Application (with verbose output)"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go test -v 

- type: Command
timeoutInSeconds: 600
name: "Build Go Function into Function Container Image"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
pwd
fn build --verbose
image=$(docker images | grep $FUNCTION_NAME  | awk -F ' ' '{print $3}') ; docker tag $image go-function-container-image    


outputArtifacts:
- name: go-function-container-image
type: DOCKER_IMAGE
# this location tag doesn't effect the tag used to deliver the container image
# to the Container Registry
location: go-function-container-image:latest

A especificação de build consiste em três partes:

  1. Configurar como executar o script, qual shell usar e quais variáveis usar
  2. Etapas de build: Comandos de shell a serem executados no servidor de build
  3. Artefatos de Saída indicam quais arquivos no final de todas as etapas de build são significativos e devem ser disponibilizados para outras etapas no pipeline (por exemplo, para publicar como artefato)

As etapas de criação podem ser resumidas como:

  1. Imprima variáveis de ambiente e a versão Go instalada no momento (no servidor de compilação padrão)
  2. Instalar golangci-lint
  3. Verificar o sucesso e a versão da instalação do golangci-lint
  4. Execute go mod tidy para organizar o arquivo go.mod com dependências
  5. Executar veterinário go para executar uma primeira inspeção nas Fontes Go
  6. Execute go fmt para formatar as origens de acordo com as regras de formatação genéricas
  7. Executar golangci-lint para lint (verificar) as fontes contra várias regras de linting
  8. Executar testes de unidade
  9. Criar origens de função em uma Imagem do Contêiner de Funções usando a CLI do Fn (armazenar no registro de imagens local)
  10. Marque a Imagem do Contêiner de Funções com o nome go-function-container-image. Este é o nome usado no próximo estágio para localizar a imagem a ser publicada

Essas etapas são em grande parte equivalentes ao build gerenciado definido no artigo anterior para o aplicativo Go que foi finalmente transformado em um executável binário implantado em uma VM. As etapas 9 e 10 são diferentes – elas transformam o aplicativo Go em uma Imagem de Contêiner de Função que é o produto final da criação.

Segundo Estágio - Publicar Artefato

Na página de visão geral do pipeline de build, clique no ícone de mais na parte inferior do estágio de build gerenciado atual. No menu de contexto que aparece, clique em Adicionar estágio. O assistente de preparação é exibido.

Clique em Entregar artefatos. Em seguida, clique em Avançar.

Digite o nome deste estágio: publish-greeter-function-container-image. Precisamos selecionar o artefato no projeto DevOps que queremos publicar. Este artefato é o go-on-oci/greeter da imagem do contêiner:${imageVersion}. Clique em Selecionar artefato(s) e selecione a imagem do contêiner.

Na área Associar artefatos ao resultado do build, temos que indicar para cada um dos artefatos selecionados qual dos resultados de um estágio de build gerenciado é a origem para publicar o artefato. A especificação de build define uma saída rotulada go-function-container-image. Esta saída refere-se à imagem do contêiner que é produzida no servidor de build pelo processo de build de função. Informe o label go-function-container-image no campo Nome do artefato de configuração/resultado de Build. Pressione o botão Adicionar para criar o estágio Entregar Artefatos.

Terceiro Estágio - Acionar Pipeline de Implantação

Na página de visão geral do pipeline de build, clique no ícone de mais na parte inferior do estágio Entregar artefatos. No menu de contexto que aparece, clique em Adicionar estágio. O assistente de preparação é exibido.

Clique em Acionar implantação. Em seguida, clique em Next.

Digite um nome para o estágio: trigger-deployment-of-greeter-function-to-go-on-oci-app e, opcionalmente, uma descrição. Clique no botão Selecionar pipeline de implantação. Selecione o aplicativo deploy-greeter-function-to-go-on-oci do pipeline. Os detalhes do pipeline são mostrados, incluindo parâmetros (imageVersion) e o artefato usado pela implantação.

Clique em Adicionar para concluir a definição de estágio e adicioná-la ao pipeline de build.

Isso conclui o pipeline de build: ele captura origens, as processa em um artefato implantável, publica o artefato no repositório de imagens e aciona o pipeline de implantação para tirá-lo de lá.

artigo sobre oci (way-to-go-on-oci)

Executar Pipeline de Build e Trigger do Pipeline de Implantação

Clique em Iniciar execução manual. Defina um valor para o parâmetro imageVersion, por exemplo, 0.2.0. Clique no botão para iniciar o pipeline de build.

Agora levará alguns minutos para concluir o pipeline de build e acionar a implantação subsequente da imagem de função recém-criada.

artigo sobre oci (way-to-go-on-oci)

Quando tudo estiver pronto e o sucesso for reportado, você poderá chamar a rota no Gateway de API que leva à função grevista para verificar se a resposta é realmente a nova esperada.

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https://<api gateway="" endpoint="">/my-api/greeting
{"message":"Extremely hot greetings from your automatically built and deployed function dear  Mickey Mouse"}

    </api>

Este é um momento para uma pequena celebração. Conseguimos um processo automatizado de ponta a ponta que extrai fontes de aplicativos Go do Repositório de Código e entrega - após linting, verificação, teste e criação - uma nova versão em execução ao vivo de uma Função OCI sem servidor. Para produzir mais atualizações na função, tudo o que precisamos fazer é confirmar a alteração e acionar o pipeline de build. E como próximo passo, podemos até mesmo acionar o pipeline de build automaticamente quando um commit (específico) acontece no Repositório de Código.

Na segunda parte deste artigo, discutiremos o uso de serviços OCI de aplicações Go em geral e de funções Go em particular. O uso do Go SDK para OCI é introduzido e as interações com o OCI Object Storage Service são demonstradas.

Go SDK para OCI – Interação com o OCI Object Storage de aplicativos Go

O Oracle Cloud Infrastructure é a plataforma que pode criar e executar aplicativos desenvolvidos em Go. Em instâncias de computação ou como funções sem servidor, e também conteinerizadas em um cluster gerenciado do Kubernetes (como discutiremos na parte cinco desta série). É bom perceber que a OCI é muito mais para equipes de desenvolvimento Go do que uma plataforma de runtime. A OCI oferece muitos serviços que podem ser aproveitados dos aplicativos. Serviços para armazenar arquivos, dados relacionais ou "NoSQL", para tratar a publicação e o consumo de mensagens. Serviços para transferência e análise de dados. E serviços que suportam operações em aplicativos, como monitoramento.

A interação com os serviços da OCI pode ser feita por meio das APIs REST disponíveis para todos os aspectos de todos os serviços. Chamar serviços REST com payloads JSON por HTTP é fácil o suficiente em um aplicativo Go. Há um fator complicador: essas chamadas de API precisam ser assinadas. A assinatura do Oracle Cloud Infrastructure usa o esquema de Autenticação "Assinatura" (com um cabeçalho de Autorização) e o processo de assinatura não é exatamente trivial. Para obter detalhes sobre esse processo de assinatura, consulte Documentação do OCI - Assinaturas de Solicitação de API REST do OCI.

Felizmente para o desenvolvimento de aplicativos Go que chamam os serviços da OCI, podemos usar o Go SDK para OCI. Esse kit de desenvolvimento de código-fonte aberto facilita a assinatura das solicitações da API do OCI. Usando o SDK, as chamadas para serviços do OCI são feitas como chamadas locais usando estruturas predefinidas fortemente tipadas, em vez de lidar com corpos de solicitação e resposta JSON. O Go SDK para OCI usa o mesmo arquivo de configuração que usamos anteriormente para a CLI Fn e a CLI do OCI. O local padrão deste arquivo é $HOME/.oci. Esse arquivo direciona o Go SDK para uma tenancy e região específicas de uma conta de usuário usando a metade da chave privada do par configurado para o usuário. Os aplicativos Go que usam o Go SDK para OCI podem simplesmente desenvolver essa configuração sem precisar lidar com nenhum dos detalhes.

A documentação do Go SDK para OCI pode ser encontrada em Documentação do OCI – SDK para Go.

Nesta seção, desenvolveremos um aplicativo Go simples que usa o OCI Object Storage Service para criar e ler arquivos. Inicialmente, esse é um aplicativo independente que pode ser compilado e executado em qualquer lugar (desde que o arquivo de configuração do OCI esteja disponível). Em seguida, discutimos a execução do aplicativo Go dentro da OCI - em uma VM ou como Função. Esse foco especial é relevante porque, quando o Go SDK é usado no OCI, ele pode aproveitar a identidade e os privilégios do componente que está executando o aplicativo. Isso significa que o código executado na OCI não precisa trazer seu próprio arquivo de configuração da OCI e, portanto, é ainda mais simples.

Qualquer Aplicativo Go falando com o OCI Object Storage Service

Primeiro, vamos criar um aplicativo Go muito simples que possa se conectar ao OCI por meio do SDK. Em seguida, usaremos essa base para adicionar a interação com o Object Storage Service.

O Cliente Simplificado da OCI

O aplicativo Go mais simples que fala com a OCI usando o Go SDK para OCI é criado da seguinte forma:

  1. Criar um diretório para o aplicativo
  2. Criar arquivo go.mod com dependências no pacote Go SDK para OCI
  3. Execute ir mod tidy para criar o arquivo go.sum e fazer download dos pacotes necessários
  4. Crie o arquivo OCI-client.go com o código para conversar com o OCI

A suposição é que o arquivo de configuração do OCI que foi discutido anteriormente neste artigo está localizado no local padrão com o nome padrão: $HOME/.oci/config. Se o arquivo estiver em um local diferente, você poderá usar a função ConfigurationProviderFromFile no pacote github.com/oracle/oci-go-sdk/v65/common, que aceita a localização personalizada do arquivo de configuração.

O arquivo go.mod contém este conteúdo:

module oci-client

go 1.16

require github.com/oracle/oci-go-sdk/v65 v65.2.0

O bit github.com/oracle/oci-go-sdk/v65 faz referência à versão mais recente (no momento da gravação) do Go SDK. As origens são baixadas com base nessa referência. No aplicativo Go, isso significa que podemos aproveitar os pacotes no SDK para acessar vários serviços no portfólio da OCI.

O arquivo oci-client.go contém este código que usa os pacotes comum e de identidade:

package main

import (
"context"
"fmt"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/identity"
)

func main() {    
c, _ := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
tenancyID, _ := common.DefaultConfigProvider().TenancyOCID()
request := identity.GetCompartmentRequest{
    CompartmentId: &tenancyID,
}
response, _ := c.GetCompartment(context.Background(), request)
fmt.Printf("Name of Root Compartment: %v", *response.Compartment.Name)
}

A primeira linha da função adquire o cliente que pode ser usado subsequentemente para a maioria das interações com o OCI, como a chamada GetCompartment que retorna os detalhes do compartimento raiz.

O aplicativo pode ser executado usando ir executar oci-client.go e produzirá uma saída muito simples:

Name of Root Compartment: <name of="" your="" oci="" tenancy="">

    </name>

Embora o resultado não seja especialmente notável, o fato de haver saída é a prova de que o Go SDK para OCI está funcionando e está pronto para ser aproveitado para atividades mais elevadas.

Observe que o código ignora completamente os erros que podem ocorrer. Se você encontrar erros, substitua os sublinhados por uma variável para capturar e tratar esse erro.

Ir Aplicativo Criando e Recuperando Objetos do OCI Object Storage Service

O OCI Object Storage Service oferece armazenamento barato e persistente para diferentes tipos de dados. Objetos, grandes e pequenos, podem ser armazenados programaticamente neste serviço para serem retidos por curtos ou longos períodos de tempo e para serem acessados programaticamente ou por meio de URLs diretos. O Object Storage fornece controle de versão, diferentes regras de retenção, criptografia de qualquer dado armazenado, gerenciamento do ciclo de vida do objeto, remoção automatizada baseada em tempo e diferentes camadas de armazenamento.

Os objetos no Object Storage Service são organizados em buckets. Um bucket é um contêiner lógico de objetos que vive em um compartimento específico. Algumas operações podem ser executadas em todos os objetos de um bucket.

O Go SDK para OCI oferece funções e tipos que facilitam e facilitam o trabalho com os aplicativos Object Storage Service from Go. As operações para manipular buckets e criar, excluir e recuperar objetos estão prontamente disponíveis.

Para obter detalhes sobre o pacote de Armazenamento de Objetos no Go SDK para OCI, confira esta referência: Documentação do Pacote de Armazenamento de Objetos no Go SDK para OCI.

O repositório de origem deste artigo contém os aplicativos de pasta/store-n-retrieve, que tem um aplicativo Go simples que se conecta à sua tenancy do OCI e cria um bucket, seguido pela criação de um objeto e recuperação desse mesmo objeto. O aplicativo usa o mesmo DefaultConfigProvider que foi usado na seção anterior para assinar solicitações para APIs do OCI usando o arquivo $HOME/.oci/config.

A dependência desse aplicativo no Go SDK para OCI é definida em go.mod.

artigo sobre oci (way-to-go-on-oci)

A primeira parte do código cria uma instância ObjectStorageClient. Muitas funções são definidas na interface subjacente - todas suportam alguma forma de interação com o Object Storage Service. O ObjectStorageClient é criado usando common.DefaultConfigProvider() (assim como antes), usando o arquivo de configuração padrão do OCI com uma referência a um arquivo que contém a chave privada.

A função getNamespace é chamada para obter o namespace da tenancy atual. Em seguida, ensureBucketExists será chamado com o nome do bucket para criar o bucket, se ele ainda não existir.

package main

import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/objectstorage"
)

const (
bucketName      = "go-bucket" // feel free to use a different name for the bucket
compartmentOCID = "<compartment-ocid></compartment-ocid>" // replace with the OCID of the go-on-oci compartment in your tenancy
objectName      = "welcome.txt" // feel free to use a different name for the object
)

func main() {
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(common.DefaultConfigProvider())
if cerr != nil {
fmt.Printf("failed to create ObjectStorageClient : %s", cerr)
}
ctx := context.Background()
namespace, cerr := getNamespace(ctx, objectStorageClient)
if cerr != nil {
fmt.Printf("failed to get namespace : %s", cerr)
} else {
fmt.Printf("Namespace : %s", namespace)
}

err := ensureBucketExists(ctx, objectStorageClient, namespace, bucketName, compartmentOCID)
if err != nil {
fmt.Printf("failed to read or create bucket : %s", err)
}
.........................

As funções chamadas neste trecho de código para recuperar o namespace e verificar a existência do bucket (e criá-lo se ele não existir) são bastante diretas. Eles são definidos da seguinte forma:

func getNamespace(ctx context.Context, client objectstorage.ObjectStorageClient) (string, error) {
request := objectstorage.GetNamespaceRequest{}
response, err := client.GetNamespace(ctx, request)
if err != nil {
    return *response.Value, fmt.Errorf("failed to retrieve tenancy namespace : %w", err)
}
return *response.Value, nil
}

func ensureBucketExists(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
req := objectstorage.GetBucketRequest{
    NamespaceName: &namespace,
    BucketName:    &name,
}
// verify if bucket exists.
response, err := client.GetBucket(ctx, req)
if err != nil {
    if response.RawResponse.StatusCode == 404 {
        err = createBucket(ctx, client, namespace, name, compartmentOCID)
        return err
    }
    return err
}
fmt.Printf("bucket %s already exists", bucketName)
return nil
}

// bucketname needs to be unique within compartment. there is no concept of "child" buckets. using "/" separator characters in the name, the suggestion of nested bucket can be created
func createBucket(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
request := objectstorage.CreateBucketRequest{
    NamespaceName: &namespace,
}
request.CompartmentId = &compartmentOCID
request.Name = &name
request.Metadata = make(map[string]string)
request.PublicAccessType = objectstorage.CreateBucketDetailsPublicAccessTypeNopublicaccess
_, err := client.CreateBucket(ctx, request)
if err != nil {
    return fmt.Errorf("failed to create bucket on OCI : %w", err)
} else {
    fmt.Printf("created bucket : %s", bucketName)
}
return nil
}

A segunda parte deste aplicativo cria um objeto no bucket e, subsequentemente, recupera esse objeto. Quando o aplicativo terminar de ser executado, ele terá um efeito persistente: o bucket foi criado (se ele ainda não existir) e um objeto foi criado ou atualizado (se o aplicativo tiver sido executado antes). Você pode fazer check-in na página de detalhes do bucket go-bucket na console do OCI para ver o bucket e o objeto que foi criado.

..................

contentToWrite := []byte("We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.")
objectLength := int64(len(contentToWrite))
err = putObject(ctx, objectStorageClient, namespace, bucketName, objectName, objectLength, ioutil.NopCloser(bytes.NewReader(contentToWrite)))
if err != nil {
    fmt.Printf("failed to write object to OCI Object storage : %s", err)
}

var contentRead []byte
contentRead, err = getObject(ctx, objectStorageClient, namespace, bucketName, objectName)
if err != nil {
    fmt.Printf("failed to get object %s from OCI Object storage : %s", objectName, err)
}
fmt.Printf("Object read from OCI Object Storage contains this content: %s", contentRead)
}

func putObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string, contentLen int64, content io.ReadCloser) error {
request := objectstorage.PutObjectRequest{
    NamespaceName: &namespace,
    BucketName:    &bucketName,
    ObjectName:    &objectname,
    ContentLength: &contentLen,
    PutObjectBody: content,
}
_, err := client.PutObject(ctx, request)
fmt.Printf("Put object %s in bucket %s", objectname, bucketName)
if err != nil {
    return fmt.Errorf("failed to put object on OCI : %w", err)
}
return nil
}

func getObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string) (content []byte, err error) {
request := objectstorage.GetObjectRequest{
    NamespaceName: &namespace,
    BucketName:    &bucketName,
    ObjectName:    &objectname,
}
response, err := client.GetObject(ctx, request)
if err != nil {
    return nil, fmt.Errorf("failed to retrieve object : %w", err)
}
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(response.Content)
if err != nil {
    return nil, fmt.Errorf("failed to read content from object on OCI : %w", err)
}
return buf.Bytes(), nil
}

Executando este aplicativo – ir executar o objeto-organizer.go – resulta nesta saída:

Namespace : idtwlqf2hanz
created bucket : go-bucket
Put object welcome.txt in bucket go-bucket
Object read from OCI Object Storage contains this content: We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.

OCI Functions baseado em Go falando com o OCI Object Storage Service

A seção anterior discutiu qualquer aplicativo Go interagindo com os serviços da OCI por meio do SDK. Então, o que mais há a dizer sobre o OCI Functions in Go? Continue lendo, porque acontece que podemos simplificar a vida quando o código Go que você desenvolve e do qual deseja trabalhar com o Go SDK for implantado como uma função do OCI ou executado em uma instância de computação no OCI. Nesses casos, podemos aproveitar a Autenticação do Controlador de Recursos (para Funções) e a Autenticação do Controlador de Instâncias (para código em execução em uma Instância de Computação), o que significa que não precisamos incluir o arquivo de Configuração do OCI, e nosso código que fala com o SDK pode ser um pouco mais simples.

Leia mais sobre Autenticação do Controlador de Recursos na Documentação do OCI sobre Autenticação do Controlador de Recursos para Funções.

Nesta seção, discutimos uma Função do OCI chamada object-broker. Você encontrará as origens no repositório de origem deste artigo, em funções de caminho/object-broker. Como antes, a função é definida usando um arquivo func.yaml com metadados e um arquivo func.go com o link entre a função e o framework Fn. O arquivo go.mod define as dependências da função. A lógica para interagir com o OCI Object Storage Service está no objeto de arquivo organizer.go. Isso define um func público CreateObject chamado de func.go.

CreateObject cria um objeto com o nome especificado no bucket especificado. As primeiras linhas na função CreateObject no objeto-organizer.go foram submetidas a alguma modificação para trabalhar com essa autenticação do controlador de recursos. O auth.ResourcePrincipalConfigurationProvider() usado agora não requer que um arquivo de configuração e uma chave privada do OCI sejam incluídos no aplicativo. Ele assume que o código é executado dentro do OCI e, mais especificamente, dentro de um recurso (que pode ser uma Função ou, por exemplo, um Servidor de Build DevOps) que é conhecido como controlador de recursos porque está incluído em um grupo dinâmico e herda permissões dessa associação de grupo. Em pouco tempo, você vai chegar às instruções para tomar as medidas necessárias para isso.

func CreateObject(objectName string, bucketName string, compartmentOCID string) (string, err) {
configurationProvider, err := auth.ResourcePrincipalConfigurationProvider()
if err != nil {
    fmt.Printf("failed to get oci configurationprovider based on resource principal authentication : %s", err)
}
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(configurationProvider)

Vamos voltar nossa atenção ao lado de func.go. Func(tion) myHandler trata o trigger da função. Ele chama CreateObject, mas não antes de determinar o nome do objeto que a solicitação deve produzir e o nome do bucket que deve conter o objeto. Esses nomes têm um valor padrão, mas a função tenta localizar valores de parâmetro de consulta de solicitação HTTP que fornecem valores específicos. Observe que isso só funciona para um trigger HTTP para a função, e não para uma chamada feita usando a chamada fn.

func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
objectName := "defaultObjectName.txt"
bucketName := "the-bucket"
fnctx := fdk.GetContext(ctx)             // fnctx contains relevant elements about the Function itself
fnhttpctx, ok := fnctx.(fdk.HTTPContext) // fnhttpctx contains relevant elements about the HTTP Request that triggered it
if ok {                                  // an HTTPContent was found which means that this was an HTTP request (not an fn invoke) that triggered the function
    u, err := url.Parse(fnhttpctx.RequestURL())
......

Talvez você queira inspecionar detalhes sobre o Contexto Fn em Documentação para Fn Go FDK do Projeto, especificamente no exemplo.

A função precisa saber em qual compartimento do OCI o bucket deve residir. Essas definições de runtime são normalmente definidas na função do aplicativo por meio de uma configuração. Os valores de configurações podem ser lidos na função a partir de um mapa no contexto, como é mostrado nesta linha:

if compartmentOCID, ok := fnctx.Config()["compartmentOCID"]; ok {

Criar e Implantar a Função usando a CLI do Fn

Supondo que você esteja em um terminal no ambiente de desenvolvimento local onde a CLI do Fn está instalada e o diretório atual é functions/object-broker, você pode executar um build local da função com saída detalhada:

fn -v build

Quando o build parece bom, o próximo passo a ser dado é a implantação da função. Se o contexto Fn estiver definido para usar o contexto go-on-OCI (verifique com contextos fn list), isso implantará a função no aplicativo go-on-OCI-app no OCI:

fn -v deploy --app go-on-oci-app

A função só poderá fazer um job significativo se a configuração tiver sido definida para o valor do OCID do compartimento. Você pode fazer isso por meio da console ou usando esta próxima instrução com a CLI Fn:

fn cf f go-on-oci-app object-broker compartmentOCID <compartment-ocid>


    </compartment-ocid>

Agora a função é implantada e tem sua configuração. Você pode esperar que uma chamada para a função com este comando seja bem-sucedida:

fn invoke go-on-oci-app object-broker

No entanto, há um aspecto final a ser tratado: a função usa APIs do OCI Object Storage Service para manipular buckets e objetos, mas precisa ter recebido permissões explicitamente para fazer isso! Usamos um grupo dinâmico e duas políticas para conseguir isso.

Permissões para Funções para manipular Objetos e Buckets

Assim como usamos grupos dinâmicos para criar um favorecido representando os pipelines de implantação e os pipelines de build, também precisamos criar um grupo dinâmico que contenha as funções às quais queremos conceder permissões. Para criar o grupo dinâmico, digite dyn na barra de pesquisa. Clique no link Grupos Dinâmicos no painel de resultados da pesquisa.

Na página de visão geral dos grupos dinâmicos e clique em Criar Grupo Dinâmico.

Informe o nome do Grupo Dinâmico para o(s) Pipeline(s) de Implantação, por exemplo, functions-in-go-on-oci e, opcionalmente, digite uma descrição. Defina a seguinte regra que seleciona todas as funções que fazem parte do compartimento:

All {resource.type = 'fnfunc', resource.compartment.id = '<compartment_id>'}

    </compartment_id>

É claro que substitua pelo identificador do compartimento no qual você está trabalhando. Em seguida, pressione Criar.

Para criar uma política no console: digite poli na barra de pesquisa e clique em Políticas > Identidade na área Serviços no pop-up de resultados da pesquisa. Isso leva você à página de visão geral de Políticas do compartimento atual.

A primeira instrução de política define a permissão para que a função gerencie objetos no compartimento. A segunda instrução adiciona a permissão para gerenciar buckets. Defina um nome, uma descrição e as seguintes instruções:

allow dynamic-group functions-in-go-on-oci to manage objects in compartment go-on-oci
allow dynamic-group functions-in-go-on-oci to manage buckets in compartment go-on-oci

A figura a seguir mostra as permissões que agora se aplicam à função quando a política que contém essas instruções é salva:

artigo sobre oci (way-to-go-on-oci)

Agora a função pode ser chamada e deve ser capaz de fazer seu trabalho usando os nomes padrão para bucket e objeto.

fn invoke go-on-oci-app object-broker

Verifique se o bucket foi criado e se ele contém o objeto recém-criado usando a console do URL do OCI para a página de buckets.

Adicionar Rota no Gateway de API à Função Trigger

Para poder chamar a função object-broker por HTTP de qualquer lugar, faremos novamente uso do Gateway de API. Digite gat na barra de pesquisa no console. Clique em >Gateways > Gerenciamento de API. Clique no link para o api-gateway. Clique em Implantações. Na lista com implantações - que contém uma única implantação - clique no link myserver-api.

Clique em Editar para abrir a especificação de implantação. Clique no link para a segunda etapa: Rotas. Role para baixo e clique em + Outra Rota.

Digite /object-broker como o caminho para esta nova rota. Selecione GET como o método e o Oracle Functions como o Tipo (de backend). Selecione o aplicativo go-on-oci-app e defina o Nome da Função como object-broker. Pressione Próximo e, em seguida, pressione Salvar Alterações para aplicar as alterações e tornar a nova rota real.

A imagem de ponta a ponta que agora é configurada do consumidor HTTP por meio do Gateway de API para Função e, finalmente, bucket e objeto tem esta aparência:

artigo sobre oci (way-to-go-on-oci)

Chame a função a partir do navegador ou usando curl na linha de comando usando:

curl -X "GET" "http://<api gateway="" endpoint="">/my-api/object-broker?objectName=new-exciting-object.txt&bucketName=the-ultimate-collection"


    </api>

Criação e Implantação Automatizadas

Esta função object-broker foi implantada manualmente na linha de comando usando a CLI Fn. Isso funcionou bem, é claro. No entanto, se você agora iniciar o desenvolvimento nessa função, passando por vários ciclos de desenvolvimento, provavelmente gostaria de introduzir a automação no processo de criação e implantação.

Assim como fizemos antes, você pode configurar facilmente os elementos necessários no OCI DevOps para obter pipelines automatizados para a implantação (da imagem do contêiner de funções no registro de imagens do contêiner) e o build (começando no repositório de código e resultando em uma imagem de contêiner recém-criada para a função). O elemento principal específico da função é o arquivo de especificação de build para o estágio de build gerenciado no pipeline de build. Este arquivo é fornecido como go-function-build-spec.yaml no mesmo diretório que func.go e func.yaml.

Depois de criar um artefato DevOps para a Imagem do Contêiner da Função, um ambiente para a Função e os dois pipelines para build e implantação, a configuração automatizada do processo DevOps ficaria assim:

artigo sobre oci (way-to-go-on-oci)

Conclusão

Uma área de foco neste artigo foram funções sem servidor, escritas em Go e em execução no Oracle Cloud Infrastructure. A criação e a implantação automatizadas dessas funções foram discutidas, assim como o uso do API Gateway para fornecer acesso à função para consumidores HTTP externos.

O segundo tópico principal foi o Go SDK para OCI para interagir com serviços do OCI de aplicativos Go. O artigo mostrou como usar o código Go para acessar o serviço Object Storage para armazenar e recuperar arquivos.

Os dois tópicos foram combinados na função object-broker. Esta Função do OCI aproveita a autenticação do controlador de recursos e as permissões concedidas por meio de um grupo dinâmico. Por meio de uma configuração de runtime, a função aprende sobre as definições específicas do ambiente atual.

No próximo artigo, interagir com o Oracle Database será o tópico principal. Criando uma conexão de um aplicativo Go com um Oracle Database local, bem como com um Autonomous Database em execução na OCI e executando operações SQL com esses bancos de dados no conforto do seu aplicativo Go. Outros tópicos incluem o trabalho com uma Oracle Wallet para gerenciamento adequado de credenciais de banco de dados, incluindo a wallet no processo de implantação e a combinação de interações com os serviços OCI Object Storage e Autonomous Database em um único aplicativo.

Oracle Chatbot
Disconnected