Skip to content

Commit

Permalink
Merge pull request #64 from stelligent/develop
Browse files Browse the repository at this point in the history
Complete work for supporting deploy/view/undeploy services
  • Loading branch information
cplee authored Jan 24, 2017
2 parents 5b288d8 + cd3ba93 commit e92fbd6
Show file tree
Hide file tree
Showing 42 changed files with 1,891 additions and 501 deletions.
23 changes: 23 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Contributing to Mu

Please review the following guidelines before contributing to Mu. Also, feel free to propose changes to these guidelines by updating this file and submitting a pull request.

## Issues

TODO

## Pull Requests

TODO

## Styleguide

When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible.

TODO

## License

By contributing your code, you agree to license your contribution under the terms of the [MIT License](LICENSE.md).

All files are released with the MIT license.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ curl -s https://raw.githubusercontent.com/stelligent/mu/master/install.sh | INST
> mu service show [-s <service_name>]
# Deploy the service to an environment
> mu service deploy <environment_name> [-s <service_name>]
> mu service deploy <environment_name>
# Set an environment variable(s) for a service
> mu service setenv <environment_name> [-s <service_name>] key=value[,...]
Expand Down Expand Up @@ -89,5 +89,20 @@ environments:
- sg-xxxxx
- sg-xxxxy
- sg-xxxxz
```
### Define the service for this repo
service:
name: my-service # The unique name of the service (default: the name of the directory that mu.yml was in)
desiredCount: 4 # The desired number of tasks to run for the service (default: 2)
dockerfile: ./Dockerfile # The relative path to the Dockerfile to build images (default: ./Dockerfile)
imageRepository: tutum/hello-world # The repository to push images to and deploy services from. Leave unset to have mu manage an ECR repository (default: none)
port: 80 # The port to expose from the container (default: 8080)
healthEndpoint: /health # The endpoint inside the container to determine if the task is healthy (default: /health)
cpu: 20 # The number of CPU units to allocate to each task (default: 10)
memory: 400 # The amount of memory in MiB to allocate to each task (default: 300)
# The paths to match on in the ALB and route to this service. Leave blank to not create an ALB target group for this service (default: none)
pathPatterns:
- /bananas
- /apples
```
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.2
0.1.3
14 changes: 1 addition & 13 deletions cli/app.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package cli

import (
"bufio"
"github.com/stelligent/mu/common"
"github.com/urfave/cli"
"os"
)

// NewApp creates a new CLI app
Expand Down Expand Up @@ -34,18 +32,8 @@ func NewApp() *cli.App {

}

// load yaml config
yamlFile, err := os.Open(c.String("config"))
if err != nil {
return err
}
defer func() {
yamlFile.Close()
}()

// initialize context
context.Initialize(bufio.NewReader(yamlFile))
return nil
return context.InitializeFromFile(c.String("config"))
}

app.Flags = []cli.Flag{
Expand Down
50 changes: 41 additions & 9 deletions cli/services.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cli

import (
"errors"
"fmt"
"github.com/stelligent/mu/common"
"github.com/stelligent/mu/workflows"
"github.com/urfave/cli"
"os"
)

func newServicesCommand(ctx *common.Context) *cli.Command {
Expand All @@ -13,6 +16,7 @@ func newServicesCommand(ctx *common.Context) *cli.Command {
Usage: "options for managing services",
Subcommands: []cli.Command{
*newServicesShowCommand(ctx),
*newServicesPushCommand(ctx),
*newServicesDeployCommand(ctx),
*newServicesSetenvCommand(ctx),
*newServicesUndeployCommand(ctx),
Expand All @@ -34,8 +38,28 @@ func newServicesShowCommand(ctx *common.Context) *cli.Command {
},
Action: func(c *cli.Context) error {
service := c.String("service")
fmt.Printf("showing service: %s\n", service)
return nil
workflow := workflows.NewServiceViewer(ctx, service, os.Stdout)
return workflow()
},
}

return cmd
}

func newServicesPushCommand(ctx *common.Context) *cli.Command {
cmd := &cli.Command{
Name: "push",
Usage: "push service to repository",
Flags: []cli.Flag{
cli.StringFlag{
Name: "tag, t",
Usage: "tag to push",
},
},
Action: func(c *cli.Context) error {
tag := c.String("tag")
workflow := workflows.NewServicePusher(ctx, tag, os.Stdout)
return workflow()
},
}

Expand All @@ -49,15 +73,19 @@ func newServicesDeployCommand(ctx *common.Context) *cli.Command {
ArgsUsage: "<environment>",
Flags: []cli.Flag{
cli.StringFlag{
Name: "service, s",
Usage: "service to deploy",
Name: "tag, t",
Usage: "tag to deploy",
},
},
Action: func(c *cli.Context) error {
environmentName := c.Args().First()
serviceName := c.String("service")
fmt.Printf("deploying service: %s to environment: %s\n", serviceName, environmentName)
return nil
if len(environmentName) == 0 {
cli.ShowCommandHelp(c, "deploy")
return errors.New("environment must be provided")
}
tag := c.String("tag")
workflow := workflows.NewServiceDeployer(ctx, environmentName, tag)
return workflow()
},
}

Expand Down Expand Up @@ -100,9 +128,13 @@ func newServicesUndeployCommand(ctx *common.Context) *cli.Command {
},
Action: func(c *cli.Context) error {
environmentName := c.Args().First()
if len(environmentName) == 0 {
cli.ShowCommandHelp(c, "undeploy")
return errors.New("environment must be provided")
}
serviceName := c.String("service")
fmt.Printf("undeploying service: %s to environment: %s\n", serviceName, environmentName)
return nil
workflow := workflows.NewServiceUndeployer(ctx, serviceName, environmentName)
return workflow()
},
}

Expand Down
18 changes: 16 additions & 2 deletions cli/services_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestNewServicesCommand(t *testing.T) {
assert.Equal(1, len(command.Aliases), "Aliases len should match")
assert.Equal("svc", command.Aliases[0], "Aliases should match")
assert.Equal("options for managing services", command.Usage, "Usage should match")
assert.Equal(4, len(command.Subcommands), "Subcommands len should match")
assert.Equal(5, len(command.Subcommands), "Subcommands len should match")
}

func TestNewServicesShowCommand(t *testing.T) {
Expand All @@ -35,6 +35,20 @@ func TestNewServicesShowCommand(t *testing.T) {
assert.NotNil(command.Action)
}

func TestNewServicesPushCommand(t *testing.T) {
assert := assert.New(t)

ctx := common.NewContext()

command := newServicesPushCommand(ctx)

assert.NotNil(command)
assert.Equal("push", command.Name, "Name should match")
assert.Equal(1, len(command.Flags), "Flags length")
assert.Equal("tag, t", command.Flags[0].GetName(), "Flags Name")
assert.NotNil(command.Action)
}

func TestNewServicesDeployCommand(t *testing.T) {
assert := assert.New(t)

Expand All @@ -46,7 +60,7 @@ func TestNewServicesDeployCommand(t *testing.T) {
assert.Equal("deploy", command.Name, "Name should match")
assert.Equal("<environment>", command.ArgsUsage, "ArgsUsage should match")
assert.Equal(1, len(command.Flags), "Flags length")
assert.Equal("service, s", command.Flags[0].GetName(), "Flags Name")
assert.Equal("tag, t", command.Flags[0].GetName(), "Flags Name")
assert.NotNil(command.Action)
}

Expand Down
41 changes: 39 additions & 2 deletions common/cluster.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
package common

import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/ecr/ecriface"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
"strings"
)

// ClusterInstanceLister for getting cluster instances
type ClusterInstanceLister interface {
ListInstances(clusterName string) ([]*ecs.ContainerInstance, error)
}

// RepositoryAuthenticator auths for a repo
type RepositoryAuthenticator interface {
AuthenticateRepository(repoURL string) (string, error)
}

// ClusterManager composite of all cluster capabilities
type ClusterManager interface {
ClusterInstanceLister
RepositoryAuthenticator
}

type ecsClusterManager struct {
ecsAPI ecsiface.ECSAPI
ecrAPI ecriface.ECRAPI
}

func newClusterManager(region string) (ClusterManager, error) {
Expand All @@ -27,9 +38,14 @@ func newClusterManager(region string) (ClusterManager, error) {
return nil, err
}
log.Debugf("Connecting to ECS service in region:%s", region)
ecs := ecs.New(sess, &aws.Config{Region: aws.String(region)})
ecsAPI := ecs.New(sess, &aws.Config{Region: aws.String(region)})

log.Debugf("Connecting to ECR service in region:%s", region)
ecrAPI := ecr.New(sess, &aws.Config{Region: aws.String(region)})

return &ecsClusterManager{
ecsAPI: ecs,
ecsAPI: ecsAPI,
ecrAPI: ecrAPI,
}, nil
}

Expand Down Expand Up @@ -62,3 +78,24 @@ func (ecsMgr *ecsClusterManager) ListInstances(clusterName string) ([]*ecs.Conta

return describeOut.ContainerInstances, nil
}

func (ecsMgr *ecsClusterManager) AuthenticateRepository(repoURL string) (string, error) {
ecrAPI := ecsMgr.ecrAPI

params := &ecr.GetAuthorizationTokenInput{}

log.Debug("Authenticating to ECR repo")

resp, err := ecrAPI.GetAuthorizationToken(params)
if err != nil {
return "", err
}

for _, authData := range resp.AuthorizationData {
if strings.HasPrefix(fmt.Sprintf("https://%s", repoURL), aws.StringValue(authData.ProxyEndpoint)) {
return aws.StringValue(authData.AuthorizationToken), nil
}
}

return "", fmt.Errorf("unable to find token for repo url:%s", repoURL)
}
54 changes: 54 additions & 0 deletions common/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package common

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/ecr/ecriface"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -54,3 +56,55 @@ func TestEcsClusterManager_ListInstances(t *testing.T) {
m.AssertNumberOfCalls(t, "DescribeContainerInstances", 1)
m.AssertNumberOfCalls(t, "ListContainerInstancesPages", 1)
}

type mockedECR struct {
mock.Mock
ecriface.ECRAPI
}

func (m *mockedECR) GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
args := m.Called()
return args.Get(0).(*ecr.GetAuthorizationTokenOutput), args.Error(1)
}
func TestEcsClusterManager_AuthenticateRepository(t *testing.T) {
assert := assert.New(t)

m := new(mockedECR)
m.On("GetAuthorizationToken").Return(
&ecr.GetAuthorizationTokenOutput{
AuthorizationData: []*ecr.AuthorizationData{
{
ProxyEndpoint: aws.String("https://foo"),
AuthorizationToken: aws.String("foo"),
},
{
ProxyEndpoint: aws.String("https://bar"),
AuthorizationToken: aws.String("bar"),
},
},
}, nil)

clusterManager := ecsClusterManager{
ecrAPI: m,
}

barTok1, err := clusterManager.AuthenticateRepository("bar")
assert.Nil(err)
assert.Equal("bar", barTok1)

barTok2, err := clusterManager.AuthenticateRepository("bar:latest")
assert.Nil(err)
assert.Equal("bar", barTok2)

fooTok1, err := clusterManager.AuthenticateRepository("foo")
assert.Nil(err)
assert.Equal("foo", fooTok1)

fooTok2, err := clusterManager.AuthenticateRepository("foo:latest")
assert.Nil(err)
assert.Equal("foo", fooTok2)

m.AssertExpectations(t)
m.AssertNumberOfCalls(t, "GetAuthorizationToken", 4)

}
Loading

0 comments on commit e92fbd6

Please sign in to comment.