func (m *SnapManager) doPrepareSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() ss, snapst, err := snapSetupAndState(t) st.Unlock() if err != nil { return err } if ss.Revision == 0 { // sideloading // to not clash with not sideload installs // and to not have clashes between them // use incremental revisions starting at 100001 // for sideloads revision := snapst.LocalRevision if revision == 0 { revision = firstLocalRevision } else { revision++ } snapst.LocalRevision = revision ss.Revision = revision } else { if err := checkRevisionIsNew(ss.Name, snapst, ss.Revision); err != nil { return err } } st.Lock() t.Set("snap-setup", ss) snapst.Candidate = &snap.SideInfo{Revision: ss.Revision} Set(st, ss.Name, snapst) st.Unlock() return nil }
func (m *SnapManager) doDownloadSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() ss, snapst, err := snapSetupAndState(t) st.Unlock() if err != nil { return err } checker := func(info *snap.Info) error { return checkRevisionIsNew(ss.Name, snapst, info.Revision) } pb := &TaskProgressAdapter{task: t} var auther store.Authenticator if ss.UserID > 0 { st.Lock() user, err := auth.User(st, ss.UserID) st.Unlock() if err != nil { return err } auther = user.Authenticator() } storeInfo, downloadedSnapFile, err := m.backend.Download(ss.Name, ss.Channel, checker, pb, auther) if err != nil { return err } ss.SnapPath = downloadedSnapFile ss.Revision = storeInfo.Revision // update the snap setup and state for the follow up tasks st.Lock() t.Set("snap-setup", ss) snapst.Candidate = &storeInfo.SideInfo Set(st, ss.Name, snapst) st.Unlock() return nil }
func (m *InterfaceManager) doDiscardConns(task *state.Task, _ *tomb.Tomb) error { st := task.State() st.Lock() defer st.Unlock() snapSetup, err := snapstate.TaskSnapSetup(task) if err != nil { return err } snapName := snapSetup.Name var snapState snapstate.SnapState err = snapstate.Get(st, snapName, &snapState) if err != nil && err != state.ErrNoState { return err } if err == nil && len(snapState.Sequence) != 0 { return fmt.Errorf("cannot discard connections for snap %q while it is present", snapName) } conns, err := getConns(st) if err != nil { return err } removed := make(map[string]connState) for id := range conns { plugRef, slotRef, err := parseConnID(id) if err != nil { return err } if plugRef.Snap == snapName || slotRef.Snap == snapName { removed[id] = conns[id] delete(conns, id) } } task.Set("removed", removed) setConns(st, conns) return nil }
func (m *SnapManager) doLinkSnap(t *state.Task, _ *tomb.Tomb) error { st := t.State() st.Lock() defer st.Unlock() ss, snapst, err := snapSetupAndState(t) if err != nil { return err } cand := snapst.Candidate m.backend.Candidate(snapst.Candidate) snapst.Sequence = append(snapst.Sequence, snapst.Candidate) snapst.Candidate = nil snapst.Active = true oldChannel := snapst.Channel if ss.Channel != "" { snapst.Channel = ss.Channel } newInfo, err := readInfo(ss.Name, cand) if err != nil { return err } st.Unlock() err = m.backend.LinkSnap(newInfo) st.Lock() if err != nil { return err } t.Set("old-channel", oldChannel) // Do at the end so we only preserve the new state if it worked. Set(st, ss.Name, snapst) return nil }
func (m *InterfaceManager) undoDiscardConns(task *state.Task, _ *tomb.Tomb) error { st := task.State() st.Lock() defer st.Unlock() var removed map[string]connState err := task.Get("removed", &removed) if err != nil && err != state.ErrNoState { return err } conns, err := getConns(st) if err != nil { return err } for id, connState := range removed { conns[id] = connState } setConns(st, conns) task.Set("removed", nil) return nil }
func (m *InterfaceManager) doSetupProfiles(task *state.Task, _ *tomb.Tomb) error { task.State().Lock() defer task.State().Unlock() // Get snap.Info from bits handed by the snap manager. ss, err := snapstate.TaskSnapSetup(task) if err != nil { return err } snapInfo, err := snapstate.Info(task.State(), ss.Name, ss.Revision) if err != nil { return err } snap.AddImplicitSlots(snapInfo) snapName := snapInfo.Name() var snapState snapstate.SnapState if err := snapstate.Get(task.State(), snapName, &snapState); err != nil { task.Errorf("cannot get state of snap %q: %s", snapName, err) return err } // Set DevMode flag if SnapSetup.Flags indicates it should be done // but remember the old value in the task in case we undo. task.Set("old-devmode", snapState.DevMode()) if ss.DevMode() { snapState.Flags |= snapstate.DevMode } else { snapState.Flags &= ^snapstate.DevMode } snapstate.Set(task.State(), snapName, &snapState) // The snap may have been updated so perform the following operation to // ensure that we are always working on the correct state: // // - disconnect all connections to/from the given snap // - remembering the snaps that were affected by this operation // - remove the (old) snap from the interfaces repository // - add the (new) snap to the interfaces repository // - restore connections based on what is kept in the state // - if a connection cannot be restored then remove it from the state // - setup the security of all the affected snaps blacklist := m.repo.AutoConnectBlacklist(snapName) affectedSnaps, err := m.repo.DisconnectSnap(snapName) if err != nil { return err } // XXX: what about snap renames? We should remove the old name (or switch // to IDs in the interfaces repository) if err := m.repo.RemoveSnap(snapName); err != nil { return err } if err := m.repo.AddSnap(snapInfo); err != nil { if _, ok := err.(*interfaces.BadInterfacesError); ok { logger.Noticef("%s", err) } else { return err } } if err := m.reloadConnections(snapName); err != nil { return err } if err := m.autoConnect(task, snapName, blacklist); err != nil { return err } if len(affectedSnaps) == 0 { affectedSnaps = append(affectedSnaps, snapInfo) } for _, snapInfo := range affectedSnaps { if err := setupSnapSecurity(task, snapInfo, m.repo); err != nil { return state.Retry } } return nil }
func doInstall(s *state.State, curActive bool, snapName, snapPath, channel string, userID int, flags snappy.InstallFlags) (*state.TaskSet, error) { if err := checkChangeConflict(s, snapName); err != nil { return nil, err } if snapPath == "" && channel == "" { channel = "stable" } var prepare *state.Task ss := SnapSetup{ Channel: channel, UserID: userID, Flags: int(flags), } ss.Name = snapName ss.SnapPath = snapPath if snapPath != "" { prepare = s.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q"), snapPath)) } else { prepare = s.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q from channel %q"), snapName, channel)) } prepare.Set("snap-setup", ss) tasks := []*state.Task{prepare} addTask := func(t *state.Task) { t.Set("snap-setup-task", prepare.ID()) tasks = append(tasks, t) } // mount mount := s.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q"), snapName)) addTask(mount) mount.WaitFor(prepare) precopy := mount if curActive { // unlink-current-snap (will stop services for copy-data) unlink := s.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), snapName)) addTask(unlink) unlink.WaitFor(mount) precopy = unlink } // copy-data (needs stopped services by unlink) copyData := s.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), snapName)) addTask(copyData) copyData.WaitFor(precopy) // security setupSecurity := s.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q security profiles"), snapName)) addTask(setupSecurity) setupSecurity.WaitFor(copyData) // finalize (wrappers+current symlink) linkSnap := s.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q available to the system"), snapName)) addTask(linkSnap) linkSnap.WaitFor(setupSecurity) return state.NewTaskSet(tasks...), nil }