Skip to content

Commit

Permalink
feat(cli): allow backend service to not expose any port
Browse files Browse the repository at this point in the history
  • Loading branch information
iamhopaul123 committed Oct 8, 2020
1 parent 030ba1d commit 8b99efe
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 40 deletions.
6 changes: 5 additions & 1 deletion internal/pkg/cli/svc_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,11 @@ func (o *deploySvcOpts) showSvcURI() error {
}
switch o.targetSvc.Type {
case manifest.BackendServiceType:
log.Successf("Deployed %s, its service discovery endpoint is %s.\n", color.HighlightUserInput(o.name), color.HighlightResource(uri))
msg := fmt.Sprintf("Deployed %s.\n", color.HighlightUserInput(o.name))
if uri != describe.BlankServiceDiscoveryURI {
msg = fmt.Sprintf("Deployed %s, its service discovery endpoint is %s.\n", color.HighlightUserInput(o.name), color.HighlightResource(uri))
}
log.Success(msg)
default:
log.Successf("Deployed %s, you can access it at %s.\n", color.HighlightUserInput(o.name), color.HighlightResource(uri))
}
Expand Down
7 changes: 5 additions & 2 deletions internal/pkg/cli/svc_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func (o *initSvcOpts) createManifest() (string, error) {
manifestMsgFmt = "Manifest file for service %s already exists at %s, skipping writing it.\n"
}
log.Successf(manifestMsgFmt, color.HighlightUserInput(o.name), color.HighlightResource(manifestPath))
log.Infoln(color.Help(fmt.Sprintf("Your manifest contains configurations like your container size and port (:%d).", o.port)))
log.Infoln(color.Help("Your manifest contains configurations like your container size and port."))
log.Infoln()

return manifestPath, nil
Expand Down Expand Up @@ -343,7 +343,6 @@ func (o *initSvcOpts) askDockerfile() error {
}

func (o *initSvcOpts) askSvcPort() error {
// Use flag before anything else
if o.port != 0 {
return nil
}
Expand All @@ -365,6 +364,10 @@ func (o *initSvcOpts) askSvcPort() error {
default:
defaultPort = strconv.Itoa(int(ports[0]))
}
// Skip asking if it is a backend service.
if o.serviceType == manifest.BackendServiceType {
return nil
}

port, err := o.prompt.Get(
fmt.Sprintf(svcInitSvcPortPrompt, color.Emphasize("port")),
Expand Down
13 changes: 12 additions & 1 deletion internal/pkg/cli/svc_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,18 @@ func TestSvcInitOpts_Ask(t *testing.T) {
mockDockerfile: func(m *mocks.MockdockerfileParser) {},
wantedErr: fmt.Errorf("some error"),
},
"skip asking for port for backend service": {
inSvcType: "Backend Service",
inSvcName: wantedSvcName,
inDockerfilePath: wantedDockerfilePath,

mockPrompt: func(m *mocks.Mockprompter) {},
mockDockerfile: func(m *mocks.MockdockerfileParser) {
m.EXPECT().GetExposedPorts().Return([]uint16{}, errors.New("no expose"))
},
mockSel: func(m *mocks.MockdockerfileSelector) {},
wantedErr: nil,
},
"asks for port if not specified": {
inSvcType: wantedSvcType,
inSvcName: wantedSvcName,
Expand Down Expand Up @@ -309,7 +321,6 @@ func TestSvcInitOpts_Ask(t *testing.T) {
require.EqualError(t, err, tc.wantedErr.Error())
} else {
require.NoError(t, err)
require.Equal(t, wantedSvcType, opts.serviceType)
require.Equal(t, wantedSvcName, opts.name)
require.Equal(t, wantedDockerfilePath, opts.dockerfilePath)
}
Expand Down
11 changes: 10 additions & 1 deletion internal/pkg/deploy/cloudformation/stack/backend_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const (
BackendServiceContainerPortParamKey = "ContainerPort"
)

const (
// BlankBackendSvcContainerPort indicates no port is exposed for service container.
BlankBackendSvcContainerPort = "-1"
)

type backendSvcReadParser interface {
template.ReadParser
ParseBackendService(template.WorkloadOpts) (*template.Content, error)
Expand Down Expand Up @@ -100,10 +105,14 @@ func (s *BackendService) Parameters() ([]*cloudformation.Parameter, error) {
if err != nil {
return nil, err
}
containerPort := BlankBackendSvcContainerPort
if s.manifest.BackendServiceConfig.ImageConfig.Port != nil {
containerPort = strconv.FormatUint(uint64(aws.Uint16Value(s.manifest.BackendServiceConfig.ImageConfig.Port)), 10)
}
return append(svcParams, []*cloudformation.Parameter{
{
ParameterKey: aws.String(BackendServiceContainerPortParamKey),
ParameterValue: aws.String(strconv.FormatUint(uint64(aws.Uint16Value(s.manifest.BackendServiceConfig.ImageConfig.Port)), 10)),
ParameterValue: aws.String(containerPort),
},
}...), nil
}
Expand Down
29 changes: 22 additions & 7 deletions internal/pkg/describe/backend_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import (
"github.com/aws/copilot-cli/internal/pkg/term/color"
)

const (
// BlankServiceDiscoveryURI is the blank service discovery URI.
BlankServiceDiscoveryURI = "-"
blankContainerPort = "-"
)

// BackendServiceDescriber retrieves information about a backend service.
type BackendServiceDescriber struct {
app string
Expand Down Expand Up @@ -71,9 +77,13 @@ func (d *BackendServiceDescriber) URI(envName string) (string, error) {
if err != nil {
return "", fmt.Errorf("retrieve service deployment configuration: %w", err)
}
port := svcParams[stack.LBWebServiceContainerPortParamKey]
if port == stack.BlankBackendSvcContainerPort {
return BlankServiceDiscoveryURI, nil
}
s := serviceDiscovery{
Service: d.svc,
Port: svcParams[stack.LBWebServiceContainerPortParamKey],
Port: port,
App: d.app,
}
return s.String(), nil
Expand All @@ -98,14 +108,19 @@ func (d *BackendServiceDescriber) Describe() (HumanJSONStringer, error) {
if err != nil {
return nil, fmt.Errorf("retrieve service deployment configuration: %w", err)
}
services = appendServiceDiscovery(services, serviceDiscovery{
Service: d.svc,
Port: svcParams[stack.LBWebServiceContainerPortParamKey],
App: d.app,
}, env)
port := svcParams[stack.LBWebServiceContainerPortParamKey]
if port != stack.BlankBackendSvcContainerPort {
services = appendServiceDiscovery(services, serviceDiscovery{
Service: d.svc,
Port: port,
App: d.app,
}, env)
} else {
port = blankContainerPort
}
configs = append(configs, &ServiceConfig{
Environment: env,
Port: svcParams[stack.LBWebServiceContainerPortParamKey],
Port: port,
Tasks: svcParams[stack.WorkloadTaskCountParamKey],
CPU: svcParams[stack.WorkloadTaskCPUParamKey],
Memory: svcParams[stack.WorkloadTaskMemoryParamKey],
Expand Down
53 changes: 44 additions & 9 deletions internal/pkg/describe/backend_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@ type backendSvcDescriberMocks struct {

func TestBackendServiceDescriber_Describe(t *testing.T) {
const (
testApp = "phonetool"
testEnv = "test"
testSvc = "jobs"
testSvcPath = "*"
prodEnv = "prod"
prodSvcPath = "*"
testApp = "phonetool"
testEnv = "test"
testSvc = "jobs"
prodEnv = "prod"
mockEnv = "mockEnv"
)
mockErr := errors.New("some error")
testCases := map[string]struct {
Expand Down Expand Up @@ -75,7 +74,7 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
shouldOutputResources: true,
setupMocks: func(m backendSvcDescriberMocks) {
gomock.InOrder(
m.storeSvc.EXPECT().ListEnvironmentsDeployedTo(testApp, testSvc).Return([]string{testEnv, prodEnv}, nil),
m.storeSvc.EXPECT().ListEnvironmentsDeployedTo(testApp, testSvc).Return([]string{testEnv, prodEnv, mockEnv}, nil),

m.svcDescriber.EXPECT().Params().Return(map[string]string{
stack.LBWebServiceContainerPortParamKey: "5000",
Expand All @@ -99,6 +98,17 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
"COPILOT_ENVIRONMENT_NAME": prodEnv,
}, nil),

m.svcDescriber.EXPECT().Params().Return(map[string]string{
stack.LBWebServiceContainerPortParamKey: "-1",
stack.WorkloadTaskCountParamKey: "2",
stack.WorkloadTaskCPUParamKey: "512",
stack.WorkloadTaskMemoryParamKey: "1024",
}, nil),
m.svcDescriber.EXPECT().EnvVars().Return(
map[string]string{
"COPILOT_ENVIRONMENT_NAME": mockEnv,
}, nil),

m.svcDescriber.EXPECT().ServiceStackResources().Return([]*cloudformation.StackResource{
{
ResourceType: aws.String("AWS::EC2::SecurityGroupIngress"),
Expand All @@ -111,6 +121,12 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
PhysicalResourceId: aws.String("sg-0758ed6b233743530"),
},
}, nil),
m.svcDescriber.EXPECT().ServiceStackResources().Return([]*cloudformation.StackResource{
{
ResourceType: aws.String("AWS::EC2::SecurityGroup"),
PhysicalResourceId: aws.String("sg-2337435300758ed6b"),
},
}, nil),
)
},
wantedBackendSvc: &backendSvcDesc{
Expand All @@ -132,6 +148,13 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
Port: "5000",
Tasks: "2",
},
{
CPU: "512",
Environment: "mockEnv",
Memory: "1024",
Port: "-",
Tasks: "2",
},
},
ServiceDiscovery: []*ServiceDiscovery{
{
Expand All @@ -140,6 +163,11 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
},
},
Variables: []*EnvVars{
{
Environment: "mockEnv",
Name: "COPILOT_ENVIRONMENT_NAME",
Value: "mockEnv",
},
{
Environment: "prod",
Name: "COPILOT_ENVIRONMENT_NAME",
Expand All @@ -164,6 +192,12 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
PhysicalID: "sg-0758ed6b233743530",
},
},
"mockEnv": {
{
Type: "AWS::EC2::SecurityGroup",
PhysicalID: "sg-2337435300758ed6b",
},
},
},
},
},
Expand All @@ -189,8 +223,9 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
enableResources: tc.shouldOutputResources,
store: mockStore,
svcDescriber: map[string]svcDescriber{
"test": mockSvcDescriber,
"prod": mockSvcDescriber,
"test": mockSvcDescriber,
"prod": mockSvcDescriber,
"mockEnv": mockSvcDescriber,
},
initServiceDescriber: func(string) error { return nil },
}
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/manifest/backend_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func NewBackendService(props BackendServiceProps) *BackendService {
// Apply overrides.
svc.Name = aws.String(props.Name)
svc.BackendServiceConfig.ImageConfig.Build.BuildArgs.Dockerfile = aws.String(props.Dockerfile)
svc.BackendServiceConfig.ImageConfig.Port = aws.Uint16(props.Port)
svc.BackendServiceConfig.ImageConfig.Port = uint16P(props.Port)
svc.BackendServiceConfig.ImageConfig.HealthCheck = healthCheck
svc.parser = template.New()
return svc
Expand Down
7 changes: 2 additions & 5 deletions internal/pkg/manifest/backend_svc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ func TestNewBackendSvc(t *testing.T) {

wantedManifest *BackendService
}{
"without healthcheck": {
"without healthcheck and port": {
inProps: BackendServiceProps{
WorkloadProps: WorkloadProps{
Name: "subscribers",
Dockerfile: "./subscribers/Dockerfile",
},
Port: 8080,
},
wantedManifest: &BackendService{
Workload: Workload{
Expand All @@ -43,7 +42,6 @@ func TestNewBackendSvc(t *testing.T) {
},
},
},
Port: aws.Uint16(8080),
},
},
TaskConfig: TaskConfig{
Expand Down Expand Up @@ -125,13 +123,12 @@ func TestBackendSvc_MarshalBinary(t *testing.T) {

wantedTestdata string
}{
"without healthcheck": {
"without healthcheck and port": {
inProps: BackendServiceProps{
WorkloadProps: WorkloadProps{
Name: "subscribers",
Dockerfile: "./subscribers/Dockerfile",
},
Port: 8080,
},
wantedTestdata: "backend-svc-nohealthcheck.yml",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

# Your service name will be used in naming your resources like log groups, ECS services, etc.
name: subscribers

# Your service is reachable at "http://subscribers.${COPILOT_SERVICE_DISCOVERY_ENDPOINT}:8080" but is not public.
type: Backend Service

Expand Down
5 changes: 1 addition & 4 deletions internal/pkg/manifest/testdata/backend-svc-nohealthcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@

# Your service name will be used in naming your resources like log groups, ECS services, etc.
name: subscribers

# Your service is reachable at "http://subscribers.${COPILOT_SERVICE_DISCOVERY_ENDPOINT}:8080" but is not public.
# Your service does not allow any traffic.
type: Backend Service

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args
build: ./subscribers/Dockerfile
# Port exposed through your container to route traffic to it.
port: 8080

# Number of CPU units for the task.
cpu: 256
Expand Down
7 changes: 7 additions & 0 deletions internal/pkg/manifest/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,10 @@ func dockerfileBuildRequired(workloadType string, svc interface{}) (bool, error)
}
return required, nil
}

func uint16P(n uint16) *uint16 {
if n == 0 {
return nil
}
return &n
}
9 changes: 4 additions & 5 deletions templates/workloads/services/backend/cf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Parameters:
Conditions:
HasAddons:
!Not [!Equals [!Ref AddonsTemplateURL, ""]]
ExposePort:
!Not [!Equals [!Ref ContainerPort, -1]]
Resources:
{{include "loggroup" . | indent 2}}

Expand All @@ -40,8 +42,7 @@ Resources:
ContainerDefinitions:
- Name: !Ref WorkloadName
Image: !Ref ContainerImage
PortMappings:
- ContainerPort: !Ref ContainerPort
PortMappings: !If [ExposePort, [{ContainerPort: !Ref ContainerPort}], !Ref "AWS::NoValue"]
{{include "envvars" . | indent 10}}
{{include "logconfig" . | indent 10}}
{{- if .HealthCheck}}
Expand Down Expand Up @@ -110,8 +111,6 @@ Resources:
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
Port: !Ref ContainerPort
ServiceRegistries: !If [ExposePort, [{RegistryArn: !GetAtt DiscoveryService.Arn, Port: !Ref ContainerPort}], !Ref "AWS::NoValue"]

{{include "addons" . | indent 2}}
Loading

0 comments on commit 8b99efe

Please sign in to comment.