From 8e9bf4bd512beec7201fa2ca390d62d5514a9487 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 7 Apr 2017 04:40:33 -0400 Subject: [PATCH 1/6] Improvements --- cli/cli.go | 52 +++++++++++++++++++++++++-------------- common/init-exporter.spec | 9 +++++-- converter/converter.go | 21 +++++++++++++++- procfile/procfile.go | 20 +++++++++------ procfile/procfile_test.go | 14 ++++++----- 5 files changed, 81 insertions(+), 35 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 119e8a9..f3f3cc8 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -30,7 +30,7 @@ import ( // App props const ( APP = "init-exporter" - VER = "0.10.0" + VER = "0.11.0" DESC = "Utility for exporting services described by Procfile to init system" ) @@ -152,13 +152,11 @@ func checkForRoot() { user, err = system.CurrentUser() if err != nil { - printError(err.Error()) - os.Exit(1) + printErrorAndExit(err.Error()) } if !user.IsRoot() { - printError("This utility must have superuser privileges (root)") - os.Exit(1) + printErrorAndExit("This utility must have superuser privileges (root)") } } @@ -277,7 +275,7 @@ func validateConfig() { printError("Errors while config validation:") for _, err := range errs { - printError(" %v\n", err) + printError(" %v", err) } os.Exit(1) @@ -328,15 +326,7 @@ func installApplication(appName string) { printErrorAndExit(err.Error()) } - if app.ProcVersion == 1 && !knf.GetB(PROCFILE_VERSION1, true) { - printError("Proc format version 1 support is disabled") - os.Exit(1) - } - - if app.ProcVersion == 2 && !knf.GetB(PROCFILE_VERSION2, true) { - printError("Proc format version 2 support is disabled") - os.Exit(1) - } + validateApplication(app) if arg.GetB(ARG_DRY_START) { os.Exit(0) @@ -345,8 +335,9 @@ func installApplication(appName string) { err = getExporter().Install(app) if err == nil { - log.Aux("User %s (%d) installed service %s", user.RealName, user.RealUID, app.Name) + log.Info("User %s (%d) installed service %s", user.RealName, user.RealUID, app.Name) } else { + log.Error(err.Error()) printErrorAndExit(err.Error()) } } @@ -359,12 +350,38 @@ func uninstallApplication(appName string) { err := getExporter().Uninstall(app) if err == nil { - log.Aux("User %s (%d) uninstalled service %s", user.RealName, user.RealUID, app.Name) + log.Info("User %s (%d) uninstalled service %s", user.RealName, user.RealUID, app.Name) } else { + log.Error(err.Error()) printErrorAndExit(err.Error()) } } +// validateApplication validate application and all services +func validateApplication(app *procfile.Application) { + if app.ProcVersion == 1 && !knf.GetB(PROCFILE_VERSION1, true) { + printErrorAndExit("Proc format version 1 support is disabled") + } + + if app.ProcVersion == 2 && !knf.GetB(PROCFILE_VERSION2, true) { + printErrorAndExit("Proc format version 2 support is disabled") + } + + errs := app.Validate() + + if len(errs) == 0 { + return + } + + printError("Errors while application validation:") + + for _, err := range errs { + printError(" - %v", err) + } + + os.Exit(1) +} + // checkProviderTargetDir check permissions on target dir func checkProviderTargetDir(dir string) error { if !fsutil.CheckPerms("DRWX", dir) { @@ -436,7 +453,6 @@ func printWarn(f string, a ...interface{}) { // printErrorAndExit print error mesage and exit with exit code 1 func printErrorAndExit(f string, a ...interface{}) { - log.Crit(f, a...) printError(f, a...) os.Exit(1) } diff --git a/common/init-exporter.spec b/common/init-exporter.spec index 22bcf27..916357b 100644 --- a/common/init-exporter.spec +++ b/common/init-exporter.spec @@ -42,7 +42,7 @@ Summary: Utility for exporting services described by Procfile to init system Name: init-exporter -Version: 0.10.0 +Version: 0.11.0 Release: 0%{?dist} Group: Development/Tools License: MIT @@ -69,7 +69,7 @@ Utility for exporting services described by Procfile to init system. %package converter Summary: Utility for converting procfiles from v1 to v2 format -Version: 0.3.0 +Version: 0.4.0 Release: 0%{?dist} %description converter @@ -132,6 +132,11 @@ rm -rf %{buildroot} ############################################################################### %changelog +* Fri Apr 07 2017 Anton Novojilov - 0.11.0-0 +- Added application validation before installation +- [converter] Added application validation before procfile converting +- Code refactoring + * Tue Apr 04 2017 Anton Novojilov - 0.10.0-0 - Added kill signal definition feature for v2 and all exporters - Added reload signal definition feature for v2 and all exporters diff --git a/converter/converter.go b/converter/converter.go index f8e300a..8b78de0 100644 --- a/converter/converter.go +++ b/converter/converter.go @@ -26,7 +26,7 @@ import ( // App props const ( APP = "init-exporter-converter" - VER = "0.3.0" + VER = "0.4.0" DESC = "Utility for converting procfiles from v1 to v2 format" ) @@ -200,6 +200,8 @@ func convert(file string) error { printErrorAndExit("Given procfile already converted to v2 format") } + validateApplication(app) + config.WorkingDir, hasCustomWorkingDirs = getWorkingDir(app) v2data, err := renderTemplate( @@ -258,6 +260,23 @@ func getWorkingDir(app *procfile.Application) (string, bool) { return dir, false } +// validateApplication validate application and all services +func validateApplication(app *procfile.Application) { + errs := app.Validate() + + if len(errs) == 0 { + return + } + + printError("Errors while application validation:") + + for _, err := range errs { + printError(" - %v", err) + } + + os.Exit(1) +} + // writeData write procfile data to file func writeData(file, data string) error { return ioutil.WriteFile(file, []byte(data), 0644) diff --git a/procfile/procfile.go b/procfile/procfile.go index 6838ade..958d794 100644 --- a/procfile/procfile.go +++ b/procfile/procfile.go @@ -123,31 +123,35 @@ func Read(path string, config *Config) (*Application, error) { // ////////////////////////////////////////////////////////////////////////////////// // // Validate validate all services in application -func (a *Application) Validate() error { +func (a *Application) Validate() []error { errs := errutil.NewErrors() errs.Add(checkRunLevel(a.StartLevel)) errs.Add(checkRunLevel(a.StopLevel)) + if a.WorkingDir == "" { + errs.Add(fmt.Errorf("Application working dir can't be empty")) + } + for _, service := range a.Services { - errs.Add(service.Validate()) + errs.Add(service.Validate()...) } - return errs.Last() + return errs.All() } // Validate validate service props and options -func (s *Service) Validate() error { +func (s *Service) Validate() []error { errs := errutil.NewErrors() errs.Add(checkValue(s.Name)) - errs.Add(s.Options.Validate()) + errs.Add(s.Options.Validate()...) - return errs.Last() + return errs.All() } // Validate validate service options -func (so *ServiceOptions) Validate() error { +func (so *ServiceOptions) Validate() []error { errs := errutil.NewErrors() errs.Add(checkPath(so.WorkingDir)) @@ -157,7 +161,7 @@ func (so *ServiceOptions) Validate() error { errs.Add(checkEnv(envName, envVal)) } - return errs.Last() + return errs.All() } // HasPreCmd return true if pre command is defined diff --git a/procfile/procfile_test.go b/procfile/procfile_test.go index 5a71028..be21018 100644 --- a/procfile/procfile_test.go +++ b/procfile/procfile_test.go @@ -16,16 +16,18 @@ import ( func Test(t *testing.T) { TestingT(t) } -type ProcfileSuite struct{} +type ProcfileSuite struct { + Config *Config +} // ////////////////////////////////////////////////////////////////////////////////// // -var _ = Suite(&ProcfileSuite{}) +var _ = Suite(&ProcfileSuite{&Config{Name: "test-app", WorkingDir: "/tmp"}}) // ////////////////////////////////////////////////////////////////////////////////// // func (s *ProcfileSuite) TestProcV1Parsing(c *C) { - app, err := Read("../testdata/procfile_v1", &Config{Name: "test-app"}) + app, err := Read("../testdata/procfile_v1", s.Config) c.Assert(err, IsNil) c.Assert(app, NotNil) @@ -54,11 +56,11 @@ func (s *ProcfileSuite) TestProcV1Parsing(c *C) { c.Assert(app.Services[2].Options.Env["SOME_ENV"], Equals, "test") c.Assert(app.Services[2].Options.WorkingDir, Equals, "/srv/service") - c.Assert(app.Validate(), IsNil) + c.Assert(app.Validate(), HasLen, 0) } func (s *ProcfileSuite) TestProcV2Parsing(c *C) { - app, err := Read("../testdata/procfile_v2", &Config{Name: "test-app"}) + app, err := Read("../testdata/procfile_v2", s.Config) c.Assert(err, IsNil) c.Assert(app, NotNil) @@ -153,5 +155,5 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) { } } - c.Assert(app.Validate(), IsNil) + c.Assert(app.Validate(), HasLen, 0) } From 7ca9c87403d3a407866eb82846bc6b0267b07502 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 7 Apr 2017 05:26:55 -0400 Subject: [PATCH 2/6] Added environment file definition feature for v2 and all exporters --- export/export_test.go | 11 ++++++---- export/systemd.go | 1 + procfile/procfile.go | 44 ++++++++++++++++++++++++++++++--------- procfile/procfile_test.go | 20 ++++++++---------- procfile/procfile_v1.go | 2 +- procfile/procfile_v2.go | 12 +++++++++-- testdata/procfile_v2 | 1 + 7 files changed, 63 insertions(+), 28 deletions(-) diff --git a/export/export_test.go b/export/export_test.go index 9418c14..0df663f 100644 --- a/export/export_test.go +++ b/export/export_test.go @@ -180,14 +180,14 @@ 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", ""}, ) c.Assert(service2Helper[4:], DeepEquals, []string{ "[[ -r /etc/profile.d/rbenv.sh ]] && source /etc/profile.d/rbenv.sh", "", - "cd /srv/service/working-dir && exec /bin/echo 'service2'", + "cd /srv/service/working-dir && exec env $(cat /srv/service/working-dir/shared/env.vars | xargs) /bin/echo 'service2'", ""}, ) @@ -323,7 +323,8 @@ func (s *ExportSuite) TestSystemdExport(c *C) { "User=service", "Group=service", "WorkingDirectory=/srv/service/service1-dir", - "Environment=STAGING=true", + "Environment=\"STAGING=true\"", + "", fmt.Sprintf("ExecStart=/bin/bash %s/test_application-service1.sh &>>/var/log/test_application/service1.log", helperDir), "", ""}, @@ -357,6 +358,7 @@ func (s *ExportSuite) TestSystemdExport(c *C) { "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", ""}, @@ -410,7 +412,7 @@ func createTestApp(helperDir, targetDir string) *procfile.Application { Options: &procfile.ServiceOptions{ Env: map[string]string{"STAGING": "true"}, WorkingDir: "/srv/service/service1-dir", - LogPath: "log/service1.log", + LogFile: "log/service1.log", KillTimeout: 10, KillSignal: "SIGQUIT", Count: 2, @@ -426,6 +428,7 @@ func createTestApp(helperDir, targetDir string) *procfile.Application { Cmd: "/bin/echo 'service2'", Application: app, Options: &procfile.ServiceOptions{ + EnvFile: "shared/env.vars", WorkingDir: "/srv/service/working-dir", ReloadSignal: "SIGUSR2", IsRespawnEnabled: true, diff --git a/export/systemd.go b/export/systemd.go index e5ff9b2..ccab39a 100644 --- a/export/systemd.go +++ b/export/systemd.go @@ -86,6 +86,7 @@ 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 }} ` diff --git a/procfile/procfile.go b/procfile/procfile.go index 958d794..3a11a17 100644 --- a/procfile/procfile.go +++ b/procfile/procfile.go @@ -55,8 +55,9 @@ type Service struct { type ServiceOptions struct { Env map[string]string // Environment variables + EnvFile string // Path to file with environment variables WorkingDir string // Working directory - LogPath string // Path to log file + LogFile string // Path to log file KillTimeout int // Kill timeout in seconds KillSignal string // Kill signal name ReloadSignal string // Reload signal name @@ -155,7 +156,12 @@ func (so *ServiceOptions) Validate() []error { errs := errutil.NewErrors() errs.Add(checkPath(so.WorkingDir)) - errs.Add(checkPath(so.LogPath)) + errs.Add(checkPath(so.LogFile)) + errs.Add(checkPath(so.EnvFile)) + + if so.IsEnvSet() && so.IsEnvFileSet() { + errs.Add(fmt.Errorf("Environment file and environment variables cannot be defined in same time")) + } for envName, envVal := range so.Env { errs.Add(checkEnv(envName, envVal)) @@ -180,6 +186,8 @@ func (s *Service) GetCommandExecWithEnv(command string) string { if s.Options.IsEnvSet() { result += "env " + s.Options.EnvString() + " " + } else if s.Options.IsEnvFileSet() { + result += "env $(cat " + s.Options.FullEnvFilePath() + " | xargs) " } switch command { @@ -225,7 +233,7 @@ func (so *ServiceOptions) IsRespawnLimitSet() bool { // IsCustomLogEnabled return true if service have custom log func (so *ServiceOptions) IsCustomLogEnabled() bool { - return so.LogPath != "" + return so.LogFile != "" } // IsEnvSet return true if service have custom env vars @@ -233,6 +241,11 @@ func (so *ServiceOptions) IsEnvSet() bool { return len(so.Env) != 0 } +// IsEnvFileSet return true if service have file with env vars +func (so *ServiceOptions) IsEnvFileSet() bool { + return so.EnvFile != "" +} + // IsFileLimitSet return true if descriptors limit is set func (so *ServiceOptions) IsFileLimitSet() bool { return so.LimitFile != 0 @@ -262,7 +275,7 @@ func (so *ServiceOptions) EnvString() string { var clauses []string for k, v := range so.Env { - clauses = append(clauses, k+"="+v) + clauses = append(clauses, fmt.Sprintf("\"%s=%s\"", k, v)) } sort.Strings(clauses) @@ -272,11 +285,20 @@ func (so *ServiceOptions) EnvString() string { // FullLogPath return absolute path to service log func (so *ServiceOptions) FullLogPath() string { - if strings.HasPrefix(so.LogPath, "/") { - return so.LogPath + if strings.HasPrefix(so.LogFile, "/") { + return so.LogFile } - return so.WorkingDir + "/" + so.LogPath + return so.WorkingDir + "/" + so.LogFile +} + +// FullEnvFilePath return absolute path to file with env vars +func (so *ServiceOptions) FullEnvFilePath() string { + if strings.HasPrefix(so.EnvFile, "/") { + return so.EnvFile + } + + return so.WorkingDir + "/" + so.EnvFile } // ////////////////////////////////////////////////////////////////////////////////// // @@ -355,14 +377,16 @@ func convertMapType(m map[interface{}]interface{}) map[string]string { // mergeServiceOptions merge two ServiceOptions structs func mergeServiceOptions(dst, src *ServiceOptions) { - mergeStringMaps(dst.Env, src.Env) + if !dst.IsEnvFileSet() && src.IsEnvSet() { + mergeStringMaps(dst.Env, src.Env) + } if dst.WorkingDir == "" { dst.WorkingDir = src.WorkingDir } - if dst.LogPath == "" { - dst.LogPath = src.LogPath + if dst.LogFile == "" { + dst.LogFile = src.LogFile } if dst.KillTimeout == 0 { diff --git a/procfile/procfile_test.go b/procfile/procfile_test.go index be21018..aba06d3 100644 --- a/procfile/procfile_test.go +++ b/procfile/procfile_test.go @@ -38,13 +38,13 @@ func (s *ProcfileSuite) TestProcV1Parsing(c *C) { 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) - c.Assert(app.Services[0].Options.LogPath, Equals, "log/my_tail_cmd.log") + c.Assert(app.Services[0].Options.LogFile, Equals, "log/my_tail_cmd.log") c.Assert(app.Services[1].Name, Equals, "my_another_tail_cmd") c.Assert(app.Services[1].Cmd, Equals, "/usr/bin/tailf /var/log/messages") c.Assert(app.Services[1].PreCmd, Equals, "echo my_another_tail_cmd") c.Assert(app.Services[1].Options, NotNil) - c.Assert(app.Services[1].Options.LogPath, Equals, "log/my_another_tail_cmd.log") + c.Assert(app.Services[1].Options.LogFile, Equals, "log/my_another_tail_cmd.log") c.Assert(app.Services[2].Name, Equals, "cmd_with_cd") c.Assert(app.Services[2].Cmd, Equals, "/usr/bin/tail -F /var/log/messages") @@ -77,7 +77,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) { c.Assert(service.Cmd, Equals, "/usr/bin/tail -F /var/log/messages") c.Assert(service.Options, NotNil) c.Assert(service.Options.WorkingDir, Equals, "/var/...") - c.Assert(service.Options.LogPath, Equals, "log/my_tail_cmd.log") + c.Assert(service.Options.LogFile, Equals, "log/my_tail_cmd.log") c.Assert(service.Options.IsCustomLogEnabled(), Equals, true) c.Assert(service.Options.RespawnCount, Equals, 5) c.Assert(service.Options.RespawnInterval, Equals, 10) @@ -85,7 +85,7 @@ 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.EnvString(), Equals, "\"RAILS_ENV=staging\" \"TEST=true\"") c.Assert(service.Options.LimitFile, Equals, 4096) c.Assert(service.Options.LimitProc, Equals, 4096) c.Assert(service.Application, NotNil) @@ -104,10 +104,8 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) { c.Assert(service.Options.RespawnCount, Equals, 7) c.Assert(service.Options.RespawnInterval, Equals, 22) c.Assert(service.Options.IsRespawnEnabled, Equals, false) - 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.EnvFile, Equals, "shared/env.file") + c.Assert(service.Options.EnvString(), Equals, "") c.Assert(service.Options.LimitFile, Equals, 8192) c.Assert(service.Options.LimitProc, Equals, 8192) c.Assert(service.Application, NotNil) @@ -117,7 +115,7 @@ func (s *ProcfileSuite) TestProcV2Parsing(c *C) { c.Assert(service.Cmd, Equals, "/usr/bin/tail -F /var/log/messages") c.Assert(service.Options, NotNil) c.Assert(service.Options.WorkingDir, Equals, "/srv/projects/my_website/current") - c.Assert(service.Options.LogPath, Equals, "log/my_one_another_tail_cmd.log") + c.Assert(service.Options.LogFile, Equals, "log/my_one_another_tail_cmd.log") c.Assert(service.Options.FullLogPath(), Equals, "/srv/projects/my_website/current/log/my_one_another_tail_cmd.log") c.Assert(service.Options.IsCustomLogEnabled(), Equals, true) c.Assert(service.Options.RespawnCount, Equals, 7) @@ -126,7 +124,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) @@ -144,7 +142,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) diff --git a/procfile/procfile_v1.go b/procfile/procfile_v1.go index 53408be..21927bd 100644 --- a/procfile/procfile_v1.go +++ b/procfile/procfile_v1.go @@ -127,7 +127,7 @@ func parseV1Command(name, command string) *Service { service.PreCmd = pre service.PostCmd = post service.Options.Env = env - service.Options.LogPath = log + service.Options.LogFile = log return service } diff --git a/procfile/procfile_v2.go b/procfile/procfile_v2.go index 8e7d3c2..da3e170 100644 --- a/procfile/procfile_v2.go +++ b/procfile/procfile_v2.go @@ -134,7 +134,7 @@ func parseV2Commands(service *Service, yaml *simpleyaml.Yaml) error { cmd, log, _ = parseCommand(cmd) if log != "" { - service.Options.LogPath = log + service.Options.LogFile = log } service.Cmd = cmd @@ -174,7 +174,7 @@ func parseV2Options(options *ServiceOptions, yaml *simpleyaml.Yaml) error { } if yaml.IsExist("log") { - options.LogPath, err = yaml.Get("log").String() + options.LogFile, err = yaml.Get("log").String() if err != nil { return fmt.Errorf("Can't parse \"log\" value: %v", err) @@ -223,6 +223,14 @@ func parseV2Options(options *ServiceOptions, yaml *simpleyaml.Yaml) error { options.Env = convertMapType(env) } + if yaml.IsExist("env_file") { + options.EnvFile, err = yaml.Get("env_file").String() + + if err != nil { + return fmt.Errorf("Can't parse \"env_file\" value: %v", err) + } + } + if yaml.IsPathExist("respawn", "count") || yaml.IsPathExist("respawn", "interval") { if yaml.IsPathExist("respawn", "count") { options.RespawnCount, err = yaml.Get("respawn").Get("count").Int() diff --git a/testdata/procfile_v2 b/testdata/procfile_v2 index e3eda15..d0ff238 100644 --- a/testdata/procfile_v2 +++ b/testdata/procfile_v2 @@ -37,6 +37,7 @@ commands: kill_timeout: 60 kill_signal: SIGQUIT reload_signal: SIGUSR2 + env_file: shared/env.file respawn: false # by default respawn option is enabled my_one_another_tail_cmd: From 6d41df3f657e6afb2234338ff2100f34354f840b Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 7 Apr 2017 06:06:40 -0400 Subject: [PATCH 3/6] Readme updated --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 30481d0..1f9f6d0 100644 --- a/readme.md +++ b/readme.md @@ -162,6 +162,7 @@ commands: kill_timeout: 60 kill_signal: SIGQUIT reload_signal: SIGUSR2 + env_file: shared/staging.env respawn: false # by default respawn option is enabled my_one_another_tail_cmd: @@ -198,6 +199,8 @@ env RAILS_ENV=staging TEST=true your_command `reload_signal` specifies which signal to use when reloading a service. +`env_file` absolute or relative path to file with environment variables. + `respawn` option controls how often the job can fail. If the job restarts more often than `count` times in `interval`, it won't be restarted anymore. From 0396f68d1bdbb8cfa1f819f679d0becca2903cd9 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 7 Apr 2017 08:36:40 -0400 Subject: [PATCH 4/6] Improved converter for better support of procfiles for local run --- converter/converter.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/converter/converter.go b/converter/converter.go index 8b78de0..06d139e 100644 --- a/converter/converter.go +++ b/converter/converter.go @@ -104,6 +104,9 @@ commands: {{ end -}} ` +// DEFAULT_WORKING_DIR is path to default working dir +const DEFAULT_WORKING_DIR = "/tmp" + // ////////////////////////////////////////////////////////////////////////////////// // type templateData struct { @@ -200,10 +203,10 @@ func convert(file string) error { printErrorAndExit("Given procfile already converted to v2 format") } - validateApplication(app) - config.WorkingDir, hasCustomWorkingDirs = getWorkingDir(app) + validateApplication(app) + v2data, err := renderTemplate( "proc_v2", PROCFILE_TEMPLATE, &templateData{config, app, hasCustomWorkingDirs}, @@ -244,16 +247,19 @@ func renderTemplate(name, templateData string, data interface{}) (string, error) // getWorkingDir return path to default working dir and flag // if custom working dirs is used func getWorkingDir(app *procfile.Application) (string, bool) { - var dir = "" + var dir = DEFAULT_WORKING_DIR for _, service := range app.Services { - if dir == "" { - dir = service.Options.WorkingDir + if dir == DEFAULT_WORKING_DIR { + if service.Options.WorkingDir != "" { + dir = service.Options.WorkingDir + } + continue } if dir != service.Options.WorkingDir { - return "/tmp", true + return DEFAULT_WORKING_DIR, true } } From 5833bb2866eb2d775b075ce04ea56d73ae35db0f Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 7 Apr 2017 08:38:59 -0400 Subject: [PATCH 5/6] Updated spec --- common/init-exporter.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/common/init-exporter.spec b/common/init-exporter.spec index 916357b..c485239 100644 --- a/common/init-exporter.spec +++ b/common/init-exporter.spec @@ -135,6 +135,7 @@ rm -rf %{buildroot} * Fri Apr 07 2017 Anton Novojilov - 0.11.0-0 - Added application validation before installation - [converter] Added application validation before procfile converting +- [converter] Improved converter for better support of procfiles for local run - Code refactoring * Tue Apr 04 2017 Anton Novojilov - 0.10.0-0 From 1509273aa5c5a25748b50ebb9dc9b5a059a0a28b Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 7 Apr 2017 10:00:58 -0400 Subject: [PATCH 6/6] Minor fix --- cli/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cli.go b/cli/cli.go index f3f3cc8..3e44dd6 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -275,7 +275,7 @@ func validateConfig() { printError("Errors while config validation:") for _, err := range errs { - printError(" %v", err) + printError(" - %v", err) } os.Exit(1)