Skip to content

Commit 0c68f8e

Browse files
committed
0.12.0
1 parent 1509273 commit 0c68f8e

File tree

9 files changed

+78
-68
lines changed

9 files changed

+78
-68
lines changed

cli/cli.go

+24-17
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,21 @@ import (
3030
// App props
3131
const (
3232
APP = "init-exporter"
33-
VER = "0.11.0"
33+
VER = "0.12.0"
3434
DESC = "Utility for exporting services described by Procfile to init system"
3535
)
3636

3737
// Supported arguments
3838
const (
39-
ARG_PROCFILE = "p:procfile"
40-
ARG_APP_NAME = "n:appname"
41-
ARG_DRY_START = "d:dry-start"
42-
ARG_UNINSTALL = "u:unistall"
43-
ARG_FORMAT = "f:format"
44-
ARG_NO_COLORS = "nc:no-colors"
45-
ARG_HELP = "h:help"
46-
ARG_VERSION = "v:version"
39+
ARG_PROCFILE = "p:procfile"
40+
ARG_APP_NAME = "n:appname"
41+
ARG_DRY_START = "d:dry-start"
42+
ARG_DISABLE_VALIDATION = "D:disable-validation"
43+
ARG_UNINSTALL = "u:unistall"
44+
ARG_FORMAT = "f:format"
45+
ARG_NO_COLORS = "nc:no-colors"
46+
ARG_HELP = "h:help"
47+
ARG_VERSION = "v:version"
4748
)
4849

4950
// Config properies
@@ -83,14 +84,15 @@ const CONFIG_FILE = "/etc/init-exporter.conf"
8384
// ////////////////////////////////////////////////////////////////////////////////// //
8485

8586
var argMap = arg.Map{
86-
ARG_APP_NAME: {},
87-
ARG_PROCFILE: {},
88-
ARG_DRY_START: {Type: arg.BOOL},
89-
ARG_UNINSTALL: {Type: arg.BOOL, Alias: "c:clear"},
90-
ARG_FORMAT: {},
91-
ARG_NO_COLORS: {Type: arg.BOOL},
92-
ARG_HELP: {Type: arg.BOOL},
93-
ARG_VERSION: {Type: arg.BOOL},
87+
ARG_APP_NAME: {},
88+
ARG_PROCFILE: {},
89+
ARG_DRY_START: {Type: arg.BOOL},
90+
ARG_DISABLE_VALIDATION: {Type: arg.BOOL},
91+
ARG_UNINSTALL: {Type: arg.BOOL, Alias: "c:clear"},
92+
ARG_FORMAT: {},
93+
ARG_NO_COLORS: {Type: arg.BOOL},
94+
ARG_HELP: {Type: arg.BOOL},
95+
ARG_VERSION: {Type: arg.BOOL},
9496
}
9597

9698
var user *system.User
@@ -367,6 +369,10 @@ func validateApplication(app *procfile.Application) {
367369
printErrorAndExit("Proc format version 2 support is disabled")
368370
}
369371

372+
if !arg.GetB(ARG_DRY_START) && arg.GetB(ARG_DISABLE_VALIDATION) {
373+
return
374+
}
375+
370376
errs := app.Validate()
371377

372378
if len(errs) == 0 {
@@ -465,6 +471,7 @@ func showUsage() {
465471

466472
info.AddOption(ARG_PROCFILE, "Path to procfile", "file")
467473
info.AddOption(ARG_DRY_START, "Dry start {s-}(don't export anything, just parse and test procfile){!}")
474+
info.AddOption(ARG_DISABLE_VALIDATION, "Disable application validation")
468475
info.AddOption(ARG_UNINSTALL, "Remove scripts and helpers for a particular application")
469476
info.AddOption(ARG_FORMAT, "Format of generated configs", "upstart|systemd")
470477
info.AddOption(ARG_NO_COLORS, "Disable colors in output")

common/init-exporter.spec

+5-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
Summary: Utility for exporting services described by Procfile to init system
4444
Name: init-exporter
45-
Version: 0.11.0
45+
Version: 0.12.0
4646
Release: 0%{?dist}
4747
Group: Development/Tools
4848
License: MIT
@@ -132,6 +132,10 @@ rm -rf %{buildroot}
132132
###############################################################################
133133

134134
%changelog
135+
* Mon Apr 10 2017 Anton Novojilov <[email protected]> - 0.12.0-0
136+
- Improved environment variables validation
137+
- Added argument for disabling application validation
138+
135139
* Fri Apr 07 2017 Anton Novojilov <[email protected]> - 0.11.0-0
136140
- Added application validation before installation
137141
- [converter] Added application validation before procfile converting

export/export_test.go

+3-7
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func (s *ExportSuite) TestUpstartExport(c *C) {
180180
c.Assert(service1Helper[4:], DeepEquals,
181181
[]string{
182182
"[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh", "",
183-
"cd /srv/service/service1-dir && exec env \"STAGING=true\" /bin/echo 'service1:pre' &>>/srv/service/service1-dir/log/service1.log && exec env \"STAGING=true\" /bin/echo 'service1' &>>/srv/service/service1-dir/log/service1.log && exec env \"STAGING=true\" /bin/echo 'service1:post' &>>/srv/service/service1-dir/log/service1.log",
183+
"cd /srv/service/service1-dir && exec env STAGING=true /bin/echo 'service1:pre' &>>/srv/service/service1-dir/log/service1.log && exec env STAGING=true /bin/echo 'service1' &>>/srv/service/service1-dir/log/service1.log && exec env STAGING=true /bin/echo 'service1:post' &>>/srv/service/service1-dir/log/service1.log",
184184
""},
185185
)
186186

@@ -323,8 +323,6 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
323323
"User=service",
324324
"Group=service",
325325
"WorkingDirectory=/srv/service/service1-dir",
326-
"Environment=\"STAGING=true\"",
327-
"",
328326
fmt.Sprintf("ExecStart=/bin/bash %s/test_application-service1.sh &>>/var/log/test_application/service1.log", helperDir),
329327
"",
330328
""},
@@ -357,8 +355,6 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
357355
"User=service",
358356
"Group=service",
359357
"WorkingDirectory=/srv/service/working-dir",
360-
"",
361-
"EnvironmentFile=/srv/service/working-dir/shared/env.vars",
362358
fmt.Sprintf("ExecStart=/bin/bash %s/test_application-service2.sh &>>/var/log/test_application/service2.log", helperDir),
363359
"ExecReload=/bin/kill -SIGUSR2 $MAINPID",
364360
""},
@@ -367,14 +363,14 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
367363
c.Assert(service1Helper[4:], DeepEquals,
368364
[]string{
369365
"[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh", "",
370-
"exec /bin/echo 'service1:pre' &>>/srv/service/service1-dir/log/service1.log && exec /bin/echo 'service1' &>>/srv/service/service1-dir/log/service1.log && exec /bin/echo 'service1:post' &>>/srv/service/service1-dir/log/service1.log",
366+
"exec env STAGING=true /bin/echo 'service1:pre' &>>/srv/service/service1-dir/log/service1.log && exec env STAGING=true /bin/echo 'service1' &>>/srv/service/service1-dir/log/service1.log && exec env STAGING=true /bin/echo 'service1:post' &>>/srv/service/service1-dir/log/service1.log",
371367
""},
372368
)
373369

374370
c.Assert(service2Helper[4:], DeepEquals,
375371
[]string{
376372
"[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh", "",
377-
"exec /bin/echo 'service2'",
373+
"exec env $(cat /srv/service/working-dir/shared/env.vars | xargs) /bin/echo 'service2'",
378374
""},
379375
)
380376

export/provider.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func renderTemplate(name, templateData string, data interface{}) (string, error)
4949

5050
if err != nil {
5151
log.Error(err.Error())
52-
return "", fmt.Errorf("Can't render template")
52+
return "", fmt.Errorf("Can't render template: %v", err)
5353
}
5454

5555
var buffer bytes.Buffer
@@ -59,7 +59,7 @@ func renderTemplate(name, templateData string, data interface{}) (string, error)
5959

6060
if err != nil {
6161
log.Error(err.Error())
62-
return "", fmt.Errorf("Can't render template")
62+
return "", fmt.Errorf("Can't render template: %v", err)
6363
}
6464

6565
return buffer.String(), nil

export/systemd.go

-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ ExecStartPre=/bin/chmod g+w /var/log/{{.Application.Name}}/{{.Service.Name}}.log
8585
User={{.Application.User}}
8686
Group={{.Application.Group}}
8787
WorkingDirectory={{.Service.Options.WorkingDir}}
88-
{{ if .Service.Options.IsEnvSet }}Environment={{.Service.Options.EnvString}}{{ end }}
89-
{{ if .Service.Options.IsEnvFileSet }}EnvironmentFile={{.Service.Options.FullEnvFilePath}}{{ end }}
9088
ExecStart=/bin/bash {{.Service.HelperPath}} &>>/var/log/{{.Application.Name}}/{{.Service.Name}}.log
9189
{{ if .Service.Options.IsReloadSignalSet }}ExecReload=/bin/kill -{{.Service.Options.ReloadSignal}} $MAINPID{{ end }}
9290
`

export/upstart.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const TEMPLATE_UPSTART_HELPER = `#!/bin/bash
2929
3030
[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh
3131
32-
cd {{.Service.Options.WorkingDir}} && {{ if .Service.HasPreCmd }}{{.Service.GetCommandExecWithEnv "pre"}} && {{ end }}{{.Service.GetCommandExecWithEnv ""}}{{ if .Service.HasPostCmd }} && {{.Service.GetCommandExecWithEnv "post"}}{{ end }}
32+
cd {{.Service.Options.WorkingDir}} && {{ if .Service.HasPreCmd }}{{.Service.GetCommandExec "pre"}} && {{ end }}{{.Service.GetCommandExec ""}}{{ if .Service.HasPostCmd }} && {{.Service.GetCommandExec "post"}}{{ end }}
3333
`
3434

3535
// TEMPLATE_UPSTART_APP contains default application template

procfile/procfile.go

+23-35
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const (
2525
REGEXP_V1_LINE = `^([A-z\d_]+):\s*(.+)`
2626
REGEXP_V2_VERSION = `(?m)^\s*version:\s*2\s*$`
2727
REGEXP_PATH_CHECK = `\A[A-Za-z0-9_\-./]+\z`
28-
REGEXP_VALUE_CHECK = `\A[A-Za-z0-9_\-]+\z`
28+
REGEXP_VALUE_CHECK = `\A[A-Za-z0-9_\-,+/:" ]+\z`
2929
)
3030

3131
// ////////////////////////////////////////////////////////////////////////////////// //
@@ -181,7 +181,7 @@ func (s *Service) HasPostCmd() bool {
181181
}
182182

183183
// GetCommandExecWithEnv return full command exec with env vars setting
184-
func (s *Service) GetCommandExecWithEnv(command string) string {
184+
func (s *Service) GetCommandExec(command string) string {
185185
var result = "exec "
186186

187187
if s.Options.IsEnvSet() {
@@ -206,26 +206,6 @@ func (s *Service) GetCommandExecWithEnv(command string) string {
206206
return result
207207
}
208208

209-
// GetCommandExec return full command exec
210-
func (s *Service) GetCommandExec(command string) string {
211-
var result = "exec "
212-
213-
switch command {
214-
case "pre":
215-
result += s.PreCmd
216-
case "post":
217-
result += s.PostCmd
218-
default:
219-
result += s.Cmd
220-
}
221-
222-
if s.Options.IsCustomLogEnabled() {
223-
result += " &>>" + s.Options.FullLogPath()
224-
}
225-
226-
return result
227-
}
228-
229209
// IsRespawnLimitSet return true if respawn options is set
230210
func (so *ServiceOptions) IsRespawnLimitSet() bool {
231211
return so.RespawnCount != 0 || so.RespawnInterval != 0
@@ -275,7 +255,7 @@ func (so *ServiceOptions) EnvString() string {
275255
var clauses []string
276256

277257
for k, v := range so.Env {
278-
clauses = append(clauses, fmt.Sprintf("\"%s=%s\"", k, v))
258+
clauses = append(clauses, fmt.Sprintf("%s=%s", k, v))
279259
}
280260

281261
sort.Strings(clauses)
@@ -465,31 +445,39 @@ func checkPath(value string) error {
465445
return nil
466446
}
467447

468-
// checkValue check any value and return error if value is insecure
469-
func checkValue(value string) error {
470-
if value == "" {
448+
// checkEnv check given env variable and return error if name or value is insecure
449+
func checkEnv(name, value string) error {
450+
if name == "" {
471451
return nil
472452
}
473453

454+
if strings.TrimSpace(value) == "" {
455+
return fmt.Errorf("Environment variable %s has empty value", name)
456+
}
457+
458+
if strings.Contains(value, " ") && !strings.Contains(value, "\"") {
459+
return fmt.Errorf("Environment variable %s has unquoted value with spaces", name)
460+
}
461+
462+
if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(name) {
463+
return fmt.Errorf("Environment variable name %s is misformatted and can't be accepted", name)
464+
}
465+
474466
if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(value) {
475-
return fmt.Errorf("Value %s is insecure and can't be accepted", value)
467+
return fmt.Errorf("Environment variable value %s is misformatted and can't be accepted", value)
476468
}
477469

478470
return nil
479471
}
480472

481-
// checkEnv check given env variable and return error if name or value is insecure
482-
func checkEnv(name, value string) error {
483-
if name == "" || value == "" {
473+
// checkValue check any value and return error if value is insecure
474+
func checkValue(value string) error {
475+
if value == "" {
484476
return nil
485477
}
486478

487-
if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(name) {
488-
return fmt.Errorf("Environment variable name %s is insecure and can't be accepted", value)
489-
}
490-
491479
if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(value) {
492-
return fmt.Errorf("Environment variable value %s is insecure and can't be accepted", value)
480+
return fmt.Errorf("Value %s is insecure and can't be accepted", value)
493481
}
494482

495483
return nil

procfile/procfile_test.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ func (s *ProcfileSuite) TestProcV1Parsing(c *C) {
3535
c.Assert(app.ProcVersion, Equals, 1)
3636
c.Assert(app.Services, HasLen, 3)
3737

38+
errs := app.Validate()
39+
40+
if len(errs) != 0 {
41+
c.Fatalf("Validation errors: %v", errs)
42+
}
43+
3844
c.Assert(app.Services[0].Name, Equals, "my_tail_cmd")
3945
c.Assert(app.Services[0].Cmd, Equals, "/usr/bin/tail -F /var/log/messages")
4046
c.Assert(app.Services[0].Options, NotNil)
@@ -71,6 +77,12 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
7177
c.Assert(app.StartLevel, Equals, 2)
7278
c.Assert(app.StopLevel, Equals, 5)
7379

80+
errs := app.Validate()
81+
82+
if len(errs) != 0 {
83+
c.Fatalf("Validation errors: %v", errs)
84+
}
85+
7486
for _, service := range app.Services {
7587
switch service.Name {
7688
case "my_tail_cmd":
@@ -85,7 +97,9 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
8597
c.Assert(service.Options.Env, NotNil)
8698
c.Assert(service.Options.Env["RAILS_ENV"], Equals, "staging")
8799
c.Assert(service.Options.Env["TEST"], Equals, "true")
88-
c.Assert(service.Options.EnvString(), Equals, "\"RAILS_ENV=staging\" \"TEST=true\"")
100+
c.Assert(service.Options.Env["JAVA_OPTS"], Equals, "\"-Xms512m -Xmx1g -XX:+HeapDumpOnIutOfMemoryError\"")
101+
c.Assert(service.Options.Env["QUEUE"], Equals, "log_syncronizer,file_downloader,log_searcher")
102+
c.Assert(service.Options.EnvString(), Equals, "HEX_HOME=/srv/projects/ploy/shared/tmp JAVA_OPTS=\"-Xms512m -Xmx1g -XX:+HeapDumpOnIutOfMemoryError\" QUEUE=log_syncronizer,file_downloader,log_searcher RAILS_ENV=staging TEST=true")
89103
c.Assert(service.Options.LimitFile, Equals, 4096)
90104
c.Assert(service.Options.LimitProc, Equals, 4096)
91105
c.Assert(service.Application, NotNil)
@@ -124,7 +138,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
124138
c.Assert(service.Options.Env, NotNil)
125139
c.Assert(service.Options.Env["RAILS_ENV"], Equals, "production")
126140
c.Assert(service.Options.Env["TEST"], Equals, "true")
127-
c.Assert(service.Options.EnvString(), Equals, "\"RAILS_ENV=production\" \"TEST=true\"")
141+
c.Assert(service.Options.EnvString(), Equals, "RAILS_ENV=production TEST=true")
128142
c.Assert(service.Options.LimitFile, Equals, 4096)
129143
c.Assert(service.Options.LimitProc, Equals, 4096)
130144
c.Assert(service.Application, NotNil)
@@ -142,7 +156,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
142156
c.Assert(service.Options.Env, NotNil)
143157
c.Assert(service.Options.Env["RAILS_ENV"], Equals, "production")
144158
c.Assert(service.Options.Env["TEST"], Equals, "true")
145-
c.Assert(service.Options.EnvString(), Equals, "\"RAILS_ENV=production\" \"TEST=true\"")
159+
c.Assert(service.Options.EnvString(), Equals, "RAILS_ENV=production TEST=true")
146160
c.Assert(service.Options.LimitFile, Equals, 1024)
147161
c.Assert(service.Options.LimitProc, Equals, 4096)
148162
c.Assert(service.Application, NotNil)

testdata/procfile_v2

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ commands:
2525
interval: 10
2626
env:
2727
RAILS_ENV: staging # if needs to be redefined or extended
28+
JAVA_OPTS: '"-Xms512m -Xmx1g -XX:+HeapDumpOnIutOfMemoryError"'
29+
QUEUE: log_syncronizer,file_downloader,log_searcher
30+
HEX_HOME: /srv/projects/ploy/shared/tmp
2831
working_directory: '/var/...' # if needs to be redefined
2932

3033
my_another_tail_cmd:

0 commit comments

Comments
 (0)