Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,128 @@
# Shift

![GitHub release](https://img.shields.io/github/release/leapfrogtechnology/shift.svg?style=flat)
![CircleCI](https://img.shields.io/circleci/build/github/leapfrogtechnology/shift/master?style=flat)
![GitHub](https://img.shields.io/github/license/leapfrogtechnology/shift.svg?style=flat)

Automated infrastructure creation and deployment tool.

## About

Shift focuses on creating your infrastructure with ease and automating your deployments.

## Usage

### 1. Install Shift:

```
$ curl -sf https://raw.githubusercontent.com/leapfrogtechnology/shift/master/install.sh | sudo sh
```

### 2. Verify Installation:

```
$ shift-cli
```

### 3. AWS Credentials

Before using shift, you have to provide your AWS credentials. Also, make sure you have the correct access for your credentials.

Simply create `~/.aws/credentials` file for storing AWS credentials. <br/>
Example:

```
[example-profile]
aws_access_key_id = xxxxxx
aws_secret_access_key = xxxxxx
```

To know more about AWS configurations, view [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)

### 4. Setup

Go to your project directory and run `setup` command to initiate the setup process.

```
$ shift-cli setup
```

- Enter the name of your project
- Choose the platform
- Choose your AWS profile that was setup earlier.
- Choose the AWS Region where you want your infrastructure to be created.
- Choose the type of deployment you want.
- Name your environment.
- If Backend:
- Enter your application port
- Enter healthcheck path
- Enter dockerfile path
- Enter Slack webhook URL
-

```
Example:

Project Name: shiftbeta
Choose Cloud Provider: AWS
Choose AWS profile: default
Region: US East (North Virginia)
Choose Deployment Type: Backend
Environment Name: dev
Application port: 8080
Heathcheck Path: /api
Dockerfile Path: ./
Slack Webhook URL: https://webhook.slack.com
```

`shift.json` file will be created in your project directory like below.

```json
{
"name": "shiftbeta",
"platform": "AWS",
"profile": "default",
"region": "us-east-1",
"type": "Backend",
"port": "80",
"dockerFilePath": "./",
"healthCheckPath": "/",
"env": {
"dev": {
"cluster": "shiftbeta-dev",
"service": "shiftbeta-dev",
"image": "009409476372.dkr.ecr.us-east-1.amazonaws.com/shiftbeta/dev-backend",
"taskDefinition": "shiftbeta-dev"
}
}
}
```

**If you do not want a project-specific config file, you can skip the above step.**

### 5. Deploy

```
$ shift-cli deploy [env]
$ shift-cli deploy dev
```

Here `dev` is the environment you specified in `shif.json`.

### 6. Destroy

```
$ shift-cli destroy [env]
```

Destroy infrastracture.

### 7. Usage with CI/CD:

Instead of setting up a `~/.aws/credentials` file. You can also use the following environment variables to set up your AWS credentials.

| Variable | Description |
| --------------------- | -------------------------------------- |
| AWS_ACCESS_KEY_ID | Your AWS access key |
| AWS_SECRET_ACCESS_KEY | Your AWS secret key |
| AWS_REGION | AWS region where you added your secret |
3 changes: 3 additions & 0 deletions cli/internals/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/leapfrogtechnology/shift/core/utils/system"
"github.com/leapfrogtechnology/shift/core/utils/system/exit"

"github.com/leapfrogtechnology/shift/deployment/internals/backend"
"github.com/leapfrogtechnology/shift/deployment/internals/frontend"
)

Expand All @@ -30,6 +31,8 @@ func Run(environment string) {

if strings.EqualFold(project.Type, "frontend") {
frontend.Deploy(project, environment)
} else if strings.EqualFold(project.Type, "backend") {
backend.Deploy(project, environment)
}

slack.Notify(project.SlackURL, fmt.Sprintf("*%s* succesfully deployed to *%s*. 🎉 🎉 🎉", project.Name, environment), "#04EBB8")
Expand Down
1 change: 1 addition & 0 deletions cli/internals/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func askEnvironmentName() string {
prompt := &survey.Input{
Message: "Environment Name (eg: dev): ",
}

survey.AskOne(prompt, &environment)

return environment
Expand Down
6 changes: 5 additions & 1 deletion cli/internals/setup/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@ func Run() {

// 3. Save Infrastructure details to shift.json.
projectRequest.Env[deploymentDetails.Environment] = structs.Env{
Bucket: infraInfo.Bucket,
Bucket: infraInfo.Bucket,
Cluster: infraInfo.Cluster,
Service: infraInfo.Service,
TaskDefinition: infraInfo.TaskDefinition,
Image: infraInfo.RepoURL,
}

storage.Save(projectRequest)
Expand Down
23 changes: 16 additions & 7 deletions core/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package structs

// Env defines the structure for a single environment
type Env struct {
Bucket string `json:"bucket,omitempty"`
Cluster string `json:"cluster,omitempty"`
BuildCommand string `json:"buildCommand,omitempty"`
Bucket string `json:"bucket,omitempty"`
Cluster string `json:"cluster,omitempty"`
Service string `json:"service,omitempty"`
Image string `json:"image,omitempty"`
TaskDefinition string `json:"taskDefinition,omitempty"`
BuildCommand string `json:"buildCommand,omitempty"`
}

// Project defines the overall structure for a project deployment.
Expand All @@ -22,8 +25,14 @@ type Project struct {
Env map[string]Env `json:"env"`
}

// Frontend defined the output for frontend infrastructure
type Frontend struct {
Bucket string `json:"bucket"`
URL string `json:"url"`
// Infrastructure defines the output for given by terraform.
type Infrastructure struct {
Bucket string `json:"bucket"`
URL string `json:"url"`
Cluster string `json:"cluster"`
ContainerDefinition string `json:"containerDefinition"`
Service string `json:"service"`
TaskDefinition string `json:"taskDefinition"`
BackendURL string `json:"backendURL"`
RepoURL string `json:"repoURL"`
}
1 change: 1 addition & 0 deletions core/utils/shell/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ func Execute(command string) error {

return errors.New(errorMessage)
}

return nil
}
62 changes: 0 additions & 62 deletions deployment/internals/backend/backend.go

This file was deleted.

50 changes: 50 additions & 0 deletions deployment/internals/backend/deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package backend

import (
"github.com/leapfrogtechnology/shift/core/structs"
"github.com/leapfrogtechnology/shift/core/utils/logger"
"github.com/leapfrogtechnology/shift/core/utils/shell"
)

func buildImage(dockerFilePath string, image string, tag string) {
err := shell.Execute("docker build " + dockerFilePath + " -t " + image + ":" + tag)
logger.FailOnError(err, "Failed to build docker image.")
}

func loginECR(region string, profile string) {
err := shell.Execute("$(aws ecr get-login --no-include-email --region " + region + " --profile " + profile + ")")
logger.FailOnError(err, "Failed to login to ECR.")
}

func pushImage(image string, tag string) {
err := shell.Execute("docker push " + image + ":" + tag)
logger.FailOnError(err, "Failed to push image to ECR.")
}

func updateService(region string, profile string, cluster string, service string) {
err := shell.Execute("aws ecs update-service --cluster " + cluster + " --service " + service + " --force-new-deployment --region " + region + " --profile " + profile)
logger.FailOnError(err, "Failed to update service.")
}

// Deploy uploads a new Docker image and updates the service.
func Deploy(project structs.Project, environment string) {
env := project.Env[environment]

image := env.Image
cluster := env.Cluster
service := env.Service

logger.Info("Building Image:")
buildImage(project.DockerFilePath, image, environment)

logger.Info("Logging in to ECR:")
loginECR(project.Region, project.Profile)

logger.Info("Pushing Docker Image to ECR:")
pushImage(image, environment)

logger.Info("Updating Service:")
updateService(project.Region, project.Profile, cluster, service)

logger.Info("Deployment Successfull. 🎉 🎉 🎉")
}
Loading