#Caché

0 Seguidores · 204 Postagens

  

InterSystems Caché é um sistema de gerenciamento de banco de dados (DBMS) multimodelo e servidor de aplicações. Veja mais detalhes aqui.

Documentação.

Artigo Danusa Calixto · Mar. 27, 2023 1m read

InterSystems FAQ 

Você pode recuperar datas e tamanhos de rotina programaticamente usando a consulta RoutineList da classe %Library.Routine (ou apenas %Routine).

A consulta RoutineList tem um argumento e o nome da rotina a ser pesquisado pode ser especificado por correspondência de prefixo ou correspondência intermediária. (Para curingas, especifique * ou ?)

No exemplo a seguir. *.MAC é especificada como argumento.

SET tStatement =  ##class(%SQL.Statement).%New()
 DO tStatement.%PrepareClassQuery("%Routine" , "RoutineList") 
 SET rs = tStatement.%Execute("*.MAC",,0) 
 DO rs.%Display() 
0
0 104
InterSystems Oficial Danusa Calixto · Mar. 27, 2023

A InterSystems está empenhada em fornecer suporte de produto de alta qualidade aos clientes para todos os produtos, novos e antigos. À medida que os produtos envelhecem – o Caché agora tem 25 anos – esse suporte evoluirá.

O InterSystems IRIS foi lançado em 2018 e é o sucessor do Caché e do Ensemble. Muitos clientes do Caché/Ensemble migraram para o IRIS ou planejam fazê-lo nos próximos anos. Os clientes que continuarem a usar Caché ou Ensemble devem estar cientes do seguinte comunicado importante:

0
0 110

Responsabilidades

•             Atuar na manutenção, correção e melhorias dos sistemas da empresa, de acordo com o seu segmento de atuação.

•             Fornecer suporte e acompanhamento nas dificuldades operacionais dos sistemas, esclarecendo dúvidas de usuários.

•             Analisar solicitações efetuadas pelos clientes, como criação de relatórios, novas telas e funcionalidades.

•             Estudar melhorias e novas possibilidades sistêmicas no mercado que possam melhorar os processos internos da empresa.

•             Desenvolver aplicativos usando o Intersystems Caché e ou IRIS.

0
0 161
Artigo Danusa Calixto · Fev. 23, 2023 3m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Primeiro artigo
    • Conceitos básicos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno
    • Como o Git pode ser usado para desenvolver software (fluxos do Git)
  • Segundo artigo
    • Fluxo de trabalho do GitLab — um processo completo do ciclo de vida do software, desde a ideia até o feedback do usuário
    • Entrega Contínua — uma abordagem de engenharia de software em que as equipes produzem software em ciclos curtos, garantindo que o software possa ser lançado de forma confiável a qualquer momento. Seu objetivo é construir, testar e lançar software com mais rapidez e frequência
  • Terceiro artigo
    • Instalação e configuração do GitLab
    • Conexão dos seus ambientes ao GitLab
  • Quarto artigo
    • Configuração da entrega contínua
  • Quinto artigo
    • Contêineres e como (e por que) podem ser usados
  • Sexto artigo
    • Principais componentes para um pipeline de entrega contínua com contêineres
    • Como todos eles trabalham juntos
  • Sétimo artigo
    • Configuração da entrega contínua com contêineres
  • Oitavo artigo
    • Configuração da entrega contínua com o InterSystems Cloud Manager
  • Nono artigo
    • Arquitetura do contêiner
  • Décimo artigo
    • CI/CD para configuração e dados
  • Décimo primeiro artigo
    • Interoperabilidade e CI/CD

Nessa série de artigos, discuti abordagens gerais de entrega contínua. É um tema extremamente vasto e essa série de artigos precisa ser vista mais como uma coleção de receitas do que algo definitivo. Se você deseja automatizar o desenvolvimento, os testes e a entrega do seu aplicativo, a entrega contínua em geral e o GitLab em particular é o melhor caminho. A entrega contínua e os contêineres permitem que você personalize seu fluxo de trabalho conforme necessário.

0
0 175
Artigo Danusa Calixto · Fev. 23, 2023 11m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Git básico
  • Fluxo Git (processo de desenvolvimento)
  • Instalação do GitLab
  • Fluxo de trabalho do GitLab
  • Entrega contínua
  • Instalação e configuração do GitLab
  • CI/CD do GitLab
  • Por que contêineres?
  • Infraestrutura dos contêineres
  • CD usando contêineres

No primeiro artigo, abordamos os fundamentos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno e como o Git pode ser usado para desenvolver software.

No segundo artigo, abordamos o fluxo de trabalho do GitLab: um processo inteiro do ciclo de vida do software e a entrega contínua.

No terceiro artigo, abordamos a instalação e configuração do GitLab e a conexão dos seus ambientes a ele

No quarto artigo, escrevemos uma configuração de CD.

No quinto artigo, falamos sobre contêineres e como (e por que) eles podem ser usados.

No sexto artigo, vamos discutir os principais componentes necessários para executar um pipeline de entrega contínua com contêineres e como eles trabalham juntos.

Neste artigo, criaremos a configuração de entrega contínua discutida nos artigos anteriores.

Fluxo de trabalho

Na nossa configuração de entrega contínua:

  • Enviamos código para o repositório do GitLab
  • Criamos a imagem docker
  • Testamos
  • Publicamos a imagem no nosso registro docker
  • Trocamos o contêiner antigo pela nova versão do registro

Ou em formato gráfico:

Vamos começar.

Criação

Primeiro, precisamos criar nossa imagem.

Nosso código seria, como sempre, armazenado no repositório, a configuração de CD em gitlab-ci.yml. No entanto, além disso (para aumentar a segurança), armazenaríamos vários arquivos específicos do servidor em um servidor de compilação.

GitLab.xml

Contém o código dos hooks de CD. Foi desenvolvido no artigo anterior e disponibilizado no GitHub. É uma pequena biblioteca para carregar código, executar vários hooks e testar código. Como alternativa preferencial, você pode usar submódulos git para incluir este projeto ou algo semelhante no seu repositório. Os submódulos são melhores porque é mais fácil mantê-los atualizados. Uma outra alternativa seria marcar as versões no GitLab e carregá-las com o comando ADD.

iris.key

Chave de licença. Como alternativa, ela pode ser baixada durante a compilação do contêiner em vez de armazenada em um servidor. É bastante arriscado armazenar no repositório.

pwd.txt

Arquivo contendo a senha padrão. Novamente, é bastante arriscado armazená-lo no repositório. Além disso, se você estiver hospedando um ambiente de produção em um servidor separado, ele poderá ter uma senha padrão diferente.

load_ci.script

O script inicial:

  • Ativa a autenticação do SO

  • Carrega GitLab.xml

  • Inicializa as configurações do utilitário GitLab

  • Carrega o código

    set sc = ##Class(Security.System).Get("SYSTEM",.Properties) write:('sc) $System.Status.GetErrorText(sc) set AutheEnabled = Properties("AutheEnabled") set AutheEnabled = $zb(+AutheEnabled,16,7) set Properties("AutheEnabled") = AutheEnabled set sc = ##Class(Security.System).Modify("SYSTEM",.Properties) write:('sc) $System.Status.GetErrorText(sc) zn "USER" do ##class(%SYSTEM.OBJ).Load(##class(%File).ManagerDirectory() _ "GitLab.xml","cdk") do ##class(isc.git.Settings).setSetting("hooks", "MyApp/Hooks/") do ##class(isc.git.Settings).setSetting("tests", "MyApp/Tests/") do ##class(isc.git.GitLab).load() halt

Observe que a primeira linha é deixada em branco de maneira intencional.

Como algumas configurações podem ser específicas do servidor, elas não são armazenadas no repositório, mas separadamente. Se o hook inicial for sempre o mesmo, você pode simplesmente armazená-lo no repositório.

gitlab-ci.yml

Agora, para a configuração da entrega contínua:

build image:
  stage: build
  tags:
    - test
  script:
    - cp -r /InterSystems/mount ci
    - cd ci
    - echo 'SuperUser' | cat - pwd.txt load_ci.script > temp.txt
    - mv temp.txt load_ci.script
    - cd ..
    - docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t docker.domain.com/test/docker:$CI_COMMIT_REF_NAME .

O que está acontecendo aqui?

Primeiro, como o docker build pode acessar apenas subdiretórios de um diretório de compilação base — na raiz do repositório do nosso caso, precisamos copiar nosso diretório "secreto" (aquele com GitLab.xmliris.key, pwd.txt e load_ci.script) no repositório clonado.

Em seguida, o primeiro acesso ao terminal requer um usuário/senha, então nós os adicionamos a load_ci.script (por isso a linha vazia no início de load_ci.script).

Por fim, criamos a imagem do docker e a marcamos adequadamente: docker.domain.com/test/docker:$CI_COMMIT_REF_NAME

onde $CI_COMMIT_REF_NAME é o nome de um branch atual. Observe que a primeira parte da tag de imagem deve ter o mesmo nome do nome do projeto no GitLab, para que possa ser vista na guia GitLab Registry (instruções sobre a marcação estão disponíveis na guia Registry).

Dockerfile

A criação da imagem docker é feita usando o Dockerfile:

FROM docker.intersystems.com/intersystems/iris:2018.1.1.611.0

ENV SRC_DIR=/tmp/src
ENV CI_DIR=$SRC_DIR/ci
ENV CI_PROJECT_DIR=$SRC_DIR

COPY ./ $SRC_DIR

RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/ \
 && cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/ \
 && $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt \
 && iris start $ISC_PACKAGE_INSTANCENAME \
 && irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script \
 && iris stop $ISC_PACKAGE_INSTANCENAME quietly

Começamos a partir do contêiner básico iris.

Primeiro, copiamos nosso repositório (e diretório "secreto") dentro do contêiner.

Em seguida, copiamos a chave de licença e GitLab.xml para o diretório mgr.

Em seguida, alteramos a senha para o valor de pwd.txt. Observe que pwd.txt é excluído nessa operação.

Depois disso, a instância é iniciada e load_ci.script é executado.

Por fim, a instância iris é interrompida.

Veja o registro do job (parcial, os registros de carregamento/compilação foram ignorados):

Running with gitlab-runner 10.6.0 (a3543a27)
  on docker 7b21e0c4
Using Shell executor...
Running on docker...
Fetching changes...
Removing ci/
Removing temp.txt
HEAD is now at 5ef9904 Build load_ci.script
From http://gitlab.eduard.win/test/docker
   5ef9904..9753a8d  master     -> origin/master
Checking out 9753a8db as master...
Skipping Git submodules setup
$ cp -r /InterSystems/mount ci
$ cd ci
$ echo 'SuperUser' | cat - pwd.txt load_ci.script > temp.txt
$ mv temp.txt load_ci.script
$ cd ..
$ docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t docker.eduard.win/test/docker:$CI_COMMIT_REF_NAME .
Sending build context to Docker daemon  401.4kB

Step 1/6 : FROM docker.intersystems.com/intersystems/iris:2018.1.1.611.0
 ---> cd2e53e7f850
Step 2/6 : ENV SRC_DIR=/tmp/src
 ---> Using cache
 ---> 68ba1cb00aff
Step 3/6 : ENV CI_DIR=$SRC_DIR/ci
 ---> Using cache
 ---> 6784c34a9ee6
Step 4/6 : ENV CI_PROJECT_DIR=$SRC_DIR
 ---> Using cache
 ---> 3757fa88a28a
Step 5/6 : COPY ./ $SRC_DIR
 ---> 5515e13741b0
Step 6/6 : RUN cp $CI_DIR/iris.key $ISC_PACKAGE_INSTALLDIR/mgr/  && cp $CI_DIR/GitLab.xml $ISC_PACKAGE_INSTALLDIR/mgr/  && $ISC_PACKAGE_INSTALLDIR/dev/Cloud/ICM/changePassword.sh $CI_DIR/pwd.txt  && iris start $ISC_PACKAGE_INSTANCENAME  && irissession $ISC_PACKAGE_INSTANCENAME -U%SYS < $CI_DIR/load_ci.script  && iris stop $ISC_PACKAGE_INSTANCENAME quietly
 ---> Running in 86526183cf7c
.
Waited 1 seconds for InterSystems IRIS to start
This copy of InterSystems IRIS has been licensed for use exclusively by:
ISC Internal Container Sharding
Copyright (c) 1986-2018 by InterSystems Corporation
Any other use is a violation of your license agreement

%SYS>
1

%SYS>
Using 'iris.cpf' configuration file

This copy of InterSystems IRIS has been licensed for use exclusively by:
ISC Internal Container Sharding
Copyright (c) 1986-2018 by InterSystems Corporation
Any other use is a violation of your license agreement

1 alert(s) during startup. See messages.log for details.
Starting IRIS

Node: 39702b122ab6, Instance: IRIS

Username:
Password:

Load started on 04/06/2018 17:38:21
Loading file /usr/irissys/mgr/GitLab.xml as xml
Load finished successfully.

USER>

USER>

[2018-04-06 17:38:22.017] Running init hooks: before

[2018-04-06 17:38:22.017] Importing hooks dir /tmp/src/MyApp/Hooks/

[2018-04-06 17:38:22.374] Executing hook class: MyApp.Hooks.Global

[2018-04-06 17:38:22.375] Executing hook class: MyApp.Hooks.Local

[2018-04-06 17:38:22.375] Importing dir /tmp/src/

Loading file /tmp/src/MyApp/Tests/TestSuite.cls as udl

Compilation started on 04/06/2018 17:38:22 with qualifiers 'c'
Compilation finished successfully in 0.194s.

Load finished successfully.

[2018-04-06 17:38:22.876] Running init hooks: after

[2018-04-06 17:38:22.878] Executing hook class: MyApp.Hooks.Local

[2018-04-06 17:38:22.921] Executing hook class: MyApp.Hooks.Global
Removing intermediate container 39702b122ab6
 ---> dea6b2123165
[Warning] One or more build-args [CI_PROJECT_DIR] were not consumed
Successfully built dea6b2123165
Successfully tagged docker.domain.com/test/docker:master
Job succeeded

Estou usando o executor GitLab Shell, e não o executor Docker. O executor Docker é usado quando você precisa de algo de dentro da imagem, por exemplo, você está criando um aplicativo Android em um contêiner java e precisa apenas de um apk. No nosso caso, precisamos de um contêiner inteiro e, para isso, precisamos do executor Shell. Então, estamos executando comandos do Docker pelo executor GitLab Shell.

 

Executar

Temos nossa imagem, agora vamos executá-la.  No caso de ramificações de recursos, podemos simplesmente destruir o contêiner antigo e iniciar o novo. No caso do ambiente, podemos executar um container temporário e substituir o contêiner do ambiente caso os testes tenham êxito (isso fica como um exercício para o leitor).

Aqui está o script.

destroy old:
  stage: destroy
  tags:
    - test
  script:
    - docker stop iris-$CI_COMMIT_REF_NAME || true
    - docker rm -f iris-$CI_COMMIT_REF_NAME || true

Esse script destrói o contêiner em execução no momento e é sempre bem-sucedido (por padrão, o docker falha se tentar parar/remover um contêiner inexistente).

Em seguida, iniciamos a nova imagem e a registramos como um ambiente. Contêiner Nginx faz proxy automático de solicitações usando a variável de ambiente VIRTUAL_HOST e a diretiva de exposição (para saber em qual porta fazer o proxy).

run image:
  stage: run
  environment:
    name: $CI_COMMIT_REF_NAME
    url: http://$CI_COMMIT_REF_SLUG. docker.domain.com/index.html
  tags:
    - test
  script:
    - docker run -d
      --expose 52773
      --env VIRTUAL_HOST=$CI_COMMIT_REF_SLUG.docker.eduard.win
      --name iris-$CI_COMMIT_REF_NAME
      docker.domain.com/test/docker:$CI_COMMIT_REF_NAME
      --log $ISC_PACKAGE_INSTALLDIR/mgr/messages.log

 

Testes

Vamos fazer alguns testes.

test image:
  stage: test
  tags:
    - test
  script:
    - docker exec iris-$CI_COMMIT_REF_NAME irissession iris -U USER "##class(isc.git.GitLab).test()"

Publicar

Por fim, vamos publicar nossa imagem no registro

publish image:
  stage: publish
  tags:
    - test
  script:
    - docker login docker.domain.com -u dev -p 123
    - docker push docker.domain.com/test/docker:$CI_COMMIT_REF_NAME

O usuário/código pode ser transmitido usando variáveis secretas do GitLab.

Agora, podemos ver a imagem no GitLab:

E outros desenvolvedores podem a extrair do registro. Na guia de ambientes, todos os ambientes estão disponíveis para a fácil navegação:

 

Conclusão

Nessa série de artigos, discuti abordagens gerais de entrega contínua. É um tema extremamente vasto e essa série de artigos precisa ser vista mais como uma coleção de receitas do que algo definitivo. Se você deseja automatizar o desenvolvimento, os testes e a entrega do seu aplicativo, a entrega contínua em geral e o GitLab em particular é o melhor caminho. A entrega contínua e os contêineres permitem que você personalize seu fluxo de trabalho conforme necessário.

Links

O que vem a seguir

É isso. Espero que eu tenha abordado os conceitos básicos da entrega contínua e dos contêineres.

Há vários tópicos sobre os quais não falei (talvez mais tarde), especialmente em relação a contêineres:

  • Os dados podem ser persistentes fora do contêiner. Veja a documentação relacionada.
  • Plataformas de orquestração como kubernetes
  • InterSystems Cloud Manager
  • Gerenciamento de ambiente - criando ambientes temporários para testes, removendo ambientes antigos após a mesclagem de ramificações de recursos
  • Docker compose para implantações de vários contêineres
  • Diminuindo o tamanho da imagem docker e os tempos de construção
  • ...
0
0 157
Artigo Danusa Calixto · Fev. 23, 2023 7m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Git básico
  • Fluxo Git (processo de desenvolvimento)
  • Instalação do GitLab
  • Fluxo de trabalho do GitLab
  • Entrega contínua
  • Instalação e configuração do GitLab
  • CI/CD do GitLab
  • Por que contêineres?
  • Infraestrutura dos contêineres
  • CI/CD do GitLab usando contêineres

No primeiro artigo, abordamos os fundamentos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno e como o Git pode ser usado para desenvolver software.

No segundo artigo, abordamos o fluxo de trabalho do GitLab: um processo inteiro do ciclo de vida do software e a entrega contínua.

No terceiro artigo, abordamos a instalação e configuração do GitLab e a conexão dos seus ambientes a ele

No quarto artigo, escrevemos uma configuração de CD.

No quinto artigo, falamos sobre contêineres e como (e por que) eles podem ser usados.

Neste artigo, vamos discutir os principais componentes necessários para executar um pipeline de entrega contínua com contêineres e como eles trabalham juntos.

A configuração deve ser assim:

Aqui podemos ver a separação de três etapas principais:

  • Criação
  • Envio
  • Execução

Criação

Nas partes anteriores, a criação era frequentemente incremental — calculamos a diferença entre o ambiente e a codebase atuais e modificamos nosso ambiente para corresponder à codebase. Com contêineres, cada build é completo. O resultado de um build é uma imagem que pode ser executada em qualquer lugar com dependências.

Envio

Depois que nossa imagem é criada e aprovada nos testes, ela é carregada no registro — servidor especializado para hospedar imagens docker. Então, é possível substituir a imagem anterior pela mesma tag. Por exemplo, devido ao novo commit para o master branch, nós construímos a nova imagem (project/version:master) e, se os testes funcionarem, podemos substituir a imagem no registro pela nova com a mesma tag, então todos que extraírem project/version:master obtêm uma nova versão.

Execução

Por fim, nossas imagens são implantadas. Uma solução de CI, como o GitLab, pode controlar isso ou um orquestrador especializado, mas o ponto é o mesmo: algumas imagens são executadas, verificadas periodicamente quanto à integridade e atualizadas se uma nova versão estiver disponível.

Confira o webinar docker explicando esses diferentes estágios.

Como alternativa, do ponto de vista do commit:

Na nossa configuração de entrega:

  • Enviamos código para o repositório do GitLab
  • Criamos a imagem docker
  • Testamos
  • Publicamos a imagem no nosso registro docker
  • Trocamos o contêiner antigo pela nova versão do registro

Para fazer isso, precisamos do seguinte:

  • Docker
  • Registro docker
  • Domínio registrado (opcional, mas recomendado)
  • Ferramentas de GUI (opcional)

 

Docker

Primeiro de tudo, precisamos executar o docker em algum lugar. Recomendo começar com um servidor mais convencional tipo o Linux, como Ubuntu, RHEL ou Suse. Não use distribuições voltadas para a nuvem, como CoreOS, RancherOS etc. — elas não são destinadas a iniciantes. Não se esqueça de trocar o driver de armazenamento para devicemapper

Em caso de grandes implantações, usar ferramentas de orquestração de contêineres, como Kubernetes, Rancher ou Swarm, pode automatizar a maioria das tarefas, mas não vamos discuti-las (pelo menos nesta parte).

 

Registro docker

Esse é o primeiro contêiner que precisamos executar e é um aplicativo do lado do servidor escalável e sem estado que armazena e permite distribuir imagens Docker.
Use o registro se quiser:

  •  controlar rigidamente onde as imagens estão sendo armazenadas
  •  possuir totalmente o pipeline de distribuição das imagens
  •  integrar o armazenamento e a distribuição de imagens firmemente no seu fluxo de trabalho de desenvolvimento interno

Veja a documentação do registro.

Conexão do registro e do GitLab

Observação: o GitLab inclui registro integrado. Você pode executá-lo em vez do registro externo. Leia os documentos do GitLab vinculados neste parágrafo.

Para conectar seu registro ao GitLab, você precisará executar seu registro com suporte HTTPS  — eu uso o Let's Encrypt para obter os certificados e segui este Gist para obter e transmitir os certificados a um contêiner. Depois de garantir a disponibilidade do registro em HTTPS (você pode verificar no navegador), siga estas instruções sobre como conectar o registro ao GitLab.  Essas instruções diferem com base no que você precisa e na sua instalação do GitLab. No meu caso, a configuração foi adicionar o certificado do registro e a chave (com o nome adequado e as permissões corretas) a /etc/gitlab/ssl e estas linhas a /etc/gitlab/gitlab.rb:

registry_external_url 'https://docker.domain.com'
gitlab_rails['registry_api_url'] = "https://docker.domain.com"

Depois de reconfigurar o GitLab, pude ver a nova guia do Registro, com informações sobre como marcar corretamente as imagens recém-criadas para que elas apareçam aqui.

 

Domínio

Na nossa configuração de Entrega Contínua, construímos automaticamente uma imagem por branch e, se a imagem passar nos testes, ela é publicada no registro e executada automaticamente. Para que nosso aplicativo fique disponível em todos os "estados" de maneira automática, por exemplo, podemos acessar:

  • Vários recursos de ramificações em <featureName>.docker.domain.com
  • Versão de teste em master.docker.domain.com
  • Versão pré-produção em preprod.docker.domain.com
  • Versão de produção em prod.docker.domain.com

Para isso, precisamos de um nome de domínio e adicionamos um registro DNS curinga que aponta *.docker.domain.com ao endereço IP de docker.domain.com. Outra opção seria usar portas diferentes.

Proxy Nginx

Como temos várias ramificações de recursos, precisamos redirecionar os subdomínios automaticamente para o contêiner correto. Para fazer isso, podemos usar o Nginx como proxy reverso. Veja aqui um guia.

Ferramentas de GUI

Para começar a trabalhar com contêineres, você pode usar a linha de comando ou uma das interfaces GUI. Há várias disponíveis, por exemplo:

  • Rancher
  • MicroBadger
  • Portainer
  • Simple Docker UI
  • ...

Eles permitem que você crie contêineres e os gerencie a partir da GUI em vez da CLI. Veja como o Rancher aparenta:

 

Runner do GitLab

Como antes, para executar scripts em outros servidores, precisaremos instalar o runner do GitLab. Discuti isso no terceiro artigo.

Você precisará usar o executor Shell, e não o executor Docker. O executor Docker é usado quando você precisa de algo de dentro da imagem, por exemplo, você está criando um aplicativo Android em um contêiner java e precisa apenas de um apk. No nosso caso, precisamos de um contêiner inteiro e, para isso, precisamos do executor Shell.

 

Conclusão

É fácil começar a executar contêineres e há muitas ferramentas disponíveis.

A entrega contínua usando contêineres difere da configuração usual de várias maneiras:

  • As dependências são atendidas no momento da compilação e, após a criação da imagem, você não precisa pensar nas dependências.
  • Reprodutibilidade — é possível reproduzir facilmente qualquer ambiente existente ao executar o mesmo contêiner localmente.
  • Velocidade — como os contêineres não têm nada além do que você adicionou explicitamente, eles podem ser construídos com mais rapidez e, principalmente, eles são construídos uma vez e usados sempre que necessário.
  • Eficiência — como acima, os contêineres produzem menos sobrecarga do que, por exemplo, VMs.
  • Escalabilidade — com ferramentas de orquestração, você pode dimensionar automaticamente seu aplicativo para a carga de trabalho e consumir apenas os recursos necessários no momento.

O que vem a seguir

      <p>
        No próximo artigo, vamos falar sobre a criação da configuração de CD que usa o contêiner Docker do InterSystems IRIS.
      </p>
    </div>
  </div>
</div>
0
0 364
InterSystems Oficial Angelo Bruno Braga · jan 16, 2023

Acabamos de lançar uma pequena atualização no gerenciador de pacotes, que foi renomeado de ZPM para IPM, conforme expliquei em Novembro.  É puramente uma versão de correção de bug, interpretando corretamente os códigos de retorno ROBOCOPY e corrigindo uma regressão que impedia a instalação de determinados pacotes.

Obtenha aqui:

https://github.com/intersystems/ipm/releases/tag/v0.5.2

0
0 133
Artigo Danusa Calixto · jan 11, 2023 20m read

Criado por Daniel Kutac, Engenheiro de vendas, InterSystems

Parte 3. Apêndice

Explicação sobre as classes OAUTH do InterSystems IRIS

Na parte anterior da nossa série, aprendemos a configurar o InterSystems IRIS para atuar como um cliente OAUTH, além de um servidor de autorização e autenticação (pelo OpenID Connect). Nesta parte final da série, vamos descrever classes que implementam o framework OAuth 2.0 do InterSystems IRIS. Também vamos discutir casos de uso para métodos selecionados de classes de API.

As classes de API que implementam o OAuth 2.0 podem ser separadas em três grupos diferentes de acordo com a finalidade. Todas as classes são implementadas no namespace %SYS. Algumas delas são públicas (por % pacote), outras não e não devem ser chamadas diretamente pelos desenvolvedores.

Classes internas

Estas classes pertencem ao pacote OAuth2.

A tabela a seguir lista algumas classes de interesse (para uma lista completa de classes, consulte a Referência de Classes online da sua instância do Caché). Nenhuma dessas classes deve ser usada diretamente por desenvolvedores de aplicativos, exceto as listadas abaixo.

  <td>
    Descrição
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.AccessToken
  </td>
  
  <td>
    PersistentOAuth2.AccessToken armazena um token de acesso do OAuth 2.0 e as informações relacionadas. É uma cópia do cliente OAUTH do token de acesso. OAuth2.AccessToken é indexado pela combinação de SessionId e ApplicationName. Portanto, apenas um escopo pode ser solicitado para cada SessionId/ApplicationName. Se uma segunda solicitação for feita com um escopo diferente e o token de acesso ainda não tiver sido concedido, o escopo na nova solicitação se tornará o escopo esperado.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.Client
  </td>
  
  <td>
    Persistente A classe OAuth2.Application descreve um cliente OAuth2 e faz referência ao servidor de autorização que usa para autorizar o aplicativo com base no RFC 6749. Um sistema cliente pode ser usado com vários servidores de autorização para diferentes aplicativos.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.Response
  </td>
  
  <td>
    Página CSP É a página de destino para respostas de um servidor de autorização do OAuth 2.0 usado de código do cliente OAuth 2.0 do InterSystems IRIS. A resposta é processada aqui e redirecionada ao alvo final.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.ServerDefinition
  </td>
  
  <td>
    Persistente Armazena informações do servidor de autorização usadas por um cliente OAUTH (essa instância do InterSystems IRIS). Podem ser definidas várias configurações de cliente para cada definição de servidor de autorização.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.Server.AccessToken
  </td>
  
  <td>
    Persistente Os tokens de acesso são gerenciados pelo OAuth2.Server.AccessToken no servidor OAUTH. A classe armazena o token de acesso e as propriedades relacionadas. Essa classe também é o meio de comunicação entre várias partes do servidor de autorização.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.Server.Auth
  </td>
  
  <td>
    Página CSP O servidor de autorização apoia o fluxo de controle de autorização para o código de autorização e os tipos de concessão implícitos conforme a especificação no RFC 6749. A classe OAuth2.Server.Auth é uma subclasse de %CSP.Page que atua como o endpoint de autorização e controla o fluxo de acordo com o RFC 6749.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.Server.Client
  </td>
  
  <td>
    Persistente OAuth2.Server.Configuration é uma classe persistente que descreve os clientes registrados com esse servidor de autorização.
  </td>
</tr>

<tr style="height:0px">
  <td>
    OAuth2.Server.Configuration
  </td>
  
  <td>
    Persistente Armazena a configuração do servidor de autorização. Todas as classes de configuração têm uma página correspondente no Portal de Gerenciamento de Sistemas onde os usuários preenchem os detalhes da configuração.
  </td>
</tr>
Nome da classe

Objetos OAuth2.Client, OAuth2.ServerDefinition, OAuth2.Server.Client e OAuth2.Configuration podem ser abertos, modificados e salvos para criar ou modificar configurações sem usar a IU. Você pode usar essas classes para manipular configurações de maneira programática.

Classes de personalização do servidor

Estas classes pertencem ao pacote %OAuth2. O pacote contém um conjunto de classes internas — utilitários. Só descrevemos as classes que podem ser usadas por desenvolvedores. Estas classes são mencionadas na página de configuração do servidor do OAuth 2.0

  <td>
    Página CSP %OAuth2.Server.Authenticate atua como uma subclasse para todas as classes Authenticate escritas por usuários, além da classe Authenticate padrão. A classe Authenticate é usada pelo endpoint de autorização no OAuth2.Server.Auth para autenticar o usuário. Essa classe permite a personalização do processo de autenticação.Os seguintes métodos talvez sejam implementados para substituir o padrão no OAuth2.Server:·        DirectLogin – use apenas quando não quiser mostrar a página de login·        DisplayLogin – implementa o formulário de login do servidor de autorização·        DisplayPermissions – implementa o formulário com uma lista de escopos solicitados Outras personalizações de aparência e visual podem ser feitas ao modificar o CSS. Os estilos CSS são definidos no método DrawStyle. loginForm é para o formulário DisplayLogin permissionForm é para o formulário DisplayPermissions
  </td>
</tr>

<tr style="height:0px">
  <td>
    %OAuth2.Server.Validate
  </td>
  
  <td>
    Página CSP Esta é a classe Validate User padrão incluída no servidor. A classe padrão usará o banco de dados do usuário da instância do Cache onde o servidor de autorização está localizado para validar o usuário. As propriedades aceitas serão o emissor (Issuer), as funções e o sub (Username). A Classe Validate User é especificada na configuração do servidor de autorização. Precisa conter um método ValidateUser, que validará uma combinação de nome de usuário/senha e retornará um conjunto de propriedades associadas ao usuário.
  </td>
</tr>

<tr style="height:0px">
  <td>
    %OAuth2.Server.Generate
  </td>
  
  <td>
    Objeto registrado O %OAuth2.Server.Generate é a classe Generate Token padrão incluída no servidor. A classe padrão gerará uma string aleatória como o token de acesso opaco. A classe Generate Token é especificada na configuração do servidor de autorização. Precisa conter um método GenerateAccessToken que será usado para gerar um token de acesso com base na array de propriedades retornada pelo método ValidateUser.
  </td>
</tr>

<tr style="height:0px">
  <td>
    %OAuth2.Server.JWT
  </td>
  
  <td>
    Objeto registrado O %OAuth2.Server.JWT é a classe Generate Token que cria um JSON Web Token incluído no servidor. A classe Generate Token é especificada na configuração do servidor de autorização. Precisa conter um método GenerateAccessToken que será usado para gerar um token de acesso com base na array de propriedades retornada pelo método ValidateUser.
  </td>
</tr>

<tr style="height:0px">
  <td>
    %OAuth2.Utils
  </td>
  
  <td>
    Objeto registrado Esta classe implementa, entre outros, o registro de várias entidades. Um código de amostra no capítulo Personalização mostra o possível uso.
  </td>
</tr>
%OAuth2.Server.Authenticate

A imagem a seguir mostra a seção correspondente da configuração do servidor de autorização OAuth 2.0

Caso você use o OpenID Connect com token de identidade formatado JWT (id_token), substitua a classe Generate Token padrão %OAuth2.Server.Generate com %OAuth2.Server.JWT na configuração ou deixe a classe Generate padrão.

Discutiremos as opções de personalização com mais detalhes mais tarde em um capítulo separado.

Classes de API públicas

As classes de API públicas são usadas por desenvolvedores de aplicativos para fornecer valores corretos para o fluxo de mensagens de aplicativos da Web, bem como para realizar a validação de tokens de acesso, introspecção e assim por diante.

Essas classes são implementadas no pacote %SYS.OAuth2. A tabela lista algumas das classes implementadas.

  <td>
    Objeto registrado A classe %SYS.OAuth2.AccessToken define as operações do cliente que permitem que um token de acesso seja usado para autorizar um servidor de recursos. O token subjacente é armazenado no OAuth2.AccessToken no banco de dados CACHESYS. OAuth2.AccessToken é indexado pela combinação de SessionId e ApplicationName. Portanto, apenas um escopo pode ser solicitado para cada SessionId/ApplicationName. Se uma segunda solicitação for feita com um escopo diferente e o token de acesso ainda não tiver sido concedido, o escopo na nova solicitação se tornará o escopo esperado.
  </td>
</tr>

<tr style="height:0px">
  <td>
    %SYS.OAuth2.Authorization
  </td>
  
  <td>
    Objeto registrado A classe %SYS.OAuth2.Authorization contém as operações usadas para autorizar um cliente ao obter um token de acesso. O token subjacente é armazenado no OAuth2.AccessToken no banco de dados CACHESYS. OAuth2.AccessToken é indexado pela combinação de SessionId e ApplicationName. Portanto, apenas um escopo pode ser solicitado para cada SessionId/ApplicationName. Se uma segunda solicitação for feita com um escopo diferente e o token de acesso ainda não tiver sido concedido, o escopo na nova solicitação se tornará o escopo esperado. Observe que essa classe está no CACHELIB e, portanto, disponível em qualquer lugar. No entanto, o armazenamento do token está no CACHESYS e, portanto, não está diretamente disponível para a maior parte do código.
  </td>
</tr>

<tr style="height:0px">
  <td>
    %SYS.OAuth2.Validation
  </td>
  
  <td>
    Objeto registrado A classe %SYS.OAuth2.Validation define os métodos usados para validar (ou invalidar) um token de acesso. O token subjacente é armazenado no OAuth2.AccessToken no banco de dados CACHESYS. OAuth2.AccessToken é indexado pela combinação de SessionId e ApplicationName. Portanto, apenas um escopo pode ser solicitado para cada SessionId/ApplicationName. Se uma segunda solicitação for feita com um escopo diferente e o token de acesso ainda não tiver sido concedido, o escopo na nova solicitação se tornará o escopo esperado.
  </td>
</tr>
%SYS.OAuth2.AccessToken

Vamos analisar alguns métodos e classes desse grupo mais a fundo.

Cada classe de aplicação cliente, que usa o token de acesso, PRECISA conferir a validade dele. Isso é feito em algum lugar no método OnPage (ou o método correspondente na página ZENMojo ou ZEN).

Este é o fragmento de código:

 // Check if we have an access token from oauth2 server
 set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,"scope1,
     scope2",.accessToken,.idtoken,.responseProperties,.error)

 // Continue with further checks if an access token exists.
 // Below are all possible tests and may not be needed in all cases.
 // The JSON object which is returned for each test is just displayed.
 if isAuthorized {
    // do whatever – call resource server API to retrieve data of interest
 }

Sempre que chamamos a API do servidor de recursos, precisamos fornecer o token de acesso. Isso é feito pelo método AddAccessToken da classe %SYS.OAuth2.AccessToken, veja o fragmento de código aqui

 set httpRequest=##class(%Net.HttpRequest).%New()
  // AddAccessToken adds the current access token to the request.
  set sc=##class(%SYS.OAuth2.AccessToken).AddAccessToken(
    httpRequest,,
    ..#SSLCONFIG,
    ..#OAUTH2APPNAME)
 if $$$ISOK(sc) {
    set sc=httpRequest.Get(.. Service API url …)
 }

No código de amostra fornecido nas partes anteriores da nossa série, é possível ver este código no método OnPreHTTP da primeira página do aplicativo (Cache1N). Esse é o melhor local para realizar a verificação do token de acesso para a página inicial do aplicativo.

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
 set scope="openid profile scope1 scope2"
    #dim %response as %CSP.Response
 if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,
    scope,.accessToken,.idtoken,.responseProperties,.error) {
      set %response.ServerSideRedirect="Web.OAUTH2.Cache2N.cls"
 }
 quit 1
}

O método IsAuthorized da classe SYS.OAuth2.AccessToken no código acima é verificar se o token de acesso válido existe e, se não existe, permite mostrar o conteúdo da página com um botão de login/link apontando para o formulário de autenticação do servidor de autorização. Caso contrário, redireciona para a segunda página, que faz o trabalho de recuperar os dados.

Podemos, no entanto, alterar o código para que fique assim:

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
 set scope="openid profile scope1 scope2"
 set sc=##class(%SYS.OAuth2.Authorization).GetAccessTokenAuthorizationCode(
    ..#OAUTH2APPNAME,scope,..#OAUTH2CLIENTREDIRECTURI,.properties)
 quit +sc
}

Essa variante tem um efeito diferente. Ao usar o método GetAccessTokenAuthorizationCode da classe %SYS.OAuth2.Authorization, navegamos diretamente até o formulário de autenticação do servidor de autenticação, sem mostrar o conteúdo da primeira página do nosso aplicativo.

Isso pode ser útil nos casos em que o aplicativo da Web é invocado de um aplicativo nativo do dispositivo móvel, onde algumas informações do usuário já foram mostradas pelo aplicativo nativo (o launcher) e não há necessidade de exibir a página da Web com um botão apontando para o servidor de autorização.

Se você usa o token JWT assinado, então precisa validar o conteúdo dele. Isso é feito pelo seguinte método:

 set valid=##class(%SYS.OAuth2.Validation).ValidateJWT(applicationName,accessToken,scope,,.jsonObject,.securityParameters,.sc)

Veja a descrição detalhada dos parâmetros do método na documentação de Referência de Classes.

Personalização

Vamos passar algum tempo descrevendo quais opções o OAUTH oferece para a personalização da IU de autenticação/autorização.

Suponha que a política da sua empresa exija um comportamento mais restritivo de concessão de escopo. Por exemplo, você pode executar um aplicativo de home banking que se conecta a vários sistemas bancários no seu banco. O banco só concede acesso ao escopo que contém informações sobre a conta bancária real que está sendo recuperada. Como o banco administra milhões de contas, é impossível definir o escopo estático para cada conta. Em vez disso, você pode gerar o escopo em tempo real — durante o processamento da autorização, como parte do código da página de autorização personalizada.

Para o propósito da demonstração, precisamos adicionar mais um escopo à configuração do servidor — veja a imagem.

Também adicionamos a referência à classe Authenticate personalizada, chamada %OAuth2.Server.Authenticate.Bank.

Então, como é a classe de autenticação bancária? Veja uma possível variante da classe. Ela melhora os formulários de autenticação e autorização com dados fornecidos pelo usuário. As informações que fluem entre os métodos BeforeAuthenticate, DisplayPermissions e AfterAuthenticate são transmitidas pela variável de propriedades da classe %OAuth2.Server.Properties

Class %OAuth2.Server.Authenticate.Bank Extends %OAuth2.Server.Authenticate
{
/// Add CUSTOM BESTBANK support for account scope.
ClassMethod BeforeAuthenticate(scope As %ArrayOfDataTypes, properties As %OAuth2.Server.Properties) As %Status
{
 // If launch scope not specified, then nothing to do
 If 'scope.IsDefined("account") Quit $$$OK
 // Get the launch context from the launch query parameter.
 Set tContext=properties.RequestProperties.GetAt("accno")
 // If no context, then nothing to do
 If tContext="" Quit $$$OK
    
 try {
    // Now the BestBank context should be queried.
    Set tBankAccountNumber=tContext
    // Add scope for accno. -> dynamically modify scope (no account:<accno> scope exists in the server configuration)
    // This particular scope is used to allow the same accno to be accessed via account
    // if it was previously selected by account or account:accno when using cookie support
    Do scope.SetAt("Access data for account "_tBankAccountNumber,"account:"_tBankAccountNumber)
    // We no longer need the account scope, since it has been processed.
    // This will prevent existence of account scope from forcing call of DisplayPermissions.
    Do scope.RemoveAt("account")
    
    // Add the accno property which AfterAuthenticate will turn into a response property
    Do properties.CustomProperties.SetAt(tBankAccountNumber,"account_number")
 } catch (e) {
    s ^dk("err",$i(^dk("err")))=e.DisplayString()
 }
 Quit $$$OK
}

/// Add CUSTOM BESTBANK support for account scope.
/// If account_number custom property was added by either BeforeAuthenticate (account)
/// or DisplayPermissions (account:accno), then add the needed response property.
ClassMethod AfterAuthenticate(scope As %ArrayOfDataTypes, properties As %OAuth2.Server.Properties) As %Status
{
 // There is nothing to do here unless account_number (account) or accno (account:accno) property exists
 try {
    // example of custom logging
    If $$$SysLogLevel>=3 {
     Do ##class(%OAuth2.Utils).LogServerScope("log ScopeArray-CUSTOM BESTBANK",%token)
    }
    If properties.CustomProperties.GetAt("account_number")'="" {
     // Add the accno query parameter to the response.
     Do properties.ResponseProperties.SetAt(properties.CustomProperties.GetAt("account_number"),"accno")
    }
 } catch (e) {
    s ^dk("err",$i(^dk("err")))=e.DisplayString()
 }
 Quit $$$OK
}

/// DisplayPermissions modified to include a text for BEST BANK account.
ClassMethod DisplayPermissions(authorizationCode As %String, scopeArray As %ArrayOfDataTypes, currentScopeArray As %ArrayOfDataTypes, properties As %OAuth2.Server.Properties) As %Status
{
 Set uilocales = properties.RequestProperties.GetAt("ui_locales")
 Set tLang = ##class(%OAuth2.Utils).SelectLanguage(uilocales,"%OAuth2Login")
 // $$$TextHTML(Text,Domain,Language)
 Set ACCEPTHEADTITLE = $$$TextHTML("OAuth2 Permissions Page","%OAuth2Login",tLang)
 Set USER = $$$TextHTML("User:","%OAuth2Login",tLang)
 Set POLICY = $$$TextHTML("Policy","%OAuth2Login",tLang)
 Set TERM = $$$TextHTML("Terms of service","%OAuth2Login",tLang)
 Set ACCEPTCAPTION = $$$TextHTML("Accept","%OAuth2Login",tLang)
 Set CANCELCAPTION = $$$TextHTML("Cancel","%OAuth2Login",tLang)
 &html<<html>>
 Do ..DrawAcceptHead(ACCEPTHEADTITLE)
 Set divClass = "permissionForm"
 Set logo = properties.ServerProperties.GetAt("logo_uri")
 Set clientName = properties.ServerProperties.GetAt("client_name")
 Set clienturi = properties.ServerProperties.GetAt("client_uri")
 Set policyuri = properties.ServerProperties.GetAt("policy_uri")
 Set tosuri = properties.ServerProperties.GetAt("tos_uri")
 Set user = properties.GetClaimValue("preferred_username")
 If user="" {
    Set user = properties.GetClaimValue("sub")
 }
 &html<<body>>
 &html<<div id="topLabel"></div>>
 &html<<div class="#(divClass)#">>
 If user '= "" {
    &html<
     <div>
     <span id="left" class="userBox">#(USER)#<br>#(##class(%CSP.Page).EscapeHTML(user))#</span>
     >
 }
 If logo '= "" {
    Set espClientName = ##class(%CSP.Page).EscapeHTML(clientName)
   &html<<span class="logoClass"><img src="#(logo)#" alt="#(espClientName)#" title="#(espClientName)#" align="middle"></span>>
 }
 If policyuri '= "" ! (tosuri '= "") {
   &html<<span id="right" class="linkBox">>
    If policyuri '= "" {
     &html<<a href="#(policyuri)#" target="_blank">#(POLICY)#</a><br>>
    }
    If tosuri '= "" {
     &html<<a href="#(tosuri)#" target="_blank">#(TERM)#</a>>
    }
   &html<</span>>
 }
 &html<</div>>
 &html<<form>>
 Write ##class(%CSP.Page).InsertHiddenField("","AuthorizationCode",authorizationCode),!
 &html<<div>>
 If $isobject(scopeArray), scopeArray.Count() > 0 {
    Set tTitle = $$$TextHTML(" is requesting these permissions:","%OAuth2Login",tLang)
   &html<<div class="permissionTitleRequest">>
    If clienturi '= "" {
     &html<<a href="#(clienturi)#" target="_blank">#(##class(%CSP.Page).EscapeHTML(clientName))#</a>>
    } Else {
     &html<#(##class(%CSP.Page).EscapeHTML(clientName))#>
    }
   &html<#(##class(%CSP.Page).EscapeHTML(tTitle))#</div>>
    Set tCount = 0
    Set scope = ""
    For {
     Set display = scopeArray.GetNext(.scope)
     If scope = "" Quit
     Set tCount = tCount + 1
     If display = "" Set display = scope
     Write "<div class='permissionItemRequest'>"_tCount_". "_##class(%CSP.Page).EscapeHTML(display)_"</div>"
    }
 }

 If $isobject(currentScopeArray), currentScopeArray.Count() > 0 {
    Set tTitle = $$$TextHTML(" already has these permissions:","%OAuth2Login",tLang)
   &html<<div>>
   &html<<div class="permissionTitleExisting">>
    If clienturi '= "" {
     &html<<a href="#(clienturi)#" target="_blank">#(##class(%CSP.Page).EscapeHTML(clientName))#</a>>
    } Else {
     &html<#(##class(%CSP.Page).EscapeHTML(clientName))#>
    }
   &html<#(##class(%CSP.Page).EscapeHTML(tTitle))#</div>>
    Set tCount = 0
    Set scope = ""
    For {
     Set display = currentScopeArray.GetNext(.scope)
     If scope = "" Quit
     Set tCount = tCount + 1
     If display = "" Set display = scope
     Write "<div class='permissionItemExisting'>"_tCount_". "_##class(%CSP.Page).EscapeHTML(display)_"</div>"
    }
   &html<</div>>
 }

 /*********************************/
 /*  BEST BANK CUSTOMIZATION      */
 /*********************************/
 try {
    If properties.CustomProperties.GetAt("account_number")'="" {
     // Display the account number obtained from account context.
     Write "<div class='permissionItemRequest'><b>Selected account is "_properties.CustomProperties.GetAt("account_number")_"</b></div>",!

     // or, alternatively, let user add some more information at this stage (e.g. linked account number)
     //Write "<div>Account Number: <input type='text' id='accno' name='p_accno' placeholder='accno' autocomplete='off' ></div>",!
    }
 } catch (e) {
    s ^dk("err",$i(^dk("err")))=e.DisplayString()
 }

 /* original implementation code continues here... */
 &html<
   <div><input type="submit" id="btnAccept" name="Accept" value="#(ACCEPTCAPTION)#"/></div>
   <div><input type="submit" id="btnCancel" name="Cancel" value="#(CANCELCAPTION)#"/></div>
    >
 &html<</form>
 </div>>
 Do ..DrawFooter()
 &html<</body>>
 &html<<html>>
 Quit 1
}

/// For CUSTOM BESTBANK we need to validate that patient entered,
/// ! javascript in this method is only needed when we let user enter some addtional data
/// within DisplayPermissions method !
ClassMethod DrawAcceptHead(ACCEPTHEADTITLE)
{
 &html<<head><title>#(ACCEPTHEADTITLE)#</title>>
 Do ..DrawStyle()
 &html<
 <script type="text/javascript">
 function doAccept()
 {
    var accno = document.getElementById("accno").value;
    var errors = "";
    if (accno !== null) {
     if (accno.length < 1) {
       errors = "Please enter account number name";
     }
    }
    if (errors) {
     alert(errors);
     return false;
    }
    
    // submit the form
    return true;
 }
 </script>
 >
 &html<</head>>
}

}

Como você pode ver, a classe %OAuth2.Server.Properties contém várias arrays, que são passadas. São elas:

·        RequestProperties — contém parâmetros da solicitação de autorização

·        CustomProperties — contêiner para a troca de dados entre o mencionado acima

·        ResponseProperties — contêiner para as propriedades serem adicionadas ao objeto de resposta JSON a uma solicitação de token

·        ServerProperties — contém propriedades compartilhadas que o servidor de autorização expõe para o código de personalização (por exemplo, logo_uri, client_uri, etc…)

Além disso, ela contém várias propriedades de "declarações", que são usadas para especificar quais declarações devem ser retornadas pelo servidor de autorização.

Para chamar essa página de autenticação corretamente, modificamos nosso código inicial da página do cliente para que fique assim:

 set scope="openid profile scope1 scope2 account"
 // this data comes from application (a form data or so...) and sets a context for our request
 // we can, through subclassing the Authenticate class, display this data to user so he/she can decide
 // whether to grant access or not
 set properties("accno")="75-452152122-5320"
 set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint(
   ..#OAUTH2APPNAME,
    scope,
   ..#OAUTH2CLIENTREDIRECTURI,
    .properties,
   .isAuthorized,
    .sc)
 if $$$ISERR(sc) {
    write "GetAuthorizationCodeEndpoint Error="
   write ..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!
 }

Como você pode ver, adicionamos o escopo da conta e o nó "accno" da array de propriedades com um valor de contexto, que pode se originar em diferentes partes do nosso aplicativo. Esse valor é transmitido dentro do token de acesso ao servidor de recursos para processamento adicional.

Existe um cenário da vida real que usa a lógica descrita acima — o padrão FHIR para a troca de históricos eletrônicos de pacientes.

Depuração

O framework do OAUTH tem depuração integrada. Isso é muito útil, pois toda a comunicação entre o cliente e os servidores é criptografada. O recurso de depuração permite capturar dados de tráfego gerados pelas classes de API antes que sejam enviados pela rede. Para depurar seu código, você pode implementar uma rotina ou classe simples de acordo com o código abaixo. Você precisa implementar este código em todas as instâncias de comunicação do InterSystems IRIS! Nesse caso, é melhor fornecer um nome de arquivo que indique a função dele dentro do processo de fluxo do OAUTH. (O código de amostra abaixo é salvo como uma rotina rr.mac, mas você decide o nome.)

 // d start^rr()
start() public {
 new $namespace
 set $namespace="%sys"
 kill ^%ISCLOG
 set ^%ISCLOG=5
 set ^%ISCLOG("Category","OAuth2")=5
 set ^%ISCLOG("Category","OAuth2Server")=5
 quit
}

 // d stop^rr()
stop() public {
 new $namespace
 set $namespace="%sys"
 set ^%ISCLOG=0
 set ^%ISCLOG("Category","OAuth2")=0
 set ^%ISCLOG("Category","OAuth2Server")=0
 quit

}

 // display^rr()
display() public {
 new $namespace
 set $namespace="%sys"
 do ##class(%OAuth2.Utils).DisplayLog("c:\temp\oauth2_auth_server.log")
 quit
}

Em seguida, antes de começar a testar, abra um terminal e invoque d start^rr() em todos os nós do InterSystems IRIS (cliente, servidor de autorização ou servidor de recursos). Depois de concluído, execute d stop^rr() e d display^rr() para preencher os arquivos de log.

Resumo

Nesta série de artigos, aprendemos a usar a implementação do OAuth 2.0 do InterSystems IRIS. Começando com a demonstração simples do aplicativo cliente na parte 1, seguido pela amostra complexa descrita na parte 2. Por fim, descrevemos as classes mais importantes da implementação do OAuth 2.0 e explicamos quando elas devem ser chamadas nos aplicativos dos usuários.

Quero agradecer em especial a Marvin Tener, pela paciência infinita ao responder às minhas perguntas, às vezes idiotas, e por revisar a série.

0
0 149
Artigo Danusa Calixto · Dez. 22, 2022 6m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Git básico
  • Fluxo Git (processo de desenvolvimento)
  • Instalação do GitLab
  • Fluxo de trabalho do GitLab
  • Entrega contínua
  • Instalação e configuração do GitLab
  • CI/CD do GitLab
  • Por que contêineres?
  • CI/CD do GitLab usando contêineres

No primeiro artigo, abordamos os fundamentos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno e como o Git pode ser usado para desenvolver software.

No segundo artigo, abordamos o fluxo de trabalho do GitLab: um processo inteiro do ciclo de vida do software e a entrega contínua.

No terceiro artigo, abordamos a instalação e configuração do GitLab e a conexão dos seus ambientes a ele

No quarto artigo, escrevemos uma configuração de CD.

Neste artigo, falaremos sobre os contêineres e como (e por que) podem ser usados.

Este artigo presume a familiaridade com os conceitos de docker e contêiner. Confira estes artigos do @Luca Ravazzolo se quiser ler sobre contêineres e imagens.

Vantagens

Há muitas vantagens em usar contêineres:

  • Portabilidade
  • Eficiência
  • Isolamento
  • Leveza
  • Imutabilidade

Vamos falar sobre cada uma em detalhes.

Portabilidade

Um contêiner embrulha um aplicativo com tudo o que ele precisa para ser executado, como arquivos de configuração e dependências. Isso permite que você execute aplicativos de maneira fácil e confiável em diferentes ambientes, como seu desktop local, servidores físicos, servidores virtuais, testes, staging, ambientes de produção e nuvens públicas ou privadas.

Outro ponto da portabilidade é que, depois de criar sua imagem do Docker e verificar que ela é executada corretamente, ela pode ser executada em qualquer outro lugar que execute o docker, que atualmente são os servidores Windows, Linux e MacOS.

Eficiência

Você só precisa que o processo do aplicativo seja executado, e não todo o sistema, etc. E os contêineres oferecem exatamente isso: eles executam apenas os processos de que você precisa explicitamente e nada mais. Como os contêineres não exigem um sistema operacional separado, eles consomem menos recursos. Enquanto uma VM costuma ter vários gigabytes de tamanho, um contêiner geralmente tem apenas algumas centenas de megabytes, tornando possível executar muito mais contêineres do que VMs em um único servidor. Como os contêineres têm um nível de uso mais alto em relação ao hardware subjacente, você precisa de menos hardware, resultando em uma redução nos custos dos servidores bare metal, bem como dos centros de processamento de dados.

Isolamento

Os contêineres isolam seu aplicativo de todo o resto e, embora vários contêineres possam ser executados no mesmo servidor, eles podem ser completamente independentes uns dos outros. Qualquer interação entre contêineres deve ser explicitamente declarada como tal. Se um contêiner falhar, ele não afetará os outros e poderá ser reiniciado rapidamente. A segurança também se beneficia desse isolamento. Por exemplo, explorar a vulnerabilidade do servidor web em um servidor bare metal pode dar a um invasor acesso a todo o servidor, mas, no caso dos contêineres, o invasor só teria acesso ao contêiner do servidor web.

Leveza

Como os contêineres não exigem um sistema operacional separado, eles podem ser iniciados, interrompidos ou reinicializados em questão de segundos, o que acelera todos os pipelines de desenvolvimento relacionados e o tempo de produção. Você pode começar a trabalhar antes e não gastar nenhum tempo na configuração. 

Imutabilidade

A infraestrutura imutável é composta por componentes imutáveis que são substituídos a cada implantação, em vez de serem atualizados no local. Esses componentes são inicializados a partir de uma imagem comum que é criada uma vez por implantação e pode ser testada e validada. A imutabilidade reduz a inconsistência e permite a replicação e a movimentação entre diferentes estados do seu aplicativo com facilidade. Mais sobre a imutabilidade.

Novas possibilidades

Todas essas vantagens nos permitem gerenciar a infraestrutura e o fluxo de trabalho de maneiras totalmente novas.

Orquestração

Há um problema com ambientes bare metal ou VM, eles ganham individualidade, o que traz várias surpresas depois, geralmente desagradáveis. A resposta para isso é a infraestrutura como código, o gerenciamento da infraestrutura em um modelo descritivo, usando as mesmas versões que a equipe de DevOps usa para o código-fonte.

Com a infraestrutura como código, um comando de implantação sempre coloca o ambiente de destino na mesma configuração, não importa o estado inicial do ambiente. Isso é alcançado ao configurar automaticamente um destino existente ou descartar o destino existente e recriar um novo ambiente.

Assim, com a infraestrutura como código, as equipes fazem alterações na descrição do ambiente e na versão do modelo de configuração, que normalmente está em formatos de código bem documentados, como JSON. O pipeline de lançamento executa o modelo para configurar ambientes de destino. Se a equipe precisar fazer alterações, ela editará a fonte, e não o destino.

Tudo isso é possível e muito mais fácil de fazer com contêineres. Leva poucos segundos para desativar um contêiner e iniciar outro, enquanto o provisionamento de uma nova VM leva alguns minutos. E nem estou falando em reverter um servidor para um estado limpo.

Escalonamento

Do ponto anterior, você pode ter uma ideia de que a infraestrutura como código é estática por si só. Não é, pois as ferramentas de orquestração também podem fornecer escalonamento horizontal (provisionando mais do mesmo) com base na carga de trabalho atual. Você só deve executar o que é necessário no momento e escalonar seu aplicativo de acordo. Isso também pode reduzir custos.

Conclusão

Os contêineres podem otimizar seu pipeline de desenvolvimento. A eliminação de inconsistências entre ambientes permite testes e depurações mais fáceis. A orquestração permite que você crie aplicativos escalonáveis.  A implantação ou reversão para qualquer ponto do histórico imutável é possível e fácil.

As organizações querem trabalhar em um nível mais alto, onde todos os problemas listados acima já estejam resolvidos e onde encontramos agendadores e orquestradores lidando com mais coisas de maneira automatizada.

O que vem a seguir

      <p>
        No próximo artigo, vamos falar sobre o provisionamento com contêineres e a criação da configuração de CD que usa o contêiner Docker do InterSystems IRIS.
      </p>
    </div>
  </div>
</div>
0
0 88
Artigo Danusa Calixto · Dez. 22, 2022 10m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Git básico
  • Fluxo Git (processo de desenvolvimento)
  • Instalação do GitLab
  • Fluxo de trabalho do GitLab
  • Entrega contínua
  • Instalação e configuração do GitLab
  • CI/CD do GitLab

No primeiro artigo, abordamos os fundamentos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno e como o Git pode ser usado para desenvolver software.

No segundo artigo, abordamos o fluxo de trabalho do GitLab: um processo inteiro do ciclo de vida do software e a entrega contínua.

No terceiro artigo, abordamos a instalação e configuração do GitLab e a conexão dos seus ambientes a ele

Neste artigo, finalmente, vamos escrever uma configuração de CD.

Plano

Ambientes

Em primeiro lugar, precisamos de vários ambientes e branches que correspondam a eles:

EnvironmentBranchDeliveryWho can commitWho can merge
TestmasterAutomaticDevelopers  OwnersDevelopers  Owners
PreprodpreprodAutomaticNo oneOwners
ProdprodSemiautomatic (press button to deliver)No one

Owners

Ciclo de desenvolvimento

E, como exemplo, desenvolveremos um novo recurso usando o fluxo do GitLab e o entregaremos usando a CD do GitLab.

  1. O recurso é desenvolvido em um branch de recursos.
  2. O branch de recurso é revisado e mesclado no master branch.
  3. Depois de um tempo (vários recursos mesclados), o master é mesclado com o preprod
  4. Depois de um tempo (teste do usuário, etc.), o preprod é mesclado com o prod

Veja como isso ficaria (marquei as partes que precisamos desenvolver para o CD em itálico):

  1. Desenvolvimento e teste
    • O desenvolvedor envia o código para o novo recurso em um branch de recursos separado
    • Depois que o recurso se torna estável, o desenvolvedor mescla nosso branch de recursos no master branch
    • O código do branch master é entregue ao ambiente de teste, onde é carregado e testado
  2. Entrega para o ambiente de pré-produção
    • O desenvolvedor cria a solicitação de mesclagem do branch master para o branch de pré-produção
    • Depois de algum tempo, o proprietário do repositório aprova a solicitação de mesclagem
    • O código do branch de pré-produção é entregue ao ambiente de pré-produção
  3. Entrega para o ambiente de produção
    • O desenvolvedor cria a solicitação de mesclagem do branch de pré-produção para o branch de produção
    • Depois de algum tempo, o proprietário do repositório aprova a solicitação de mesclagem
    • O proprietário do repositório aperta o botão "Implantar"
    • O código do branch de produção é entregue ao ambiente de produção

Ou o mesmo, mas em formato de gráfico:

Aplicativo

Nosso aplicativo consiste em duas partes:

  • API REST desenvolvida na plataforma InterSystems
  • Web application de JavaScript cliente

Estágios

Com o plano acima, podemos determinar as etapas que precisamos definir na nossa configuração de entrega contínua:

  • Carregamento — para importar o código do lado do servidor para o InterSystems IRIS
  • Teste — para testar o código do servidor e cliente
  • Pacote — para criar o código do cliente
  • Implantação — para "publicar" o código do cliente usando o servidor web

Veja como isso fica no arquivo de configuração gitlab-ci.yml:

stages:
  - load
  - test
  - package
  - deploy

Scripts

Carregamento

Em seguida, vamos definir os scripts. Documentos de scripts. Primeiro, vamos definir um script load server que carrega o código do lado do servidor:

load server:
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test
  stage: load
  script: csession IRIS "##class(isc.git.GitLab).load()"

O que acontece aqui?

  • load server é o nome de um script
  • em seguida, descrevemos o ambiente em que esse script é executado
  • only: master — informa ao GitLab que esse script só deve ser executado quando houver um commit para o master branch
  • tags: test especifica que esse script só deve ser executado em um runner com a tag test
  • stage especifica o estágio para um script
  • script define o código para executar. No nosso caso, chamamos o classmethod load da classe isc.git.GitLab

Observação importante

Para InterSystems IRIS, troque csession por iris session.

Para Windows, use: irisdb -s ../mgr -U TEST "##class(isc.git.GitLab).load()

Agora, vamos escrever a classe isc.git.GitLab correspondente. Todos os pontos de entrada nessa classe ficam desta forma:

ClassMethod method()
{
    try {
        // code
        halt
    } catch ex {
        write !,$System.Status.GetErrorText(ex.AsStatus()),!
        do $system.Process.Terminate(, 1)
    }
}

Observe que esse método pode terminar de duas maneiras:

  • interrompendo o processo atual — que é registrado no GitLab como uma conclusão bem-sucedida
  • chamando $system.Process.Terminate — que termina o processo de maneira anormal e o GitLab registra isso como um erro

Dito isso, aqui está nosso código de carregamento:

/// Do a full load
/// do ##class(isc.git.GitLab).load()
ClassMethod load()
{
    try {
        set dir = ..getDir()
        do ..log("Importing dir " _ dir)
        do $system.OBJ.ImportDir(dir, ..getExtWildcard(), "c", .errors, 1)
        throw:$get(errors,0)'=0 ##class(%Exception.General).%New("Load error")

        halt
    } catch ex {
        write !,$System.Status.GetErrorText(ex.AsStatus()),!
        do $system.Process.Terminate(, 1)
    }
}

Dois métodos de utilitários são chamados:

  • getExtWildcard — para obter uma lista das extensões de arquivo relevantes
  • getDir — para obter o diretório do repositório

Como podemos obter o diretório?

Quando o GitLab executa um script, primeiro, ele especifica várias variáveis de ambiente. Uma delas é a CI_PROJECT_DIR — o caminho completo onde o repositório é clonado e onde o job é executado. Ele pode ser obtido facilmente no nosso método getDir :

ClassMethod getDir() [ CodeMode = expression ]
{
##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR"))
}

####Testes

Aqui está o script de teste:

load test:
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test
  stage: test
  script: csession IRIS "##class(isc.git.GitLab).test()"
  artifacts:
    paths:
      - tests.html

O que mudou? O nome e o código do script, é claro, mas o artefato também foi adicionado. Um artefato é uma lista de arquivos e diretórios que são anexados a um job depois que ele é concluído com sucesso. No nosso caso, depois que os testes forem concluídos, podemos gerar a página HTML redirecionando para os resultados dos testes e disponibilizá-la a partir do GitLab. 

Observe que há bastante copiar e colar do estágio de carregamento — o ambiente é o mesmo, partes do script, como ambientes, podem ser rotuladas separadamente e anexadas a um script. Vamos definir o ambiente de teste:

.env_test: &env_test
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test

Agora, nosso script de teste fica assim:

load test:
  <<: *env_test
  script: csession IRIS "##class(isc.git.GitLab).test()"
  artifacts:
    paths:
      - tests.html

Em seguida, vamos executar os testes usando o framework UnitTest.

/// do ##class(isc.git.GitLab).test()
ClassMethod test()
{
    try {
        set tests = ##class(isc.git.Settings).getSetting("tests")
        if (tests'="") {
            set dir = ..getDir()
            set ^UnitTestRoot = dir

            $$$TOE(sc, ##class(%UnitTest.Manager).RunTest(tests, "/nodelete"))
            $$$TOE(sc, ..writeTestHTML())
            throw:'..isLastTestOk() ##class(%Exception.General).%New("Tests error")
        }
        halt
    } catch ex {
        do ..logException(ex)
        do $system.Process.Terminate(, 1)
    }
}

A definição do teste, nesse caso, é um caminho relativo à raiz do repositório onde os testes de unidade são armazenados. Se estiver vazio, pulamos testes. O método writeTestHTML é usado para gerar o html com um redirecionamento para os resultados dos testes:

ClassMethod writeTestHTML()
{
    set text = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "html").Data.Read()
    set text = $replace(text, "!!!", ..getURL())
    
    set file = ##class(%Stream.FileCharacter).%New()
    set name = ..getDir() _  "tests.html"
    do file.LinkToFile(name)
    do file.Write(text)
    quit file.%Save()
}

ClassMethod getURL()
{
    set url = ##class(isc.git.Settings).getSetting("url")
    set url = url _ $system.CSP.GetDefaultApp("%SYS")
    set url = url_"/%25UnitTest.Portal.Indices.cls?Index="_ $g(^UnitTest.Result, 1) _ "&$NAMESPACE=" _ $zconvert($namespace,"O","URL")
    quit url
}

ClassMethod isLastTestOk() As %Boolean
{
    set in = ##class(%UnitTest.Result.TestInstance).%OpenId(^UnitTest.Result)
    for i=1:1:in.TestSuites.Count() {
        #dim suite As %UnitTest.Result.TestSuite
        set suite = in.TestSuites.GetAt(i)
        return:suite.Status=0 $$$NO
    }
    quit $$$YES
}

XData html
{
<html lang="en-US">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="refresh" content="0; url=!!!"/>
<script type="text/javascript">
window.location.href = "!!!"
</script>
</head>
<body>
If you are not redirected automatically, follow this <a href='!!!'>link to tests</a>.
</body>
</html>
}

Pacote

Nosso cliente é uma página HTML simples:

<html>
<head>
<script type="text/javascript">
function initializePage() {
  var xhr = new XMLHttpRequest();
  var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/version";
  xhr.open("GET", url, true);
  xhr.send();
  xhr.onloadend = function (data) {
    document.getElementById("version").innerHTML = "Version: " + this.response;
  };
  
  var xhr = new XMLHttpRequest();
  var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/author";
  xhr.open("GET", url, true);
  xhr.send();
  xhr.onloadend = function (data) {
    document.getElementById("author").innerHTML = "Author: " + this.response;
  };
}
</script>
</head>
<body  onload="initializePage()">
<div id = "version"></div>
<div id = "author"></div>
</body>
</html>

E, para criá-la, precisamos substituir ${CI_ENVIRONMENT_URL} pelo seu valor. Claro, um aplicativo real provavelmente exigiria npm, mas esse é apenas um exemplo. Aqui está o script:

package client:
  <<: *env_test
  stage: package
  script: envsubst < client/index.html > index.html
  artifacts:
    paths:
      - index.html

Implantação

Por fim, implantamos nosso cliente ao copiar index.html para o diretório raiz do servidor web.

deploy client:
  <<: *env_test
  stage: deploy
  script: cp -f index.html /var/www/html/index.html

É isso!

Vários ambientes

O que fazer se você precisar executar o mesmo script (semelhante) em vários ambientes? Partes do script também podem ser rótulos, então aqui está uma configuração de exemplo que carrega o código em ambientes de teste e pré-produção:

stages:
  - load
  - test

.env_test: &env_test
  environment:
    name: test
    url: http://test.hostname.com
  only:
    - master
  tags:
    - test
    
.env_preprod: &env_preprod
  environment:
    name: preprod
    url: http://preprod.hostname.com
  only:
    - preprod
  tags:
    - preprod

.script_load: &script_load
  stage: load
  script: csession IRIS "##class(isc.git.GitLab).loadDiff()"

load test:
  <<: *env_test
  <<: *script_load

load preprod:
  <<: *env_preprod
  <<: *script_load

Assim, podemos fugir de copiar e colar o código.

Veja a configuração de CD completa aqui. Ela segue o plano original de mover código entre os ambientes de teste, pré-produção e produção.

Conclusão

A entrega contínua pode ser configurada para automatizar qualquer fluxo de trabalho de desenvolvimento necessário.

Links

O que vem a seguir

No próximo artigo, vamos criar a configuração de CD que usa o contêiner Docker do InterSystems IRIS.

0
0 101
Artigo Danusa Calixto · Dez. 15, 2022 5m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Git básico
  • Fluxo Git (processo de desenvolvimento)
  • Instalação do GitLab
  • Fluxo de trabalho do GitLab
  • Entrega contínua
  • Instalação e configuração do GitLab
  • CI/CD do GitLab

No primeiro artigo, abordamos os fundamentos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno e como o Git pode ser usado para desenvolver software.

No segundo artigo, abordamos o fluxo de trabalho do GitLab: um processo inteiro do ciclo de vida do software e a entrega contínua.

Neste artigo, vamos discutir:

  • Instalação e configuração do GitLab
  • Conexão dos seus ambientes ao GitLab

Instalação do GitLab

Vamos instalar o GitLab no local. Há várias maneiras de instalar o GitLab — da fonte, pacote, em um contêiner. Não descreverei todos os passos aqui, há um guia para isso. Ainda assim, algumas observações.

Pré-requisitos:

  • Servidor separado — como é um web application e um recurso bastante intensivo, é melhor executar em um servidor separado
  • Linux
  • (Opcional, mas altamente recomendável) Domínio — necessário para executar páginas e proteger a configuração inteira

Configuração

Primeiro de tudo, você provavelmente precisa enviar e-mails com notificações.

Em seguida, recomendo instalar Páginas. Como discutido no artigo anterior — artefatos do script podem ser enviados para o GitLab. O usuário pode fazer o download deles, mas é útil poder abri-los diretamente no navegador e, para isso, precisamos de páginas.

Por que você precisa de páginas:

Como as páginas html podem ter um redirecionamento onload, elas podem ser usadas para enviar o usuário para onde precisamos. Por exemplo, veja este código que gera uma página html que envia um usuário para o último teste de unidade executado (no momento da geração do html):

ClassMethod writeTestHTML()
{
  set text = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "html").Data.Read()
  set text = $replace(text, "!!!", ..getURL())
  
  set file = ##class(%Stream.FileCharacter).%New()
  set name = "tests.html"
  do file.LinkToFile(name)
  do file.Write(text)
  quit file.%Save()
}

ClassMethod getURL()
{
  set url = "http://host:57772"
  set url = url _ $system.CSP.GetDefaultApp("%SYS")
  set url = url_"/%25UnitTest.Portal.Indices.cls?Index="_ $g(^UnitTest.Result, 1) _ "&$NAMESPACE=" _ $zconvert($namespace,"O","URL")
  quit url
}

XData html
{

  
  
    If you are not redirected automatically, follow this link to tests.
  

}

Encontrei um bug usando as páginas (erro 502 ao procurar artefatos), veja aqui a correção.

 

Conexão dos seus ambientes ao GitLab

Para executar scripts de CD, você precisa de ambientes, servidores configurados para executar seu aplicativo. Presumindo que você tem um servidor Linux com o produto InterSystems instalado (digamos InterSystems IRIS, mas funciona também com o Caché e Ensemble), estas etapas conectam o ambiente ao GitLab:

  1. Instalar o runner do GitLab
  2. Registrar o runner com o GitLab
  3. Permitir que o runner chame o InterSystems IRIS

Observação importante sobre a instalação do runner GitLab, NÃO clone servidores após instalar o runner do GitLab.  Os resultados são imprevisíveis e muito indesejados.

Registrar o runner com o GitLab

Após executar o inicial:

sudo gitlab-runner register

você verá vários prompts e, embora a maioria das etapas seja bastante direta, várias não são:

Insira o token gitlab-ci para este runner

Há vários tokens disponíveis:

  • Um para o sistema inteiro (disponível nas configurações de administração)
  • Um para cada projeto (disponível nas configurações do projeto)

Conforme você conecta um runner para executar a CD para um projeto específico, você precisa especificar um token para este projeto.

Insira as tags gitlab-ci para este runner (separadas por vírgulas):

Na configuração de CD, você pode filtrar quais scripts vão ser executados em quais tags. Então, no caso mais simples, especifique uma tag, que seria o nome do ambiente.

Insira o executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
docker

Se você estiver usando o servidor habitual sem docker, escolha shell.  O Docker será discutido nas partes posteriores.

Permitir que o runner chame o InterSystems IRIS

Depois de conectar o runner ao GitLab, precisamos permitir que ele interaja com o InterSystems IRIS, para isso:

  1. O usuário gitlab-runner precisa conseguir chamar csession. Para fazer isso, adicione-o ao grupo cacheusr: 
    • usermod -a -G cacheusr gitlab-runner
  2. Crie o usuário gitlab-runner no InterSystems IRIS e dê a ele funções para realizar tarefas de CD (escreva para DB, etc.)
  3. Permitir a autenticação no nível do SO

Para 2 e 3, outras abordagens podem ser usadas, como a transmissão de usuário/código, mas acho que a autenticação de SO é preferível. 

Conclusão

Nesta parte:

  • GitLab instalado
  • Ambientes conectados ao GitLab

O que vem a seguir

Na próxima parte, escrevemos nossa configuração de entrega contínua.

0
0 156
Artigo Danusa Calixto · Nov. 19, 2022 20m read

Criado por Daniel Kutac, Engenheiro de vendas, InterSystems

Aviso: se você ficar confuso com os URLs usados: a série original usou telas de uma máquina chamada dk-gs2016. As novas capturas de tela foram tiradas em uma máquina diferente. Você pode tratar o URL WIN-U9J96QBJSAG como se fosse o dk-gs2016 com segurança.

Parte 2. Servidor de autorização, servidor OpenID Connect

Na parte anterior desta série curta, aprendemos sobre o caso de uso simples – atuando como um cliente OAUTH[1]. Agora, é hora de levar nossa experiência a um nível completamente novo. Vamos construir um ambiente muito mais complexo, onde o InterSystems IRIS vai desempenhar todas as funções OAUTH.

Já sabemos como fazer um cliente, então vamos nos concentrar no servidor de autorização e, ainda mais, no provedor OpenID Connect[2].

Como na parte anterior, precisamos preparar o ambiente. Desta vez, será mais complicado, pois há mais partes móveis.

Antes de entrarmos nos detalhes do nosso exemplo, precisamos falar algumas palavras sobre o OpenID Connect.

Como você deve lembrar, na parte anterior, recebemos a solicitação – para ter a autorização do Google – para nos autenticarmos primeiro com o Google. A autenticação não faz parte do framework OAUTH. Na verdade, há vários frameworks de autenticação, independentes do OAUTH. Um deles é chamado OpenID. Originalmente uma iniciativa independente, ele agora aproveita a infraestrutura fornecida pelo framework OAUTH, ou seja, as estruturas de comunicação e dados. Assim, nasceu o OpenID Connect. Na verdade, muitas pessoas o chamam de OAUTH em esteroides. De fato, com o OpenID Connect, você pode não só autorizar, mas também autenticar usando interfaces bem conhecidas do framework OAUTH.

Demonstração complexa do OpenID Connect

Aproveitaremos grande parte do código do cliente da parte 1. Isso nos poupa muito trabalho, para que possamos nos concentrar na configuração do ambiente.

Pré-requisitos

Desta vez, precisamos adicionar uma infraestrutura PKI ao servidor web já existente com SSL habilitado. Precisamos de criptografia exigida pelo OpenID Connect. Se você quer autenticar alguém, precisa ter certeza absoluta de que ninguém mais pode se passar pelo agente (cliente, servidor de autenticação...) que envia os dados confidenciais pela rede. É aqui que entra a criptografia baseada em X.509.

Observação: a partir do Cache 2017.1, não é mais necessário usar certificados X.509 para gerar JWT/JWKS (JSON Web Key Set). Devido à compatibilidade com versões anteriores e simplicidade, usamos essa opção.

PKI

A rigor, não precisamos usar a infraestrutura Caché PKI, mas é mais conveniente do que usar ferramentas como openssl diretamente para gerar todos os certificados.

Não entraremos em detalhes sobre a geração de certificados aqui, pois você pode encontrá-los na documentação do InterSystems IRIS. Como resultado da geração de certificados, criaremos 3 pares de chaves públicas/privadas e certificados associados.

Vamos chamá-los de

·        root_ca (root_ca.cer) para nossa autoridade de certificação emissora

·        auth (auth.cer e auth.key) para o servidor de autorização e OpenID

·        client (client.cer e client.key) para o servidor de aplicação do cliente

Credenciais X.509

Precisamos definir credenciais X.509 em servidores individuais para que eles possam assinar e validar JSON Web Tokens (JWT) trocados durante nossa demonstração

Configuração do servidor de autenticação e autorização

Sem entrar em detalhes sobre como definir credenciais X.509, apenas mostramos uma captura de tela das credenciais da instância AUTHSERVER.

Como a imagem indica, o AUTHSERVER possui a própria chave privada e o certificado, enquanto só possui o certificado com a chave pública de CLIENT

Configuração do servidor cliente

Da mesma forma, as credenciais definidas na instância CLIENT

Aqui o CLIENTE possui a chave privada e o certificado, mas somente o certificado com chave pública de AUTHSERVER.

Configuração do servidor de recursos

Não precisamos definir credenciais X509 na instância RESSERVER em nossa configuração de exemplo.

Configuração do OAUTH

Como a configuração descrita na parte 1 desta série, precisamos configurar nossos servidores para OAUTH. Vamos começar com a instância AUTHSERVER, pois é o componente central na configuração geral do OAUTH.

AUTHSERVER

No Portal de Gerenciamento de Sistemas, acesse System Administration (Administração do Sistema) > Security (Segurança) > OAuth 2.0 > Server Configuration (Configuração do Servidor).

Clique no link do menu e preencha os itens do formulário:

·        nome do host

·        porta (opcional)

·        prefixo (opcional) – esses três campos compõem o Issuer endpoint (endpoint do emissor)

·        especifique as condições para retornar o token de atualização

·        verifique os tipos de concessão compatíveis, para nossa demonstração basta verificar todos os quatro tipos. No entanto, apenas o código de autorização é usado.

·        opcionalmente, confira o público-alvo necessário – isso adiciona a propriedade aud no código de autorização e solicitações implícitas

·        opcionalmente, confira a sessão de usuário de suporte - isso significa que o cookie httpOnly é usado pelo servidor de autorização para manter o usuário atual deste navegador conectado.  A segunda solicitação e as solicitações subsequentes do token de acesso não pedirão o nome de usuário e a senha.

·        especifique os intervalos de endpoint

·        defina os escopos compatíveis com esse servidor

·        aceite o padrão ou insira valores de opções de personalização – observação: altere o valor da classe de token Generate de %OAuth2.Server.Generate para %OAuth2.Server.JWT para que um JWT seja usado como token de acesso em vez de um token opaco.

·        forneça o nome da configuração SSL registrada para estabelecer o SSL sobre o HTTP conforme exigido pelo OAuth 2.0

·        Preencha as configurações do JSON Web Token (JWT) 

Veja esta captura de tela da configuração de exemplo

Após definir a configuração do servidor, precisamos fornecer a configuração do cliente do servidor. Na página com o formulário de configuração do servidor, clique no botão Client Configurations e pressione Create New Configuration for your CLIENT and RESSERVER instances (Criar nova configuração para as instâncias CLIENT e RESSERVER).

Esta imagem mostra a configuração do CLIENT.

Deixe a guia JWT Token vazia — com valores padrão. Como você pode ver, nós preenchemos os campos com dados sem sentido, diferente de um caso de aplicação real.

Da mesma forma, a configuração do RESSERVER

Como você pode ver, há apenas informações muito básicas necessárias para o servidor de recursos, ou seja, você precisa definir o tipo de cliente para o servidor de recursos. Com CLIENT, você precisa fornecer mais informações, o tipo de cliente (confidencial, pois nosso cliente é executado como um web app capaz de manter o cliente em segredo no servidor, e não enviar para o agente cliente).

CLIENT

No SMP, acesse System Administration (Administração do Sistema) > Security (Segurança) > OAuth 2.0 > Client Configurations (Configurações do cliente).

Clique no botão Create Server Configuration (Criar configuração de servidor), preencha o formulário e salve.

Confira se o endpoint do emissor corresponde ao valor que definimos anteriormente na instância AUTHSERVER! Você também precisa modificar os endpoints do servidor de autorização de acordo com a configuração do seu servidor web. No nosso caso, apenas incorporamos 'authserver' em cada campo de entrada.

Agora, clique no link Client Configurations (Configurações do cliente) ao lado do Issuer Endpoint (Endpoint emissor) recém-criado e clique no botão Create Client Configuration (Criar configuração de cliente).

Ótimo! Agora, temos CLIENT e AUTHSERVER configurados. Isso pode ser suficiente para muitos casos de uso, pois o servidor de recursos pode ser apenas um namespace de AUTHSERVER, já protegido. Porém, vamos considerar que queremos cobrir um caso de uso em que um médico externo está tentando recuperar dados do nosso sistema clínico interno. Portanto, para permitir que esse médico recupere os dados, queremos armazenar as informações da conta dele DENTRO do nosso servidor de recursos para auditorias e fins forenses. Nesse caso, precisamos continuar e definir as configurações em RESSERVER.

RESSERVER

No SMP, acesse System Administration (Administração do Sistema) > Security (Segurança) > OAuth 2.0 > Client Configurations (Configurações do cliente).

Clique no botão Create Server Configuration (Criar configuração de servidor), preencha o formulário e salve.

Usamos a função de descoberta, um novo recurso implementado no Cache 2017.1

Como você pode ver, essa configuração está usando os mesmos dados que a configuração correspondente na instância CLIENT.

Agora, clique no link Client Configurations (Configurações do cliente) ao lado do Issuer Endpoint (Endpoint emissor) recém-criado e clique no botão Create Client Configuration (Criar configuração de cliente).

A criação do WT a partir das credenciais X.509 não é recomendada, mas nós as usamos para a compatibilidade.

Isso! Foi um processo tedioso, mas necessário. Agora, podemos avançar e começar a programar!

Aplicativo cliente

Para manter as coisas o mais simples possível, reciclaremos grande parte do código do nosso exemplo do Google que descrevemos na parte 1.

O aplicativo cliente tem apenas duas páginas de CSP, sendo executado no aplicativo /csp/myclient, sem segurança aplicada – ele é apenas executado como usuário não autenticado.

Página 1

Class Web.OAUTH2.Cache1N Extends %CSP.Page
{

Parameter OAUTH2CLIENTREDIRECTURI = "https://dk-gs2016/client/csp/myclient/Web.OAUTH2.Cache2N.cls";

Parameter OAUTH2APPNAME = "demo client";

ClassMethod OnPage() As %Status
{
  &html<<html>

<body>
  <h1>Authenticating and Authorizing against Cache&acute; OAuth2 provider</h1>
  <p>This page demo shows how to call Cache&acute; API functions using OAuth2 authorization.
  <p>We are going to call Cache&acute; authentication and authorization server to grant our application access to data stored at another
  Cache&acute; server.
 >

  // Get the url for authorization endpoint with appropriate redirect and scopes.
  // The returned url is used in the button below.

  // DK: use 'dankut' account to authenticate!
  set scope="openid profile scope1 scope2"
  set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint(
    ..#OAUTH2APPNAME,
    scope,
    ..#OAUTH2CLIENTREDIRECTURI,
    .properties,
    .isAuthorized,
    .sc)
  if $$$ISERR(sc) {
    write "GetAuthorizationCodeEndpoint Error="
    write ..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!
  } 

  &html<
  <div class="portalLogoBox"><a class="portalLogo" href="#(url)#">Authorize for <b>ISC</b></a></div>
  </body></html>>
  Quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
  #dim %response as %CSP.Response
  set scope="openid profile scope1 scope2"
  if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) {
    set %response.ServerSideRedirect="Web.OAUTH2.Cache2N.cls"
  }
  quit 1
}

}

Página 2

Class Web.OAUTH2.Cache2N Extends %CSP.Page
{

Parameter OAUTH2APPNAME = "demo client";

Parameter OAUTH2ROOT = "https://dk-gs2016/resserver";

Parameter SSLCONFIG = "SSL4CLIENT";

ClassMethod OnPage() As %Status
{
    &html<<html>




<body>>
    
    // Check if we have an access token from oauth2 server
    set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,"scope1 scope2",.accessToken,.idtoken,.responseProperties,.error)
    
    // Continue with further checks if an access token exists.
    // Below are all possible tests and may not be needed in all cases.
    // The JSON object which is returned for each test is just displayed.
    if isAuthorized {
        write "<h3>Authorized!</h3>",!
        
        
        // Validate and get the details from the access token, if it is a JWT.
        set valid=##class(%SYS.OAuth2.Validation).ValidateJWT(..#OAUTH2APPNAME,accessToken,"scope1 scope2",,.jsonObject,.securityParameters,.sc)
        if $$$ISOK(sc) {
            if valid {
                write "Valid JWT"_"<br>",!    
            } else {
                write "Invalid JWT"_"<br>",!    
            }
            write "Access token="
            do jsonObject.%ToJSON()
            write "<br>",!
        } else {
            write "JWT Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!    
        }
        write "<br>",!

        // Call the introspection endpoint and display result -- see RFC 7662.
        set sc=##class(%SYS.OAuth2.AccessToken).GetIntrospection(..#OAUTH2APPNAME,accessToken,.jsonObject)
        if $$$ISOK(sc) {
            write "Introspection="
            do jsonObject.%ToJSON()
            write "<br>",!
        } else {
            write "Introspection Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!    
        }
        write "<br>",!
        
        if idtoken'="" {
            // Validate and display the IDToken -- see OpenID Connect Core specification.
            set valid=##class(%SYS.OAuth2.Validation).ValidateIDToken(
                ..#OAUTH2APPNAME,
                idtoken,
                accessToken,,,
                .jsonObject,
                .securityParameters,
                .sc)
            if $$$ISOK(sc) {
                if valid {
                    write "Valid IDToken"_"<br>",!    
                } else {
                    write "Invalid IDToken"_"<br>",!    
                }
                write "IDToken="
                do jsonObject.%ToJSON()
                write "<br>",!
            } else {
                write "IDToken Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!    
            }
        } else {
            write "No IDToken returned"_"<br>",!
        }
        write "<br>",!
    
        // not needed for the application logic, but provides information about user that we can pass to Delegated authentication
    
        // Call the userinfo endpoint and display the result -- see OpenID Connect Core specification.
        set sc=##class(%SYS.OAuth2.AccessToken).GetUserinfo(
            ..#OAUTH2APPNAME,
            accessToken,,
            .jsonObject)
        if $$$ISOK(sc) {
            write "Userinfo="
            do jsonObject.%ToJSON()
            write "<br>",!
        } else {
            write "Userinfo Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!    
        }
        write "<p>",!

        /***************************************************
        *                                                  *
        *   Call the resource server and display result.   *
        *                                                  *
        ***************************************************/
                
        // option 1 - resource server - by definition - trusts data coming from authorization server,
        //     so it serves data to whoever is asking
        //  as long as access token passed to resource server is valid
        
        // option 2 - alternatively, you can use delegated authentication (OpenID Connect) 
        //  and call into another CSP application (with delegated authentication protection)
        //  - that's what we do here in this demo
        
        
        write "<4>Call resource server (delegated auth)","</h4>",!
        set httpRequest=##class(%Net.HttpRequest).%New()
        // AddAccessToken adds the current access token to the request.
        set sc=##class(%SYS.OAuth2.AccessToken).AddAccessToken(
            httpRequest,,
            ..#SSLCONFIG,
            ..#OAUTH2APPNAME)
        if $$$ISOK(sc) {
            set sc=httpRequest.Get(..#OAUTH2ROOT_"/csp/portfolio/oauth2test.demoResource.cls")
        }
        if $$$ISOK(sc) {
            set body=httpRequest.HttpResponse.Data
            if $isobject(body) {
                do body.Rewind()
                set body=body.Read()
            }
            write body,"<br>",!
        }
        if $$$ISERR(sc) {
            write "Resource Server Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!    
        }
        write "<br>",!
    
        write "<h4>Call resource server - no auth, just token validity check","</h4>",!
        set httpRequest=##class(%Net.HttpRequest).%New()
        // AddAccessToken adds the current access token to the request.
        set sc=##class(%SYS.OAuth2.AccessToken).AddAccessToken(
            httpRequest,,
            ..#SSLCONFIG,
            ..#OAUTH2APPNAME)
        if $$$ISOK(sc) {
            set sc=httpRequest.Get(..#OAUTH2ROOT_"/csp/portfolio2/oauth2test.demoResource.cls")
        }
        if $$$ISOK(sc) {
            set body=httpRequest.HttpResponse.Data
            if $isobject(body) {
                do body.Rewind()
                set body=body.Read()
            }
            write body,"<br>",!
        }
        if $$$ISERR(sc) {
            write "Resource Server Error="_..EscapeHTML($system.Status.GetErrorText(sc))_"<br>",!    
        }
        write "<br>",!
    } else {
        write "Not Authorized!<p>",!
        write "<a href='Web.OAUTH2.Cache1N.cls'>Authorize me</a>"
    }    
    &html<</body></html>>
    Quit $$$OK
}

}

As seguintes capturas de tela retratam o processamento:

Página de login do servidor de autenticação do OpenID Connect/autorização na instância AUTHSERVER

Página de consentimento do usuário em AUTHSERVER

E, por fim, a página resultante

Como você pode ver, lendo o código, realmente quase não há diferença em relação ao código do cliente que mostramos na parte 1. Há algo novo na página 2. São algumas informações de depuração e a verificação da validade do JWT. Depois de validar o JWT retornado, podemos realizar a introspeção dos dados do AUTHSERVER sobre a identidade do usuário. Simplesmente apresentamos essas informações nos resultados da página, mas podemos fazer mais com elas. Como no caso de uso de um médico externo mencionado acima, podemos usar as informações de identidade e transmiti-las ao servidor de recursos para fins de autenticação, se necessário. Ou apenas passar essa informação como um parâmetro para a chamada da API ao servidor de recursos.

Os próximos parágrafos descrevem como usamos as informações de identidade do usuário em mais detalhes.

Aplicativo de recurso

O servidor de recursos pode ser o mesmo servidor que o servidor de autorização/autenticação e, muitas vezes, esse é o caso. No entanto, na nossa demonstração, criamos para os dois servidores instâncias do InterSystems IRIS separadas.

Então, temos dois casos possíveis, como trabalhar com o contexto de segurança no servidor de recursos.

Alternativa 1 — sem autenticação

Esse é o caso simples. O servidor de autorização/autenticação são apenas a mesma instância do Caché. Nesse caso, podemos simplesmente transmitir o token de acesso a um aplicativo CSP, que é criado especialmente para um único propósito — enviar dados a aplicativos clientes que usam o OAUTH para autorizar a solicitação de dados.

A configuração do aplicativo CSP de recurso (chamamos de /csp/portfolio2) pode parecer com a captura de tela abaixo.

Colocamos o mínimo de segurança na definição do aplicativo, permitindo que apenas a página CSP específica seja executada.

Como opção, o servidor de recursos pode fornecer uma API REST em vez de páginas da Web clássicas. Em situações reais, o refinamento do contexto de segurança depende do usuário.

Um exemplo de código-fonte:

Class oauth2test.demoResource Extends %CSP.Page
{

ClassMethod OnPage() As %Status
{
    set accessToken=##class(%SYS.OAuth2.AccessToken).GetAccessTokenFromRequest(.sc)
    if $$$ISOK(sc) {
        set sc=##class(%SYS.OAuth2.AccessToken).GetIntrospection("RESSERVER resource",accessToken,.jsonObject)
        if $$$ISOK(sc) {        
            // optionally validate against fields in jsonObject

            w "<p><h3>Hello from Cach&eacute; server: <i>/csp/portfolio2</i> application!</h3>"
            w "<p>running code as <b>$username = "_$username_"</b> with following <b>$roles = "_$roles_"</b> at node <b>"_$p($zu(86),"*",2)_"</b>."
        }
    } else {
        w "<h3>NOT AUTHORIZED!</h3>"    
        w "<pre>"
        w
        i $d(%objlasterror) d $system.OBJ.DisplayError()
        w "</pre>"
    }
    Quit $$$OK
}

}

Alternativa 2 — autenticação delegada

Esse é outro caso extrema, queremos usar a identidade do usuário no servidor de recursos o máximo possível, como se o usuário estivesse trabalhando com o mesmo contexto de segurança que os usuários internos do servidor de recursos.

Uma das opções possíveis é usar a autenticação delegada.

Para essa definição funcionar, precisamos concluir mais algumas etapas para configurar o servidor de recursos.

·        Ativar a autenticação delegada

·        Fornecer a rotina ZAUTHENTICATE

·        Configurar o Web application (no nosso caso, chamamos em /csp/portfolio)

A implementação da rotina ZAUTHENTICATE é bastante simples e direta, já que confiamos no AUTHSERVER que forneceu a identidade do usuário e o escopo (perfil de segurança) dele, então basta aceitar o nome de usuário e transmitir com o escopo ao banco de dados de usuários do servidor de recursos (com a tradução necessária entre o escopo do OAUTH e as funções do InterSystems IRIS). É isso. O resto é realizado perfeitamente pelo InterSystems IRIS.

Veja o exemplo de uma rotina ZAUTHENTICATE

#include %occErrors
#include %occInclude

ZAUTHENTICATE(ServiceName, Namespace, Username, Password, Credentials, Properties) PUBLIC
{
    set tRes=$SYSTEM.Status.OK()
    try {        
        set Properties("FullName")="OAuth account "_Username
        //set Properties("Roles")=Credentials("scope")
        set Properties("Username")=Username
        //set Properties("Password")=Password
        // temporary hack as currently we can't pass Credentials array from GetCredentials() method
        set Properties("Password")="xxx"    // we don't really care about oauth2 account password
        set Properties("Roles")=Password
    } catch (ex) {
        set tRes=$SYSTEM.Status.Error($$$AccessDenied)
    }
    quit tRes
}

GetCredentials(ServiceName,Namespace,Username,Password,Credentials) Public 
{
    s ts=$zts
    set tRes=$SYSTEM.Status.Error($$$AccessDenied)        

     try {
         If ServiceName="%Service_CSP" {
            set accessToken=##class(%SYS.OAuth2.AccessToken).GetAccessTokenFromRequest(.sc)
            if $$$ISOK(sc) {
                set sc=##class(%SYS.OAuth2.AccessToken).GetIntrospection("RESSERVER resource",accessToken,.jsonObject)
                if $$$ISOK(sc) {
                    // todo: watch out for potential collision between standard account and delegated (openid) one!
                    set Username=jsonObject.username
                    set Credentials("scope")=$p(jsonObject.scope,"openid profile ",2)
                    set Credentials("namespace")=Namespace
                    // temporary hack
                    //set Password="xxx"
                    set Password=$tr(Credentials("scope")," ",",")
                    set tRes=$SYSTEM.Status.OK()
                } else {
                    set tRes=$SYSTEM.Status.Error($$$GetCredentialsFailed) 
                }
            }    
        } else {
            set tRes=$SYSTEM.Status.Error($$$AccessDenied)        
        }
     } catch (ex) {
         set tRes=$SYSTEM.Status.Error($$$GetCredentialsFailed)
    }
    Quit tRes
}

A própria página CSP pode ser bastante simples:

Class oauth2test.demoResource Extends %CSP.Page
{

ClassMethod OnPage() As %Status
{
    // access token authentication is performed by means of Delegated authentication!
    // no need to do it, again, here

    // This is a dummy resource server which just gets the access token from the request and
    // uses the introspection endpoint to ensure that the access token is valid.
    // Normally the response would not be security related, but would contain some interesting
    // data based on the request parameters.
    w "<p><h3>Hello from Cach&eacute; server: <i>/csp/portfolio</i> application!</h3>"
    w "<p>running code as <b>$username = "_$username_"</b> with following <b>$roles = "_$roles_"</b> at node <b>"_$p($zu(86),"*",2)_"</b>."
    Quit $$$OK
}

}

Por fim, a configuração do Web application para /csp/portfolio

Se você estiver muito paranoico, pode definir Classes permitidas como fizemos na primeira variante. Ou, novamente, use a API REST. No entanto, isso está muito além do escopo do nosso tema.

Na próxima vez, vamos explicar classes individuais, apresentadas pelo framework OAUTH do InterSystems IRIS. Descreveremos as APIs e quando/onde chamá-las.

 

[1] Quando mencionamos o OAUTH, queremos dizer o OAuth 2.0 conforme especificado no RFC 6749 - https://tools.ietf.org/html/rfc6749. Usamos a abreviação OAUTH apenas por simplicidade.

[2] O OpenID Connect é mantido pela OpenID Foundation – http://openid.net/connect

0
0 218
Artigo Daniel Kutac · Nov. 19, 2022 14m read

Este artigo e os próximos dois artigos da série são um guia do usuário para desenvolvedores ou administradores de sistema que precisam usar o framework OAuth 2.0 (chamado de OAUTH para simplificar) em suas aplicações baseadas no produto InterSystems.

Criado por Daniel Kutac, Engenheiro de vendas sênior, InterSystems

Histórico de correções e alterações após a publicação

  • 3 de agosto de 2016 – Correção da captura de tela da configuração do Google Client; atualização da captura de tela das APIs do Google para refletir a nova versão das páginas.
  • 28 de agosto de 2016 – Alterações do código JSON devido às mudanças no suporte ao JSON do Caché 2016.2.
  • 3 de maio de 2017 – Atualizações do texto e imagens para refletir a nova interface gráfica e os novos recursos lançados no Caché 2017.1. 
  • 19 de fevereiro de 2018 – Alteração de Caché para InterSystems IRIS para refletir os desenvolvimentos mais recentes. Porém, é importante salientar que, apesar da alteração do nome do produto, o artigo aborda todos os produtos da InterSystems: InterSystems IRIS Data Platform, Ensemble e Caché.
  • 17 de agosto de 2020 – Tudo muda, especialmente o software. Consulte o URL do OAuth2 do Google atualizado na resposta do Micholai Mitchko.

Parte 1. Cliente

Introdução

Esta é a primeira parte de uma série de três artigos sobre a implementação do Open Authorization Framework na InterSystems.

Nesta primeira parte, apresentamos uma breve introdução do tópico e mostramos um cenário simples em que a aplicação InterSystems IRIS atua como cliente de um servidor de autorização, solicitando alguns recursos protegidos.

A segunda parte descreverá um cenário mais complexo, em que a InterSystems IRIS atua como servidor de autorização e também como servidor de autenticação via OpenID Connect.

A última parte da série descreverá partes individuais das classes do framework OAUTH conforme implementadas pela InterSystems IRIS.

Sobre o Open Authorization Framework[1]

Muitos de vocês já ouviram falar do Open Authorization Framework e para que ele pode ser usado. Vamos resumir para quem ainda não tiver ouvido falar dele.

O Open Authorization Framework, OAUTH, atualmente na versão 2.0, é um protocolo que permite principalmente que aplicações web troquem informações de forma segura estabelecendo uma confiança indireta entre um cliente (aplicação que solicita dados) e o proprietário dos recursos (aplicação que detém os dados solicitados). A confiança é fornecida por uma entidade que tanto o cliente quanto o servidor de recursos reconhecem e na qual confiam. Essa entidade é chamada de servidor de autorização.

Veja um caso de uso simples:

Vamos supor que Jenny (na terminologia do OAUTH, é o proprietário dos recursos) esteja trabalhando em um projeto na empresa JennyCorp. Ela cria um plano de projeto para um possível negócio maior e convida seu parceiro comercial John (usuário cliente) da empresa JohnInc para revisar o documento. Mas ela não está contente de dar ao John acesso à VPN de sua empresa, então ela coloca o documento no Google Drive (o servidor de recursos) ou outra ferramenta de armazenamento em nuvem similar. Ao fazer isso, ela estabeleceu uma confiança entre ela e o Google (o servidor de autorização). Ela compartilha o documento com John (John já usa o serviço Google Drive, e Jenny sabe qual é o e-mail dele).

Quando John deseja ler o documento, ele faz a autenticação em sua conta do Google e, em seu dispositivo móvel (tablet, notebook, etc.), abre um editor de documentos (o servidor cliente) e carrega o arquivo do projeto da Jenny.

Embora pareça bem simples, há muita comunicação entre as duas pessoas e o Google. Todas as comunicações seguem a especificação do OAuth 2.0, então o cliente de John (o leitor de documentos) precisa primeiro fazer a autenticação no Google (essa etapa não é coberta pelo OAUTH) e, após John consentir autorização no formulário fornecido pelo Google, o Google autoriza que o leitor de documentos acesse o documento emitindo um token de acesso. O leitor de documentos usa o token de acesso para emitir uma solicitação ao serviço Google Drive para obter o arquivo de Jenny.

O diagrama abaixo mostra a comunicação entre todas as partes

Nota: embora todas as comunicações do OAUTH 2.0 sejam feitas por solicitações HTTP, os servidores não precisam ser aplicações web.

Vamos ilustrar esse cenário simples com a InterSystems IRIS

Demonstração simples do Google Drive

Nesta demonstração, vamos criar uma aplicação de pequeno porte baseada em Cloud Solution Provider (CSP) que solicita recursos (lista de arquivos) armazenados no serviço Google Drive com nossa própria conta (e também uma lista de nossos calendários, como bônus).

Pré-requisitos

Antes de começarmos a programar a aplicação, precisamos preparar o ambiente. Precisaremos de um servidor web com SSL ativado e um perfil do Google.

Configuração do servidor web

Conforme informado acima, precisamos estabelecer comunicação com o servidor de autorização com SSL, pois isso é exigido pelo OAuth 2.0 por padrão. Queremos manter nossos dados seguros, certo?

Está fora do escopo deste artigo descrever como configurar um servidor web com suporte ao SSL, então consulte os manuais de usuário do servidor web de sua preferência. Usaremos o servidor IIS da Microsoft neste exemplo específico.

Configuração do Google

Para nos registrarmos no Google, precisamos usar o Google API Manager: https://console.developers.google.com/apis/library?project=globalsummit2016demo

Para o propósito da demonstração, criamos uma conta GlobalSummit2016Demo. É preciso confirmar se a API do Drive está ativada

Agora, está na hora de definir as credenciais

Observe o seguinte:

_Authorized JavaScript (JavaScript autorizado) –  permitimos somente scripts originados localmente em relação à página chamadora

_Authorized redirect URIs (URIs de redirecionamento autorizados) –  teoricamente, podemos redirecionar nossa aplicação cliente para qualquer site, mas, ao usar a implementação do OAUTH da InterSystems IRIS, precisamos redirecioná-la para https://localhost/csp/sys/oauth2/OAuth2.Response.cls. É possível definir vários URIs de redirecionamento autorizados, conforme mostrado na captura de tela, mas, para esta demonstração, só precisamos da segunda entrada.

Por último, precisamos configurar a InterSystems IRIS como cliente do servidor de autorização do Google

Configuração do Caché

A configuração do cliente OAUTH2 da InterSystems IRIS é um processo de duas etapas. Primeiro, precisamos criar uma configuração de servidor.

No SMP, acesse System Administration (Administração do Sistema) > Security (Segurança) > OAuth 2.0 > Client Configurations (Configurações do cliente).

Clique no botão Create Server Configuration (Criar configuração de servidor), preencha o formulário e salve.

Todas as informações inseridas no formulário estão disponíveis no site do console de desenvolvedores do Google. A InterSystems IRIS tem suporte à descoberta automática do Open ID. Entretanto, não estamos usando esse recurso. Inserimos todas as informações manualmente 

Agora, clique no link Client Configurations (Configurações do cliente) ao lado do Issuer Endpoint (Endpoint emissor) recém-criado
e clique no botão Create Client Configuration (Criar configuração de cliente).

Deixe as abas Client Information (Informações do cliente) e JWT Settings (Configurações do JWT) em branco (com os valores padrão) e preencha a aba Client credentials (Credenciais do cliente).

Nota: estamos criando um Confidential Client (Cliente confidencial – é mais seguro que o público e significa que o segredo do cliente nunca deixa a aplicação do servidor cliente – nunca é transmitido ao navegador)

Além disso, confirme se Use SSL/TLS (Usar SSL/TLS) está marcado e forneça o nome do host (localhost, já que estamos redirecionando localmente para a aplicação cliente) e, posteriormente, a porta e o prefixo (útil quando há várias instâncias da InterSystems IRIS na mesma máquina). Com base nas informações preenchidas, o URL de redirecionamento do cliente é computado e exibido na linha acima.

Na captura de tela acima, fornecemos uma configuração SSL chamada GOOGLE. O nome é usado somente para ajudar a determinar qual configuração SSL dentre várias é usada por esse canal de comunicação específico. O Caché está usando configurações SSL/TLS para armazenar todas as informações necessárias para receber/enviar tráfego seguro ao servidor (neste caso, os URIs OAuth 2.0 do Google).

Consulte mais detalhes na documentação.

Preencha os valores Client ID (ID do cliente) e Client Secret (Segredo do cliente) obtidos pelo formulário de definição das credenciais do Google (ao fazer a configuração manual).

Agora, concluímos todas as etapas de configuração e podemos prosseguir para a programação de uma aplicação CSP.

Aplicação cliente

A aplicação cliente é uma aplicação CSP web simples. Ela é composta por um código fonte no servidor, definido e executado pelo servidor web, e uma interface do usuário, exibida ao usuário por um navegador. O exemplo de código fornecido espera que a aplicação cliente seja executada no namespace GOOGLE. Modifique o caminho /csp/google/  para o seu namespace.

Servidor cliente

O servidor cliente é uma aplicação simples de duas páginas. Dentro da aplicação, nós vamos:

·        Montar o URL de redirecionamento para o servidor de autorização do Google

·        Fazer solicitações à API do Google Drive e à API do Google Agenda e exibir o resultado

Página 1

Esta é uma página da aplicação, na qual decidimos fazer uma chamada aos recursos do Google.

Veja abaixo um código minimalista, mas completamente funcional, que representa a página.

Class Web.OAUTH2.Google1N Extends %CSP.Page{Parameter OAUTH2CLIENTREDIRECTURI = "https://localhost/csp/google/Web.OAUTH2.Google2N.cls";Parameter OAUTH2APPNAME = "Google";ClassMethod OnPage() As %Status{&html<<html><head></head><body style="text-align: center;"><!-- insert the page content here --><h1>Google OAuth2 API</h1><p>This page demo shows how to call Google API functions using OAuth2 authorization.<p>We are going to retrieve information about user and his/her Google Drive files as well as calendar entries.>// we need to supply openid scope to authenticate to Googleset scope="openid https://www.googleapis.com/auth/userinfo.email "_"https://www.googleapis.com/auth/userinfo.profile "_"https://www.googleapis.com/auth/drive.metadata.readonly "_"https://www.googleapis.com/auth/calendar.readonly"set properties("approval_prompt")="force"set properties("include_granted_scopes")="true"set url=##class(%SYS.OAuth2.Authorization).GetAuthorizationCodeEndpoint(..#OAUTH2APPNAME,scope,..#OAUTH2CLIENTREDIRECTURI,.properties,.isAuthorized,.sc) w !,"<p><a href='"_url_"'><img border='0' alt='Google Sign In' src='images/google-signin-button.png' ></a>" &html<</body></html>>Quit $$$OK}ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]{#dim %response as %CSP.Responseset scope="openid https://www.googleapis.com/auth/userinfo.email "_"https://www.googleapis.com/auth/userinfo.profile "_"https://www.googleapis.com/auth/drive.metadata.readonly "_"https://www.googleapis.com/auth/calendar.readonly"if ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error) {set %response.ServerSideRedirect="Web.OAUTH2.Google2N.cls"}quit 1}}

Veja abaixo uma breve explicação do código:

1.      Método OnPreHTTP: primeiro, verificamos se, por acaso, já obtivemos um token de acesso válido como resultado de uma autorização do Google. Isso pode acontecer, por exemplo, quando simplesmente atualizamos a página. Caso não tenhamos, precisamos fazer a autorização. Se já tivermos o token, apenas redirecionamos para a página que mostra os resultados

2.       Método OnPage: só chegamos a este método se não tivermos um token de acesso válido disponível. Então, precisamos iniciar a comunicação: fazer a autenticação e autorização no Google para que ele nos conceda o token de acesso.

3.       Definimos uma string de escopo e uma array de propriedades que modificam o comportamento da janela de autenticação do Google (precisamos fazer a autenticação no Google antes que ele possa nos autorizar com base em nossa identidade).

4.       Por último, recebemos o URL de uma página de login do Google e a mostramos ao usuário, seguida pela página de consentimento.

Mais uma nota:

Especificamos a verdadeira página de redirecionamento em https://www.localhost/csp/google/Web.OAUTH2.Google2N.cls no parâmetro OAUTH2CLIENTREDIRECTURI. Entretanto, usamos a página de sistema do framework OAUTH da InterSystems IRIS na definição das credenciais do Google! O redirecionamento é tratado internamente por nossa classe manipuladora OAUTH.

Página 2

Esta página mostra os resultados da autorização do Google e, em caso de êxito, fazemos chamadas à API do Google para obter os dados. Novamente, o código abaixo é minimalista, mas completamente funcional. Deixamos a exibição dos dados recebidos de uma maneira mais estruturada para a imaginação dos leitores.

Include %occIncludeClass Web.OAUTH2.Google2N Extends %CSP.Page{Parameter OAUTH2APPNAME = "Google";Parameter OAUTH2ROOT = "https://www.googleapis.com";ClassMethod OnPage() As %Status{&html<<html><head></head><body>>// Check if we have an access tokenset scope="openid https://www.googleapis.com/auth/userinfo.email "_"https://www.googleapis.com/auth/userinfo.profile "_"https://www.googleapis.com/auth/drive.metadata.readonly "_"https://www.googleapis.com/auth/calendar.readonly"set isAuthorized=##class(%SYS.OAuth2.AccessToken).IsAuthorized(..#OAUTH2APPNAME,,scope,.accessToken,.idtoken,.responseProperties,.error)if isAuthorized { // Google has no introspection endpoint - nothing to call - the introspection endpoint and display result -- see RFC 7662.w "<h3>Data from <span style='color:red;'>GetUserInfo API</span></h3>"// userinfo has special API, but could be also retrieved by just calling Get() method with appropriate urltry {set tHttpRequest=##class(%Net.HttpRequest).%New()$$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME))$$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).GetUserinfo(..#OAUTH2APPNAME,accessToken,,.jsonObject))w jsonObject.%ToJSON()} catch (e) {w "<h3><span style='color: red;'>ERROR: ",$zcvt(e.DisplayString(),"O","HTML")_"</span></h3>"}/*********************************************Retrieve info from other APIs*********************************************/w "<hr>"do ..RetrieveAPIInfo("/drive/v3/files")do ..RetrieveAPIInfo("/calendar/v3/users/me/calendarList")} else {w "<h1>Not authorized!</h1>"}&html<</body></html>>Quit $$$OK}ClassMethod RetrieveAPIInfo(api As %String){w "<h3>Data from <span style='color:red;'>"_api_"</span></h3><p>"try {set tHttpRequest=##class(%Net.HttpRequest).%New()$$$THROWONERROR(sc,##class(%SYS.OAuth2.AccessToken).AddAccessToken(tHttpRequest,"query","GOOGLE",..#OAUTH2APPNAME))$$$THROWONERROR(sc,tHttpRequest.Get(..#OAUTH2ROOT_api))set tHttpResponse=tHttpRequest.HttpResponses tJSONString=tHttpResponse.Data.Read()if $e(tJSONString)'="{" {// not a JSONd tHttpResponse.OutputToDevice()} else {w tJSONStringw "<hr/>"/*// new JSON API&html<<table border=1 style='border-collapse: collapse'>>s tJSONObject={}.%FromJSON(tJSONString)set iterator=tJSONObject.%GetIterator()while iterator.%GetNext(.key,.value) {if $isobject(value) {set iterator1=value.%GetIterator()w "<tr><td>",key,"</td><td><table border=1 style='border-collapse: collapse'>"while iterator1.%GetNext(.key1,.value1) {if $isobject(value1) {set iterator2=value1.%GetIterator()w "<tr><td>",key1,"</td><td><table border=0 style='border-collapse: collapse'>"while iterator2.%GetNext(.key2,.value2) {write !, "<tr><td>",key2, "</td><td>",value2,"</td></tr>"}// this way we can go on and on into the embedded objects/arraysw "</table></td></tr>"} else {write !, "<tr><td>",key1, "</td><td>",value1,"</td></tr>"}}w "</table></td></tr>"} else {write !, "<tr><td>",key, "</td><td>",value,"</td></tr>"}}&html<</table><hr/>>*/}} catch (e) {w "<h3><span style='color: red;'>ERROR: ",$zcvt(e.DisplayString(),"O","HTML")_"</span></h3>"}}}

 

Vamos dar uma olhada no código:

1.       Antes de tudo, precisamos verificar se temos um token de acesso válido (para verificarmos se fomos autorizados)

2.       Caso afirmativo, podemos enviar solicitações às APIs oferecidas pelo Google usando o token de acesso emitido

3.       Para isso, usamos a classe padrão %Net.HttpRequest, mas adicionamos o token de acesso ao método GET ou POST de acordo com a especificação da API chamada

4.       Como é possível ver, o framework OAUTH implementou o método GetUserInfo() para sua comodidade, mas você pode obter as informações do usuário diretamente usando a especificação da API do Google da mesma maneira como feito no método auxiliar RetrieveAPIInfo()

5.       Como é comum no mundo do OAUTH trocar dados no formato JSON, apenas lemos os dados recebidos e os colocamos no navegador. Cabe ao desenvolvedor da aplicação analisar e formatar os dados recebidos para apresentá-los ao usuário de alguma forma que faça sentido. Mas isso está além do escopo desta demonstração. (Embora tenhamos colocado um código comentado que mostra como a análise pode ser feita.)

 Veja abaixo uma captura de tela da saída exibindo os dados JSON não tratados.

Prossiga para a parte 2, que descreve como a InterSystems IRIS atua como servidor de autorização e provedor do OpenID Connect.

0
0 240
InterSystems Oficial Angelo Bruno Braga · Nov. 10, 2022

Tenho o prazer de anunciar um marco no ciclo de vida do gerenciador de pacotes ObjectScript, ZPM. O gerenciador de pacotes oferece aos desenvolvedores a capacidade de empacotar o código ObjectScript e as configurações de implantação e as informações de versão de maneira conveniente. Ao longo dos últimos anos, evoluiu muito para uma parte integrante de muitos fluxos de trabalho de desenvolvimento.

0
0 193
Artigo Danusa Calixto · Nov. 9, 2022 10m read

Nesta série de artigos, quero apresentar e discutir várias abordagens possíveis para o desenvolvimento de software com tecnologias da InterSystems e do GitLab. Vou cobrir tópicos como:

  • Git básico
  • Fluxo Git (processo de desenvolvimento)
  • Instalação do GitLab
  • Fluxo de trabalho do GitLab
  • Entrega contínua
  • Instalação e configuração do GitLab
  • CI/CD do GitLab

No artigo anterior, abordamos os fundamentos do Git, por que um entendimento de alto nível dos conceitos do Git é importante para o desenvolvimento de software moderno e como o Git pode ser usado para desenvolver software. Ainda assim, nosso foco foi na parte da implementação do desenvolvimento de software, mas esta parte apresenta:

  • Fluxo de trabalho do GitLab — um processo completo do ciclo de vida do software, desde a ideia até o feedback do usuário
  • Entrega Contínua — uma abordagem de engenharia de software em que as equipes produzem software em ciclos curtos, garantindo que o software possa ser lançado de forma confiável a qualquer momento. Seu objetivo é construir, testar e lançar software com mais rapidez e frequência.

Fluxo de trabalho do GitLab

O fluxo de trabalho do GitLab é uma sequência lógica de possíveis ações a serem tomadas durante todo o ciclo de vida do processo de desenvolvimento de software.

O fluxo de trabalho do GitLab leva em consideração o fluxo do GitLab, que discutimos em um artigo anterior. Veja como funciona:

  1. Ideia: todas as novas propostas começam com uma ideia.
  2. Problema: a maneira mais eficaz de discutir uma ideia é criar um problema para ela. Sua equipe e seus colaboradores podem ajudar você a aprimorar e melhorar a ideia no rastreador de problemas.
  3. Plano: quando a discussão chega a um acordo, é hora de programar. Porém, primeiro, precisamos priorizar e organizar nosso fluxo de trabalho ao atribuir problemas a marcos e quadro de problemas.
  4. Código: agora estamos prontos para escrever nosso código, já que está tudo organizado.
  5. Commit: depois de satisfeitos com o rascunho, podemos enviar nosso código para um feature-branch com controle de versão. O fluxo do GitLab foi explicado em detalhes no artigo anterior.
  6. Teste: executamos nossos scripts usando o CI GitLab, para construir e testar nosso aplicativo.
  7. Revisão: assim que nosso script funcionar e nossos testes e compilações forem bem-sucedidos, estamos prontos para que nosso código seja revisado e aprovado.
  8. Staging: agora é hora de implantar nosso código em um ambiente de staging para verificar se tudo funciona como esperado ou se ainda precisamos de ajustes.
  9. Produção: quando tudo estiver funcionando como deve, é hora de implantar no nosso ambiente de produção!
  10. Feedback: agora é hora de olhar para trás e verificar qual etapa do nosso trabalho precisa ser melhorada.

Novamente, o processo em si não é novo (ou exclusivo do GitLab) e pode ser alcançado com outras ferramentas da sua escolha.

Vamos discutir várias dessas etapas e o que elas implicam. Também há documentação disponível.

Problema e plano

As etapas iniciais do fluxo de trabalho do GitLab são centradas em um problema: um recurso, bug ou outro tipo de trabalho semanticamente separado.

O problema tem várias finalidades, como:

  • Gerenciamento: um problema tem data de vencimento, pessoa designada, tempo gasto e estimativas, etc. para ajudar a monitorar a resolução do problema.
  • Administrativo: um problema faz parte de um marco, quadro kanban, que nos permite rastrear nosso software à medida que ele avança de versão para versão.
  • Desenvolvimento: um problema tem uma discussão e commits associados a ele.

A etapa de planejamento nos permite agrupar os problemas por prioridade, marco, quadro kanban e ter uma visão geral disso.

O desenvolvimento foi discutido na parte anterior, basta seguir qualquer fluxo git que quiser. Depois que desenvolvemos nosso novo recurso e o mesclamos no master: o que vem depois?

Entrega contínua

A entrega contínua é uma abordagem de engenharia de software em que as equipes produzem software em ciclos curtos, garantindo que o software possa ser lançado de forma confiável a qualquer momento. Seu objetivo é construir, testar e lançar software com mais rapidez e frequência. A abordagem ajuda a reduzir o custo, o tempo e o risco da entrega de alterações, permitindo mais atualizações incrementais para aplicativos em produção. Um processo de implantação simples e repetível é importante para a entrega contínua.

Entrega contínua no GitLab

No GitLab, a configuração da entrega contínua é definida por repositório como um arquivo de configuração YAML.

  • A configuração de entrega contínua é uma série de estágios consecutivos.
  • Cada estágio tem um ou vários scripts que são executados em paralelo.

O script define uma ação e quais condições devem ser atendidas para executá-la:

  • O que fazer (executar o comando do SO, executar um contêiner)?
  • Quando executar o script:
    • Quais são os gatilhos (commit de um branch específico)?
    • Nós o executamos se os estágios anteriores falharam?
  • Executar manualmente ou automaticamente?
  • Em que ambiente executar o script?
  • Quais artefatos salvar após a execução dos scripts (eles são carregados do ambiente para o GitLab para facilitar o acesso)?

Ambiente - é um servidor ou contêiner configurado no qual você pode executar seus scripts.

Runners executam scripts em ambientes específicos. Eles são conectados ao GitLab e executam scripts conforme necessário.

O runner pode ser implantado em um servidor, contêiner ou até mesmo na sua máquina local.

Como acontece a entrega contínua?

  1. O novo commit é enviado para o repositório.
  2. O GitLab verifica a configuração de entrega contínua.
  3. A configuração de entrega contínua contém todos os scripts possíveis para todos os casos, para que sejam filtrados para um conjunto de scripts que devem ser executados para esse commit específico (por exemplo, um commit para o branch master aciona apenas ações relacionadas a um branch master). Esse conjunto é chamado de pipeline.
  4. O pipeline é executado em um ambiente de destino e os resultados da execução são salvos e exibidos no GitLab.

Por exemplo, aqui está um pipeline executado após um commit em um branch master:

Ele consiste em quatro etapas, executadas consecutivamente

  1. O estágio de carregamento carrega o código em um servidor
  2. O estágio de teste executa testes de unidade
  3. O estágio de pacote consiste em dois scripts executados em paralelo:
    • Compilação cliente
    • Código de exportação do servidor (principalmente para fins informativos)
  4. O estágio de implantação move o cliente criado para o diretório do servidor web.

Como podemos ver, todos os scripts foram executados com sucesso. Se um dos scripts falhar, por padrão, os scripts posteriores não são executados (mas podemos alterar esse comportamento):

Se abrirmos o script, podemos ver o log e determinar por que ele falhou:

Running with gitlab-runner 10.4.0 (857480b6)
 on test runner (ab34a8c5)
Using Shell executor...
Running on gitlab-test...
&lt;span class="term-fg-l-green term-bold">Fetching changes...&lt;/span>
Removing diff.xml
Removing full.xml
Removing index.html
Removing tests.html
HEAD is now at a5bf3e8 Merge branch '4-versiya-1-0' into 'master'
From http://gitlab.eduard.win/test/testProject
 * [new branch] 5-versiya-1-1 -> origin/5-versiya-1-1
 a5bf3e8..442a4db master -> origin/master
 d28295a..42a10aa preprod -> origin/preprod
 3ac4b21..7edf7f4 prod -> origin/prod
&lt;span class="term-fg-l-green term-bold">Checking out 442a4db1 as master...&lt;/span>
&lt;span class="term-fg-l-green term-bold">Skipping Git submodules setup&lt;/span>
&lt;span class="term-fg-l-green term-bold">$ csession ensemble "##class(isc.git.GitLab).loadDiff()"&lt;/span>

[2018-03-06 13:58:19.188] Importing dir /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/

[2018-03-06 13:58:19.188] Loading diff between a5bf3e8596d842c5cc3da7819409ed81e62c31e3 and 442a4db170aa58f2129e5889a4bb79261aa0cad0

[2018-03-06 13:58:19.192] Variable modified
var=$lb("MyApp/Info.cls")

Load started on 03/06/2018 13:58:19
Loading file /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/MyApp/Info.cls as udl
Load finished successfully.

[2018-03-06 13:58:19.241] Variable items
var="MyApp.Info.cls"
var("MyApp.Info.cls")=""

Compilation started on 03/06/2018 13:58:19 with qualifiers 'cuk /checkuptodate=expandedonly'
Compiling class MyApp.Info
Compiling routine MyApp.Info.1
ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1]
 TEXT:  quit, "1.0" }
Detected 1 errors during compilation in 0.010s.

[2018-03-06 13:58:19.252] ERROR #5475: Error compiling routine: MyApp.Info.1. Errors: ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1]
 > ERROR #5030: An error occurred while compiling class 'MyApp.Info'
&lt;span class="term-fg-l-red term-bold">ERROR: Job failed: exit status 1
&lt;/span>

O erro de compilação causou a falha do nosso script.

Conclusão

  • O GitLab é compatível com todos os principais estágios de desenvolvimento de software.
  • A entrega contínua pode ajudar você a automatizar tarefas de construção, teste e implantação do seu software.

O que vem a seguir?

No próximo artigo, vamos:

  • Instalar o GitLab.
  • Conectá-lo a diversos ambientes com os produtos InterSystems instalados.
  • Escrever uma configuração de entrega contínua.

Vamos discutir como a entrega contínua deve funcionar.

Em primeiro lugar, precisamos de vários ambientes e branches que correspondam a eles. O código entra nesse branch e é entregue ao ambiente de destino:

AmbienteBranchEntregaQuem pode fazer enviosQuem pode mesclar
TestemasterAutomáticoDesenvolvedores  ProprietáriosDesenvolvedores  Proprietários
PreprodpreprodAutomáticoNinguémProprietários
ProdprodSemiautomático (pressionar botão para entregar)NinguémProprietários

E, como exemplo, desenvolveremos um novo recurso usando o fluxo do GitLab e o entregaremos usando a CD do GitLab.

  1. O recurso é desenvolvido em um branch de recursos.
  2. O branch de recurso é revisado e mesclado no master branch.
  3. Depois de um tempo (vários recursos mesclados), o master é mesclado com o preprod
  4. Depois de um tempo (teste do usuário, etc.), o preprod é mesclado com o prod

Veja como ficaria:

  1. Desenvolvimento e teste
    • O desenvolvedor envia o código para o novo recurso em um branch de recursos separado
    • Depois que o recurso se torna estável, o desenvolvedor mescla nosso branch de recursos no master branch
    • O código do branch master é entregue ao ambiente de teste, onde é carregado e testado
  2. Entrega para o ambiente de pré-produção
    • O desenvolvedor cria a solicitação de mesclagem do branch master para o branch de pré-produção
    • Depois de algum tempo, o proprietário do repositório aprova a solicitação de mesclagem
    • O código do branch de pré-produção é entregue ao ambiente de pré-produção
  3. Entrega para o ambiente de produção
    • O desenvolvedor cria a solicitação de mesclagem do branch de pré-produção para o branch de produção
    • Depois de algum tempo, o proprietário do repositório aprova a solicitação de mesclagem
    • O proprietário do repositório aperta o botão "Implantar"
    • O código do branch de produção é entregue ao ambiente de produção

Ou o mesmo, mas em formato gráfico:

 

0
0 302
Anúncio Cristiano Silva · Out. 25, 2022

Eu gostaria de anunciar o lançamento de algo realmente bastante interessante - revolucionário na verdade. Isso pode soar exagerado, mas acho que você não viu nada parecido com isso, ou mesmo pensou que fosse possível!

Lançamos um novo módulo JavaScript/Node.js chamado glsdb, mais informações:

https://github.com/robtweed/glsdb

No entanto, para os propósitos deste anúncio aqui, quero apenas focar em uma parte do glsdb: suas APIs que abstraem classes IRIS (ou Caché) como objetos JavaScript equivalentes.

0
0 122
Pergunta Gilmar Silveira · Set. 13, 2021

tem este wsdl

https://apphom.correios.com.br/SigepMasterJPA/AtendeClienteService/Aten…

<xs:element name="consultaCEP" type="tns:consultaCEP"/>

tem um method  consultaCEP

quero passar um Cep e receber o retorno

<script language=cache runat=server>
       // instancia a classe cliente SOAP
               Set cliente=##class(AtendeClienteService.AtendeClientePort).%New()
               set cep="",cep="88133150"
               set resp=cliente.consultaCEP(cep)
              resp              
             
</script>

Alguém no grupo já conseguiu fazer isso

teria como ajudar

2
0 544
Artigo Julio Esquerdo · Out. 10, 2022 15m read

Olá,

De acordo com a Wikipédia, o kafka é:

"uma plataforma open-source de processamento de streams desenvolvida pela Apache Software Foundationescrita em Scalae JavaO projeto tem como objetivo fornecer uma  plataforma unificada, de alta capacidade e baixa latência para tratamento de dados em tempo real. Sua camada de armazenamento é, essencialmente, uma "fila de mensagens de publishers/subscribers maciçamente escalável projetada como um log de transações distribuído", tornando-o altamente valioso para infra-estruturas corporativas que processam transmissão de dados."

0
0 557