Close

Uso de las marcas de función de Launch Darkly con Bitbucket Pipelines

Foto de Warren Marusiak
Warren Marusiak

Divulgador técnico sénior

Implementar un nuevo código en un entorno de producción es arriesgado. Los errores pueden llegar a la producción incluso después de realizar pruebas unitarias, de integración y de sistema del código en entornos de pruebas y de ensayo. Tradicionalmente, los desarrolladores tienen dos opciones una vez que un error llega a la producción y los usuarios se ven afectados. Pueden anular el código con errores o poner en marcha una corrección ; ambas soluciones llevan tiempo. Ahora, los desarrolladores pueden activar o desactivar una función en un entorno con solo hacer clic en un botón al envolver los cambios de código relacionados en una marca de función. El impacto del código con errores en los usuarios se puede mitigar de inmediato y se puede desarrollar y aplicar una corrección de forma segura. Este artículo lo demuestra mediante Bitbucket Pipelines y las marcas de función de Launch Darkly en la aplicación de demostración ImageLabeller.

Una demostración de la marca de función de ImageLabeller

ImageLabeller es una pequeña aplicación que utiliza el aprendizaje automático para etiquetar imágenes. ImageLabeller se implementa en cinco entornos: un entorno de pruebas, uno de ensayo, y los entornos de producción Production-us-west-2, Production-us-east-1 y Production-ca-central-1. Este artículo demuestra cómo utilizar las marcas de función para gestionar los cambios en el componente SubmitImage de ImageLabeller. SubmitImage es una AWS Lambda escrita en Go. Utilizarás Launch Darkly para gestionar las marcas de función, Bitbucket para el control de código fuente y Bitbucket Pipelines para la funcionalidad de CI/CD.

Cómo usar las marcas de función de Launch Darkly con Bitbucket Pipelines

Pide a tu administrador local de Launch Darkly que cree un proyecto y un entorno. En la siguiente captura de pantalla hay un proyecto, PMMImageLabellerDemo, con cinco entornos. Los entornos de pruebas y ensayo son entornos de preproducción. Anota la clave del SDK de cada entorno. Más adelante, las claves del SDK se añadirán como variables de repositorio en Bitbucket.

En este ejemplo, las canalizaciones de Bitbucket se implementan en estos entornos cuando el código se confirma en una rama de funciones. Production-us-west-2, Production-us-east-1 y Production-ca-central-1 son entornos de producción que corresponden a los entornos de AWS. Las canalizaciones de Bitbucket se implementan en estos entornos cuando el código se fusiona en la rama principal, desde una rama de funciones, mediante una solicitud de incorporación de cambios.

Captura de pantalla de Bitbucket Pipelines

En Launch Darkly, crea una marca de función para el proyecto. Selecciona el entorno de pruebas y ajusta la configuración de las marcas de función. En la siguiente captura de pantalla, la marca de función está configurada para que dé como resultado verdadero por defecto en la región de pruebas. Si un usuario específico, AtlassianTestUser@atlassian.com, hace la solicitud, la marca de función indicará false. De esta forma, el usuario con un nombre específico, como los usuarios de prueba con un conjunto de pruebas del sistema, puede hacer que el código se ejecute de una manera, mientras que los usuarios normales del mismo entorno hacen que el código se ejecute de manera diferente.

Este comportamiento se puede ajustar por entorno. Las marcas de función permiten al desarrollador implementar código nuevo en todas las regiones y, al mismo tiempo, solo permiten que el código se ejecute en entornos determinados. En el caso de esta demo, la marca está configurada como false en el entorno de ensayo y en los tres entornos de producción. El nuevo código solo se ejecutará en el entorno de pruebas.

Captura de pantalla de segmentación por usuarios

Obtén las claves del SDK para cada entorno en Launch Darkly. A continuación, ve a Bitbucket y añade variables de repositorio a cada repositorio que utilice esta marca. La siguiente captura de pantalla muestra que se han añadido cinco variables de repositorio. ld_test_env contiene la clave del SDK de Launch Darkly para el entorno de pruebas. ld_staging_env contiene la clave del SDK de Launch Darkly para el entorno de ensayo. Estas variables del repositorio son referencias posteriores en el archivo bitbucket-pipelines.yml del repositorio.

Claves del SDK

Los valores de las claves del SDK pueden ser referencias en el archivo bitbucket-pipeline.yml del repositorio después de añadir las claves del SDK como variables del repositorio. STACK_PARAMETERS se añade al paso de implementación de production-ca-central-1 en el siguiente fragmento de código. STACK_PARAMETERS envía el valor de la clave del SDK correspondiente al archivo AWS CloudFormation template.yml como parámetro.

- pipe: atlassian/aws-sam-deploy:1.2.0
  variables:
    AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
    AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
    AWS_DEFAULT_REGION: 'ca-central-1'
    STACK_NAME: 'OpenDevOpsSubmitImage'
    CAPABILITIES: [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND' ]
    TEMPLATE: 'https://s3.amazonaws.com/open-devops-code-ca-central-1-${AWS_ACCOUNT_ID}/submit-image-packaged.yml'
    WAIT: 'true'
    DEBUG: 'true'
    S3_BUCKET: 'open-devops-code-ca-central-1-${AWS_ACCOUNT_ID}'
    SAM_TEMPLATE: 'build/template.yaml'
    STACK_PARAMETERS: '[{
      "ParameterKey": "LaunchDarklySDKKey",
      "ParameterValue": "${ld_prod_cac1_env}"
    }]'

Añade una sección de parámetros con LaunchDarklySDKKey de tipo String a la sección Parámetros del archivo template.yml del repositorio. Este parámetro recibe el valor del STACK_PARAMETER de LaunchDarklySDKKey establecido en el archivo bitbucket-pipelines.yml.

Parameters:
  LaunchDarklySDKKey:
    Type: String

Actualiza también el recurso AWS Lambda para la función SubmitImage del archivo template.yml. Añade LaunchDarklySDKKey como variable de entorno.

Resources:
  SubmitImageFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: submitImage/
      Handler: submit-image
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Policies:
        - AmazonDynamoDBFullAccess
        - AmazonS3FullAccess
      Events:
        CatchAll:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /submit-image
            Method: GET
      Environment:
        Variables:
          LaunchDarklySDKKey:
            Ref: LaunchDarklySDKKey

La variable de entorno LaunchDarklySdkKey estará visible en la consola de AWS Lambda una vez que las canalizaciones de Bitbucket se implementen en el entorno. Este valor de esta clave es exclusivo del entorno. Por ejemplo, la variable de entorno LaunchDarklySdkKey en el entorno de pruebas será diferente a la de production-us-west-2.

Captura de pantalla de configuraciones de entorno

SubmitImage es una AWS Lambda escrita en Go. Para usar Launch Darkly con Go, importa las siguientes dependencias.

"gopkg.in/launchdarkly/go-sdk-common.v2/lduser"
ld "gopkg.in/launchdarkly/go-server-sdk.v5"

Añade una función para recuperar el valor de la marca de función de Launch Darkly en tiempo de ejecución.

func getLaunchDarklyFlags(username string) (bool, error) {
  client, _ := ld.MakeClient(os.Getenv("LaunchDarklySDKKey"), 5 * time.Second)
  flagKey := "SubmitImageDemoFeature"

  userUuid, uuidErr := uuid.NewRandom()
  if uuidErr != nil {
    return false, uuidErr
  }

  var user lduser.User
  if(username == "") {
    user = lduser.NewAnonymousUser(userUuid.String())
  } else {
    user = lduser.NewUser(username)
  }

  showFeature, _ := client.BoolVariation(flagKey, user, false)

  if showFeature {
    return true, nil
  } else {
    return false, nil
  }
}

Llama a la función con una cadena vacía para obtener el valor predeterminado de la marca o con el correo electrónico de un usuario para obtener el valor objetivo. La configuración que se muestra arriba debe devolver "true" para el usuario anónimo y "false" para el usuario AtlasianTestUser@atlassian.com.

flagVal, flagErr  := getLaunchDarklyFlags("")
  if flagErr != nil {
    return "", flagErr
  }
  fmt.Println("DEMO flagVal for anonymous user: ", flagVal)

  flagVal, flagErr  = getLaunchDarklyFlags("AtlassianTestUser@atlassian.com")
  if flagErr != nil {
    return "", flagErr
  }
  fmt.Println("DEMO flagVal for AtlassianTestUser@atlassian.com: ", flagVal)

Ve a los registros de AWS CloudWatch tras ejecutar el código para comprobar que se han obtenido los valores correctos de la marca.

Captura de pantalla de registro de eventos
Copiar las reglas de objetivos

Ve a Configuración de administrador y, a continuación, a Claves de API para obtener una lista de las claves de API de cada entorno. Estas claves de API se devuelven a Split durante las llamadas a la API en código para obtener la versión correcta de una división. Esta guía utiliza las claves del servidor para cada entorno.

Configuración de administrador

Ve a tu repositorio de Bitbucket, luego a Configuración del repositorio, luego a Variables del repositorio y añade variables para cada clave de API.

Variables de repositorio en la configuración del repositorio

Edita el archivo bitbucket-pipelines.yml y añade STACK_PARAMETERS al paso de implementación de AWS SAM. Esto se hace por entorno. El siguiente fragmento de código de YAML muestra el paso de implementación de la región de PRUEBAS, que se encuentra en AWS US-WEST-1. Por lo tanto, el paso hace referencia a la configuración anterior de variables del repositorio de split_test_env. Usa la variable de repositorio adecuada para cada entorno.

- pipe: atlassian/aws-sam-deploy:1.2.0
  variables:
    AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
    AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
    AWS_DEFAULT_REGION: 'us-west-1'
    STACK_NAME: 'OpenDevOpsSubmitImage'
    CAPABILITIES: [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND' ]
    TEMPLATE: 'https://s3.amazonaws.com/open-devops-code-us-west-1-${AWS_ACCOUNT_ID}/submit-image-packaged.yml'
    WAIT: 'true'
    DEBUG: 'true'
    S3_BUCKET: 'open-devops-code-us-west-1-${AWS_ACCOUNT_ID}'
    SAM_TEMPLATE: 'build/template.yaml'
    STACK_PARAMETERS: '[{
      "ParameterKey": "SplitIOSDKKey",
      "ParameterValue": "${split_test_env}"
    }]'

Edite el archivo AWS CloudFormation template.yml y añade una sección de parámetros que haga referencia a la clave del SDK de Split.

Parameters:
  SplitIOSDKKey:
    Type: String

En el archivo template.yml, añade una sección de entorno a cada recurso de AWS Lambda que necesite acceder a Split. Esta guía demuestra

Environment:
  Variables:
    SplitIOSDKKey:
      Ref: SplitIOSDKKey

Importa las siguientes dependencias al archivo Go que utilizará el SDK de Split.

"github.com/splitio/go-client/v6/splitio/client"
"github.com/splitio/go-client/v6/splitio/conf"

Esta función crea un cliente y recupera el valor de la marca de función para "SubmitImageDemoSplit" creado en la interfaz de usuario de Split. Se necesita un solo parámetro, el nombre de usuario.

func getSplitIOFlag(username string) (string, error) {
  splitIOSDKKey := os.Getenv("SplitIOSDKKey")

  cfg := conf.Default()
  factory, err := client.NewSplitFactory(splitIOSDKKey, cfg)
  if err != nil {
    fmt.Printf("SDK init error: %s\n", err)
    return "", err
  }

  splitClient := factory.Client()
  err = splitClient.BlockUntilReady(10)
  if err != nil {
    fmt.Printf("SDK timeout: %s\n", err)
    return "", err
  }

  treatment := splitClient.Treatment(username, "SubmitImageDemoSplit", nil)
  fmt.Printf("SPLIT_DEMO treatment is %s, username is %s\n", treatment, username)

  return treatment, nil
}

Llama a la función con una dirección de correo electrónico. En este caso, someRandomUser@atlassian.com extraerá el valor predeterminado de la marca de función, ya que no forma parte de una lista de permitidos asociada a la marca de función. AtlassianTestUser@atlassian.com extraerá el valor de la marca de función asociada a la lista de permisos de la que es miembro.

Consulta el resultado de los registros de AWS CloudWatch una vez ejecutado el código. Ten en cuenta que la marca de función devuelve "off" cuando someRandomUser@atlassian.com accede a ella y la marca de función devuelve "on" cuando AtlassianTestUser@atlassian.com accede a ella.

Registrar eventos

En conclusión...

Las marcas de función de Launch Darkly se integran fácilmente en una aplicación implementada mediante Bitbucket Pipelines. Las marcas de función permiten a los desarrolladores controlar la ejecución del código implementado. Esto puede hacer que responder a las implementaciones con errores sea más rápido y reducir el impacto en los usuarios. Tómate tu tiempo para crear una instancia de Bitbucket y Launch Darkly y poner a prueba las capacidades de tu equipo.

Warren Marusiak
Warren Marusiak

Warren is a Canadian developer from Vancouver, BC with over 10 years of experience. He came to Atlassian from AWS in January of 2021.


Compartir este artículo

Lecturas recomendadas

Consulta estos recursos para conocer los tipos de equipos de DevOps o para estar al tanto de las novedades sobre DevOps en Atlassian.

Ilustración de Devops

La comunidad de DevOps

Ilustración de Devops

Ruta de aprendizaje de DevOps

Ilustración de un mapa

Pruébalo gratis

Suscríbete para recibir el boletín de DevOps

Thank you for signing up