func newPemFromBytes(conf Config, pem []byte) (*KeyStore, error) { ca, err := buildWholeCAsBy(conf, pem) if err != nil { return nil, errors.New("Could not build ca for keyStore config.").CausedBy(err) } certs, err := loadCertificatesFrom(pem) if err != nil { return nil, errors.New("Could not load certs from PEM.").CausedBy(err) } if len(certs) <= 0 { return nil, errors.New("The provieded PEM does not contain a certificate.") } privateKey, err := loadPrivateKeyFrom(pem) if err != nil { return nil, err } return &KeyStore{ enabled: true, config: conf, pem: pem, ca: ca, cert: certs[0], privateKey: privateKey, }, nil }
// Run runs this execution. // This method is a blocking method and could only be executed at this instance once. func (instance *Execution) Run() (values.ExitCode, error) { err := instance.handleBeforeRun() if err != nil { return values.ExitCode(1), err } exitCode, err := instance.preExecution() if err != nil || exitCode != 0 { return exitCode, err } instance.logger.Log(logger.Debug, "Start service '%s' with command: %s", instance.Name(), instance.commandLineOf(instance.cmd)) exitCode, lastState, err := instance.runBare() if lastState == Killed { err = StoppedOrKilledError{error: errors.New("Process was killed.")} instance.logger.Log(logger.Debug, "Service '%s' ended after kill: %d", instance.Name(), exitCode) } else if lastState == Stopped { err = StoppedOrKilledError{error: errors.New("Process was stopped.")} instance.logger.Log(logger.Debug, "Service '%s' ended successful after stop: %d", instance.Name(), exitCode) } else if err != nil { instance.logger.Log(logger.Fatal, err) } else if instance.service.config.SuccessExitCodes.Contains(exitCode) { instance.logger.Log(logger.Debug, "Service '%s' ended successful: %d", instance.Name(), exitCode) } else { instance.logger.Log(logger.Error, "Service '%s' ended with unexpected code: %d", instance.Name(), exitCode) err = errors.New("Unexpected error code %d generated by service '%s'", exitCode, instance.Name()) } instance.postExecution() return exitCode, err }
// Lock locks the current thread to this mutex. // If this is not possible an error will be returned. // This method is blocking until locking is possible. func (instance *Mutex) Lock() error { var err error defer func() { p := recover() if p != nil { if s, ok := p.(string); ok { if s != "send on closed channel" { panic(p) } else { err = errors.New("Lock interrupted.") } } else { panic(p) } } }() select { case instance.channel <- true: return nil default: if err != nil { return err } return errors.New("Lock interrupted.") } }
func enrichWithElementAndItsChildren(pd *PickedDefinitions, elementID IDType) error { if pd.IDToDefinition[elementID.String()] != nil { return nil } element, err := pd.GetSourceElementBy(elementID) if err != nil { return err } if element == nil { return nil } pd.IDToDefinition[elementID.String()] = element if valueType, ok := element.(WithValueType); ok { for _, idType := range ExtractAllIDTypesFrom(valueType.ValueType()) { err := enrichWithElementAndItsChildren(pd, idType) if err != nil { return errors.New("Could not extract valueType '%v' of type '%s'.", idType, elementID).CausedBy(err) } } } if children, ok := element.(WithChildren); ok { for _, child := range children.Children() { err := enrichWithElementAndItsChildren(pd, child.ID()) if err != nil { return errors.New("Could not extract child '%v' of type '%s'.", child.ID(), elementID).CausedBy(err) } } } return nil }
func (instance *Execution) sendSignal(s values.Signal) error { if instance.isKillSignal(s) { if !instance.setStateTo(Killed) { if s == values.KILL || instance.service.config.StopSignal == s { return nil } else { return errors.New("Service '%v' is not running.", instance) } } } else if instance.isStopSignal(s) { if !instance.setStateTo(Stopped) { if s == values.KILL || instance.service.config.StopSignal == s { return nil } else { return errors.New("Service '%v' is not running.", instance) } } } cmd := (*instance).cmd process := cmd.Process ps := cmd.ProcessState if process == nil || ps != nil { instance.setStateTo(Down) return nil } if s != values.NOOP { return sendSignalToService((*instance).service, process, s, instance.service.config.StopSignalTarget) } return nil }
// Set the given string to the current object from a string. // Returns an error object if there are any problems while transforming the string. func (instance *Level) Set(value string) error { if valueAsInt, err := strconv.Atoi(value); err == nil { for _, candidate := range AllLevels { if int(candidate) == valueAsInt { (*instance) = candidate return nil } } return errors.New("Illegal level: " + value) } lowerValue := strings.ToLower(value) switch lowerValue { case "warn": *instance = Warning return nil case "err": *instance = Error return nil } for _, candidate := range AllLevels { if candidate.String() == lowerValue { (*instance) = candidate return nil } } return errors.New("Illegal level: " + value) }
// GeneratePem generates a new PEM with the config of the current KeyStore instance and returns it. // This PEM will be stored in the KeyStore instance. func (instance KeyStore) GeneratePem(name string) ([]byte, *x509.Certificate, error) { if !instance.enabled { return []byte{}, nil, errors.New("KeyStore is not enabled.") } privateKey, privateKeyBytes, publicKey, err := generatePrivateKey(instance.Config()) if err != nil { return []byte{}, nil, errors.New("Could not generate pem for '%v'.", name).CausedBy(err) } certificateDerBytes, err := instance.generateClientCertificate(name, publicKey, privateKey) if err != nil { return []byte{}, nil, err } cert, err := x509.ParseCertificate(certificateDerBytes) if err != nil || cert == nil { return []byte{}, nil, errors.New("Wow! Could not parse right now created certificate for '%v'?", name).CausedBy(err) } pemBytes := []byte{} pemBytes = append(pemBytes, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificateDerBytes})...) pemBytes = append(pemBytes, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: instance.cert.Raw})...) pemBytes = append(pemBytes, pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privateKeyBytes})...) return pemBytes, cert, nil }
func (instance *parsedPackage) fileFor(object posEnabled) (*ast.File, error) { tokenFile := instance.fileSet.File(object.Pos()) if tokenFile == nil { return nil, errors.New("Package %v does not contain object %v.", instance.pkg.Path(), object) } if file, ok := instance.sourceFiles[tokenFile.Name()]; ok { return file, nil } return nil, errors.New("Package %v does not contain file %v.", instance.pkg.Path(), tokenFile.Name()) }
// WriteToYamlFile writes the config of the current instance to the given yaml file. func (instance Config) WriteToYamlFile(fileName values.String) error { content, err := yaml.Marshal(instance) if err != nil { return errors.New("Could not write config to '%v'.", fileName).CausedBy(err) } if err := ioutil.WriteFile(fileName.String(), content, 0744); err != nil { return errors.New("Could not write marshalled config to '%v'.", fileName).CausedBy(err) } return nil }
func (instance Config) validateRequireStringOrNotValue(value values.String, fieldName string, isAllowedMethod func() bool) error { if isAllowedMethod() { if value.IsEmpty() { return errors.New("There is no %s set for type %v.", fieldName, instance.Type) } } else { if !value.IsEmpty() { return errors.New("There is no %s allowed for type %v.", fieldName, instance.Type) } } return nil }
// LoadCertificateFromFile loads a certificate from the given filename and returns it. func LoadCertificateFromFile(filename string) (*x509.Certificate, error) { fileContent, err := ioutil.ReadFile(filename) if err != nil { return nil, errors.New("Could not read certificate from %v.", filename).CausedBy(err) } certificates, err := loadCertificatesFrom(fileContent) if err != nil { return nil, errors.New("Could not read certificate from %v.", filename).CausedBy(err) } if len(certificates) <= 0 { return nil, errors.New("File %v does not contain a valid certificate.", filename) } return certificates[0], nil }
// LoadFromYamlFile loads the caretakerd config from the given yaml file. func LoadFromYamlFile(fileName values.String) (Config, error) { result := NewConfig() content, err := ioutil.ReadFile(fileName.String()) if err != nil { if os.IsNotExist(err) { return Config{}, ConfigDoesNotExistError{fileName: fileName.String()} } return Config{}, errors.New("Could not read config from '%v'.", fileName).CausedBy(err) } if err := yaml.Unmarshal(content, &result); err != nil { return Config{}, errors.New("Could not unmarshal config from '%v'.", fileName).CausedBy(err) } return result, nil }
// ValidateMaster validates whether there is exactly one service defined as master. Returns an error object if there are more services defined as masters. func (instance Configs) ValidateMaster() error { masters := []string{} for name, service := range instance { if service.Type == Master { masters = append(masters, name) } } if len(masters) == 0 { return errors.New("There is no service of type master defined.") } if len(masters) > 1 { return errors.New("There are more then 0 service of type master defined: %s", strings.Join(masters, ", ")) } return nil }
// NewCaretakerd creates a new Caretakerd instance from the given config func NewCaretakerd(conf Config, syncGroup *usync.Group) (*Caretakerd, error) { err := conf.Validate() if err != nil { return nil, err } log, err := logger.NewLogger(conf.Logger, "caretakerd", syncGroup) if err != nil { return nil, errors.New("Could not create logger for caretakerd.").CausedBy(err) } ks, err := keyStore.NewKeyStore(bool(conf.RPC.Enabled), conf.KeyStore) if err != nil { return nil, err } ctl, err := control.NewControl(conf.Control, ks) if err != nil { return nil, err } services, err := service.NewServices(conf.Services, syncGroup, ks) if err != nil { return nil, err } result := Caretakerd{ open: true, config: conf, logger: log, control: ctl, keyStore: ks, services: services, lock: new(sync.Mutex), syncGroup: syncGroup, signalChannel: nil, } runtime.SetFinalizer(&result, finalize) return &result, nil }
// Run starts the caretakerd execution, every service and required resource. // This is a blocking method. func (instance *Execution) Run() (values.ExitCode, error) { autoStartableServices := instance.executable.Services().GetAllAutoStartable() // Start all non-master services first to start the master properly. for _, target := range autoStartableServices { if target.Config().Type != service.Master { instance.startAndLogProblemsIfNeeded(target) } } // Now start the master. masterStarted := false for _, target := range autoStartableServices { if target.Config().Type == service.Master { err := instance.Start(target) if err != nil { (*instance).masterError = err } masterStarted = true } } if !masterStarted { (*instance).masterError = errors.New("No master was started. There are no master configured?") } if (*instance).masterError != nil { instance.stopOthers() } instance.wg.Wait() exitCode := values.ExitCode(-1) if (*instance).masterExitCode != nil { exitCode = *(*instance).masterExitCode } return exitCode, (*instance).masterError }
func newFromEnvironment(conf Config) (*KeyStore, error) { pem := os.Getenv("CTD_PEM") if len(strings.TrimSpace(pem)) <= 0 { return nil, errors.New("There is an %v keyStore confgiured but the CTD_PEM environment varaible is empty.", conf.Type) } return newPemFromBytes(conf, []byte(pem)) }
func newFomFile(conf Config) (*KeyStore, error) { pem, err := ioutil.ReadFile(conf.PemFile.String()) if err != nil { return nil, errors.New("Could not read pem from '%v'.", conf.PemFile).CausedBy(err) } return newPemFromBytes(conf, pem) }
func (instance Configs) validateService(service Config, name string) error { err := service.Validate() if err != nil { return errors.New("Config of '%v' service is not valid.", name).CausedBy(err) } return nil }
func (instance *Config) handleServiceMapEnv(full string, serviceName string, key string, subKey string, value string) error { targetKey := strings.ToUpper(key) if handler, ok := serviceSubEnvKeyToFunction[targetKey]; ok { return instance.Services.ConfigureSub(serviceName, subKey, value, handler) } return errors.New("Unknown configuration type '%s' for service '%s'.", key, serviceName) }
func checkForIsCa(name string, sec *keyStore.KeyStore) error { if !sec.IsCA() { return errors.New("It is not possible to generate a new certificate for service '%v' with a caretakerd certificate that is not a CA. "+ "Use trusted access for service '%v', configure caretakerd to generate its own certificate or provide a CA enabled certificate for caretakerd.", name, name) } return nil }
// SetFromInt tries to set the given int value to this instance. // Returns an error object if there are any problems while transforming the plain int. func (i *NonNegativeInteger) SetFromInt(value int) error { if value < 0 { return errors.New("This intger value should not be negative. But got: %v", value) } (*i) = NonNegativeInteger(value) return nil }
// Set sets the given string to current object from a string. // Returns an error object if there are any problems while transforming the string. func (instance *Integer) Set(value string) error { valueAsInt, err := strconv.Atoi(value) if err != nil { return errors.New("Illegal integer value: " + value) } return instance.SetFromInt(valueAsInt) }
func loadPrivateKeyFrom(p []byte) (interface{}, error) { if len(p) > 0 { rp := p block := new(pem.Block) for block != nil && len(rp) > 0 { block, rp = pem.Decode(rp) if block != nil && block.Type == "RSA PRIVATE KEY" { privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return nil, errors.New("Could not parse privateKey.").CausedBy(err) } return privateKey, nil } } } return nil, errors.New("The PEM does not contain a valid private key.") }
// Set sets the given string to current object from a string. // Returns an error object if there are any problems while transforming the string. func (i *ExitCode) Set(value string) error { valueAsInt, err := strconv.Atoi(value) if err != nil { return errors.New("Illegal exit Code value: " + value) } (*i) = ExitCode(valueAsInt) return nil }
func newGenerated(conf Config) (*KeyStore, error) { pem, cert, privateKey, err := generatePem(conf) if err != nil { return nil, errors.New("Could not generate pem for keyStore config.").CausedBy(err) } ca, err := buildWholeCAsBy(conf, pem) if err != nil { return nil, errors.New("Could not build CA bundle for keyStore config.").CausedBy(err) } return &KeyStore{ enabled: true, config: conf, pem: pem, ca: ca, cert: cert, privateKey: privateKey, }, nil }
// ConfigureSub executes a configuring action for a service with the given name. func (s *Configs) ConfigureSub(serviceName string, key string, value string, with func(conf *Config, key string, value string) error) error { conf, ok := (*s)[serviceName] if !ok { return errors.New("There does no service with name '%s' exist.", serviceName) } err := with(&conf, key, value) (*s)[serviceName] = conf return err }
func generateFileForPem(conf Config, pem []byte) (string, error) { permission := conf.PemFilePermission.ThisOrDefault().AsFileMode() f, err := os.OpenFile(conf.PemFile.String(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, permission) if err != nil { return "", errors.New("Could not create pemFile '%s'.", conf.PemFile).CausedBy(err) } defer f.Close() if !conf.PemFileUser.IsEmpty() { _, lerr := user.Lookup(conf.PemFileUser.String()) if lerr != nil { return "", errors.New("Could not set ownership of pemFile '%s' to '%s'.", conf.PemFile, conf.PemFileUser).CausedBy(err) } //f.Chown(kfu.Uid, kfu.Gid) TODO! } f.Write(pem) f.Sync() return conf.PemFile.String(), nil }
// CheckedString is like String but also returns an optional error if there are any // validation errors. func (instance Protocol) CheckedString() (string, error) { switch instance { case TCP: return "tcp", nil case Unix: return "unix", nil } return "", errors.New("Illegal protocol: %d", instance) }
// CheckedString is like String but also returns an optional error if there are any // validation errors. func (instance SignalTarget) CheckedString() (string, error) { switch instance { case Process: return "process", nil case ProcessGroup: return "processGroup", nil } return "", errors.New("Illegal signal target: %d", instance) }
// Set sets the given string to current object from a string. // Returns an error object if there are any problems while transforming the string. func (instance *SocketAddress) Set(value string) error { match := uriPattern.FindStringSubmatch(value) if match != nil && len(match) == 3 { var protocol Protocol err := protocol.Set(match[1]) if err != nil { return err } switch protocol { case TCP: return instance.SetTCP(match[2]) case Unix: return instance.SetUnix(match[2]) } return errors.New("Unknown protocol %v in address '%v'.", protocol, value) } return errors.New("Illegal socket address: %s", value) }