Skip to content
This repository was archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
Add logging to service's container definition
Browse files Browse the repository at this point in the history
Log to Cloudwatch Logs to enable future development of `fargate service
<service name> logs`.

* Don't segfault if there are no tasks yet in service ps and service info
* If ECR repository already exists, use it rather than returning an error
* Favor AWS SDK constants over local constants
  • Loading branch information
jpignata committed Dec 23, 2017
1 parent db5e9ab commit 7b7cdff
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 21 deletions.
32 changes: 32 additions & 0 deletions cloudwatchlogs/log_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cloudwatchlogs

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
awscwl "github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/jpignata/fargate/console"
)

func (cwl *CloudWatchLogs) CreateLogGroup(logGroupName string, a ...interface{}) string {
formattedLogGroupName := fmt.Sprintf(logGroupName, a...)
_, err := cwl.svc.CreateLogGroup(
&awscwl.CreateLogGroupInput{
LogGroupName: aws.String(formattedLogGroupName),
},
)

if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
switch awsErr.Code() {
case awscwl.ErrCodeResourceAlreadyExistsException:
return formattedLogGroupName
default:
console.ErrorExit(awsErr, "Could not create Cloudwatch Logs log group")
}
}
}

return formattedLogGroupName
}
23 changes: 23 additions & 0 deletions cloudwatchlogs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cloudwatchlogs

import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/jpignata/fargate/console"
)

type CloudWatchLogs struct {
svc *cloudwatchlogs.CloudWatchLogs
}

func New() CloudWatchLogs {
sess, err := session.NewSession()

if err != nil {
console.ErrorExit(err, "Error creating CloudWatch Logs session")
}

return CloudWatchLogs{
svc: cloudwatchlogs.New(sess),
}
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var rootCmd = &cobra.Command{
Use: "fargate",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if verbose {
verbose = true
console.Verbose = true
}
},
Expand Down
18 changes: 16 additions & 2 deletions cmd/service_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strconv"
"strings"

CWL "github.com/jpignata/fargate/cloudwatchlogs"
"github.com/jpignata/fargate/console"
"github.com/jpignata/fargate/docker"
EC2 "github.com/jpignata/fargate/ec2"
Expand All @@ -18,6 +19,8 @@ import (
"github.com/spf13/cobra"
)

const logGroupFormat = "/fargate/service/%s"

var validRuleTypes = regexp.MustCompile("(?i)^host|path$")

var cpu int16
Expand Down Expand Up @@ -163,21 +166,31 @@ func extractEnvVars() {
func createService(serviceName string) {
console.Info("Creating %s", serviceName)

cwl := CWL.New()
ec2 := EC2.New()
ecr := ECR.New()
ecs := ECS.New()
elbv2 := ELBV2.New()
iam := IAM.New()

var targetGroupArn string
var (
targetGroupArn string
repositoryUri string
)

if ecr.IsRepositoryCreated(serviceName) {
repositoryUri = ecr.GetRepositoryUri(serviceName)
} else {
repositoryUri = ecr.CreateRepository(serviceName)
}

clusterName := ecs.CreateCluster()
repositoryUri := ecr.CreateRepository(serviceName)
repository := docker.Repository{
Uri: repositoryUri,
}
subnetIds := ec2.GetDefaultVpcSubnetIds()
ecsTaskExecutionRoleArn := iam.CreateEcsTaskExecutionRole()
logGroupName := cwl.CreateLogGroup(logGroupFormat, serviceName)

if image == "" {
var tag string
Expand Down Expand Up @@ -226,6 +239,7 @@ func createService(serviceName string) {
Memory: strconv.FormatInt(int64(memory), 10),
Name: serviceName,
Port: port.Port,
LogGroupName: logGroupName,
},
)
ecs.CreateService(
Expand Down
2 changes: 1 addition & 1 deletion cmd/service_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func init() {
}

func deployService(serviceName string) {
console.Info("Creating %s", serviceName)
console.Info("Deploying %s", serviceName)

ecs := ECS.New()
service := ecs.DescribeService(serviceName)
Expand Down
3 changes: 1 addition & 2 deletions cmd/service_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ func infoService(serviceName string) {
eniIds = append(eniIds, task.EniId)
}

enis := ec2.DescribeNetworkInterfaces(eniIds)

console.KeyValue("Service Name", "%s\n", serviceName)
console.KeyValue("Status", "\n")
console.KeyValue(" Desired", "%d\n", service.DesiredCount)
Expand All @@ -51,6 +49,7 @@ func infoService(serviceName string) {
if len(tasks) > 0 {
console.Header("== Tasks ==")

enis := ec2.DescribeNetworkInterfaces(eniIds)
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 1, '\t', 0)
fmt.Fprintln(w, "IMAGE\tSTATUS\tDESIRED STATUS\tCREATED\tIP\tCPU\tMEMORY\t")
Expand Down
4 changes: 2 additions & 2 deletions cmd/service_ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func psService(serviceName string) {
eniIds = append(eniIds, task.EniId)
}

enis := ec2.DescribeNetworkInterfaces(eniIds)

if len(tasks) > 0 {
enis := ec2.DescribeNetworkInterfaces(eniIds)

w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 1, '\t', 0)
fmt.Fprintln(w, "IMAGE\tSTATUS\tDESIRED STATUS\tCREATED\tIP\tCPU\tMEMORY\t")
Expand Down
2 changes: 1 addition & 1 deletion ec2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func New() EC2 {
sess, err := session.NewSession()

if err != nil {
color.Red("Error creating VPC session: ", err)
color.Red("Error creating EC2 session: ", err)
os.Exit(1)
}

Expand Down
24 changes: 24 additions & 0 deletions ecr/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
awsecr "github.com/aws/aws-sdk-go/service/ecr"
"github.com/jpignata/fargate/console"
)
Expand All @@ -26,6 +27,29 @@ func (ecr *ECR) CreateRepository(repositoryName string) string {
return aws.StringValue(resp.Repository.RepositoryUri)
}

func (ecr *ECR) IsRepositoryCreated(repositoryName string) bool {
resp, err := ecr.svc.DescribeRepositories(
&awsecr.DescribeRepositoriesInput{
RepositoryNames: aws.StringSlice([]string{repositoryName}),
},
)

if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
switch awsErr.Code() {
case awsecr.ErrCodeRepositoryNotFoundException:
return false
default:
console.ErrorExit(awsErr, "Could not create Cloudwatch Logs log group")
}
}

console.ErrorExit(err, "Couldn't describe Amazon ECR repositories")
}

return len(resp.Repositories) == 1
}

func (ecr *ECR) GetRepositoryUri(repositoryName string) string {
resp, err := ecr.svc.DescribeRepositories(
&awsecr.DescribeRepositoriesInput{
Expand Down
10 changes: 8 additions & 2 deletions ecs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
)

type ECS struct {
svc *awsecs.ECS
svc *awsecs.ECS
sess *session.Session
}

func New() ECS {
Expand All @@ -21,6 +22,11 @@ func New() ECS {
}

return ECS{
svc: awsecs.New(sess),
svc: awsecs.New(sess),
sess: sess,
}
}

func (ecs *ECS) Region() *string {
return ecs.sess.Config.Region
}
4 changes: 4 additions & 0 deletions ecs/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ func (ecs *ECS) DescribeTasksForService(serviceName string) []Task {
var tasks []Task
taskArns := ecs.ListTasksForService(serviceName)

if len(taskArns) == 0 {
return tasks
}

resp, err := ecs.svc.DescribeTasks(
&awsecs.DescribeTasksInput{
Cluster: aws.String(clusterName),
Expand Down
29 changes: 18 additions & 11 deletions ecs/task_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"github.com/jpignata/fargate/console"
)

const logStreamPrefix = "fargate"

var taskDefinitionCache = make(map[string]*awsecs.TaskDefinition)

type CreateTaskDefinitionInput struct {
Expand All @@ -16,22 +18,27 @@ type CreateTaskDefinitionInput struct {
Memory string
Name string
Port int64
LogGroupName string
}

func (ecs *ECS) CreateTaskDefinition(input *CreateTaskDefinitionInput) string {
console.Debug("Creating ECS task definition")

const essential = true
const launchType = "FARGATE"
const networkMode = "awsvpc"

compatbilities := []string{launchType}
logConfiguration := &awsecs.LogConfiguration{
LogDriver: aws.String(awsecs.LogDriverAwslogs),
Options: map[string]*string{
"awslogs-region": ecs.Region(),
"awslogs-group": aws.String(input.LogGroupName),
"awslogs-stream-prefix": aws.String(logStreamPrefix),
},
}

containerDefinition := &awsecs.ContainerDefinition{
Name: aws.String(input.Name),
Essential: aws.Bool(essential),
Image: aws.String(input.Image),
Environment: input.Environment(),
Name: aws.String(input.Name),
Essential: aws.Bool(true),
Image: aws.String(input.Image),
Environment: input.Environment(),
LogConfiguration: logConfiguration,
}

if input.Port != 0 {
Expand All @@ -47,9 +54,9 @@ func (ecs *ECS) CreateTaskDefinition(input *CreateTaskDefinitionInput) string {
resp, err := ecs.svc.RegisterTaskDefinition(
&awsecs.RegisterTaskDefinitionInput{
Family: aws.String(input.Name),
RequiresCompatibilities: aws.StringSlice(compatbilities),
RequiresCompatibilities: aws.StringSlice([]string{awsecs.CompatibilityFargate}),
ContainerDefinitions: []*awsecs.ContainerDefinition{containerDefinition},
NetworkMode: aws.String(networkMode),
NetworkMode: aws.String(awsecs.NetworkModeAwsvpc),
Memory: aws.String(input.Memory),
Cpu: aws.String(input.Cpu),
ExecutionRoleArn: aws.String(input.ExecutionRoleArn),
Expand Down

0 comments on commit 7b7cdff

Please sign in to comment.