// changeInterfaces controls the interfaces system. // Plugs can be connected to and disconnected from slots. // When enableInternalInterfaceActions is true plugs and slots can also be // explicitly added and removed. func changeInterfaces(c *Command, r *http.Request) Response { var a interfaceAction decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&a); err != nil { return BadRequest("cannot decode request body into an interface action: %v", err) } if a.Action == "" { return BadRequest("interface action not specified") } if !c.d.enableInternalInterfaceActions && a.Action != "connect" && a.Action != "disconnect" { return BadRequest("internal interface actions are disabled") } if len(a.Plugs) > 1 || len(a.Slots) > 1 { return NotImplemented("many-to-many operations are not implemented") } if a.Action != "connect" && a.Action != "disconnect" { return BadRequest("unsupported interface action: %q", a.Action) } if len(a.Plugs) == 0 || len(a.Slots) == 0 { return BadRequest("at least one plug and slot is required") } var change *state.Change var taskset *state.TaskSet var err error state := c.d.overlord.State() state.Lock() defer state.Unlock() switch a.Action { case "connect": summary := fmt.Sprintf("Connect %s:%s to %s:%s", a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) change = state.NewChange("connect-snap", summary) taskset, err = ifacestate.Connect(state, a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) case "disconnect": summary := fmt.Sprintf("Disconnect %s:%s from %s:%s", a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) change = state.NewChange("disconnect-snap", summary) taskset, err = ifacestate.Disconnect(state, a.Plugs[0].Snap, a.Plugs[0].Name, a.Slots[0].Snap, a.Slots[0].Name) } if err == nil { change.AddAll(taskset) } if err != nil { return BadRequest("%v", err) } state.EnsureBefore(0) return AsyncResponse(nil, &Meta{Change: change.ID()}) }
func ensureUbuntuCore(chg *state.Change, userID int) error { var ss snapstate.SnapState ubuntuCore := "ubuntu-core" err := snapstateGet(chg.State(), ubuntuCore, &ss) if err != state.ErrNoState { return err } // FIXME: workaround because we are not fully state based yet installed, err := (&snappy.Overlord{}).Installed() snaps := snappy.FindSnapsByName(ubuntuCore, installed) if len(snaps) > 0 { return nil } return installSnap(chg, ubuntuCore, "stable", userID, 0) }
func waitChange(chg *state.Change) error { select { case <-chg.Ready(): } // TODO case <-daemon.Dying(): st := chg.State() st.Lock() defer st.Unlock() return chg.Err() }
func installSnap(chg *state.Change, name, channel string, userID int, flags snappy.InstallFlags) error { st := chg.State() ts, err := snapstateInstall(st, name, channel, userID, flags) if err != nil { return err } // ensure that each of our task runs after the existing tasks chgts := state.NewTaskSet(chg.Tasks()...) for _, t := range ts.Tasks() { t.WaitAll(chgts) } chg.AddAll(ts) return nil }
func ensureChange(c *C, r *state.TaskRunner, sb *stateBackend, chg *state.Change) { for i := 0; i < 10; i++ { sb.ensureBefore = time.Hour r.Ensure() r.Wait() chg.State().Lock() s := chg.Status() chg.State().Unlock() if s.Ready() { return } if sb.ensureBefore > 0 { break } } var statuses []string chg.State().Lock() for _, t := range chg.Tasks() { statuses = append(statuses, t.Summary()+":"+t.Status().String()) } chg.State().Unlock() c.Fatalf("Change didn't reach final state without blocking: %s", strings.Join(statuses, " ")) }
func change2changeInfo(chg *state.Change) *changeInfo { status := chg.Status() chgInfo := &changeInfo{ ID: chg.ID(), Kind: chg.Kind(), Summary: chg.Summary(), Status: status.String(), Ready: status.Ready(), SpawnTime: chg.SpawnTime(), } readyTime := chg.ReadyTime() if !readyTime.IsZero() { chgInfo.ReadyTime = &readyTime } if err := chg.Err(); err != nil { chgInfo.Err = err.Error() } tasks := chg.Tasks() taskInfos := make([]*taskInfo, len(tasks)) for j, t := range tasks { done, total := t.Progress() taskInfo := &taskInfo{ ID: t.ID(), Kind: t.Kind(), Summary: t.Summary(), Status: t.Status().String(), Log: t.Log(), Progress: taskInfoProgress{ Done: done, Total: total, }, SpawnTime: t.SpawnTime(), } readyTime := t.ReadyTime() if !readyTime.IsZero() { taskInfo.ReadyTime = &readyTime } taskInfos[j] = taskInfo } chgInfo.Tasks = taskInfos return chgInfo }