Skip to content

Commit

Permalink
Added support for replicated/global mode for service & stack (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
subfuzion authored and generalhenry committed Sep 27, 2016
1 parent 6ae36cb commit c39131f
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 67 deletions.
25 changes: 20 additions & 5 deletions api/rpc/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const serviceRoleLabelName = "io.amp.role"
// Service is used to implement ServiceServer
type Service struct{}

// SwarmMode is needed to export isServiceSpec_Mode type, which consumers can use to
// create a variable and assign either a ServiceSpec_Replicated or ServiceSpec_Global struct
type SwarmMode isServiceSpec_Mode

func init() {
docker, err = client.NewClient(dockerSock, defaultVersion, nil, defaultHeaders)
if err != nil {
Expand Down Expand Up @@ -57,6 +61,21 @@ func (s *Service) Remove(ctx context.Context, req *RemoveRequest) (*RemoveRespon
func Create(ctx context.Context, req *ServiceCreateRequest) (*ServiceCreateResponse, error) {

serv := req.ServiceSpec

var serviceMode swarm.ServiceMode
switch mode := serv.Mode.(type) {
case *ServiceSpec_Replicated:
serviceMode = swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{
Replicas: &mode.Replicated.Replicas,
},
}
case *ServiceSpec_Global:
serviceMode = swarm.ServiceMode{
Global: &swarm.GlobalService{},
}
}

service := swarm.ServiceSpec{
Annotations: swarm.Annotations{
Name: serv.Name,
Expand Down Expand Up @@ -88,17 +107,13 @@ func Create(ctx context.Context, req *ServiceCreateRequest) (*ServiceCreateRespo
LogDriver: nil, //*Driver
},
Networks: nil, //[]NetworkAttachmentConfig
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{
Replicas: &serv.Replicas,
},
},
UpdateConfig: &swarm.UpdateConfig{
Parallelism: 0,
Delay: 0,
FailureAction: "",
},
EndpointSpec: nil, // &EndpointSpec
Mode: serviceMode,
}

// add environment
Expand Down
221 changes: 179 additions & 42 deletions api/rpc/service/service.pb.go

Large diffs are not rendered by default.

22 changes: 16 additions & 6 deletions api/rpc/service/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ package service;
message ServiceSpec {
string image = 1;
string name = 2;
uint64 replicas = 3;
repeated string env = 4;
map<string, string> labels = 5;
map<string, string> container_labels = 6;
repeated PublishSpec publish_specs = 7;
oneof mode {
ReplicatedService replicated = 3;
GlobalService global = 4;
}
repeated string env = 5;
map<string, string> labels = 6;
map<string, string> container_labels = 7;
repeated PublishSpec publish_specs = 8;

}

message PublishSpec {
Expand All @@ -19,6 +23,13 @@ message PublishSpec {
uint32 internal_port = 4;
}

message ReplicatedService {
uint64 replicas = 1;
}

message GlobalService {
}

message ServiceCreateRequest {
ServiceSpec service_spec = 1;
}
Expand All @@ -35,7 +46,6 @@ message RemoveResponse {
string ident = 1;
}


service Service {
rpc Create (ServiceCreateRequest) returns (ServiceCreateResponse) {}
rpc Remove (RemoveRequest) returns (RemoveResponse) {}
Expand Down
42 changes: 36 additions & 6 deletions api/rpc/stack/parse.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package stack

import (
"fmt"
"strings"

"github.com/appcelerator/amp/api/rpc/service"
Expand All @@ -12,6 +13,7 @@ import (
type serviceSpec struct {
Image string `yaml:"image"`
Public []publishSpec `yaml:"public"`
Mode string `yaml:"mode"`
Replicas uint64 `yaml:"replicas"`
Environment interface{} `yaml:"environment"`
Labels interface{} `yaml:"labels"`
Expand Down Expand Up @@ -79,11 +81,6 @@ func ParseStackfile(ctx context.Context, in string) (stack *Stack, err error) {
}
}

replicas := spec.Replicas
if replicas == 0 {
replicas = 1
}

publishSpecs := []*service.PublishSpec{}
for _, p := range spec.Public {
publishSpecs = append(publishSpecs, &service.PublishSpec{
Expand All @@ -94,11 +91,44 @@ func ParseStackfile(ctx context.Context, in string) (stack *Stack, err error) {
})
}

// add service mode and replicas to spec
var swarmMode service.SwarmMode
replicas := spec.Replicas
mode := spec.Mode

// supply a default value for mode only if it is empty and replicas is positive
if mode == "" && replicas > 0 {
mode = "replicated"
}

switch mode {
case "replicated":
if replicas < 1 {
// if replicated then must have at least 1 replica
replicas = 1
}
swarmMode = &service.ServiceSpec_Replicated{
Replicated: &service.ReplicatedService{Replicas: replicas},
}
case "global":
if replicas != 0 {
// global mode can't specify replicas (only allowed 1 per node)
err = fmt.Errorf("replicas can only be used with replicated mode")
return
}
swarmMode = &service.ServiceSpec_Global{
Global: &service.GlobalService{},
}
default:
err = fmt.Errorf("invalid option for mode: %s", mode)
return
}

stack.Services = append(stack.Services, &service.ServiceSpec{
Name: name,
Image: spec.Image,
PublishSpecs: publishSpecs,
Replicas: replicas,
Mode: swarmMode,
Env: env,
Labels: labels,
ContainerLabels: containerLabels,
Expand Down
29 changes: 29 additions & 0 deletions api/rpc/stack/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,33 @@ var (
},
}

sample8_1 = map[string]serviceSpec{
"pinger": {
Image: "appcelerator/pinger",
Mode: "replicated",
Replicas: 3,
Public: []publishSpec{
{
PublishPort: 3000,
InternalPort: 3000,
},
},
},
}

sample8_2 = map[string]serviceSpec{
"pinger": {
Image: "appcelerator/pinger",
Mode: "global",
Public: []publishSpec{
{
PublishPort: 3000,
InternalPort: 3000,
},
},
},
}

// map of filenames to a map of serviceSpec elements (each file has one or more)
compareSpecs = map[string]map[string]serviceSpec{
"sample-01.yml": sample1,
Expand All @@ -143,6 +170,8 @@ var (
"sample-06-2-service-labels.yml": sample6,
"sample-07-1-container-labels.yml": sample7,
"sample-07-2-container-labels.yml": sample7,
"sample-08-1-mode.yml": sample8_1,
"sample-08-2-mode.yml": sample8_2,
}
)

Expand Down
7 changes: 7 additions & 0 deletions api/rpc/stack/test_samples/sample-08-1-mode.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pinger:
image: appcelerator/pinger
mode: replicated
replicas: 3
public:
- publish_port: 3000
internal_port: 3000
6 changes: 6 additions & 0 deletions api/rpc/stack/test_samples/sample-08-2-mode.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pinger:
image: appcelerator/pinger
mode: global
public:
- publish_port: 3000
internal_port: 3000
43 changes: 35 additions & 8 deletions cmd/amp/service-create.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,31 @@ var (
// service name
name string

// service mode
mode = "replicated"

// number of tasks
replicas uint64 = 1
replicas uint64

// environment variables
env []string
env = []string{}

// service labels
serviceLabels []string
serviceLabels = []string{}

// container labels
containerLabels []string
containerLabels = []string{}

// ports
publishSpecs []string
publishSpecs = []string{}
)

func init() {
flags := createCmd.Flags()
flags.StringVar(&name, "name", name, "Service name")
flags.StringSliceVarP(&publishSpecs, "publish", "p", publishSpecs, "Publish a service externally. Format: [published-name|published-port:]internal-service-port[/protocol], i.e. '80:3000/tcp' or 'admin:3000'")
flags.Uint64Var(&replicas, "replicas", replicas, "Number of tasks (default none)")
flags.StringSliceVarP(&publishSpecs, "publish", "p", publishSpecs, "Publish a service externally. Format: [published-name|published-port:]internal-service-port[/protocol], ex: '80:3000/tcp' or 'admin:3000'")
flags.StringVar(&mode, "mode", mode, "Service mode (replicated or global)")
flags.Uint64Var(&replicas, "replicas", replicas, "Number of tasks")
flags.StringSliceVarP(&env, "env", "e", env, "Set environment variables (default [])")
flags.StringSliceVarP(&serviceLabels, "label", "l", serviceLabels, "Set service labels (default [])")
flags.StringSliceVar(&containerLabels, "container-label", containerLabels, "Set container labels for service replicas (default [])")
Expand All @@ -70,11 +74,34 @@ func create(amp *client.AMP, cmd *cobra.Command, args []string) error {
return err
}

// add service mode to spec
var swarmMode service.SwarmMode
switch mode {
case "replicated":
if replicas < 1 {
// if replicated then must have at least 1 replica
replicas = 1
}
swarmMode = &service.ServiceSpec_Replicated{
Replicated: &service.ReplicatedService{Replicas: replicas},
}
case "global":
if replicas != 0 {
// global mode can't specify replicas (only allowed 1 per node)
return fmt.Errorf("replicas can only be used with replicated mode")
}
swarmMode = &service.ServiceSpec_Global{
Global: &service.GlobalService{},
}
default:
return fmt.Errorf("invalid option for mode: %s", mode)
}

spec := &service.ServiceSpec{
Image: image,
Name: name,
Replicas: replicas,
Env: env,
Mode: swarmMode,
Labels: stringmap(serviceLabels),
ContainerLabels: stringmap(containerLabels),
PublishSpecs: parsedSpecs,
Expand Down

0 comments on commit c39131f

Please sign in to comment.