-
Notifications
You must be signed in to change notification settings - Fork 136
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
Refactor: Config to Application State #138
Changes from 2 commits
fa036fe
680a226
63d03da
98c22dc
6c9c6f6
8f759b2
b33e7cb
2500db4
e47f19b
eddf937
f08783b
b1bc44b
739f24c
622c9dd
232a03f
9776ebf
9f9d973
d887c28
8bbd8f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,44 +5,26 @@ import ( | |
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"sync" | ||
|
||
"github.com/joyent/containerpilot/backends" | ||
"github.com/joyent/containerpilot/discovery" | ||
"github.com/joyent/containerpilot/services" | ||
"github.com/joyent/containerpilot/tasks" | ||
"github.com/joyent/containerpilot/telemetry" | ||
"github.com/joyent/containerpilot/utils" | ||
) | ||
|
||
var ( | ||
// Version is the version for this build, set at build time via LDFLAGS | ||
Version string | ||
// GitHash is the short-form commit hash of this build, set at build time | ||
GitHash string | ||
log "github.com/Sirupsen/logrus" | ||
) | ||
|
||
// Passing around config as a context to functions would be the ideomatic way. | ||
// But we need to support configuration reload from signals and have that reload | ||
// effect function calls in the main goroutine. Wherever possible we should be | ||
// accessing via `GetConfig` at the "top" of a goroutine and then use the config | ||
// as context for a function after that. | ||
var ( | ||
globalConfig *Config | ||
configLock = new(sync.RWMutex) | ||
) | ||
|
||
func GetConfig() *Config { | ||
configLock.RLock() | ||
defer configLock.RUnlock() | ||
return globalConfig | ||
} | ||
|
||
// Config is the top-level ContainerPilot Configuration | ||
type Config struct { | ||
|
@@ -58,90 +40,19 @@ type Config struct { | |
BackendsConfig json.RawMessage `json:"backends,omitempty"` | ||
TasksConfig json.RawMessage `json:"tasks,omitempty"` | ||
TelemetryConfig json.RawMessage `json:"telemetry,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessarily in scope for this PR, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we can remove it - we really don't re-serialize the config anyway, so the extra tag options are mostly useless. |
||
Services []*services.Service | ||
Backends []*backends.Backend | ||
Tasks []*tasks.Task | ||
Telemetry *telemetry.Telemetry | ||
PreStartCmd *exec.Cmd | ||
PreStopCmd *exec.Cmd | ||
PostStopCmd *exec.Cmd | ||
Command *exec.Cmd | ||
QuitChannels []chan bool | ||
ConfigFlag string | ||
|
||
ConfigFlag string | ||
} | ||
|
||
const ( | ||
// Amount of time to wait before killing the application | ||
defaultStopTimeout int = 5 | ||
) | ||
|
||
func LoadConfig() (*Config, error) { | ||
|
||
var configFlag string | ||
var versionFlag bool | ||
|
||
if !flag.Parsed() { | ||
flag.StringVar(&configFlag, "config", "", | ||
"JSON config or file:// path to JSON config file.") | ||
flag.BoolVar(&versionFlag, "version", false, "Show version identifier and quit.") | ||
flag.Parse() | ||
} | ||
if versionFlag { | ||
fmt.Printf("Version: %s\nGitHash: %s\n", Version, GitHash) | ||
os.Exit(0) | ||
} | ||
if configFlag == "" { | ||
configFlag = os.Getenv("CONTAINERPILOT") | ||
} | ||
|
||
if cfg, err := parseConfig(configFlag); err != nil { | ||
return nil, err | ||
} else { | ||
return initializeConfig(cfg) | ||
} | ||
} | ||
|
||
func ReloadConfig(configFlag string) (*Config, error) { | ||
if cfg, err := parseConfig(configFlag); err != nil { | ||
return nil, err | ||
} else { | ||
return initializeConfig(cfg) | ||
} | ||
} | ||
|
||
func initializeConfig(cfg *Config) (*Config, error) { | ||
// ParseDiscoveryService ... | ||
func (cfg *Config) ParseDiscoveryService() (discovery.DiscoveryService, error) { | ||
var discoveryService discovery.DiscoveryService | ||
discoveryCount := 0 | ||
|
||
// onStart has been deprecated for preStart. Remove in 2.0 | ||
if cfg.PreStart != nil && cfg.OnStart != nil { | ||
fmt.Println("The onStart option has been deprecated in favor of preStart. ContainerPilot will use only the preStart option provided") | ||
} | ||
|
||
// alias the onStart behavior to preStart | ||
if cfg.PreStart == nil && cfg.OnStart != nil { | ||
fmt.Println("The onStart option has been deprecated in favor of preStart. ContainerPilot will use the onStart option as a preStart") | ||
cfg.PreStart = cfg.OnStart | ||
} | ||
|
||
preStartCmd, err := utils.ParseCommandArgs(cfg.PreStart) | ||
if err != nil { | ||
return nil, fmt.Errorf("Could not parse `preStart`: %s", err) | ||
} | ||
cfg.PreStartCmd = preStartCmd | ||
|
||
preStopCmd, err := utils.ParseCommandArgs(cfg.PreStop) | ||
if err != nil { | ||
return nil, fmt.Errorf("Could not parse `preStop`: %s", err) | ||
} | ||
cfg.PreStopCmd = preStopCmd | ||
|
||
postStopCmd, err := utils.ParseCommandArgs(cfg.PostStop) | ||
if err != nil { | ||
return nil, fmt.Errorf("Could not parse `postStop`: %s", err) | ||
} | ||
cfg.PostStopCmd = postStopCmd | ||
|
||
for _, discoveryBackend := range []string{"Consul", "Etcd"} { | ||
switch discoveryBackend { | ||
case "Consul": | ||
|
@@ -156,75 +67,124 @@ func initializeConfig(cfg *Config) (*Config, error) { | |
} | ||
} | ||
} | ||
|
||
if discoveryCount == 0 { | ||
return nil, errors.New("No discovery backend defined") | ||
} else if discoveryCount > 1 { | ||
return nil, errors.New("More than one discovery backend defined") | ||
} | ||
return discoveryService, nil | ||
} | ||
|
||
func parseCommand(name string, args json.RawMessage) (*exec.Cmd, error) { | ||
cmd, err := utils.ParseCommandArgs(args) | ||
if err != nil { | ||
return nil, fmt.Errorf("Could not parse `%s`: %s", name, err) | ||
} | ||
return cmd, nil | ||
} | ||
|
||
// InitLogging ... | ||
func (cfg *Config) InitLogging() error { | ||
if cfg.LogConfig != nil { | ||
err := cfg.LogConfig.init() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return cfg.LogConfig.init() | ||
} | ||
return nil | ||
} | ||
|
||
if cfg.StopTimeout == 0 { | ||
cfg.StopTimeout = defaultStopTimeout | ||
// ParsePreStart ... | ||
func (cfg *Config) ParsePreStart() (*exec.Cmd, error) { | ||
// onStart has been deprecated for preStart. Remove in 2.0 | ||
if cfg.PreStart != nil && cfg.OnStart != nil { | ||
log.Warnf("The onStart option has been deprecated in favor of preStart. ContainerPilot will use only the preStart option provided") | ||
} | ||
|
||
if backends, err := backends.NewBackends(cfg.BackendsConfig, | ||
discoveryService); err != nil { | ||
// alias the onStart behavior to preStart | ||
if cfg.PreStart == nil && cfg.OnStart != nil { | ||
log.Warnf("The onStart option has been deprecated in favor of preStart. ContainerPilot will use the onStart option as a preStart") | ||
cfg.PreStart = cfg.OnStart | ||
} | ||
return parseCommand("preStart", cfg.PreStart) | ||
} | ||
|
||
// ParsePreStop ... | ||
func (cfg *Config) ParsePreStop() (*exec.Cmd, error) { | ||
return parseCommand("preStop", cfg.PreStop) | ||
} | ||
|
||
// ParsePostStop ... | ||
func (cfg *Config) ParsePostStop() (*exec.Cmd, error) { | ||
return parseCommand("postStop", cfg.PostStop) | ||
} | ||
|
||
// ParseBackends ... | ||
func (cfg *Config) ParseBackends(discoveryService discovery.DiscoveryService) ([]*backends.Backend, error) { | ||
backends, err := backends.NewBackends(cfg.BackendsConfig, discoveryService) | ||
if err != nil { | ||
return nil, err | ||
} else { | ||
cfg.Backends = backends | ||
} | ||
return backends, nil | ||
} | ||
|
||
if services, err := services.NewServices(cfg.ServicesConfig, | ||
discoveryService); err != nil { | ||
// ParseServices ... | ||
func (cfg *Config) ParseServices(discoveryService discovery.DiscoveryService) ([]*services.Service, error) { | ||
services, err := services.NewServices(cfg.ServicesConfig, discoveryService) | ||
if err != nil { | ||
return nil, err | ||
} else { | ||
cfg.Services = services | ||
} | ||
return services, nil | ||
} | ||
|
||
if cfg.TelemetryConfig != nil { | ||
if t, err := telemetry.NewTelemetry(cfg.TelemetryConfig); err != nil { | ||
return nil, err | ||
} else { | ||
cfg.Telemetry = t | ||
// create a new service for Telemetry | ||
if telemetryService, err := services.NewService( | ||
t.ServiceName, | ||
t.Poll, | ||
t.Port, | ||
t.TTL, | ||
t.Interfaces, | ||
t.Tags, | ||
discoveryService); err != nil { | ||
return nil, err | ||
} else { | ||
cfg.Services = append(cfg.Services, telemetryService) | ||
} | ||
} | ||
// ParseStopTimeout ... | ||
func (cfg *Config) ParseStopTimeout() (int, error) { | ||
if cfg.StopTimeout == 0 { | ||
return defaultStopTimeout, nil | ||
} | ||
return cfg.StopTimeout, nil | ||
} | ||
|
||
if cfg.TasksConfig != nil { | ||
tasks, err := tasks.NewTasks(cfg.TasksConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
cfg.Tasks = tasks | ||
// ParseTelemetry ... | ||
func (cfg *Config) ParseTelemetry() (*telemetry.Telemetry, error) { | ||
if cfg.TelemetryConfig == nil { | ||
return nil, nil | ||
} | ||
t, err := telemetry.NewTelemetry(cfg.TelemetryConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return t, nil | ||
} | ||
|
||
configLock.Lock() | ||
globalConfig = cfg | ||
configLock.Unlock() | ||
// CreateTelemetryService ... | ||
func CreateTelemetryService(t *telemetry.Telemetry, discoveryService discovery.DiscoveryService) (*services.Service, error) { | ||
// create a new service for Telemetry | ||
svc, err := services.NewService( | ||
t.ServiceName, | ||
t.Poll, | ||
t.Port, | ||
t.TTL, | ||
t.Interfaces, | ||
t.Tags, | ||
discoveryService) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return svc, nil | ||
} | ||
|
||
return cfg, nil | ||
// ParseTasks ... | ||
func (cfg *Config) ParseTasks() ([]*tasks.Task, error) { | ||
if cfg.TasksConfig == nil { | ||
return nil, nil | ||
} | ||
tasks, err := tasks.NewTasks(cfg.TasksConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return tasks, nil | ||
} | ||
|
||
func parseConfig(configFlag string) (*Config, error) { | ||
// ParseConfig ... | ||
func ParseConfig(configFlag string) (*Config, error) { | ||
if configFlag == "" { | ||
return nil, errors.New("-config flag is required") | ||
} | ||
|
@@ -245,15 +205,16 @@ func parseConfig(configFlag string) (*Config, error) { | |
return nil, fmt.Errorf( | ||
"Could not apply template to config: %s", err) | ||
} | ||
cfg, err := unmarshalConfig(template) | ||
cfg, err := UnmarshalConfig(template) | ||
if cfg != nil { | ||
// store so we can reload | ||
cfg.ConfigFlag = configFlag | ||
} | ||
return cfg, err | ||
} | ||
|
||
func unmarshalConfig(data []byte) (*Config, error) { | ||
// UnmarshalConfig unmarshalls the raw config bytes into a Config struct | ||
func UnmarshalConfig(data []byte) (*Config, error) { | ||
config := &Config{} | ||
if err := json.Unmarshal(data, &config); err != nil { | ||
syntax, ok := err.(*json.SyntaxError) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you're WIP on this, but this whole comment will need reworking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops!