func opClientServiceDestroy(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceDestroy("non-existent") if params.IsCodeNotFound(err) { err = nil } return func() {}, err }
func opClientServiceSetCharm(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceSetCharm("nosuch", "local:quantal/wordpress", false) if params.IsCodeNotFound(err) { err = nil } return func() {}, err }
func opClientAddServiceUnits(c *gc.C, st *api.State, mst *state.State) (func(), error) { _, err := st.Client().AddServiceUnits("nosuch", 1, "") if params.IsCodeNotFound(err) { err = nil } return func() {}, err }
func opClientDestroyRelation(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().DestroyRelation("nosuch1", "nosuch2") if params.IsCodeNotFound(err) { err = nil } return func() {}, err }
// reconcileInstances compares the initially started watcher for machines, // units and services with the opened and closed ports of the instances and // opens and closes the appropriate ports for each instance. func (fw *Firewaller) reconcileInstances() error { for _, machined := range fw.machineds { m, err := machined.machine() if params.IsCodeNotFound(err) { if err := fw.forgetMachine(machined); err != nil { return err } continue } else if err != nil { return err } instanceId, err := m.InstanceId() if err != nil { return err } instances, err := fw.environ.Instances([]instance.Id{instanceId}) if err == environs.ErrNoInstances { return nil } else if err != nil { return err } _, machineId, err := names.ParseTag(machined.tag, names.MachineTagKind) if err != nil { return err } initialPorts, err := instances[0].Ports(machineId) if err != nil { return err } // Check which ports to open or to close. toOpen := Diff(machined.ports, initialPorts) toClose := Diff(initialPorts, machined.ports) if len(toOpen) > 0 { logger.Infof("opening instance ports %v for %q", toOpen, machined.tag) if err := instances[0].OpenPorts(machineId, toOpen); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toOpen) } if len(toClose) > 0 { logger.Infof("closing instance ports %v for %q", toClose, machined.tag) if err := instances[0].ClosePorts(machineId, toClose); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toClose) } } return nil }
// commonLoop implements the loop structure common to the client // watchers. It should be started in a separate goroutine by any // watcher that embeds commonWatcher. It kills the commonWatcher's // tomb when an error occurs. func (w *commonWatcher) commonLoop() { defer close(w.in) w.wg.Add(1) go func() { // When the watcher has been stopped, we send a Stop request // to the server, which will remove the watcher and return a // CodeStopped error to any currently outstanding call to // Next. If a call to Next happens just after the watcher has // been stopped, we'll get a CodeNotFound error; Either way // we'll return, wait for the stop request to complete, and // the watcher will die with all resources cleaned up. defer w.wg.Done() <-w.tomb.Dying() if err := w.call("Stop", nil); err != nil { logger.Errorf("error trying to stop watcher: %v", err) } }() w.wg.Add(1) go func() { // Because Next blocks until there are changes, we need to // call it in a separate goroutine, so the watcher can be // stopped normally. defer w.wg.Done() for { result := w.newResult() err := w.call("Next", &result) if err != nil { if params.IsCodeStopped(err) || params.IsCodeNotFound(err) { if w.tomb.Err() != tomb.ErrStillAlive { // The watcher has been stopped at the client end, so we're // expecting one of the above two kinds of error. // We might see the same errors if the server itself // has been shut down, in which case we leave them // untouched. err = tomb.ErrDying } } // Something went wrong, just report the error and bail out. w.tomb.Kill(err) return } select { case <-w.tomb.Dying(): return case w.in <- result: // Report back the result we just got. } } }() w.wg.Wait() }
func opClientServiceUpdate(c *gc.C, st *api.State, mst *state.State) (func(), error) { args := params.ServiceUpdate{ ServiceName: "no-such-charm", CharmUrl: "cs:quantal/wordpress-42", ForceCharmUrl: true, SettingsStrings: map[string]string{"blog-title": "foo"}, SettingsYAML: `"wordpress": {"blog-title": "foo"}`, } err := st.Client().ServiceUpdate(args) if params.IsCodeNotFound(err) { err = nil } return func() {}, err }
// unitsChanged responds to changes to the assigned units. func (fw *Firewaller) unitsChanged(change *unitsChange) error { changed := []*unitData{} for _, name := range change.units { unit, err := fw.st.Unit(names.UnitTag(name)) if err != nil && !params.IsCodeNotFound(err) { return err } var machineTag string if unit != nil { machineTag, err = unit.AssignedMachine() if params.IsCodeNotFound(err) { continue } else if err != nil && !params.IsCodeNotAssigned(err) { return err } } if unitd, known := fw.unitds[name]; known { knownMachineTag := fw.unitds[name].machined.tag if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag { fw.forgetUnit(unitd) changed = append(changed, unitd) logger.Debugf("stopped watching unit %s", name) } } else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil { err = fw.startUnit(unit, machineTag) if err != nil { return err } changed = append(changed, fw.unitds[name]) logger.Debugf("started watching unit %s", name) } } if err := fw.flushUnits(changed); err != nil { return errgo.Annotate(err, "cannot change firewall ports") } return nil }
// flushInstancePorts opens and closes ports global on the machine. func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []instance.Port) error { // If there's nothing to do, do nothing. // This is important because when a machine is first created, // it will have no instance id but also no open ports - // InstanceId will fail but we don't care. if len(toOpen) == 0 && len(toClose) == 0 { return nil } m, err := machined.machine() if params.IsCodeNotFound(err) { return nil } if err != nil { return err } _, machineId, err := names.ParseTag(machined.tag, names.MachineTagKind) if err != nil { return err } instanceId, err := m.InstanceId() if err != nil { return err } instances, err := fw.environ.Instances([]instance.Id{instanceId}) if err != nil { return err } // Open and close the ports. if len(toOpen) > 0 { if err := instances[0].OpenPorts(machineId, toOpen); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toOpen) logger.Infof("opened ports %v on %q", toOpen, machined.tag) } if len(toClose) > 0 { if err := instances[0].ClosePorts(machineId, toClose); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toClose) logger.Infof("closed ports %v on %q", toClose, machined.tag) } return nil }
// initVersions collects state relevant to an upgrade decision. The returned // agent and client versions, and the list of currently available tools, will // always be accurate; the chosen version, and the flag indicating development // mode, may remain blank until uploadTools or validate is called. func (c *UpgradeJujuCommand) initVersions(client *api.Client, cfg *config.Config) (*upgradeContext, error) { agent, ok := cfg.AgentVersion() if !ok { // Can't happen. In theory. return nil, fmt.Errorf("incomplete environment configuration") } if c.Version == agent { return nil, errUpToDate } clientVersion := version.Current.Number findResult, err := client.FindTools(clientVersion.Major, -1, "", "") var availableTools coretools.List if params.IsCodeNotImplemented(err) { availableTools, err = findTools1dot17(cfg) } else { availableTools = findResult.List } if err != nil { return nil, err } err = findResult.Error if findResult.Error != nil { if !params.IsCodeNotFound(err) { return nil, err } if !c.UploadTools { // No tools found and we shouldn't upload any, so if we are not asking for a // major upgrade, pretend there is no more recent version available. if c.Version == version.Zero && agent.Major == clientVersion.Major { return nil, errUpToDate } return nil, err } } return &upgradeContext{ agent: agent, client: clientVersion, chosen: c.Version, tools: availableTools, apiClient: client, config: cfg, }, nil }
// machineLifeChanged starts watching new machines when the firewaller // is starting, or when new machines come to life, and stops watching // machines that are dying. func (fw *Firewaller) machineLifeChanged(tag string) error { m, err := fw.st.Machine(tag) found := !params.IsCodeNotFound(err) if found && err != nil { return err } dead := !found || m.Life() == params.Dead machined, known := fw.machineds[tag] if known && dead { return fw.forgetMachine(machined) } if !known && !dead { err = fw.startMachine(tag) if err != nil { return err } logger.Debugf("started watching %q", tag) } return nil }
// watchLoop watches the service's exposed flag for changes. func (sd *serviceData) watchLoop(exposed bool) { defer sd.tomb.Done() w, err := sd.service.Watch() if err != nil { sd.fw.tomb.Kill(err) return } defer watcher.Stop(w, &sd.tomb) for { select { case <-sd.tomb.Dying(): return case _, ok := <-w.Changes(): if !ok { sd.fw.tomb.Kill(watcher.MustErr(w)) return } if err := sd.service.Refresh(); err != nil { if !params.IsCodeNotFound(err) { sd.fw.tomb.Kill(err) } return } change, err := sd.service.IsExposed() if err != nil { sd.fw.tomb.Kill(err) return } if change == exposed { continue } exposed = change select { case sd.fw.exposedChange <- &exposedChange{sd, change}: case <-sd.tomb.Dying(): return } } } }
// watchLoop watches the unit for port changes. func (ud *unitData) watchLoop(latestPorts []instance.Port) { defer ud.tomb.Done() w, err := ud.unit.Watch() if err != nil { ud.fw.tomb.Kill(err) return } defer watcher.Stop(w, &ud.tomb) for { select { case <-ud.tomb.Dying(): return case _, ok := <-w.Changes(): if !ok { ud.fw.tomb.Kill(watcher.MustErr(w)) return } if err := ud.unit.Refresh(); err != nil { if !params.IsCodeNotFound(err) { ud.fw.tomb.Kill(err) } return } change, err := ud.unit.OpenedPorts() if err != nil { ud.fw.tomb.Kill(err) return } if samePorts(change, latestPorts) { continue } latestPorts = append(latestPorts[:0], change...) select { case ud.fw.portsChange <- &portsChange{ud, change}: case <-ud.tomb.Dying(): return } } } }
// watchLoop watches the machine for units added or removed. func (md *machineData) watchLoop(unitw apiwatcher.StringsWatcher) { defer md.tomb.Done() defer watcher.Stop(unitw, &md.tomb) for { select { case <-md.tomb.Dying(): return case change, ok := <-unitw.Changes(): if !ok { _, err := md.machine() if !params.IsCodeNotFound(err) { md.fw.tomb.Kill(watcher.MustErr(unitw)) } return } select { case md.fw.unitsChange <- &unitsChange{md, change}: case <-md.tomb.Dying(): return } } } }
// startMachine creates a new data value for tracking details of the // machine and starts watching the machine for units added or removed. func (fw *Firewaller) startMachine(tag string) error { machined := &machineData{ fw: fw, tag: tag, unitds: make(map[string]*unitData), ports: make([]instance.Port, 0), } m, err := machined.machine() if params.IsCodeNotFound(err) { return nil } else if err != nil { return errgo.Annotate(err, "cannot watch machine units") } unitw, err := m.WatchUnits() if err != nil { return err } select { case <-fw.tomb.Dying(): stop("units watcher", unitw) return tomb.ErrDying case change, ok := <-unitw.Changes(): if !ok { stop("units watcher", unitw) return watcher.MustErr(unitw) } fw.machineds[tag] = machined err = fw.unitsChanged(&unitsChange{machined, change}) if err != nil { stop("units watcher", unitw) delete(fw.machineds, tag) return errgo.Annotatef(err, "cannot respond to units changes for %q", tag) } } go machined.watchLoop(unitw) return nil }