func assertUnitState(name string, js job.JobState, out io.Writer) (ret bool) { u, err := cAPI.Unit(name) if err != nil { log.Warningf("Error retrieving Unit(%s) from Registry: %v", name, err) return } if u == nil { log.Warningf("Unit %s not found", name) return } if job.JobState(u.CurrentState) != js { log.Debugf("Waiting for Unit(%s) state(%s) to be %s", name, job.JobState(u.CurrentState), js) return } ret = true msg := fmt.Sprintf("Unit %s %s", name, u.CurrentState) if u.MachineID != "" { ms := cachedMachineState(u.MachineID) if ms != nil { msg = fmt.Sprintf("%s on %s", msg, machineFullLegend(*ms, false)) } } fmt.Fprintln(out, msg) return }
func (f *HostKeyFile) GetHostKeys() (map[string][]gossh.PublicKey, error) { in, err := os.Open(f.path) if err != nil { return nil, err } defer in.Close() hostKeys := make(map[string][]gossh.PublicKey) n := 0 s := bufio.NewScanner(in) for s.Scan() { n++ line := s.Bytes() hosts, key, err := parseKnownHostsLine(line) if err != nil { log.Warningf("%v:%d - %v\n", f.path, n, err) continue } if hosts == "" { // Comment/empty line continue } // It is permissible to have several lines for the same host name(s) hostKeys[hosts] = append(hostKeys[hosts], key) } return hostKeys, nil }
func createUnit(name string, uf *unit.UnitFile) (*schema.Unit, error) { if uf == nil { return nil, fmt.Errorf("nil unit provided") } u := schema.Unit{ Name: name, Options: schema.MapUnitFileToSchemaUnitOptions(uf), } // TODO(jonboulle): this dependency on the API package is awkward, and // redundant with the check in api.unitsResource.set, but it is a // workaround to implementing the same check in the RegistryClient. It // will disappear once RegistryClient is deprecated. if err := api.ValidateName(name); err != nil { return nil, err } if err := api.ValidateOptions(u.Options); err != nil { return nil, err } j := &job.Job{Unit: *uf} if err := j.ValidateRequirements(); err != nil { log.Warningf("Unit %s: %v", name, err) } err := cAPI.CreateUnit(&u) if err != nil { return nil, fmt.Errorf("failed creating unit %s: %v", name, err) } log.Debugf("Created Unit(%s) in Registry", name) return &u, nil }
// getUnitFromObject takes a *etcd.Node containing a Unit's jobModel, and // instantiates and returns a representative *job.Unit, transitively fetching the // associated UnitFile as necessary func (r *EtcdRegistry) getUnitFromObjectNode(node *etcd.Node) (*job.Unit, error) { var err error var jm jobModel if err = unmarshal(node.Value, &jm); err != nil { return nil, err } var unit *unit.UnitFile // New-style Jobs should have a populated UnitHash, and the contents of the Unit are stored separately in the Registry if !jm.UnitHash.Empty() { unit = r.getUnitByHash(jm.UnitHash) if unit == nil { log.Warningf("No Unit found in Registry for Job(%s)", jm.Name) return nil, nil } } else { // Old-style Jobs had "Payloads" instead of Units, also stored separately in the Registry unit, err = r.getUnitFromLegacyPayload(jm.Name) if err != nil { log.Errorf("Error retrieving legacy payload for Job(%s)", jm.Name) return nil, nil } else if unit == nil { log.Warningf("No Payload found in Registry for Job(%s)", jm.Name) return nil, nil } log.Infof("Migrating legacy Payload(%s)", jm.Name) if err := r.storeOrGetUnitFile(*unit); err != nil { log.Warningf("Unable to migrate legacy Payload: %v", err) } jm.UnitHash = unit.Hash() log.Infof("Updating Job(%s) with legacy payload Hash(%s)", jm.Name, jm.UnitHash) if err := r.updateJobObjectNode(&jm, node.ModifiedIndex); err != nil { log.Warningf("Unable to update Job(%s) with legacy payload Hash(%s): %v", jm.Name, jm.UnitHash, err) } } ju := &job.Unit{ Name: jm.Name, Unit: *unit, } return ju, nil }
func lsUnitsDir(dir string) ([]string, error) { filterFunc := func(name string) bool { if !unit.RecognizedUnitType(name) { log.Warningf("Found unrecognized file in %s, ignoring", path.Join(dir, name)) return true } return false } return pkg.ListDirectory(dir, filterFunc) }
func (s *Server) Run() { log.Infof("Establishing etcd connectivity") var err error for sleep := time.Second; ; sleep = pkg.ExpBackoff(sleep, time.Minute) { if s.restartServer { _, err = s.hrt.Beat(s.mon.TTL) if err == nil { log.Infof("hrt.Beat() success") break } } else { _, err = s.hrt.Register(s.mon.TTL) if err == nil { log.Infof("hrt.Register() success") break } } log.Warningf("Server register machine failed: %v, retrying in %d sec.", err, sleep) time.Sleep(sleep) } go s.Supervise() log.Infof("Starting server components") s.stopc = make(chan struct{}) s.wg = sync.WaitGroup{} beatc := make(chan *unit.UnitStateHeartbeat) components := []func(){ func() { s.api.Available(s.stopc) }, func() { s.mach.PeriodicRefresh(machineStateRefreshInterval, s.stopc) }, func() { s.agent.Heartbeat(s.stopc) }, func() { s.aReconciler.Run(s.agent, s.stopc) }, func() { s.usGen.Run(beatc, s.stopc) }, func() { s.usPub.Run(beatc, s.stopc) }, } if s.disableEngine { log.Info("Not starting engine; disable-engine is set") } else { components = append(components, func() { s.engine.Run(s.engineReconcileInterval, s.stopc) }) } for _, f := range components { f := f s.wg.Add(1) go func() { f() s.wg.Done() }() } }
func assertUnitState(name string, js job.JobState, out io.Writer) (ret bool) { var state string u, err := cAPI.Unit(name) if err != nil { log.Warningf("Error retrieving Unit(%s) from Registry: %v", name, err) return } if u == nil { log.Warningf("Unit %s not found", name) return } // If this is a global unit, CurrentState will never be set. Instead, wait for DesiredState. if suToGlobal(*u) { state = u.DesiredState } else { state = u.CurrentState } if job.JobState(state) != js { log.Debugf("Waiting for Unit(%s) state(%s) to be %s", name, job.JobState(state), js) return } ret = true msg := fmt.Sprintf("Unit %s %s", name, u.CurrentState) if u.MachineID != "" { ms := cachedMachineState(u.MachineID) if ms != nil { msg = fmt.Sprintf("%s on %s", msg, machineFullLegend(*ms, false)) } } fmt.Fprintln(out, msg) return }
// Units enumerates all files recognized as valid systemd units in // this manager's units directory. func (m *systemdUnitManager) Units() (units []string, err error) { fis, err := ioutil.ReadDir(m.UnitsDir) if err != nil { return } for _, fi := range fis { name := fi.Name() if !unit.RecognizedUnitType(name) { log.Warningf("Found unrecognized file in %s, ignoring", path.Join(m.UnitsDir, name)) continue } units = append(units, name) } return }
// getUnitFromObject takes a *etcd.Node containing a Unit's jobModel, and // instantiates and returns a representative *job.Unit, transitively fetching the // associated UnitFile as necessary func (r *EtcdRegistry) getUnitFromObjectNode(node *etcd.Node, unitHashLookupFunc func(unit.Hash) *unit.UnitFile) (*job.Unit, error) { var err error var jm jobModel if err = unmarshal(node.Value, &jm); err != nil { return nil, err } var unit *unit.UnitFile unit = unitHashLookupFunc(jm.UnitHash) if unit == nil { log.Warningf("No Unit found in Registry for Job(%s)", jm.Name) return nil, nil } ju := &job.Unit{ Name: jm.Name, Unit: *unit, } return ju, nil }
func (a *Agent) unloadUnit(unitName string) error { a.registry.ClearUnitHeartbeat(unitName) a.cache.dropTargetState(unitName) errStop := a.um.TriggerStop(unitName) if errStop != nil { log.Warningf("TriggerStop on systemd unit %s returned: %v", unitName, errStop) } else { log.Infof("Stopped unit(%s)", unitName) } a.uGen.Unsubscribe(unitName) // unit should be unloaded and unit file should be removed, only if the unit // could be successfully stopped. Otherwise the unit could get into a state // where the unit cannot be stopped via fleet, because the unit file was // already removed. See also https://github.com/coreos/fleet/issues/1216. var errUnload error if errStop == nil { errUnload = a.um.Unload(unitName) } return errUnload }
func getHTTPClient() (client.API, error) { endpoints := strings.Split(globalFlags.Endpoint, ",") if len(endpoints) > 1 { log.Warningf("multiple endpoints provided but only the first (%s) is used", endpoints[0]) } ep, err := url.Parse(endpoints[0]) if err != nil { return nil, err } if len(ep.Scheme) == 0 { return nil, errors.New("URL scheme undefined") } tun := getTunnelFlag() tunneling := tun != "" dialUnix := ep.Scheme == "unix" || ep.Scheme == "file" tunnelFunc := net.Dial if tunneling { sshClient, err := ssh.NewSSHClient(globalFlags.SSHUserName, tun, getChecker(), true, getSSHTimeoutFlag()) if err != nil { return nil, fmt.Errorf("failed initializing SSH client: %v", err) } if dialUnix { tgt := ep.Path tunnelFunc = func(string, string) (net.Conn, error) { log.Debugf("Establishing remote fleetctl proxy to %s", tgt) cmd := fmt.Sprintf(`fleetctl fd-forward %s`, tgt) return ssh.DialCommand(sshClient, cmd) } } else { tunnelFunc = sshClient.Dial } } dialFunc := tunnelFunc if dialUnix { // This commonly happens if the user misses the leading slash after the scheme. // For example, "unix://var/run/fleet.sock" would be parsed as host "var". if len(ep.Host) > 0 { return nil, fmt.Errorf("unable to connect to host %q with scheme %q", ep.Host, ep.Scheme) } // The Path field is only used for dialing and should not be used when // building any further HTTP requests. sockPath := ep.Path ep.Path = "" // If not tunneling to the unix socket, http.Client will dial it directly. // http.Client does not natively support dialing a unix domain socket, so the // dial function must be overridden. if !tunneling { dialFunc = func(string, string) (net.Conn, error) { return net.Dial("unix", sockPath) } } // http.Client doesn't support the schemes "unix" or "file", but it // is safe to use "http" as dialFunc ignores it anyway. ep.Scheme = "http" // The Host field is not used for dialing, but will be exposed in debug logs. ep.Host = "domain-sock" } tlsConfig, err := pkg.ReadTLSConfigFiles(globalFlags.CAFile, globalFlags.CertFile, globalFlags.KeyFile) if err != nil { return nil, err } trans := pkg.LoggingHTTPTransport{ Transport: http.Transport{ Dial: dialFunc, TLSClientConfig: tlsConfig, }, } hc := http.Client{ Transport: &trans, } return client.NewHTTPClient(&hc, *ep) }