Skip to content

Commit

Permalink
Merge pull request #21 from funbox/develop
Browse files Browse the repository at this point in the history
Version 0.12.0
  • Loading branch information
andyone authored Apr 10, 2017
2 parents 7198532 + d129cf4 commit 58c24e0
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 68 deletions.
41 changes: 24 additions & 17 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,21 @@ import (
// App props
const (
APP = "init-exporter"
VER = "0.11.0"
VER = "0.12.0"
DESC = "Utility for exporting services described by Procfile to init system"
)

// Supported arguments
const (
ARG_PROCFILE = "p:procfile"
ARG_APP_NAME = "n:appname"
ARG_DRY_START = "d:dry-start"
ARG_UNINSTALL = "u:unistall"
ARG_FORMAT = "f:format"
ARG_NO_COLORS = "nc:no-colors"
ARG_HELP = "h:help"
ARG_VERSION = "v:version"
ARG_PROCFILE = "p:procfile"
ARG_APP_NAME = "n:appname"
ARG_DRY_START = "d:dry-start"
ARG_DISABLE_VALIDATION = "D:disable-validation"
ARG_UNINSTALL = "u:unistall"
ARG_FORMAT = "f:format"
ARG_NO_COLORS = "nc:no-colors"
ARG_HELP = "h:help"
ARG_VERSION = "v:version"
)

// Config properies
Expand Down Expand Up @@ -83,14 +84,15 @@ const CONFIG_FILE = "/etc/init-exporter.conf"
// ////////////////////////////////////////////////////////////////////////////////// //

var argMap = arg.Map{
ARG_APP_NAME: {},
ARG_PROCFILE: {},
ARG_DRY_START: {Type: arg.BOOL},
ARG_UNINSTALL: {Type: arg.BOOL, Alias: "c:clear"},
ARG_FORMAT: {},
ARG_NO_COLORS: {Type: arg.BOOL},
ARG_HELP: {Type: arg.BOOL},
ARG_VERSION: {Type: arg.BOOL},
ARG_APP_NAME: {},
ARG_PROCFILE: {},
ARG_DRY_START: {Type: arg.BOOL},
ARG_DISABLE_VALIDATION: {Type: arg.BOOL},
ARG_UNINSTALL: {Type: arg.BOOL, Alias: "c:clear"},
ARG_FORMAT: {},
ARG_NO_COLORS: {Type: arg.BOOL},
ARG_HELP: {Type: arg.BOOL},
ARG_VERSION: {Type: arg.BOOL},
}

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

if !arg.GetB(ARG_DRY_START) && arg.GetB(ARG_DISABLE_VALIDATION) {
return
}

errs := app.Validate()

if len(errs) == 0 {
Expand Down Expand Up @@ -465,6 +471,7 @@ func showUsage() {

info.AddOption(ARG_PROCFILE, "Path to procfile", "file")
info.AddOption(ARG_DRY_START, "Dry start {s-}(don't export anything, just parse and test procfile){!}")
info.AddOption(ARG_DISABLE_VALIDATION, "Disable application validation")
info.AddOption(ARG_UNINSTALL, "Remove scripts and helpers for a particular application")
info.AddOption(ARG_FORMAT, "Format of generated configs", "upstart|systemd")
info.AddOption(ARG_NO_COLORS, "Disable colors in output")
Expand Down
6 changes: 5 additions & 1 deletion common/init-exporter.spec
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

Summary: Utility for exporting services described by Procfile to init system
Name: init-exporter
Version: 0.11.0
Version: 0.12.0
Release: 0%{?dist}
Group: Development/Tools
License: MIT
Expand Down Expand Up @@ -132,6 +132,10 @@ rm -rf %{buildroot}
###############################################################################

%changelog
* Mon Apr 10 2017 Anton Novojilov <[email protected]> - 0.12.0-0
- Improved environment variables validation
- Added argument for disabling application validation

* Fri Apr 07 2017 Anton Novojilov <[email protected]> - 0.11.0-0
- Added application validation before installation
- [converter] Added application validation before procfile converting
Expand Down
10 changes: 3 additions & 7 deletions export/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func (s *ExportSuite) TestUpstartExport(c *C) {
c.Assert(service1Helper[4:], DeepEquals,
[]string{
"[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh", "",
"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",
"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",
""},
)

Expand Down Expand Up @@ -323,8 +323,6 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
"User=service",
"Group=service",
"WorkingDirectory=/srv/service/service1-dir",
"Environment=\"STAGING=true\"",
"",
fmt.Sprintf("ExecStart=/bin/bash %s/test_application-service1.sh &>>/var/log/test_application/service1.log", helperDir),
"",
""},
Expand Down Expand Up @@ -357,8 +355,6 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
"User=service",
"Group=service",
"WorkingDirectory=/srv/service/working-dir",
"",
"EnvironmentFile=/srv/service/working-dir/shared/env.vars",
fmt.Sprintf("ExecStart=/bin/bash %s/test_application-service2.sh &>>/var/log/test_application/service2.log", helperDir),
"ExecReload=/bin/kill -SIGUSR2 $MAINPID",
""},
Expand All @@ -367,14 +363,14 @@ func (s *ExportSuite) TestSystemdExport(c *C) {
c.Assert(service1Helper[4:], DeepEquals,
[]string{
"[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh", "",
"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",
"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",
""},
)

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

Expand Down
4 changes: 2 additions & 2 deletions export/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func renderTemplate(name, templateData string, data interface{}) (string, error)

if err != nil {
log.Error(err.Error())
return "", fmt.Errorf("Can't render template")
return "", fmt.Errorf("Can't render template: %v", err)
}

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

if err != nil {
log.Error(err.Error())
return "", fmt.Errorf("Can't render template")
return "", fmt.Errorf("Can't render template: %v", err)
}

return buffer.String(), nil
Expand Down
2 changes: 0 additions & 2 deletions export/systemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ ExecStartPre=/bin/chmod g+w /var/log/{{.Application.Name}}/{{.Service.Name}}.log
User={{.Application.User}}
Group={{.Application.Group}}
WorkingDirectory={{.Service.Options.WorkingDir}}
{{ if .Service.Options.IsEnvSet }}Environment={{.Service.Options.EnvString}}{{ end }}
{{ if .Service.Options.IsEnvFileSet }}EnvironmentFile={{.Service.Options.FullEnvFilePath}}{{ end }}
ExecStart=/bin/bash {{.Service.HelperPath}} &>>/var/log/{{.Application.Name}}/{{.Service.Name}}.log
{{ if .Service.Options.IsReloadSignalSet }}ExecReload=/bin/kill -{{.Service.Options.ReloadSignal}} $MAINPID{{ end }}
`
Expand Down
2 changes: 1 addition & 1 deletion export/upstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const TEMPLATE_UPSTART_HELPER = `#!/bin/bash
[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh
cd {{.Service.Options.WorkingDir}} && {{ if .Service.HasPreCmd }}{{.Service.GetCommandExecWithEnv "pre"}} && {{ end }}{{.Service.GetCommandExecWithEnv ""}}{{ if .Service.HasPostCmd }} && {{.Service.GetCommandExecWithEnv "post"}}{{ end }}
cd {{.Service.Options.WorkingDir}} && {{ if .Service.HasPreCmd }}{{.Service.GetCommandExec "pre"}} && {{ end }}{{.Service.GetCommandExec ""}}{{ if .Service.HasPostCmd }} && {{.Service.GetCommandExec "post"}}{{ end }}
`

// TEMPLATE_UPSTART_APP contains default application template
Expand Down
58 changes: 23 additions & 35 deletions procfile/procfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
REGEXP_V1_LINE = `^([A-z\d_]+):\s*(.+)`
REGEXP_V2_VERSION = `(?m)^\s*version:\s*2\s*$`
REGEXP_PATH_CHECK = `\A[A-Za-z0-9_\-./]+\z`
REGEXP_VALUE_CHECK = `\A[A-Za-z0-9_\-]+\z`
REGEXP_VALUE_CHECK = `\A[A-Za-z0-9_\-.,+/:;" ]+\z`
)

// ////////////////////////////////////////////////////////////////////////////////// //
Expand Down Expand Up @@ -181,7 +181,7 @@ func (s *Service) HasPostCmd() bool {
}

// GetCommandExecWithEnv return full command exec with env vars setting
func (s *Service) GetCommandExecWithEnv(command string) string {
func (s *Service) GetCommandExec(command string) string {
var result = "exec "

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

// GetCommandExec return full command exec
func (s *Service) GetCommandExec(command string) string {
var result = "exec "

switch command {
case "pre":
result += s.PreCmd
case "post":
result += s.PostCmd
default:
result += s.Cmd
}

if s.Options.IsCustomLogEnabled() {
result += " &>>" + s.Options.FullLogPath()
}

return result
}

// IsRespawnLimitSet return true if respawn options is set
func (so *ServiceOptions) IsRespawnLimitSet() bool {
return so.RespawnCount != 0 || so.RespawnInterval != 0
Expand Down Expand Up @@ -275,7 +255,7 @@ func (so *ServiceOptions) EnvString() string {
var clauses []string

for k, v := range so.Env {
clauses = append(clauses, fmt.Sprintf("\"%s=%s\"", k, v))
clauses = append(clauses, fmt.Sprintf("%s=%s", k, v))
}

sort.Strings(clauses)
Expand Down Expand Up @@ -465,31 +445,39 @@ func checkPath(value string) error {
return nil
}

// checkValue check any value and return error if value is insecure
func checkValue(value string) error {
if value == "" {
// checkEnv check given env variable and return error if name or value is insecure
func checkEnv(name, value string) error {
if name == "" {
return nil
}

if strings.TrimSpace(value) == "" {
return fmt.Errorf("Environment variable %s has empty value", name)
}

if strings.Contains(value, " ") && !strings.Contains(value, "\"") {
return fmt.Errorf("Environment variable %s has unquoted value with spaces", name)
}

if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(name) {
return fmt.Errorf("Environment variable name %s is misformatted and can't be accepted", name)
}

if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(value) {
return fmt.Errorf("Value %s is insecure and can't be accepted", value)
return fmt.Errorf("Environment variable value %s is misformatted and can't be accepted", value)
}

return nil
}

// checkEnv check given env variable and return error if name or value is insecure
func checkEnv(name, value string) error {
if name == "" || value == "" {
// checkValue check any value and return error if value is insecure
func checkValue(value string) error {
if value == "" {
return nil
}

if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(name) {
return fmt.Errorf("Environment variable name %s is insecure and can't be accepted", value)
}

if !regexp.MustCompile(REGEXP_VALUE_CHECK).MatchString(value) {
return fmt.Errorf("Environment variable value %s is insecure and can't be accepted", value)
return fmt.Errorf("Value %s is insecure and can't be accepted", value)
}

return nil
Expand Down
21 changes: 18 additions & 3 deletions procfile/procfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ func (s *ProcfileSuite) TestProcV1Parsing(c *C) {
c.Assert(app.ProcVersion, Equals, 1)
c.Assert(app.Services, HasLen, 3)

errs := app.Validate()

if len(errs) != 0 {
c.Fatalf("Validation errors: %v", errs)
}

c.Assert(app.Services[0].Name, Equals, "my_tail_cmd")
c.Assert(app.Services[0].Cmd, Equals, "/usr/bin/tail -F /var/log/messages")
c.Assert(app.Services[0].Options, NotNil)
Expand Down Expand Up @@ -71,6 +77,12 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
c.Assert(app.StartLevel, Equals, 2)
c.Assert(app.StopLevel, Equals, 5)

errs := app.Validate()

if len(errs) != 0 {
c.Fatalf("Validation errors: %v", errs)
}

for _, service := range app.Services {
switch service.Name {
case "my_tail_cmd":
Expand All @@ -85,7 +97,10 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
c.Assert(service.Options.Env, NotNil)
c.Assert(service.Options.Env["RAILS_ENV"], Equals, "staging")
c.Assert(service.Options.Env["TEST"], Equals, "true")
c.Assert(service.Options.EnvString(), Equals, "\"RAILS_ENV=staging\" \"TEST=true\"")
c.Assert(service.Options.Env["JAVA_OPTS"], Equals, "\"-Xms512m -Xmx1g -XX:+HeapDumpOnIutOfMemoryError\"")
c.Assert(service.Options.Env["QUEUE"], Equals, "log_syncronizer,file_downloader,log_searcher")
c.Assert(service.Options.Env["LC_ALL"], Equals, "en_US.UTF-8")
c.Assert(service.Options.EnvString(), Equals, "HEX_HOME=/srv/projects/ploy/shared/tmp JAVA_OPTS=\"-Xms512m -Xmx1g -XX:+HeapDumpOnIutOfMemoryError\" LC_ALL=en_US.UTF-8 QUEUE=log_syncronizer,file_downloader,log_searcher RAILS_ENV=staging TEST=true")
c.Assert(service.Options.LimitFile, Equals, 4096)
c.Assert(service.Options.LimitProc, Equals, 4096)
c.Assert(service.Application, NotNil)
Expand Down Expand Up @@ -124,7 +139,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
c.Assert(service.Options.Env, NotNil)
c.Assert(service.Options.Env["RAILS_ENV"], Equals, "production")
c.Assert(service.Options.Env["TEST"], Equals, "true")
c.Assert(service.Options.EnvString(), Equals, "\"RAILS_ENV=production\" \"TEST=true\"")
c.Assert(service.Options.EnvString(), Equals, "RAILS_ENV=production TEST=true")
c.Assert(service.Options.LimitFile, Equals, 4096)
c.Assert(service.Options.LimitProc, Equals, 4096)
c.Assert(service.Application, NotNil)
Expand All @@ -142,7 +157,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) {
c.Assert(service.Options.Env, NotNil)
c.Assert(service.Options.Env["RAILS_ENV"], Equals, "production")
c.Assert(service.Options.Env["TEST"], Equals, "true")
c.Assert(service.Options.EnvString(), Equals, "\"RAILS_ENV=production\" \"TEST=true\"")
c.Assert(service.Options.EnvString(), Equals, "RAILS_ENV=production TEST=true")
c.Assert(service.Options.LimitFile, Equals, 1024)
c.Assert(service.Options.LimitProc, Equals, 4096)
c.Assert(service.Application, NotNil)
Expand Down
4 changes: 4 additions & 0 deletions testdata/procfile_v2
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ commands:
interval: 10
env:
RAILS_ENV: staging # if needs to be redefined or extended
JAVA_OPTS: '"-Xms512m -Xmx1g -XX:+HeapDumpOnIutOfMemoryError"'
QUEUE: log_syncronizer,file_downloader,log_searcher
HEX_HOME: /srv/projects/ploy/shared/tmp
LC_ALL: "en_US.UTF-8"
working_directory: '/var/...' # if needs to be redefined

my_another_tail_cmd:
Expand Down

0 comments on commit 58c24e0

Please sign in to comment.