Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for replicated/global mode for service & stack #256

Merged
merged 1 commit into from
Sep 27, 2016
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
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