Giter Club home page Giter Club logo

happy's Introduction

Happy Path Deployment Tool

Visit the Happy Path documentation for more details: https://chanzuckerberg.github.io/happy/

The Happy Path Deployment Tool is an open-source project led by the Chan Zuckerberg Initiative (CZI). It is a platform for deploying and managing containerized applications at scale in adherence to CZI security practices. The tool is designed to be easy to use and operate, and it is available for both on-premises and cloud deployments. Happy builds and deploys your application, and once it is released, helps you support it.

Happy Path is based on these principles:

  • Operational simplicity: Happy Path takes the bite out of a complex container orchestration operations and infrastructure management
  • Extensibility: Happy Path functionality can be extended through various Terraform hooks, custom Terraform modules and Helm Chart customization
  • Reliability: Happy Path is reliable and is production-ready, used by multiple engineering teams at CZI

Security

Please note: If you believe you have found a security issue, please responsibly disclose by contacting us at [email protected]

Repository structure

This project is a monorepo for the components of a Happy ecosystem:

  • cli/ - the happy CLI tool
  • api/ - the happy API server
  • shared/ - components shared between api, cli and the terraform/provider
  • hvm/ - the happy version manager
  • terraform/ - a collection of TF modules we use to provision long-lived infrastructure and application stacks
  • terraform/provider - happy terraform provider
  • helm-charts/stack - an experimental helm chart
  • examples/ - sample applications that illustrate various concepts that happy supports, such as sidecars, tasks, multi-service deployments, GPU intense workloads, and so on

Features

  • Manages short-lived infrastructure (we deploy into your compute)
  • Groups services together (we call it a stack for co-deployment), each stack is isolated, and you can have multiple stacks created for the same application.
  • Easily promote changes from lower to higher environments
  • Supports automated deployments through github workflows
  • Has an extensive set of Github workflows
  • Supports both AWS ECS and EKS runtimes, and allows for an easy migration between the two
  • Abstracts out IaC code with the intent that developers should only focus on the application code
  • Supports Linkerd service mesh, mTLS and service-to-service authorization when deployed on EKS with Linkerd installed
  • Plays nicely with external-dns, karpenter, cluster-autoscaler
  • Integrates with Datadog for dashboarding (assuming you have a datadog agent deployed into your EKS)
  • Provides service discovery and autoscaling capabilities
  • Supports both amd64 and arm64 architectures
  • Supports metrics collection, health monitoring through healthchecks, and synthetics
  • Supports rolling updates to minimize downtime

Prerequisites

You will need to have Docker desktop, AWS CLI, and terraform installed to use Happy. You can install them and other useful tools by running

brew tap chanzuckerberg/tap
brew install awscli helm kubectx kubernetes-cli aws-oidc linkerd jq terraform
brew install --cask docker

Docker Desktop needs to be running; and aws cli needs to be configured by running aws configure, with profiles setup. Make sure cat ~/.aws/config does not return an empty string (we assume you already have an AWS account).

In addition to the above, you will need an up and running EKS cluster, that contains a happy environment namespace (it contains a secret called integration-secret).

Integration secret can be set up via happy-env-eks terraform module,

module "happy_env" {
  source = "../../happy-env-eks"
  eks-cluster = {
    cluster_id              = "my-eks-cluster",
    cluster_arn             = "arn:aws:eks:us-west-2:00000000000:cluster/my-eks-cluster",
    cluster_endpoint        = "https://A1B2C3D4.gr7.us-west-2.eks.amazonaws.com",
    cluster_ca              = "...",
    cluster_oidc_issuer_url = "https://oidc.eks.us-west-2.amazonaws.com/id/A1B2C3D4",
    cluster_version         = "1.27",
    worker_iam_role_name    = "my-eks-cluster-eks-node-role-name",
    worker_security_group   = "my-eks-cluster-worker-security-group",
    oidc_provider_arn       = "arn:aws:iam::00000000000:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/A1B2C3D4",
  }
  okta_teams   = []
  base_zone_id = "ROUTE53_EXTERNAL_ZONE_ID"
  cloud-env = {
    database_subnet_group = "db-subnet-group"
    database_subnets      = ["subnet-xxxxxxxxxxxxxxxxx"...]
    private_subnets       = ["subnet-xxxxxxxxxxxxxxxxx"...]
    public_subnets        = ["subnet-xxxxxxxxxxxxxxxxx"...]
    vpc_cidr_block        = "10.0.0.0/16"
    vpc_id                = "vpc-xxxxxxxxxxxxxxxxx"
  }
  tags = {
    project   = "happy"
    env       = "rdev"
    service   = "happy"
    owned_by  = "happy"
  }
  providers = {
    aws.czi-si = aws.czi-si
  }
}

provider "aws" {
  alias = "czi-si"
}

This module will create a namespace

Another approach is to create the secret explicitly. Create a file called integration-secret.json with the following content:

{
    "kind": "k8s",
    "cloud_env": {
        "database_subnet_group": "db-subnet-group",
        "database_subnets": [
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx"
        ],
        "private_subnets": [
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx"
        ],
        "public_subnets": [
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx",
            "subnet-xxxxxxxxxxxxxxxxx"
        ],
        "vpc_cidr_block": "10.0.0.0/16",
        "vpc_id": "vpc-xxxxxxxxxxxxxxxxx"
    },
    "vpc_id": "vpc-xxxxxxxxxxxxxxxxx",
    "zone_id": "ROUTE53_EXTERNAL_ZONE_ID",
    "external_zone_name": "external.dns.zone",
    "eks_cluster": {
        "cluster_arn": "arn:aws:eks:us-west-2:00000000000:cluster/my-eks-cluster",
        "cluster_ca": "...",
        "cluster_endpoint": "https://A1B2C3D4.gr7.us-west-2.eks.amazonaws.com",
        "cluster_id": "my-eks-cluster",
        "cluster_oidc_issuer_url": "https://oidc.eks.us-west-2.amazonaws.com/id/A1B2C3D4",
        "cluster_version": "1.27",
        "oidc_provider_arn": "arn:aws:iam::00000000000:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/A1B2C3D4",
        "worker_iam_role_name": "my-eks-cluster-eks-node-role-name",
        "worker_security_group": "my-eks-cluster-worker-security-group"
    },
    "dbs": {},
    "dynamo_locktable_name": "dynamo-locktable-name",
    "ecrs": {},
    "hapi_config": {
        "assume_role_arn": "arn:aws:iam::00000000000:role/tfe-si",
        "base_url": "https://hapi.external.dns.zone",
        "kms_key_id": "kms-key-id",
        "oidc_authz_id": "oidc-authz-id",
        "oidc_issuer": "oidc-issuer",
        "scope": "happy"
    },
    "oidc_config": {
        "client_id": "xxxxxxxxxxxxxxxxx",
        "client_secret": "yyyyyyyyyyyyyyyyyy",
        "config_uri": "https://xxxxxxxxxxxxxxxxx:[email protected]/oauth2/",
        "idp_url": "my.okta.com"
    },
    "tags": {
        "env": "rdev",
        "owned_by": "happy"
    },
    "tfe": {
        "org": "happy",
        "url": "https://app.terraform.io"
    }
}

Substitute the values with the ones appropriate to your setup. hapi_config and oidc_confug sections are optional.

Create a happy namespace (happy-rdev) and apply the integration secret into it:

kubectl create ns happy-rdev
kubectl create secret generic integration-secret --from-file=integration_secret=./integration-secret.json -n happy-rdev

Install

Install happy:

MacOS

brew tap chanzuckerberg/tap
brew install happy

Alternatively, you can install hvm and install happy using hvm:

brew tap chanzuckerberg/tap
brew install hvm
hvm install chanzuckerberg happy <version>
hvm set-default chanzuckerberg happy <version>

Linux

Binaries are available on the releases page. Download one for your architecture, put it in your path and make it executable.

Instructions on downloading the binary:

  1. Go here: https://github.com/chanzuckerberg/happy/releases to find which version of happy you want.
  2. Run curl -s https://raw.githubusercontent.com/chanzuckerberg/happy/master/download.sh | bash -s -- -b HAPPY_PATH VERSION
    1. HAPPY_PATH is the directory where you want to install happy
    2. VERSION is the release you want
  3. To verify you installed the desired version, you can run happy version.

Getting started

Setting up a brand new application

Create a folder called myapp

mkdir myapp
cd myapp

Bootstrap the Happy application:

happy bootstrap --force

Answer the prompts:

  • What would you like to name this application?: myapp.
  • Your application will be deployed to multiple environments. Which environments would you like to deploy to?: rdev
  • Which aws profile do you want to use in rdev?: select the appropriate aws configuration profile
  • Which aws region should we use in rdev?: select the aws region with the EKS cluster
  • Which EKS cluster should we use in rdev?: select the cluster you will be deploying to
  • Which happy namespace should we use in rdev?: select the namespace that has a Happy environment configured
  • Would you like to use dockerfile ./Dockerfile as a service in your stack?: Y
  • What would you like to name the service for ./Dockerfile?: myapp
  • What kind of service is myapp?: EXTERNAL
  • Which port does service myapp listen on?: use a port number other than 80 or 443
  • Which uri does myapp respond on?: /
  • File /tmp/myapp/docker-compose.yml already exists. Would you like to overwrite it, save a backup, or skip the change?: overwrite (if prompted)

At this point, your folder structure looks like

.
├── .happy
│   ├── config.json
│   └── terraform
│       └── envs
│           └── rdev
│               ├── main.tf
│               ├── outputs.tf
│               ├── providers.tf
│               ├── variables.tf
│               └── versions.tf
├── Dockerfile
└── docker-compose.yml

Happy configuration is blended from three sources: config.json for environment and application structure setup; main.tf to wire the terraform code and provide baseline parameters, and docker-compose.yaml to indicate where relevant Dockerfile files are located. Multiple environments (think dev, staging, and prod) can be defined with unique configuration settings.

Let's create a new stack: happy create myapp-stack, not that we have namespaced the stack name, as stack names are unique in the entire environment due to DNS constraints. Once the stack is created, happy will display a list of endpoints:

[INFO]: service_endpoints: {
	"EXTERNAL_MYAPP_ENDPOINT": "https://myapp-stack.<PARENT-DNS-ZONE-NAME>",
	"PRIVATE_MYAPP_ENDPOINT": "http://myapp-myapp.<NAMESPACE>.svc.cluster.local:80"
}

EXTERNAL_MYAPP_ENDPOINT is accessible from your browser. PRIVATE_MYAPP_ENDPOINT is accessible by other applications on running on the same cluster (if Linkerd is enabled, you can have granular controls over who can connect to it). Try curl-ing the EXTERNAL_MYAPP_ENDPOINT. You will get a default nginx response.

Now, list stacks out: happy list, you will get a human readable output. For machine-readable output, run happy list --output json. If you intend to update the application after changes were made, run happy update myapp-stack. Close the session by deleting the stack: happy update myapp-stack.

Sample apps

Clone this repo:

git clone https://github.com/chanzuckerberg/happy.git

Navigate to an example app and try happy out:

cd examples/typical_app
happy list
happy create mystack
happy update mystack
happy delete mystack

Integration Testing

All release-please pull requests automatically trigger an Integration Test workflow, which has to complete successfully for Happy to be released. This workflow does not run automatically on feature pull requests. If you wish to run an integration test on a pull request, add a happy:integration-test label to it.

Contributing

This project adheres to the Contributor Covenant code of conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to [email protected]. //

happy's People

Contributors

dependabot[bot] avatar czi-github-helper[bot] avatar alexlokshin-czi avatar jakeyheath avatar hspitzley-czi avatar dspadea avatar dtsai-czi avatar kuannie1 avatar ademartini-czi avatar jgadling avatar srm78 avatar naihsuanshao avatar katyho avatar abiju-czi avatar jayengee avatar cyberious avatar bento007 avatar alldoami avatar manasav3 avatar rzlim08 avatar

Stargazers

 avatar Sean Fitzgerald avatar Gully Burns avatar  avatar Andrew Tolopko avatar Kuni Katsuya avatar  avatar Alden Golab avatar  avatar  avatar  avatar  avatar  avatar Mark avatar  avatar  avatar Nikolay Kolev avatar

Watchers

Girish Patangay avatar  avatar James Cloos avatar  avatar Amine Raounak avatar  avatar  avatar  avatar Kostas Georgiou avatar  avatar  avatar

happy's Issues

feat: "happy init"

We would like to create a happy init command that will help set up some of the necessary configuration to onboard an application repo to be happy compatible.

NOTE: There is some configuration and automation needed to set up terraform enterprise with the appropriate orgs/teams/workspaces/etc. This is not that. Instead, we focus on the application side for now.

The basic structure for a happy-enabled repo is:

➜  happy-repo tree -a                                                                    ()
.
├── docker-compose.yml
└── .happy
    ├── config.yml
    └── terraform
        ├── envs
        │   ├── dev
        │   ├── prod
        │   └── staging
        └── modules

7 directories, 2 files

Happy init should:

  • deduce where the root of the repo is
  • make sure we don't already have .happy , etc configured
  • create the basic structure (.happy, .happy/terraform, .happy/config.yml, .happy/..., docker-compose.yml)
  • populate the happy config and Terraform directories with some standard boilerplate code and configuration
    • we might need to ask the user for input to templatize these

Panic Validating docker-compose

MustParse panics if the semantic version isn't formatted correctly (https://github.com/chanzuckerberg/happy/blame/172d22d7303b7ea790ac54a977755773e05871d3/pkg/util/preconditions.go#L33). I doubt this is the behavior you would want here. When I was testing out CLI usage, I ran happy build and got this:

% happy build
panic: Invalid Semantic Version

goroutine 1 [running]:
github.com/Masterminds/semver/v3.MustParse(...)
        github.com/Masterminds/semver/[email protected]/version.go:214
github.com/chanzuckerberg/happy/pkg/util.ValidateEnvironment({0x2326448, 0xc0000a8000})
        github.com/chanzuckerberg/happy/pkg/util/preconditions.go:33 +0x706
github.com/chanzuckerberg/happy/cmd.glob..func5(0x2dc3ea0, {0x2020dca, 0x0, 0x0})
        github.com/chanzuckerberg/happy/cmd/root.go:39 +0x6c
github.com/spf13/cobra.(*Command).execute(0x2dc3ea0, {0x2dfd0e8, 0x0, 0x0})
        github.com/spf13/[email protected]/command.go:835 +0x52f
github.com/spf13/cobra.(*Command).ExecuteC(0x2dc43a0)
        github.com/spf13/[email protected]/command.go:974 +0x3bc
github.com/spf13/cobra.(*Command).Execute(...)
        github.com/spf13/[email protected]/command.go:902
github.com/chanzuckerberg/happy/cmd.Execute(...)
        github.com/chanzuckerberg/happy/cmd/root.go:46
main.main()
        github.com/chanzuckerberg/happy/main.go:17 +0x68

I understand that I don't have a file set up properly, but I think using an API that allows you to return an error with a meaningful message would be more helpful than a panic. This also seems to happen with commands like checking the happy version:

% happy version
panic: Invalid Semantic Version

goroutine 1 [running]:
github.com/Masterminds/semver/v3.MustParse(...)
        github.com/Masterminds/semver/[email protected]/version.go:214
github.com/chanzuckerberg/happy/pkg/util.ValidateEnvironment({0x2326448, 0xc000034098})
        github.com/chanzuckerberg/happy/pkg/util/preconditions.go:33 +0x706
github.com/chanzuckerberg/happy/cmd.glob..func5(0x2dc34a0, {0x2020dca, 0x0, 0x0})
        github.com/chanzuckerberg/happy/cmd/root.go:39 +0x6c
github.com/spf13/cobra.(*Command).execute(0x2dc34a0, {0x2dfd0e8, 0x0, 0x0})
        github.com/spf13/[email protected]/command.go:835 +0x52f
github.com/spf13/cobra.(*Command).ExecuteC(0x2dc43a0)
        github.com/spf13/[email protected]/command.go:974 +0x3bc
github.com/spf13/cobra.(*Command).Execute(...)
        github.com/spf13/[email protected]/command.go:902
github.com/chanzuckerberg/happy/cmd.Execute(...)
        github.com/chanzuckerberg/happy/cmd/root.go:46
main.main()
        github.com/chanzuckerberg/happy/main.go:17 +0x68

bug: migrate logs: ResourceNotFoundException the specified log stream does not exist

% happy migrate --reset treeids
[INFO]: using task definition arn:aws:ecs:us-west-2:0000000:task-definition/foobar7
[ERROR]: could not get logs for task, could not get logs: ResourceNotFoundException: The specified log stream does not exist.
[INFO]: using task definition arn:aws:ecs:us-west-2:0000000:task-definition/foobar:7
[ERROR]: could not get logs for task, could not get logs: ResourceNotFoundException: The specified log stream does not exist.

happy v0.10.0

fix: ECS tries to pull a non-existent image on certain slices

Related to #238--what is the intended behavior for when slices are built, tagged, and pushed? If none of the services named in the integration secret belong to the profile defined by the slice, it seems that ECS looks to pull an image for a tag that doesn't get pushed and consequently hangs. Would it make sense to just push all services defined in docker-compose just so ECS at least finds something and doesn't hang?

bug: happy logs should support one-off tasks

The Python Happy CLI supports tailing the logs of one-off tasks (migrations, db deletions) as well as services. The golang CLI only supports logs for services:

Expected:

% ./scripts/happy logs daterange migrations --since 2h
2022-04-21T18:53:29.261000+00:00 fargate/db/154c60567bff414cbe750a1b4aa3f300 ALTER TABLE
2022-04-21T18:53:29.331000+00:00 fargate/db/154c60567bff414cbe750a1b4aa3f300 ALTER TABLE
2022-04-21T18:53:29.338000+00:00 fargate/db/154c60567bff414cbe750a1b4aa3f300 ALTER TABLE
2022-04-21T18:53:29.343000+00:00 fargate/db/154c60567bff414cbe750a1b4aa3f300 ALTER TABLE

Actual:

% happy logs daterange migrations --since 2h
[FATAL]: error retrieving tasks for service 'daterange-migrations': cannot retrieve ECS tasks: operation error ECS: ListTasks, https response error StatusCode: 400, RequestID: 71454f50-4cf5-4e4f-b3e1-3f5354991891, ServiceNotFoundException: Service not found.

fix: namespacing in the infrastructure of happypath

Here are two examples of namespacing issues for happy applications shared in the same account:

We should review the happy-env-ecs module and make sure all resources it creates are properly namespaced by environment so we can avoid created ambiguous resources that we aren't sure what happy application it is attached to. I think this should be as simple as making sure all the unique identifiers for resources follow our conventions of using a combination of env, service, component, owner

feat: increase visibility on TFE client errors

what happened:
the deploy-staging github action https://github.com/chanzuckerberg/eng-portal/actions/runs/2424427561/attempts/6 errored out with [FATAL]: failed to get workspace for stack eng-portal: failed to get workspace: could not read workspace staging-eng-portal: resource not found but running the update locally does not result in this error.

the fix:

  • create a new TFE team eng-portal-pros with all permissions
  • create a new token for the team
  • copy the token into github actions secrets

thoughts:
it wasn't clear at which level of abstraction the client failed to find the workspace. perhaps having an output for API client health would have pointed us in the right direction here?

feat: rds information in the integration secret

It would be great if the RDS information such as the host, username, and password were in the integration secret. These all need to be consumed by the application code to connect to the database so it makes sense that they would live there. I can contribute this change, just documenting the use case here because as of right now, I had to go to the statefile to see the RDS password and manually copy it into my parameter store.

feat: command that checks if a stack exists or not

we do things like (happy --profile="" list | grep -q $STACK_NAME in a couple of places to determine if we should create or update a happy stack.

2 options:

  • something like happy get stack <stack_name> will output information about the stack if it exists or an error otherwise.
  • happy update allows passing in a flag to create if missing.

feat: multiservice/application support

Currently the happy path stack supports only running single service.We will be working to support multiple services/applications in a single happy path stack

feat: rdev stacks + stateful deps

Think about a way to help facilitate the management of stateful dependencies (s3, sql db, etc) for happy apps. In particular, is there anything nice we can build for rdev (dev,staging,prod have a good handle already via terraform since they don't have to pay the rds bootstrap cost more than once)

Check user authentication status and issue warning

If the user has not authenticated with TF Enterprise (recently), then happy commands file. E.g. happy list reports:
failed to get workspace: could not read workspace <stack>: unauthorized.

The CLI could check the user's authentication status and explain that the user needs to (re)authenticate.

This behavior existed in the Happy CLI Python version.

feat: As an engineer using Happy CLI, I would like to know how long do stages of the run take, so I can stay informed

Oftentimes, happy create stack takes a while, and due to the verbosity of the output, it is not trivial to understand how long does each phase of the run take. It will be beneficial to display a summary at the end of the run, indicating how long did the entire process take, and break it down by each key component. For example:

docker build: 20m
docker push: 1m
terraform: 2m
------------------
total: 23m

chore: consolidate tailing aws logs to use the Go SDK

We should phase out shelling out to external dependencies and instead directly use Go clients + sdks. One particular example is we have 2 paths for tailing aws logs:

  • aws cli --
    func (s *Orchestrator) Logs(stackName string, service string, since string) error {
    // TODO get logs path from ECS instead of generating
    logPrefix := s.backend.Conf().LogGroupPrefix()
    logPath := fmt.Sprintf("%s/%s/%s", logPrefix, stackName, service)
    awsProfile := s.backend.Conf().AwsProfile()
    regionName := "us-west-2"
    awsArgs := []string{"aws", "--profile", awsProfile, "--region", regionName, "logs", "tail", "--since", since, "--follow", logPath}
    awsCmd, err := exec.LookPath("aws")
    if err != nil {
    return errors.Wrap(err, "failed to locate the AWS cli")
    }
    cmd := &exec.Cmd{
    Path: awsCmd,
    Args: awsArgs,
    Stderr: os.Stderr,
    Stdout: os.Stdout,
    }
    log.Println(cmd)
    if err := s.executor.Run(cmd); err != nil {
    return errors.Wrap(err, "failed to get logs from AWS")
    }
    return nil
    }
  • go sdk --
    package aws
    import (
    "context"
    cwlv2 "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
    )
    type GetLogsFunc func(*cwlv2.GetLogEventsOutput, error) error
    func (b *Backend) getLogs(
    ctx context.Context,
    input *cwlv2.GetLogEventsInput,
    f GetLogsFunc,
    ) error {
    paginator := cwlv2.NewGetLogEventsPaginator(
    b.cwlGetLogEventsAPIClient,
    input,
    )
    for paginator.HasMorePages() {
    err := f(paginator.NextPage(ctx))
    if isStop(err) {
    return nil
    }
    if err != nil {
    return err
    }
    }
    return nil
    }

We should get rid of the aws cli path and replace it with the Go SDK path

prompt TFE login when TFE token is expired

We currently error out when the TFE token is expired or dormant and should handle this more gracefully instead.

Need to figure out if:

  • terraform login. Required if there is a token that is now permanently unauthorized. Say you deleted the token from the TFE UI.
  • or "open browser to refresh token". Can do this when the token is "dormant"

I'm not sure if the client has enough information to distinguish between 2,3 scenarios so we might just have to pick one or the other.

edit:

  • terraform login. Required if the token does not exist. this is handled already

fix: I don't understand which images happy chooses to push/build

Here is an example workflow for pushing to staging https://github.com/chanzuckerberg/eng-portal/runs/6327429188?check_suite_focus=true. Specifically this line. When happy does a build and push, it uses this command:

/usr/bin/docker compose --file /home/runner/work/eng-portal/eng-portal/docker-compose.yml --profile * build

Looking at my docker-compose file, this means it should build two images: eng-portal-local-dev and eng-portal:

version: "3.8"

services:
  eng-portal-local-dev:
    build:
      context: .
      target: devenv
    restart: always
    ports:
      - 3000:3000
      - 7007:7007
    volumes:
      - ./backstage:/app/backstage
      - /app/backstage/node_modules/
      - /app/backstage/packages/app/node_modules/
      - /app/backstage/packages/backend/node_modules/
    profiles:
      - local-dev
    depends_on:
      - db
    environment:
      - AWS_PROFILE=czi-si-readonly 
      - CHAMBER_SERVICE=happy-ldev-ep
      - AWS_DEFAULT_REGION
      - AWS_DEFAULT_OUTPUT
      - AWS_ACCESS_KEY_ID
      - AWS_SECRET_ACCESS_KEY
      - AWS_SESSION_TOKEN
  db:
    image: postgres
    profiles:
      - local-dev
      - prod-builder
    restart: always
    ports:
      - 5432:5432
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
  eng-portal:
      build:
        context: .
        target: prodenv
      restart: always
      profiles:
        - prod-builder
      depends_on:
        - db
      ports:
      - 7007:7007
      environment:
        - AWS_PROFILE=czi-si-readonly 
        - CHAMBER_SERVICE=happy-ldev-ep
        - AWS_DEFAULT_REGION
        - AWS_DEFAULT_OUTPUT
        - AWS_ACCESS_KEY_ID
        - AWS_SECRET_ACCESS_KEY
        - AWS_SESSION_TOKEN

And when it pushes, I would expect only the eng-portal service image to make it into ECR. However, looking at this workflow logs, it only builds the eng-portal-local-dev container and pushes that to ECR. Does anyone know why eng-portal-local-dev is making it into ECR? Based on #238 it should only push images that have the same name as the ones defined in the happy environment (in this case "eng-portal").

fix: detecting when stacks are sharing a resource

I was currently using the following code to attach an inline policy to my ECS role:

module "ssm-reader-policy" {
  source = "github.com/chanzuckerberg/cztack//aws-params-reader-policy?ref=v0.43.3"
  #TODO: how to do this in happy? there are no tags
  project   = "happy"
  env       = var.deployment_stage
  service   = local.service_name
  region    = data.aws_region.current.name
  role_name = var.task_role.name
}

However, the role var.task_role.name was a role that was given to me by the integration secret, which means it is shared between all services in the same cluster. I notice that while two or more people are making changes to their stacks, the role's inline policy was changing over time. Sometimes, it would have the policy and sometimes it would not. I think this is because of how the stacks are sharing this ECS role and each stack can have a different branch of the terraform code that is being applied to their stack.

Is there a better way to handle this? I don't know how else I would provide permissions to my stack that wouldn't be altered by other stacks. I also wonder if we should detect these kinds of shared resources and guard against them.

feat: add logging about what services are built and pushed when calling happy push

I didn't realize until yesterday that when I did a happy push, happy will only push the images that match the same name as the services that are listed in the happy-env-ecr terraform module. This took me a while to debug. The root cause was that I was changing the name of the services in my docker compose without really realizing the implications. If we could add logs to show what services from the docker compose were being built and pushed, I think that would have helped a lot.

I'll also add a minor complaint here even though it doesn't really belong as part of the feature request. It is a little counter-intuitive that happy prepends a docker registry URL even if one doesn't exist in the docker compose file. As part of my debugging of the issue above, I had an image that I thought would only be built locally. It looked something likethis:

services:
  eng-portal:
    image: eng-portal-local-dev

Notice the image attribute doesn't have a ECR repo prepended to it. However, since the service name matches what existed in the happy-env-ecr terraform module, happy built it and pushed it to the service's ECR. The code that adds the prepended URL is here I believe.

feat: cacheing images

I have repo that currently takes a very long time to build the Docker container due to a large npm install. Whenever I run happy create or happy update, it tries to rebuild the images. Is this by design? Here is the use case where this was frustrating:

  1. I create some Terraform
  2. I fixed some code
  3. I create a stack to test my changes
  4. I realize the Terraform is not right, so I make an update to the terraform
  5. I run happy update <stack_name>
  6. Even though I can use the same image, it rebuilds it

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.