Pular para o conteúdo principal

Papel

Quando usar: quando o produto tem perfis de usuário reconhecíveis ("leitor", "operador"). E, noutro sentido, quando o código precisa saber se esta release é a ativa.

"Papel" aparece em dois lugares diferentes na plataforma, e vale não confundir:

PapelO que éOnde se decide
Papel do usuárioAgrupamento de permissões (viewer, operator, admin)nnp/iam.yaml; o gateway expande
Papel da releaseSe esta release está active ou standbyArquivo de papel que o Module Manager escreve no pod

1. Papel do usuário: agrupa, não autoriza​

nnp/iam.yaml:

apiVersion: console.nnumbers.io/v1alpha3
kind: IamDescriptor
product: sample
permissions:
- {id: context.read, description: Ver o contexto verificado pelo serviço (whoami)}
- {id: item.read, description: Ver itens e o estado das varreduras}
- {id: item.create, description: Criar item}
- {id: item.delete, description: Apagar item}
- {id: item.scan, description: Iniciar a varredura assíncrona de um item}
roles:
viewer: {description: Leitura, permissions: [context.read, item.read]}
operator: {description: "Leitura, criação e varredura", permissions: [context.read, item.read, item.create, item.scan]}
admin: {description: Tudo do produto, permissions: ["*"]}
  • O papel existe no realm como nnc:<produto>:<papel> (aqui, nnc:sample:operator) e é atribuído a quem opera o módulo. Quem lê o realm e expande o papel é o gateway: na chamada, o serviço recebe as permissões já expandidas.
  • O serviço nunca decide por papel. ServiceContext.roles() existe para mostrar ao cliente e para log, não para autorizar. Quem autoriza é a permissão da rota (veja Permissão).
  • "*" vale todas as permissões tenant do produto — não é poder de plataforma.
  • Papel novo é uma linha no descritor mais a atribuição no realm; nada de código.

2. Papel da release: ativa ou standby​

Numa troca de release convivem N e N-1. O Module Manager diz a cada pod qual é o papel da release dele, e o SDK relê esse estado a cada consulta (não há cache para envelhecer):

@Component
@Profile("serve")
public class SampleService {

private final RoleWatcher role;

/** O texto do papel desta release: `active` ou `standby`. */
String roleText() {
return role.refresh().role().text();
}

/** Esta release é a ATIVA? */
public boolean activeRole() {
return role.refresh().role() == Role.ACTIVE;
}
}

O whoami publica o papel — é a forma mais rápida de saber, em produção, QUAL release respondeu:

@NnsRoute("whoami")
Views.WhoamiView whoami(ServiceContext sc, Introspection who) {
return Views.WhoamiView.of(who, sc, service.module.iam(), service.roleText(), SampleModule.MARKER, ScanEngine.ENGINE);
}

Em desenvolvimento (sem arquivo de papel) a release única se considera ativa; em produção, sem o arquivo do Module Manager, ela é standby — falha fechada.

3. O que só a release ativa faz​

Trabalho de manutenção (reconciliar, expurgar, fechar o que ficou pela metade) roda numa release só, senão duas releases se atropelam. A forma disso no SDK é @NnsPeriodic: o SDK relê o papel a cada disparo e não chama o método na standby.

@Component
@Profile("serve")
static class Periodic {
private final SampleService service;
private final ActionReaper reaper;

Periodic(SampleService service, ActionReaper reaper) {
this.service = service;
this.reaper = reaper;
}

@NnsPeriodic(name = "scan-reconcile", every = "1m")
void scanReconcile(Ctx ctx) {
service.reconcileScans(ctx, Duration.ofMinutes(2));
}

@NnsPeriodic(name = "action-reaper", every = "1m")
void reap(Ctx ctx) {
reaper.reap(ctx);
}
}

O RoleState.live() (o conjunto de releases vivas, active + standby) é o que o Reaper usa para saber quem pode assumir as Actions de uma release que saiu: ActionReaper.of(store, releaseId, () -> role.refresh().live()).

Como testar​

O papel é estado de arquivo: o teste escreve o arquivo e confere o efeito.

@Test
void whoamiShowsTheVerifiedContextOfTheToken() {
String token = ctxToken(ctx("whoami", "sample.context.read"), "GET", "/v1/sample/whoami", null);
HttpResponse<String> r = call("GET", "/v1/sample/whoami", token, null);
// Sem arquivo de papel, em desenvolvimento, a release única é a ativa.
assertThat(body(r).get("role").asText()).isEqualTo("active");
}

E a tarefa que só a ativa roda tem teste pelo gancho do SDK, sem esperar o relógio (veja Tarefa periódica):

PeriodicTasks tasks = context.getBean(PeriodicTasks.class);
assertThat(tasks.names()).containsExactlyInAnyOrder("scan-reconcile", "action-reaper");
assertThat(tasks.runOnce("scan-reconcile")).isTrue();
assertThat(service.reconciled).hasValue(1);

Erros comuns​

  • if (sc.roles().contains("admin")) no handler — autorização por papel. A permissão já chegou expandida; use sc.can(…).
  • @Scheduled em vez de @NnsPeriodic — o agendador do Spring não conhece o papel: a standby também dispara, e as duas releases escrevem no mesmo lugar.
  • Usar roleText() para decidir — compare com Role.ACTIVE (role.refresh().role()), que é enum; o texto é para o fio e para o log.
  • Guardar o papel num campo — a troca de release acontece com o pod no ar. Chame refresh() a cada uso; é barato e é o que o SDK garante.
  • Achar que a standby não serve tráfego — serve: ela atende requisição normalmente; o que ela não faz é o trabalho de manutenção.
  • Criar uma dependência entre papéis no mesmo módulo — a comunicação entre releases do mesmo módulo não existe: se precisa de coordenação, ela é feita pelo banco.