Fabric: 'papéis' compatíveis com v2 ou semelhantes

Criado em 22 abr. 2017  ·  7Comentários  ·  Fonte: fabric/fabric

Sinopse

No momento em que este artigo foi escrito, o branch v2 tinha uma classe Group que deveria ser capaz de servir como unidades anteriormente conhecidas como 'funções', também conhecidas como "um monte de hosts para fazer coisas com / em".

No entanto, ainda não há uma maneira específica de organizar ou rotular Group objetos; está "pronto" o suficiente para o caso de uso de API puro de usuários avançados que desejam implementar sua própria maneira específica de criá-los, mas não tem nada para usuários orientados a CLI ou pessoas intermediárias que querem algo em estrutura para construir.

Colocando de outra forma, a menos que você esteja rolando puramente com a API, ter objetos Group espalhados em algum lugar é inútil se a CLI ou os bits de chamada de tarefa não tiverem como localizá-los!

Fundo

Na v1, as funções eram efetivamente um único namespace plano mapeando rótulos de string simples para o que seriam Grupos na v2, e eles podiam ser selecionados na CLI em tempo de execução ( fab --roles=web,db ) e / ou registrados como alvos padrão para tarefas ( @task('db') \n def migrate(): ), bem como hosts.

Os usuários os definiram em env.roledefs , um dicionário simples; qualquer funcionalidade intermediária a avançada girava em torno de modificá-la, geralmente em tempo de execução (via pré-tarefa ou sub-rotina), às vezes no tempo de carregamento do módulo.

Casos de uso / necessidades / sub-recursos específicos

  • Mapeamento básico e ingênuo para uso / referência em qualquer outro lugar no sistema: coloque um nome, obtenha de volta algum iterável de Group s e / ou Connection s.

    • Muitas vezes o aliasing quer ir junto com isso, então, por exemplo, Lexicon vez de dict .

    • Construções ainda mais profundas, como 'agrupamento', por exemplo, você tem mapeamentos diretos chamados db , web , lb , mas então um nome de 2ª camada chamado prod que é sempre a união dos outros três. Eu esqueci se eu adicionei isso a Lexicon ainda. É possível que haja outras subclasses de mapa por aí que também fazem isso.

    • Além disso / alternativamente, coisas como globbing ou outras sintaxes de string, embora eu pessoalmente prefira aproveitar o fato de que Python não é "digitado em string" ...

  • 'Mapeamento reverso' útil para que você possa identificar a quais grupos uma determinada conexão pertence.

    • Problemático: como não há atualmente nenhum estado compartilhado global, a resposta ingênua para isso - usar a identidade - cai porque você pode criar tecnicamente vários objetos Connection idênticos.

    • Especialmente porque o Grupo pode criá-los implicitamente em seu nome, se você apenas fornecer strings de host abreviadas, embora essa seja apenas uma opção conveniente.

    • No entanto, dada a restrição de nenhum estado global, não consigo ver problemas óbvios com o uso de testes de igualdade, então isso deve ser factível, por exemplo, if cxn in group funcionaria mesmo se cxn fosse um objeto distinto do membro igual dentro de group .

    • A única coisa que vem à mente é se houvesse links fortes e com estado de uma conexão para um grupo (teriam que ser grupos, plural) segurando-o, em vez de vice-versa, mas não consigo ver grandes razões para isso de improviso.

  • Fortemente relacionado ao anterior: capacidade de inspecionar / exibir qual é a "função atualmente em execução" (algo que as pessoas queriam há muito tempo na v1 que não era trivial devido ao seu design)

    • O problema principal é que se trata realmente de duas questões semi-distintas: "de que função (ões) é a parte do host atual, em geral" (basicamente, aquele caso de uso anterior da pesquisa inversa), mas também "qual (is) função (ões) era (m) maquinaria de execução especificamente solicitada a correr contra ".

    • Em outras palavras, dado host 'foo' pertencente às funções A, B e C: dentro de uma determinada tarefa cujo contexto é 'foo', mas que foi executado por causa de uma solicitação para 'executar na função A', é um usuário procurando uma resposta de "A, B e C" (as funções 'foo' são no geral) ou apenas "A" (a função atualmente em execução)?

    • Isso realmente se parece com duas chamadas API distintas, embora as solicitações de recursos que me lembro de ter misturado as duas.

  • Seleção de alvos no CLI, globalmente e / ou por tarefa

    • Uma extensão do sistema CLI do Invoke para considerar "sinalizadores que todas as tarefas superam o que definem" pode ser útil ou necessária para isso. O que se encaixa firmemente no território pyinvoke / invoke # 205, na verdade, de modo que acabou de receber uma prioridade mais alta do que já era (o que era muito alto).

  • Idem para os padrões de nível de tarefa

    • Embora os padrões de destino no nível da tarefa realmente desejem ser: conexão, conexões, objs de grupo, objs de grupo ou nome avaliando _para_ objs de grupo (esse último é a única coisa que pertence diretamente a este tíquete, indiscutivelmente)

  • Idem para os padrões de nível de coleção (NOVO na v2!)

    • Ou seja, "todas as tarefas em $ submodule são executadas por padrão na função db "

    • O mesmo negócio do ponto anterior - este padrão deseja permitir uma série de valores diferentes, não apenas uma chave de string.

  • Há mais alguma coisa nova e empolgante habilitada por uma abordagem OO que realmente queira acompanhar isso? Lembre-se de que a ênfase deve estar na construção de blocos e na habilitação de usuários avançados, não em, por exemplo, reinventar sistemas totalmente como Chef ou Ansible.

Ideias / preocupações de implementação

  • Se usarmos o sistema de configuração como o vetor de armazenamento principal, os valores "querem" ser primitivos para que possam ser armazenados em yaml, json etc, mas isso é uma lata de worms que termina com "armazenar todos os kwargs de Grupo / Conexão em um grande e velho list-o-dicts ", etc.
  • Se esperamos que as definições estejam principalmente em Python, podemos simplesmente dizer "instanciar objetos de grupo", e então temos a opção de fundir esses dados no sistema de configuração ou deixá-lo autônomo de alguma forma.

    • Acho que prefiro o último porque colocar literalmente tudo nos dados de configuração aninhados parece que vai levar a más notícias.

  • As construções mais profundas, como aliasing e agrupamento, adicionam complexidade e questões de ordenação (ou seja, imagine uma configuração de alias trivial em que o valor de key1 é um grupo, mas o valor de key2 é key1; agora você tem que rastrear a estrutura duas vezes para resolver ou verificar a key2)

    • entretanto, se optarmos por uma abordagem principalmente "faça em python", ela se tornará muito parecida com a API do sistema de configuração, onde você pode começar com uma estrutura declarativa, mas qualquer coisa a mais é habilitada por chamadas de método após a configuração inicial. Eu não acho isso horrível? EDIT: e eu acho que é exatamente assim que Lexicon funciona de qualquer maneira.

  • Independentemente do formato, temos que descobrir como os usuários avançados desejarão gerá-lo em tempo real a partir de fontes externas ou semelhantes; isso mais os problemas com aliasing e tal, implica que podemos não querer isso em uma estrutura ingênua "armazenada" em algum lugar, mas como uma API em algum objeto ou objetos que são chamados para gerá-la.

    • Eu suspeito que podemos querer trabalhar 'para baixo' a partir da seleção de funções / grupos, chegando a qualquer que seja a API de nível mais alto para "transformar o que o usuário forneceu em uma unidade acionável de alvos", porque os usuários mais avançados necessariamente desejarão tem controle total sobre a implementação dessa chamada de API. Então, podemos, como sempre, fornecer o que parece ser um caso comum útil, mas que está claramente marcado como "apenas uma maneira de fazê-lo".

    • @RedKrieg tem uma ideia bacana ao longo dessas linhas, onde temos @group como @task , e as funções não são unidades de trabalho executáveis, mas, em vez disso, geram objetos Group.

    • Esta abordagem reutiliza nativamente a hierarquia de tarefas (coleção), o que é prático (por que reinventar a roda) e elegante (porque em casos do mundo real, as definições de função / grupo frequentemente são mapeadas muito próximas às tarefas que as utilizam!)



      • Também funciona bem mesmo se seus grupos NÃO mapeiam para suas tarefas, porque você pode simplesmente escrever as definições em seu nível de coleção raiz. Mole-mole.



    • Não está claro para mim se isso é melhor retornar um único Grupo de cada função, ou se queremos a capacidade de produzir vários grupos (ou conexões), ou se é melhor não como funções decoradas, mas apenas como chamadas de API Coleção (por exemplo, como as configurações de nível de coleção são armazenadas).

    • Por exemplo, o caso de uso em que os dados de grupo / função são dinâmicos e fora do Fabric ainda precisa ser resolvido aqui (é por isso que antes observei que primeiro devemos identificar a API de nível mais alto para este espaço; então, precisamos ver como isso se mescla com esta ideia de nível intermediário.)

Feature

Comentários muito úteis

Olá, não sei o que aconteceu com este software depois de muitos anos, mas realmente senti falta do conceito de "funções" em [email protected] , especialmente ao executar $ fab -R dev

Todos 7 comentários

Da lista de correio:

Implementamos nossa própria API REST interna, que preenche env.roledefs dinamicamente, dependendo do projeto que está sendo implementado, e depende fortemente de não incorporar strings de host no fabfile do projeto ou especificá-las na CLI.

Nossos casos de uso são:

  1. Base de código sem ambiente https://12factor.net/config. Ambientes (funções) e suas respectivas strings de host são armazenados em um banco de dados centralizado. Cada fabfile.py tem algo assim (ele preenche env.roledefs quando o arquivo é importado):
EnvironmentDatabaseAPIClient(
    'https://rest.api.url/schema/',
    env.service_name,
).apply_env()
  1. Número de ambientes de servidor - vários ambientes de teste (alguns deles são privados, alguns públicos) e vários ambientes de produção (para clientes diferentes). Cada ambiente consiste em um ou mais hosts e é mapeado para a função de malha.

  2. Cada serviço ( env.service_name no exemplo acima) possui um conjunto diferente de ambientes.

  3. Também temos meta-papéis (grupos de papéis). Eles são prefixados com group- : group-production , group-test , group-external , group-internal , group-all . Isso nos permite implantar em várias funções de servidor sem especificá-las uma por uma, por exemplo group-all implanta em todas as funções, tanto de produção quanto de teste.

  4. Temos tarefas de malha especiais para imprimir informações sobre grupos de funções, funções e hosts.

  5. Também contamos muito com o mapeamento reverso das strings de host de volta aos nomes de funções (as strings de hosts são exclusivas por service_name). Isso é usado para registro e notificações de implantação. Basicamente, registramos as implantações de serviço em cada host e enviamos uma notificação do Slack quando o serviço foi implantado em todos os hosts em uma função. O servidor EnvironmentDatabaseAPI é responsável por isso (ele mantém os logs e o estado de implantação). Isso é feito decorando as tarefas de tecido com um decorador que envia env.host , env.port e env.service_name (mais informações de confirmação) de volta ao servidor API.

  6. Planejamos adicionar autenticação de implantação no futuro, também muito provavelmente para extrair mais env variáveis ​​do servidor para disponibilizá-las no contexto da tarefa.

Obrigado @ max-arnold! Eu reconheço muitos deles em meus próprios casos de uso no passado também. O bit de mapeamento reverso em particular, lembro-me de aparecer na v1 algumas vezes, então o adicionei à lista.

Para que o Fabric v2 se torne útil para mim, eu precisaria de uma maneira de dizer a fab qual conjunto de hosts executar uma tarefa.

Anteriormente, defini funções e executei fab -R ... . (Na verdade, as funções foram definidas programaticamente usando um intervalo de endereços IP, mas isso não é um requisito e uma lista estática dentro de um arquivo YAML seria suficiente.)

Não consegui encontrar um equivalente no Fabric v2 e também não consegui emular esse recurso usando:

  • um arquivo de configuração fabric.yaml contendo
active_hostset: null
hostsets:
  myhostset:
  - ...
  • active_hostset = config["hostsets"][config["active_hostset"]] em fabfile.py
  • env INVOKE_ACTIVE_HOSTSET=myhostset fab ...

Em vez da lista esperada de hosts, recebo KeyError: 'active_hostset' .

Mapeamos diferentes conjuntos de hosts para cada função de cada um de nossos ambientes no fabric v1 e o ambiente é definido executando uma tarefa role.environment:staging para especificá-lo. Portanto, essa tarefa influencia os hosts usados ​​pelas tarefas a seguir.

Na v2, tentamos usar uma tarefa personalizada, mas o problema é que Executor.expand_calls é executado antes de nossa tarefa role.environment executada e, portanto, nenhuma das tarefas a seguir conhece o ambiente para construir dinamicamente suas listas de hosts.

Tornar Executor.expand_calls um gerador permite que a execução de tarefas influencie a execução de tarefas posteriores. Portanto, meu exemplo acima funciona, onde temos um Task que precisa saber seu ambiente para expandir adequadamente as funções para hosts. por exemplo, fab role.environment dev deploy.app - a tarefa role.environment agora é executada antes de deploy.app ser expandido, e assim deploy.app conhece o ambiente e pode configurar seus hosts e então é expandido para o conjunto correto de tarefas.

Eu fiz um protótipo disso em meus garfos:
https://github.com/pyinvoke/invoke/compare/master...rectalogic : expand-generator
https://github.com/fabric/fabric/compare/master...rectalogic : expand-generator

Olá, não sei o que aconteceu com este software depois de muitos anos, mas realmente senti falta do conceito de "funções" em [email protected] , especialmente ao executar $ fab -R dev

Também usamos funções para representar o mesmo conjunto de operações em ambientes diferentes. Talvez separar o conceito de uma função nomeada e um ambiente nomeado seja útil? Como em, a função da web no ambiente de desenvolvimento.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

jmcgrath207 picture jmcgrath207  ·  5Comentários

peteruhnak picture peteruhnak  ·  6Comentários

peteruhnak picture peteruhnak  ·  4Comentários

omzev picture omzev  ·  6Comentários

haydenflinner picture haydenflinner  ·  5Comentários